Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why comparing numbers with min() and max() is slower than conditional statement

Tags:

php

I had to optimize code which ran slowly with large dataset which was looking good. After testing and logging execution times I found loop with few max() comparisons of two integers. Then after quick peek in the documentation I saw the following comment: "I had several occasions that using max is a lot slower then using a if/then/else construct. Be sure to check this in your routines!"

So I replace it with ternary operator and the execution time was around 2/3 faster.

Then I became curious why max() is working so much slower than a simple statement and what is behind this functions. But I couldn't find any explanation so far.

Also in the past I come across similar problems with array_search() which is working much slower than foreach with statement.

I ran the test on PHP 7.1.

$firstLoop = microtime(true);

for ($i = 0; $i < 1000000; $i++) {
    $testOne = max($i, 1000000);
}

error_log(microtime(true) - $firstLoop); 

$secondLoop = microtime(true); // 1.3123650550842

for ($i = 0; $i < 1000000; $i++) {
    $testTwo = $i > 1000000 ? $i : 1000000; 
}

error_log(microtime(true) - $secondLoop); // 0.090374946594238
like image 706
hrench Avatar asked Nov 23 '25 02:11

hrench


1 Answers

max is a complete function call and allows an arbitrary list of arguments. This has a lot of overhead compared to a simple comparison which can be performed inline without setting up a complete function call context. In your example you're doing a million function calls, and this adds up in total.

Two different methods (that support (and can do) different things) isn't necessary implemented in the same way even if they give the same result as in your example.

You can see this from the opcodes generated by the VM, the first call generates the following statements:

   6     6    >   INIT_FCALL                                               'max'
         7        SEND_VAR                                                 !1
         8        SEND_VAL                                                 1000000
         9        DO_ICALL                                         $8      
        10        ASSIGN                                                   !2, $8
   5    11        POST_INC                                         ~10     !1
        12        FREE                                                     ~10
        13    >   IS_SMALLER                                       ~11     !1, 1000000

While the second case generates just JMP calls:

        27      > JMP                                                      ->36
  14    28    >   IS_SMALLER                                       ~18     1000000, !1
        29      > JMPZ                                                     ~18, ->32
        30    >   QM_ASSIGN                                        ~19     !1
        31      > JMP                                                      ->33
        32    >   QM_ASSIGN                                        ~19     1000000
        33    >   ASSIGN                                                   !4, ~19
  13    34        POST_INC                                         ~21     !1
        35        FREE                                                     ~21
        36    >   IS_SMALLER                                       ~22     !1, 1000000

JMP calls doesn't have to set up the whole function call structure, and thus can be executed a lot faster.

It's also worth nothing that the difference in general seems to be about 1/2 the time for the simple comparison on the current versions of PHP.

like image 132
MatsLindh Avatar answered Nov 25 '25 17:11

MatsLindh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!