-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Also cache the computed stack size in INIT_FCALL_BY_NAME, speed up calling global functions in other files #7761
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
One of the test failures on Travis seems to be related:
|
} else if (opline->opcode == ZEND_INIT_FCALL_BY_NAME) {
| LOAD_ADDR FCARG1a, Z_STR_P(zv + 1);
| lea FCARG2a, aword [r2 + opline->result.num]
| EXT_CALL zend_jit_find_func_helper, r0 Edit: Still, it's strange - is it possible for something to switch from the JIT to non-JIT? Or is this some other error - https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit Edit: I can reproduce this crash in
|
The computed stack size only depends on the function definition fbc and the number of opcodes in extended_value, and that doesn't change. INIT_FCALL_BY_NAME is used when the function definition is not available when a file is being compiled, commonly due to the function being declared in a different file. Precomputing the stack size is an optimization already done by INIT_FCALL used when the function is known. Replacing `hallo` in Zend/micro_bench.php with a require_once to load `hallo` from a different file in a build with opcache enabled (but not the jit): ``` Before (best run) - total time and subtracting time needed for an empty loop empty_loop 0.162 func_elsewhere() 0.615 0.475 After (best run): empty_loop 0.159 (unaffected, just noise) func_elsewhere() 0.579 0.420 ```
f910edd
to
fa00d04
Compare
fa00d04
to
1c3dd19
Compare
This is ready for review - https://dev.azure.com/phpazuredevops/PHP/_build/results?buildId=22204&view=logs&j=0d3730b8-0d59-574b-ab4a-5e189a74d53b&t=492e5677-f211-533f-274d-3a4af39d4cce just seems to have been slow overall and hit the 60 minute timeout. E.g. the build step took 3 times longer than normal compared to the unrelated previous jobs - https://dev.azure.com/phpazuredevops/PHP/_build/results?buildId=22220&view=logs&j=0d3730b8-0d59-574b-ab4a-5e189a74d53b To make this easier to review or bisect if needed, I'd rather start using the precomputed cache size in the JIT in a separate commit |
|.if X64 | ||
| LOAD_ADDR CARG3, opline | ||
|.else | ||
| sub r4, 12 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused about why it's 12 bytes in other calls I've seen with 3 arguments rather than something smaller for a 4-byte pointer (in the examples I used as reference), but even if it was too large it wouldn't cause bugs
I think it doesn't make a lot of sense to implement this optimization for INIT_FCALL_BY_NAME only. The same idea may be also applied for INIT_METHOD_CALL, INIT_STATIC_METHOD_CALL, INIT_NS_FCALL_BY_NAME and NEW. Then we may measure impact on performance of real-life apps. I'm not sure, if this is a good idea or not, yet. Caching is always a compromise between the cost of calculation and memory consumption. Sometimes, it's cheaper to perform a branch-less calculation, than save a few CPU cycles, but extend cache size and get much more significant penalty because of cache misses. This needs to be checked. I'll help with JIT related changes if this will speed up real-life apps without JIT. |
INIT_NS_FCALL_BY_NAME is possible, but I wasn't sure if rebinding
|
yeah |
The computed stack size only depends on the function definition fbc and the number
of opcodes in extended_value, and that doesn't change.
INIT_FCALL_BY_NAME is used when the function definition is not available when a
file is being compiled, commonly due to the function being declared in a
different file.
Precomputing the stack size is an optimization already done by INIT_FCALL
used when the function is known.
Replacing
hallo
in Zend/micro_bench.php with a require_once to loadhallo
from a different file in a build with opcache enabled (but not the jit):
(Not sure how reliable these benchmarks are - they're possibly subject to power management affecting performance or some other factors)
Note that opcache eliminates calls to empty functions if the empty function is defined within the same file, so func() would be doing the same thing as empty_loop when opcache was enabled and take the same time as empty_loop.
I noticed this optimization while reviewing #7728 .
Extensions that modify the function definition in place can clear the run time cache entries corresponding to the opline of INIT_FCALL_BY_NAME (set CACHED_PTR to 0) if the function name matches, to force the stack size to be recomputed.