[php-src] Issue #13193: Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection)
| From: | sboikov1983 | Date: | Fri, 19 Jan 2024 07:26:44 +0000 |
| Subject: | [php-src] Issue #13193: Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection) | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-246290@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/13193
Author: 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
<?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](https://github.com/php/php-src/commit/abe3673d1fd09a35c25b4da6248f7e8c106aa37e). 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