Sunday, November 4, 2012

PHP Switch vs In_Array vs IsSet, Performance test / Alternatives / Benchmark

Hi, All :)
Today I just made a benchmark of PHP's Alternatives of looking up in array with ~30 keys and is around 60 keys. Not that big, but, that is the one i was needed to test for the work ;-)

Test Stand:
Hardware:
Intel i7 860 @ 2.8Ghz
Asus P7H55-M PRO
8GB DDR @ 1600Mhz Timings 8-8-8-24
SSD 80GB Intel SA2M080

Software
Windows 7 x64
Apache 2.2.17 (x86)
PHP 5.3.5

Let's begin.

The goal: Find language and locale by country in predefined values.
Starting point: The switch is used there by one of previous developers.

Test case php's Switch:
set_time_limit(0);
$time_start = microtime(true);
$country='ve';


$iterations = 1000000;
$i=0;
while($i<$iterations){
    $property ='';
    $lang='';
    $locale='';
    switch ($country) {
            case 'ar': $property .= 'ar';  $lang = 'es';  $locale = 'ar';  break; 
            case 'au': $property .= 'au';  $lang = 'en';  $locale = 'au';  break; 
            case 'br': $property .= 'br';  $lang = 'pt-BR'; $locale = 'br';  break; 
            case 'ca': $property .= 'ca';  $lang = 'en';  $locale = 'ca';  break; 
            case 'co': $property .= 'co';  $lang = 'es';  $locale = 'co';  break; 
            case 'fr': $property .= 'fr';  $lang = 'fr';  $locale = 'fr';  break; 
            case 'de': $property .= 'de';  $lang = 'de';  $locale = 'de';  break; 
            case 'hu': $property .= 'hu';  $lang = 'hu';  $locale = 'hu';  break; 
            case 'il': $property .= 'il';  $lang = 'he';  $locale = 'il';  break; 
            case 'it': $property .= 'it';  $lang = 'it';  $locale = 'it';  break; 
            case 'mx': $property .= 'mx';  $lang = 'es';  $locale = 'mx';  break; 
            case 'nl': $property .= 'nl';  $lang = 'nl';  $locale = 'nl';  break; 
            case 'no': $property .= 'no';  $lang = 'no';  $locale = 'no';  break; 
            case 'pt': $property .= 'pt';  $lang = 'pt-PT'; $locale = 'pt';  break; 
            case 'sa': $property .= 'sa';  $lang = 'ar';  $locale = 'sa';  break; 
            case 'za': $property .= 'za';  $lang = null;  $locale = 'za';  break; 
            case 'es': $property .= 'es';  $lang = 'es';  $locale = 'es';  break; 
            case 'se': $property .= 'se';  $lang = 'sv';  $locale = 'se';  break; 
            case 'ch': $property .= 'ch';  $lang = null;  $locale = 'ch';  break; 
            case 'th': $property .= 'th';  $lang = 'th';  $locale = 'th';  break; 
            case 'tr': $property .= 'tr';  $lang = 'tr';  $locale = 'tr';  break; 
            case 'ae': $property .= 'ae';  $lang = 'ar';  $locale = 'ae';  break; 
            case 'gb':
            case 'uk': $property .= 'uk';     $locale = 'uk';  break; 
            case 'us': $property .= 'us';        break; 
            case 've': $property .= 've';  $lang = 'es';  $locale = 've';  break; 
            default: $property .= 'row';     $locale = $country; break; 
    }
    ++$i;
}
$time_end = microtime(true);
echo number_format($time_end-$time_start, 6, '.', '')." seconds - switch 26 options\n";

Test case for php's In_array:
set_time_limit(0);
$time_start = microtime(true);
$country='ve';

$arr = array(
    'ar'=>array('property'=>'ar','lang'=>'es','locale'=>'ar')
    ,'au'=>array('property'=>'au','lang'=>'en','locale'=>'au')
    ,'br'=>array('property'=>'br','lang'=>'pt-BR','locale'=>'br')
    ,'ca'=>array('property'=>'ca','lang'=>'en','locale'=>'ca')
    ,'co'=>array('property'=>'co','lang'=>'es','locale'=>'co')
    ,'fr'=>array('property'=>'fr','lang'=>'fr','locale'=>'fr')
    ,'de'=>array('property'=>'de','lang'=>'de','locale'=>'de')
    ,'hu'=>array('property'=>'hu','lang'=>'hu','locale'=>'hu')
    ,'il'=>array('property'=>'il','lang'=>'he','locale'=>'il')
    ,'it'=>array('property'=>'it','lang'=>'it','locale'=>'it')
    ,'mx'=>array('property'=>'mx','lang'=>'es','locale'=>'mx')
    ,'nl'=>array('property'=>'nl','lang'=>'nl','locale'=>'nl')
    ,'no'=>array('property'=>'no','lang'=>'no','locale'=>'no')
    ,'pt'=>array('property'=>'pt','lang'=>'pt-PT','locale'=>'pt')
    ,'sa'=>array('property'=>'sa','lang'=>'ar','locale'=>'sa')
    ,'za'=>array('property'=>'za','lang'=>NULL,'locale'=>'za')
    ,'es'=>array('property'=>'es','lang'=>'es','locale'=>'es')
    ,'se'=>array('property'=>'se','lang'=>'sv','locale'=>'se')
    ,'ch'=>array('property'=>'ch','lang'=>NULL,'locale'=>'ch')
    ,'th'=>array('property'=>'th','lang'=>'th','locale'=>'th')
    ,'tr'=>array('property'=>'tr','lang'=>'tr','locale'=>'tr')
    ,'ae'=>array('property'=>'ae','lang'=>'ar','locale'=>'ae')
    ,'gb'=>array('property'=>'gb','lang'=>'en','locale'=>'uk')
    ,'uk'=>array('property'=>'uk','lang'=>'en','locale'=>'uk')
    ,'us'=>array('property'=>'us','lang'=>'en','locale'=>'us')
    ,'ve'=>array('property'=>'ve','lang'=>'es','locale'=>'ve')
);

$iterations = 1000000;
$i=0;
while($i<$iterations){
    $property ='';
    $lang='';
    $locale='';
    if(in_array($country, $arr) !== FALSE){
        $property = $arr[$country]['property'];
        $lang = $arr[$country]['lang'];
        $locale = $arr[$country]['locale'];
    }else{
        $property = 'row';
        $lang = 'en';
        $locale = $country;
    }
    ++$i;
}
$time_end = microtime(true);
echo number_format($time_end-$time_start, 6, '.', '')." seconds - in_array 26 options \n";

Test case for PHP's IsSet:
set_time_limit(0);
$time_start = microtime(true);
$country='ve';

$arr = array(
    'ar'=>array('property'=>'ar','lang'=>'es','locale'=>'ar')
    ,'au'=>array('property'=>'au','lang'=>'en','locale'=>'au')
    ,'br'=>array('property'=>'br','lang'=>'pt-BR','locale'=>'br')
    ,'ca'=>array('property'=>'ca','lang'=>'en','locale'=>'ca')
    ,'co'=>array('property'=>'co','lang'=>'es','locale'=>'co')
    ,'fr'=>array('property'=>'fr','lang'=>'fr','locale'=>'fr')
    ,'de'=>array('property'=>'de','lang'=>'de','locale'=>'de')
    ,'hu'=>array('property'=>'hu','lang'=>'hu','locale'=>'hu')
    ,'il'=>array('property'=>'il','lang'=>'he','locale'=>'il')
    ,'it'=>array('property'=>'it','lang'=>'it','locale'=>'it')
    ,'mx'=>array('property'=>'mx','lang'=>'es','locale'=>'mx')
    ,'nl'=>array('property'=>'nl','lang'=>'nl','locale'=>'nl')
    ,'no'=>array('property'=>'no','lang'=>'no','locale'=>'no')
    ,'pt'=>array('property'=>'pt','lang'=>'pt-PT','locale'=>'pt')
    ,'sa'=>array('property'=>'sa','lang'=>'ar','locale'=>'sa')
    ,'za'=>array('property'=>'za','lang'=>NULL,'locale'=>'za')
    ,'es'=>array('property'=>'es','lang'=>'es','locale'=>'es')
    ,'se'=>array('property'=>'se','lang'=>'sv','locale'=>'se')
    ,'ch'=>array('property'=>'ch','lang'=>NULL,'locale'=>'ch')
    ,'th'=>array('property'=>'th','lang'=>'th','locale'=>'th')
    ,'tr'=>array('property'=>'tr','lang'=>'tr','locale'=>'tr')
    ,'ae'=>array('property'=>'ae','lang'=>'ar','locale'=>'ae')
    ,'gb'=>array('property'=>'gb','lang'=>'en','locale'=>'uk')
    ,'uk'=>array('property'=>'uk','lang'=>'en','locale'=>'uk')
    ,'us'=>array('property'=>'us','lang'=>'en','locale'=>'us')
    ,'ve'=>array('property'=>'ve','lang'=>'es','locale'=>'ve')
);

$iterations = 1000000;
$i=0;
while($i<$iterations){
    $property ='';
    $lang='';
    $locale='';
    if(isset($arr[$country])){
        $property = $arr[$country]['property'];
        $lang = $arr[$country]['lang'];
        $locale = $arr[$country]['locale'];
    }else{
        $property = 'row';
        $lang = 'en';
        $locale = $country;
    }
    ++$i;
}
$time_end = microtime(true);
echo number_format($time_end-$time_start, 6, '.', '')." seconds - isset 26 options array \n";


To tell you the truth i was waited for In_Array to be slower than switch :), but let's take a look at the benchmark results.
PHP's Switch vs In_Array vs IsSet Benchmark Results:
*Notes:
The runs are (loop) 1 Million times
The time is counted as average for 10 runs * 1 million loops.
The time surrounded by closures is the time for the same run, but with *2 amount of keys in arrays.

The times
The key is set to last key in array
----------------------
For the Switch took: 2.094s. (3.726s)
For the In_Array took: 1.617s. (2.414s)
For the IsSet took: 0.848s. (0.899s.)
----------------------

The match is set to first key in array case:
----------------------
For the Switch: 0.569s. (0.629s) (great speed increase).
For the In_Array: 1.617s. (2.425s) (the execution still unchanged).
For the IsSet: 0.860s. (0.897s) (same with isset).
----------------------

No Match Case:
----------------------
For the Switch: 2.032s.(3.509s) (faster than last match. LOL)
For the In_Array: 1.657s. (2.372s) (Time almost not changing here)
For the IsSet: 0.540s. (0.507s) (Great increase!)
----------------------

Conclusion:
The best way to find whether the key is set in array is to use IsSet php's function.
As we see by the tests, Switch can bring performance only in case the key one of the first, the in_array in any case scanning array to the end and isset is as fast as lightning and becomes even faster when no match is found !
Also please note, as the amount of cases and array keys will become bigger, the switch and in_array will become slower, while isset doesn't have such issue.

The code isset($arr[$country]) brought to us best results.
Have fun playing around.

Sincerely,
Ruskevych Valentin

No comments: