php / php/php-src

array_key_exists on $GLOBALS takes linear time in PHP 8.1.0+

Open
#9,429 3 comments 6 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Category: Engine Status: Needs Triage
Dominant language
C
Stars
40.4k
Forks
8.1k
Avg merge
2d 13h
Merged PRs (30d)
96

Description

Description

Compare the following in PHP 8.0.22 vs 8.1.9 (https://3v4l.org/91CFl)

<?php

for ($i = 0; $i < 100000; $i++) {
    $GLOBALS["a$i"] = rand(0, 1000000);
}

$start = microtime(true);
for ($runs = 1000; $runs > 0; $runs--) {
    array_key_exists('a1111111', $GLOBALS);
}
$end = microtime(true);
printf("[GLOBALS]array_key_exists() took %.5f seconds\n", $end - $start);

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 https://github.com/php/php-src/commit/3c68f38fdaec7a6f81f986a639bdd29716cf8f22, 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 of array_key_exists($foo, $GLOBALS) in constant-time in 8.1+.

PHP Version

PHP 8.1.9

Operating System

Ubuntu 20.04

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the PHP 8.0.22/8.1.9 benchmark from the issue, then inspect commit 3c68f38fdaec7a6f81f986a639bdd29716cf8f22, which restricted $GLOBALS usage. Done means array_key_exists($foo, $GLOBALS) no longer scales linearly with the size of $GLOBALS while preserving its null-value semantics; compare the result with isset behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, php
Domain
backend, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.