2

I am creating a web app in pure PHP and I have to interchange some status between functions, this status can be around 5 like for example: user_created, user_active, user_inactive and so on. So, what't the most efficient way to do this?

Inicially I was returning strings from my functions, but i know that strings comparison can be quite slow and more on if the name of the status get longer and longer, so I thought on creating constants with integer values, but it got me thinking... if the PHP parser still needs to read the whole constant name before comparing, is not it the same than using a string, or a variable? is there a more efficient way to do this?

sorry if it looks like a trivial question, I just try to optimize every aspect of my app

if it's not totally clear what i am going to do is really simple:

function getUserStatus() {
    // return some value
}

and then use it as follows

$userStatus = getUserStatus();
switch ($userStatus) {
    // all possible cases
}
Valerio Bozz
  • 1,176
  • 16
  • 32
  • 1
    If you're concerned about performance, you could try profiling both approaches and see which one performs better. See [Simplest way to profile a PHP script](https://stackoverflow.com/questions/21133/simplest-way-to-profile-a-php-script) – Phil Aug 22 '18 at 04:21
  • Thank you @Phil , I will take a look and get back to post if anything interesting comes out of this –  Aug 22 '18 at 04:44
  • 1
    You've got some good answers below that answer your actual question, so I'll cover something else. " I just try to optimize every aspect of my app". Don't. Don't address this sort of thing before you know you have to. Google "premature optimisation" and familiarise yerself with the topic. In this case the better consideration is code maintainability and clarity, and as others have observed: using constants in this case is better for that. THAT is the reason you should be using constants. Not saving the odd microsecond here and there. – Adam Cameron Aug 22 '18 at 06:23

3 Answers3

3

I'm going to address several aspects of your question, to hopefully give you a broader understanding.

For your actual question, the answer is that the runtime stage and the parsing stage of your application are separate concepts. PHP does have to parse your source code, but that source code is then converted into another format known as Opcode. Opcode is a lower level, more efficient representation of your code. The runtime also caches this representation, which means it doesn't have to parse your source code on every invocation.

When you compare two strings, the time it takes is dependent on the length of the shortest string (generally speaking). Integers, however, can generally be compared much more efficiently, and that is what performance benefit you are trying to achieve by using integer-based constants. The lookup of a constant's value does not necessarily require traversing the name of the constant as a string, because its representation in opcode is not, necessarily, the same as it is in your source code. However, as is pointed out in another answer, it seems that PHP is actually optimized for string comparisons and they actually out-perform constant integer comparisons by a small degree!

That doesn't, necessarily, mean you shouldn't switch to constants, anyway, however. The real benefit of using constants vs string literals for these types of comparisons is that the runtime is able to help you out when you, say, make a typo. If your codebase is littered with string comparisons of the type $var == "some special value", the parser is more than happy to let you introduce a typo in "some special value" in obscure areas of your code. If you use constants, however, a comparison such as $var == MY_SPECIAL_CONSTANT will warn you if you mistype the name of the constant. It also allows you to define the concrete value in a single place (the constant definition) such that if you ever need to change it, it's a one-line change instead of a massive find-replace effort.

A lot of PHP code tends to be "string based programming" and these types of codebases can be brittle and easily susceptible to typos. Constants can really help here and this where their value really lies, rather than in performance.

Jason McClellan
  • 2,931
  • 3
  • 23
  • 32
  • String comparison seems to be faster than Integer based constant (Also to my surprise) so stating "and that is what performance benefit you are trying to achieve by using integer-based constants" seems to be invalid – Sander Visser Aug 22 '18 at 04:42
  • That is very surprising, indeed. This is the type of thing that could easily change between PHP versions, however - we're talking about microseconds. It's certainly the *perceived* benefit he's trying to achieve, even if it's currently a seemingly flawed approach. – Jason McClellan Aug 22 '18 at 04:45
  • 1
    Added the explanation to my own answer, opcache has a setting for this `opcache.interned_strings_buffer` – Sander Visser Aug 22 '18 at 04:52
  • 1
    Great find! I edited my answer to reflect your findings, as well. I suppose it makes sense that a language that is often used for string based programming would optimize heavily for string comparisons. I'm pleasantly surprised they were able to do it so well. Either that or they've got a lot of work to do with constant integer comparisons! – Jason McClellan Aug 22 '18 at 05:03
1

You can test it with something like below.

Note that PHP Optimizes a lot, and stores compiled byte code in its cache. http://php.net/manual/en/intro.opcache.php

The results

string 0.38328790664673 constant 0.50211310386658

string 0.38391804695129 constant 0.51568698883057

What surprises me is that String seems to be faster.

I noted the following setting in the opcache config:

opcache.interned_strings_buffer integer
The amount of memory used to store interned strings, in megabytes. 
This configuration directive is ignored in PHP < 5.3.0.

A pretty neat setting with like 0 documentation. PHP uses a technique called string interning to improve performance so, for example, if you have the string "foobar" 1000 times in your code, internally PHP will store 1 immutable variable for this string and just use a pointer to it for the other 999 times you use it. Cool. This setting takes it to the next level. instead of having a pool of these immutable string for each SINGLE php-fpm process, this setting shares it across ALL of your php-fpm processes. It saves memory and improves performance, especially in big applications.

So stating that string comparison is slower than constant comparison is a wrong assumption in PHP.

BUT: You can break this optimalization example:

$state = "State";
switch($string) {
    case "Offline" . $state:
    break;
}

The result of this will be: string 0.61401081085205 constant 0.51961803436279

In this case the constant comparison will be faster.

The performance improvements where added to PHP5.4 and here is the RFC https://wiki.php.net/rfc/performanceimprovements

But note that constants generally make for better refactor able code and therefor better maintainable. Furthermore the performance hit is negligible

function doSomethingString() {
    return "OfflineState";
}

const OFFLINE_STATE = 1;
function doSomethingConstant() {
    return OFFLINE_STATE;
}

function dummy() {}

// String
echo('string' . PHP_EOL);
$start = microtime(true);
for($i = 0; $i < 10000000; $i++) {
    switch(doSomethingString()) {
        case "OfflineState":
            dummy();
            break;
    }
}
echo(PHP_EOL);
$end = microtime(true);
echo($end - $start);
echo(PHP_EOL);


//Constant
echo('constant' . PHP_EOL);
$start = microtime(true);
for($i = 0; $i < 10000000; $i++) {
    switch(doSomethingConstant()) {
        case OFFLINE_STATE:
            dummy();
            break;
    }
}
echo(PHP_EOL);
$end = microtime(true);
echo($end - $start);
echo(PHP_EOL);

My php version:

PHP 7.2.8-1+ubuntu18.04.1+deb.sury.org+1 (cli) (built: Jul 25 2018 10:52:19) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.8-1+ubuntu18.04.1+deb.sury.org+1, Copyright (c) 1999-2018, by Zend Technologies
Sander Visser
  • 4,144
  • 1
  • 31
  • 42
  • Thank you man. I ran a similar test and got similar results. I am surprised on how oprimized is this aspect in PHP, I've got really different results in other languages like Python. –  Aug 22 '18 at 04:44
  • I'm going through the opcache docs cause I really really want to understand why. Maybe Opcache uses immutable strings or something under the hood. – Sander Visser Aug 22 '18 at 04:45
  • I will try to run the same test on PHP 5.4 and see if this difference remains. –  Aug 22 '18 at 04:48
  • Found it! `opcache.interned_strings_buffer` seems to play a role here and strings are cached – Sander Visser Aug 22 '18 at 04:49
  • That settings creates a buffer with immutable strings therefor string comparision is faster than integer based constant comparision – Sander Visser Aug 22 '18 at 04:50
  • @Dknacht I expect 5.2 to perform slower with strings or by tweaking the opcache setting (This is how you normally optimize large applications) – Sander Visser Aug 22 '18 at 04:54
  • Great! @Sander Visser, I will try to test with this version (5.2) and come back with the results to see if that meets your espectations –  Aug 22 '18 at 04:56
  • @Dknacht I was wrong https://wiki.php.net/rfc/performanceimprovements here is the official RFC, and was implemented in php 5.4 – Sander Visser Aug 22 '18 at 05:11
  • And added an example how you can break PHP internal optimalization ;) enjoy testing. – Sander Visser Aug 22 '18 at 05:25
  • jajaja really interesting. Thanks for the detailed answer and all complete info. My test will have to take place tomorrow because its too late down here. –  Aug 22 '18 at 05:59
1

i know that strings comparison can be quite slow

This is not really accurate within the context of a web app.

A string comparison takes microseconds.

Loading a web page, accessing a database, etc take milliseconds or seconds (thousands of times longer).

So this kind of micro-optimization is hardly ever worth it. Instead you should focus on what makes the clearest, most maintainable code.

Performance optimization of your code comes into play later, if you are looping over millions of items or serving thousands of requests per second.

if the PHP parser still needs to read the whole constant name before comparing, is not it the same than using a string, or a variable?

Using constants (and having the values of the constants be numbers rather than strings) is probably slightly faster than using string values, and probably clearer and maintainable code also. But again, in the course of loading a web page, you will not be able to tell a difference.

We Are All Monica
  • 13,000
  • 8
  • 46
  • 72
  • 1
    "Using constants (and having the values of the constants be numbers rather than strings) is probably slightly faster than using string values" Apparently not to my surprise – Sander Visser Aug 22 '18 at 04:29
  • 1
    But I got your point, I just read a quote that got me thinking, it said "computer power is cheap, memory is cheap, developer time is expensive", basically to tell me that if I am going to make a choise, I must to prefer redability over raw performance. –  Aug 22 '18 at 04:33
  • 1
    @Dknacht constants provide better maintainability because refactoring your code becomes easier and yes this is way more important – Sander Visser Aug 22 '18 at 04:38