Skip to content

Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection) #13193

Closed
@sboikov1983

Description

@sboikov1983

Description

We noticed significant performance degradation in our application after migration to PHP 8.2.13 (and PHP 8.3.0 has the same issue). Our application performs a lot of computations and in some cases execution time increased from 2 to 6 hours.

Here is a simple script demonstrating the problem (run as 'php -dmemory_limit=-1 perf_test.php'):

<?php
class PerfTest {
    /*
     Create 'object' with many fields:
    {
        "obj_0": {"field_0": 1, "field_1": 2, ...},
        "obj_1": {"field_0": 1, "field_1": 2, ...},
        ...
    }
     */
    private static function generateObject(): array {
        $resultObj = [];
        for ($i = 0; $i < 100; $i++) {
            $nestedObj = [];
            for ($j = 0; $j < 20; $j++) {
                $nestedObj["field_$j"] = 0;
            }
            $resultObj["obj_$i"] = $nestedObj;
        }
        return $resultObj;
    }

    public function run(): void {
        $objects = [];
        for ($i = 0; $i < 50000; $i++) {
            $objects [] = self::generateObject();
        }

        $start0 = microtime(true);
        foreach ($objects as $object) {
            foreach ($object as $nestedObj) {
                // No-op, just iterate generated objects.
            }
        }
        echo "Iteration time: " . round((microtime(true) - $start0), 1) . " sec\n";
    }
}
(new PerfTest())->run();

Here is timing for PHP 8.2.12 and PHP 8.2.13 when GC is enabled and disabled:

PHP 8.2.12 -dzend.enable_gc=1: Iteration time: 0.8 sec
PHP 8.2.12 -dzend.enable_gc=0: Iteration time: 0.1 sec
PHP 8.2.13 -dzend.enable_gc=1: Iteration time: 8 sec
PHP 8.2.13 -dzend.enable_gc=0: Iteration time: 0.1 sec

For 8.2.12 enabling GC doesn't have major impact on performance, but for 8.2.13 enabling GC increases time from 0.1 sec to 8 seconds (the same for 8.3.0). This is just a simple script to demonstrate the issue, in real application impact on performance is dramatic.

As I understand reason of additional GC overhead is this commit. I understand that it fixes potential memory leak, but perphaps there is a way to optimize it, so it won't cause such dramatic performance degradation?

I also understand that it's possible to selectively disable/enable GC in the application, or somehow get rid of 'foreach' loops. But since our application has thousands of such loops, changing the code would be very hard (and potentially the same issue can be in the 3rd party libraries which we can't change).

PHP Version

PHP 8.3

Operating System

Ubuntu 20.04, Windows 11

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions