-
Notifications
You must be signed in to change notification settings - Fork 8k
zend_compile: Optimize array_map() with callable convert callback into foreach
#20934
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
base: master
Are you sure you want to change the base?
Conversation
|
I like this! I assume something similar is also possible for Closures passed directly as arg, like |
|
On that note, I wonder whether it would make sense to expose this as API, add a function pointer on zend_internal_function, and whenever a function is encountered during compilation and it has this function pointer, it's called with the ast of its arguments and can emit opcodes by itself (or just return false and normal compilation happens). Rather than centralizing this in compiler (the file is big enough :-P, and it would make it extensible; extensions could play around with this too). |
It probably would for all the reasons that you mentioned. |
…nto foreach
For:
<?php
function plus1($x) {
return $x + 1;
}
$array = array_fill(0, 100, 1);
$count = 0;
for ($i = 0; $i < 100_000; $i++) {
$count += count(array_map(plus1(...), $array));
}
var_dump($count);
This is ~1.1× faster:
Benchmark 1: /tmp/test/before -d opcache.enable_cli=1 /tmp/test/test6.php
Time (mean ± σ): 172.2 ms ± 0.5 ms [User: 167.8 ms, System: 4.2 ms]
Range (min … max): 171.6 ms … 173.1 ms 17 runs
Benchmark 2: /tmp/test/after -d opcache.enable_cli=1 /tmp/test/test6.php
Time (mean ± σ): 155.1 ms ± 1.3 ms [User: 150.6 ms, System: 4.2 ms]
Range (min … max): 154.2 ms … 159.3 ms 18 runs
Summary
/tmp/test/after -d opcache.enable_cli=1 /tmp/test/test6.php ran
1.11 ± 0.01 times faster than /tmp/test/before -d opcache.enable_cli=1 /tmp/test/test6.php
With JIT it becomes ~1.7× faster:
Benchmark 1: /tmp/test/before -d opcache.enable_cli=1 -d opcache.jit=tracing /tmp/test/test6.php
Time (mean ± σ): 166.9 ms ± 0.6 ms [User: 162.7 ms, System: 4.1 ms]
Range (min … max): 166.1 ms … 167.9 ms 17 runs
Benchmark 2: /tmp/test/after -d opcache.enable_cli=1 -d opcache.jit=tracing /tmp/test/test6.php
Time (mean ± σ): 94.5 ms ± 2.7 ms [User: 90.4 ms, System: 3.9 ms]
Range (min … max): 92.5 ms … 103.1 ms 31 runs
Summary
/tmp/test/after -d opcache.enable_cli=1 -d opcache.jit=tracing /tmp/test/test6.php ran
1.77 ± 0.05 times faster than /tmp/test/before -d opcache.enable_cli=1 -d opcache.jit=tracing /tmp/test/test6.php
55be63c to
fb31333
Compare
I assume getting scoping right is getting complicated quickly. Even preserving the Closure and compiling it as: $c = fn ($x) => $x + 1;
foreach ($array as $key => $val) $result[$key] = $c($val);is not immediately obvious to me that it is safe (e.g. with regard to variable capturing and scoping). I've opted to support only CALLABLE_CONVERT for now, since those should already be pretty useful once PFA lands and they don't come with the concerns above. |
For:
This is ~1.1× faster:
With JIT it becomes ~1.7× faster: