-
-
Save sanmai/e1e7556e5ec219a4166e072beb50cb2e to your computer and use it in GitHub Desktop.
Various StartsWith implementations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
Run with all available processing power: | |
sudo nice -n -20 php startswith_benchmark.php | sort -h | |
Running with PHP 7.0.7-3 gives: | |
1.6±0.0014 msec - strncmp_startswith_shortcut | |
1.71±0.0014 msec - strncmp_startswith | |
1.78±0.0013 msec - substr_compare_startswith | |
1.84±0.0013 msec - substr_startswith | |
2.89±0.0025 msec - strpos_startswith | |
78.86±0.1781 msec - preg_match_startswith | |
*/ | |
function substr_startswith($haystack, $needle) { | |
return substr($haystack, 0, strlen($needle)) === $needle; | |
} | |
function preg_match_startswith($haystack, $needle) { | |
return preg_match('~'.preg_quote($needle,'~').'~A', $haystack) > 0; | |
} | |
function substr_compare_startswith($haystack, $needle) { | |
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0; | |
} | |
function strpos_startswith($haystack, $needle) { | |
return strpos($haystack, $needle) === 0; | |
} | |
function strncmp_startswith($haystack, $needle) { | |
return strncmp($haystack, $needle, strlen($needle)) === 0; | |
} | |
function strncmp_startswith_shortcut($haystack, $needle) { | |
return $haystack[0] === $needle[0] | |
? strncmp($haystack, $needle, strlen($needle)) === 0 | |
: false; | |
} | |
// helper functions | |
function randstr($len) { | |
return substr(base64_encode(openssl_random_pseudo_bytes(ceil($len * 3 / 4))), 0, $len); | |
} | |
function avg_value($values) { | |
return array_sum($values) / sizeof($values); | |
} | |
function stddev_value($values) { | |
$mean = array_sum($values) / sizeof($values); | |
return (float) sqrt(array_reduce($values, function ($variance, $delay) use ($mean) { | |
return $variance + pow($delay - $mean, 2); | |
}, 0) / (sizeof($values))); | |
} | |
echo 'generating tests'; | |
for ($i = 0; $i < 100000; $i += 1) { | |
if ($i % 1000 === 0) echo '.'; | |
// 99,9% of random cases are failing | |
$test_cases[] = $randomCase = [ | |
'haystack' => randstr(mt_rand(1, 7000)), | |
'needle' => randstr(mt_rand(1, 3000)), | |
]; | |
// in real life not every call fails | |
// therefore we need to benchmark succeding calls too | |
$randomCase['haystack'] = $randomCase['needle'].$randomCase['needle']; | |
$test_cases[] = $randomCase; | |
} | |
echo "done!\n"; | |
foreach ([ | |
'substr_startswith', | |
'preg_match_startswith', | |
'substr_compare_startswith', | |
'strpos_startswith', | |
'strncmp_startswith', | |
'strncmp_startswith_shortcut', | |
] as $func) { | |
$times = []; | |
foreach($test_cases as $tc) { | |
$start = microtime(true); | |
$func($tc['haystack'], $tc['needle']); | |
$times[] = microtime(true) - $start; | |
} | |
// seconds to msec | |
$times = array_map(function ($seconds) { | |
return $seconds * 1000 * 1000; | |
}, $times); | |
$elapsed = round(array_sum($times), 4); | |
$avg = round(avg_value($times), 2); | |
$stddev = round(stddev_value($times), 2); | |
$stderr = round($stddev / sqrt(sizeof($times)), 4); | |
echo "{$avg}±{$stderr} msec - $func\n"; // (sigma $stddev total $elapsed) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment