-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
As per title. This makes first-class callables slower than legacy, ugly callables.
Consider the following example:
function dummy() : void{
}
function encodeToString(callable $writerFunc) : void{
$writerFunc();
}
$loops = 100000000;
$start = hrtime(true);
for($i = 0; $i < $loops; $i++) {
encodeToString('dummy');
}
echo "legacy callable: " . number_format((hrtime(true) - $start) / $loops, 2) . "ns\n";
$start = hrtime(true);
for($i = 0; $i < $loops; $i++) {
encodeToString(dummy(...));
}
echo "1c callable inline: " . number_format((hrtime(true) - $start) / $loops, 2) . "ns\n";
$start = hrtime(true);
$writerFunc = dummy(...);
for($i = 0; $i < $loops; $i++) {
encodeToString($writerFunc);
}
echo "1c callable outside loop: " . number_format((hrtime(true) - $start) / $loops, 2) . "ns\n";In PHP 8.4, this gives the following results:
legacy callable: 45.16ns
1c callable inline: 64.63ns
1c callable outside loop: 40.14ns
To my knowledge, there is no good reason why a first-class callable of a global or static method should be reallocated every time these loop statements are executed. They have no lexical scope dependency - don't need any local vars, have no $this, etc.
My gut feeling is that these types of dependency-free 1cc should be able to be treated similarly to literals and constants.
As an aside: This is something that's also been bothering me about the new pipe operator in 8.5. I don't know how the underlying implementation works, but if it doesn't get optimised down to simple function calls at compile time, then pipe code using first-class callables will be quite a bit slower than nested function calls on account of all the closure reallocations.
PHP Version
PHP 8.4.12 (cli) (built: Sep 7 2025 21:40:54) (ZTS Visual C++ 2022 x64)
Copyright (c) The PHP Group
Zend Engine v4.4.12, Copyright (c) Zend Technologies
with Zend OPcache v8.4.12, Copyright (c), by Zend Technologies
Operating System
Windows 11