0

I just wanted to know which switch statement below would execute faster each having 40 cases ?

$field = 'Z';
switch (true) {
   case $field == 'A':
   case $field == 'X':
   // etc up to 40 cases
}

OR this one:

$field = 'Z';
switch ($field){
   case 'A':
   case 'B':
   // etc up to 40 cases
}

Also if in first case strict comparison === would be better in terms of execution or a normal comparison == ?

DavidG
  • 149
  • 3
  • 9
  • There won't be much of a difference, so I would go with the normal syntax. If every nanosecond counts all you can do is test it on your system with your version of PHP. or perhaps not use PHP at all, but something like C. – KIKO Software Jun 15 '22 at 08:46
  • Use `match` to have strict comparison. Also I don't think there is significant speed difference unless you execute same code 9999999999 times per millisecond. – Justinas Jun 15 '22 at 08:46
  • Don't miss the `break` keyword. – Markus Zeller Jun 15 '22 at 08:49
  • 1
    Logically, it should be the second one, because this is how switch is intended to work. Speed-wise, they are equal. – Your Common Sense Jun 15 '22 at 08:52
  • 1
    Thanks for you valuable input. Even if none of the `case` turns out to be `true` the switch statement would still take the time for the comparisons it performed. Right ? – DavidG Jun 15 '22 at 09:09
  • If performance is of concern, do a [`Gatling`](https://gatling.io/) test on your application for profiling both types of codes. – nice_dev Jun 15 '22 at 09:13
  • On the one hand, the normal version with literal values might be compiled to a jump table rather than a series of branches, which would then execute quicker. On the other hand, an optimising compiler can detect that the two are equivalent, and produce identical code. (In case anyone asks, yes PHP is a compiled language, and yes it does do this kind of optimisation, particularly with OpCache enabled.) On the gripping hand, since the comparisons are to strings, I'm not sure such an optimisation is possible anyway. – IMSoP Jun 15 '22 at 10:19

1 Answers1

-2

I found this to be quite an interesting question and did some crude benchmarks to get some numbers.

Dataset

I'm using a dataset of all 25 letters uppercased (A-Z), and I've implemented the switch in three ways:

  1. The normal way: case $value: return $value;
  2. The loose comparison: case $value == 'A': return $value;
  3. The strict comparison: case $value === 'A': return $value;

Test

The time calculated is the time used for calling the switch with every single letter of the dataset once.

Results

The output was as follows on my system running PHP 8.0.18:

Iterations: 1000000
switch_with_loose_comparisons
Tot: 2.4946439266205 seconds
Avg: 2.4484488964081E-6 seconds
Max: 0.00097203254699707 seconds
Min: 9.5367431640625E-7 seconds
switch_with_strict_comparisons
Tot: 2.9073448181152 seconds
Avg: 2.8613615036011E-6 seconds
Max: 0.00032496452331543 seconds
Min: 1.9073486328125E-6 seconds
switch_with_values
Tot: 1.0886509418488 seconds
Avg: 1.0394973754883E-6 seconds
Max: 0.00041389465332031 seconds
Min: 0 seconds

The noteworthy figures of this is the total amount of time used for 1 million iterations of the dataset where the largest difference is.

Conclusion

The normal way is faster!

Keep in mind the total times are worth 25 million calls to each function, so in practice it shouldn't really matter what you use in production as the differences are negligible for single calls.

Code

function switch_with_loose_comparisons(string $input) : string
{
    switch (true) {
        case $input == 'A':
            return $input;
        case $input == 'B':
            return $input;
        case $input == 'C':
            return $input;
        case $input == 'D':
            return $input;
        case $input == 'E':
            return $input;
        case $input == 'F':
            return $input;
        case $input == 'G':
            return $input;
        case $input == 'H':
            return $input;
        case $input == 'I':
            return $input;
        case $input == 'J':
            return $input;
        case $input == 'K':
            return $input;
        case $input == 'L':
            return $input;
        case $input == 'M':
            return $input;
        case $input == 'N':
            return $input;
        case $input == 'O':
            return $input;
        case $input == 'P':
            return $input;
        case $input == 'Q':
            return $input;
        case $input == 'R':
            return $input;
        case $input == 'S':
            return $input;
        case $input == 'T':
            return $input;
        case $input == 'U':
            return $input;
        case $input == 'V':
            return $input;
        case $input == 'W':
            return $input;
        case $input == 'X':
            return $input;
        case $input == 'Y':
            return $input;
        case $input == 'Z':
            return $input;
    }
}

function switch_with_strict_comparisons(string $input) : string
{
    switch (true) {
        case $input === 'A':
            return $input;
        case $input === 'B':
            return $input;
        case $input === 'C':
            return $input;
        case $input === 'D':
            return $input;
        case $input === 'E':
            return $input;
        case $input === 'F':
            return $input;
        case $input === 'G':
            return $input;
        case $input === 'H':
            return $input;
        case $input === 'I':
            return $input;
        case $input === 'J':
            return $input;
        case $input === 'K':
            return $input;
        case $input === 'L':
            return $input;
        case $input === 'M':
            return $input;
        case $input === 'N':
            return $input;
        case $input === 'O':
            return $input;
        case $input === 'P':
            return $input;
        case $input === 'Q':
            return $input;
        case $input === 'R':
            return $input;
        case $input === 'S':
            return $input;
        case $input === 'T':
            return $input;
        case $input === 'U':
            return $input;
        case $input === 'V':
            return $input;
        case $input === 'W':
            return $input;
        case $input === 'X':
            return $input;
        case $input === 'Y':
            return $input;
        case $input === 'Z':
            return $input;
    }
}

function switch_with_values(string $input) : string
{
    switch ($input) {
        case 'A':
            return $input;
        case 'B':
            return $input;
        case 'C':
            return $input;
        case 'D':
            return $input;
        case 'E':
            return $input;
        case 'F':
            return $input;
        case 'G':
            return $input;
        case 'H':
            return $input;
        case 'I':
            return $input;
        case 'J':
            return $input;
        case 'K':
            return $input;
        case 'L':
            return $input;
        case 'M':
            return $input;
        case 'N':
            return $input;
        case 'O':
            return $input;
        case 'P':
            return $input;
        case 'Q':
            return $input;
        case 'R':
            return $input;
        case 'S':
            return $input;
        case 'T':
            return $input;
        case 'U':
            return $input;
        case 'V':
            return $input;
        case 'W':
            return $input;
        case 'X':
            return $input;
        case 'Y':
            return $input;
        case 'Z':
            return $input;
    }
}

$letters = range('A', 'Z');

$results = [];

$functions = [
    'switch_with_loose_comparisons',
    'switch_with_strict_comparisons',
    'switch_with_values',
];

$iterations = 100000;

$totals = [];

foreach ($functions as $function) {
    $total_start = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
        $start = microtime(true);
        
        foreach ($letters as $letter) {
            $function($letter);
        }
        
        $results[$function][] = microtime(true) - $start;
    }
    
    $totals[$function] = microtime(true) - $total_start;
}

$averages = [];
$max = [];
$min = [];

foreach ($results as $function => $times) {
    $averages[$function] = array_sum($times) / count($times);
    $max[$function] = max($times);
    $min[$function] = min($times);
}

echo "Iterations: $iterations\n";

foreach ($functions as $function) {
    echo "$function\n";
    echo "Tot: $totals[$function] seconds\n";
    echo "Avg: $averages[$function] seconds\n";
    echo "Max: $max[$function] seconds\n";
    echo "Min: $min[$function] seconds\n";
}
Morten H
  • 150
  • 5
  • 1
    "Crude benchmarks" are **never** correct. https://stackoverflow.com/questions/2842695/what-is-microbenchmarking There is nothing interesting in supporting some groundless fantasies – Your Common Sense Jun 15 '22 at 09:31
  • That is why is stated the benchmarks as being crude, and why I've said in the conclusion that it doesn't matter which you choose as the single call differences are negligible. – Morten H Jun 15 '22 at 09:36