[php-src] Issue #8283: Performance: array_reduce is drastically slower (~1000x) than implementing the same logic with foreach
| From: | Firehed | Date: | Wed, 30 Mar 2022 21:38:09 +0000 |
| Subject: | [php-src] Issue #8283: Performance: array_reduce is drastically slower (~1000x) than implementing the same logic with foreach | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-240583@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/8283
Author: Firehed
### Description
The following code:
```php
<?php
declare(strict_types=1);
// generate a large sample of pseudo-random data which may have duplicates
$data = [];
for ($i = 0; $i < 40000; $i++) {
$data[] = crc32((string)$i);
}
echo 'Foreach';
$start = hrtime(true);
$result = [];
foreach ($data as $item) {
@$result[$item]++;
}
$durationNs = hrtime(true) - $start;
echo sprintf('%f s', $durationNs / 1_000_000_000);
echo "\n\n";
unset($start, $result, $item, $durationNs);
echo 'Array reduce';
$start = hrtime(true);
$result = array_reduce($data, function ($carry, $item) {
@$carry[$item]++;
return $carry;
}, []);
$durationNs = hrtime(true) - $start;
echo sprintf('%f s', $durationNs / 1_000_000_000);
```
Resulted in this output:
```
Foreach 0.007446s
Array reduce 8.685649s
```
But I expected this output instead:
```
(comparable runtimes)
```
Possibly impactful opcache ini settings:
```
$ php -i | rg opcache
Additional .ini files parsed => /usr/local/etc/php/8.1/conf.d/ext-opcache.ini
with Zend OPcache v8.1.3, Copyright (c), by Zend Technologies
Zend OPcache
opcache.blacklist_filename => no value => no value
opcache.consistency_checks => 0 => 0
opcache.dups_fix => Off => Off
opcache.enable => On => On
opcache.enable_cli => Off => Off
opcache.enable_file_override => Off => Off
opcache.error_log => no value => no value
opcache.file_cache => no value => no value
opcache.file_cache_consistency_checks => On => On
opcache.file_cache_only => Off => Off
opcache.file_update_protection => 2 => 2
opcache.force_restart_timeout => 180 => 180
opcache.huge_code_pages => Off => Off
opcache.interned_strings_buffer => 8 => 8
opcache.jit => tracing => tracing
opcache.jit_bisect_limit => 0 => 0
opcache.jit_blacklist_root_trace => 16 => 16
opcache.jit_blacklist_side_trace => 8 => 8
opcache.jit_buffer_size => 0 => 0
opcache.jit_debug => 0 => 0
opcache.jit_hot_func => 127 => 127
opcache.jit_hot_loop => 64 => 64
opcache.jit_hot_return => 8 => 8
opcache.jit_hot_side_exit => 8 => 8
opcache.jit_max_exit_counters => 8192 => 8192
opcache.jit_max_loop_unrolls => 8 => 8
opcache.jit_max_polymorphic_calls => 2 => 2
opcache.jit_max_recursive_calls => 2 => 2
opcache.jit_max_recursive_returns => 2 => 2
opcache.jit_max_root_traces => 1024 => 1024
opcache.jit_max_side_traces => 128 => 128
opcache.jit_prof_threshold => 0.005 => 0.005
opcache.lockfile_path => /tmp => /tmp
opcache.log_verbosity_level => 1 => 1
opcache.max_accelerated_files => 10000 => 10000
opcache.max_file_size => 0 => 0
opcache.max_wasted_percentage => 5 => 5
opcache.memory_consumption => 128 => 128
opcache.opt_debug_level => 0 => 0
opcache.optimization_level => 0x7FFEBFFF => 0x7FFEBFFF
opcache.preferred_memory_model => no value => no value
opcache.preload => no value => no value
opcache.preload_user => no value => no value
opcache.protect_memory => Off => Off
opcache.record_warnings => Off => Off
opcache.restrict_api => no value => no value
opcache.revalidate_freq => 2 => 2
opcache.revalidate_path => Off => Off
opcache.save_comments => On => On
opcache.use_cwd => On => On
opcache.validate_permission => Off => Off
opcache.validate_root => Off => Off
opcache.validate_timestamps => On => On
```
Note that
opcache_get_status() returns false when running this script via
CLI, which is where I encountered this.
General observation: this doesn't appear to be something that gets incrementally slower the
further along the array the algorithm is. By adding an echo '.' in either
section, it's apparent that even the very first item (with an empty $carry) is drastically
slower in array_reduce than in the body of foreach.
I know they're not 100% comparable, but both should have O(n) complexity so I would not expect
_three orders of magnitude_ performance difference to produce the same result.
### PHP Version
8.1.3
### Operating System
macOS 12.3