-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
array_key_exists on $GLOBALS takes linear time in PHP 8.1.0+ #9429
Comments
What the change does is always rebuilding Hence, as you are passing the whole of To fix this, there are essentially a couple options: a) special case array_key_exists on $GLOBALS, b) revert that $GLOBALS change or c) just mitigate it by caching $GLOBALS until the next global symtable update adding a new value there? |
It's intended - https://wiki.php.net/rfc/restrict_globals_usage was the RFC for that in php 8.1 - it seemed obvious it'd copy all of the globals every time the full $GLOBALS was read 3c68f38#diff-b3cd91650e41ac88d84b327235481f7846530bb7bc9d23b974b3a95326175578 - I think maybe this can be special cased by replacing this with an alternative opcode to ARRAY_KEY_EXISTS with a different name for globals, which will probably require a new opcode. This should behave exactly like
function test(string $key) {
return array_key_exists($key, $GLOBALS);
} Miscellaneous thoughts:
|
It is not the size of the Superglobal, if you create a collection with an key or index rand, the element it is much faster but rather it looks like <?php
$start = microtime(true);
if(empty($GLOBALS['' . 'rand'])) {
$GLOBALS['' . 'rand'] = array();
}
for ($i = 0; $i < 500000; $i++) {
$rand['' . $i] = rand(0, 499999);
//$GLOBALS['a' . $i] = $rand;
}
$arr = NULL;
unset($arr);
$arr = range(0, count($rand) - 1);
$GLOBALS['' . 'rand'] = array_combine($arr, $rand);
$arr = NULL;
unset($arr);
$mid = microtime(true);
for ($runs = 1000; $runs > 0; $runs--) {
array_key_exists(1111111, $GLOBALS['' . 'rand']);
}
$end = microtime(true);
printf('array_key_exists(1111111, $GLOBALS[\'\' . \'rand\']) took %1$.5F seconds for write array %2$.5F seconds and total time %3$.5F' . "\n", $end - $mid, $mid - $start, $end - $start );
var_dump(memory_get_usage(true), ini_get('memory_limit'));
?> note* the Variable Variables <?php
$start = microtime(true);
for ($i = 0; $i < 100000; $i++) {
$rand = rand(0, 99999);
${$i} = $rand; // As of PHP 5.4 $GLOBALS is now initialized just-in-time
}
$mid = microtime(true);
//$copy = $GLOBALS;
$return = NULL;
for ($runs = 1000; $runs > 0; $runs--) {
$false = array_key_exists('1111111', $GLOBALS);
if($false) {
$return = $false;
}
}
$copy = NULL;
unset($copy);
$end = microtime(true);
printf('array_key_exists(\'1111111\', $GLOBALS) took %1$.5F seconds for write array %2$.5F seconds and total time %3$.5F' . "\n", $end - $mid, $mid - $start, $end - $start );
var_dump(memory_get_usage(true), ini_get('memory_limit'), $return);
?> From php >= 8.1.0 and array_key_exists for $key php < 8.1.0
|
Description
Compare the following in PHP 8.0.22 vs 8.1.9 (https://3v4l.org/91CFl)
It takes 0.00002 seconds in 8.0.22, and 1.9 seconds in 8.1.9.
Further testing makes it clear that the speed of array_key_exists on $GLOBALS increases linearly with the size of $GLOBALS.
This regression doesn't affect array_key_exists on non-$GLOBALS arrays, and doesn't affect the speed of
isset($GLOBALS[$foo])
.The commit that introduced the slowdown was 3c68f38, which restricted usage of $GLOBALS in various ways. I'm not sure if this particular side effect was known or intended.
array_key_exists is used somewhat often on $GLOBALS in MediaWiki, most prominently here.
The behavior can be mostly replaced by calling
isset($GLOBALS[$foo])
, which remains fast, although that has different semantics when the value is null. I'm not aware of a better way to get the full behavior ofarray_key_exists($foo, $GLOBALS)
in constant-time in 8.1+.PHP Version
PHP 8.1.9
Operating System
Ubuntu 20.04
The text was updated successfully, but these errors were encountered: