Skip to content

perf: move EG() and CG() in ZTS builds into __thread storage - #23227

Open
henderkes wants to merge 8 commits into
php:masterfrom
henderkes:perf/1-tls-eg-cg
Open

perf: move EG() and CG() in ZTS builds into __thread storage#23227
henderkes wants to merge 8 commits into
php:masterfrom
henderkes:perf/1-tls-eg-cg

Conversation

@henderkes

Copy link
Copy Markdown
Contributor

replay of #22231

Moves EG and CG into __thread storage after all. We first moved them into constant offsets (#22287) from *_tsrm_ls_cache, but I couldn't find a way to stop gcc or clang from reloading _tsrm_ls_cache base pointer between function calls, leading to an extra pointer load once per function.

This eliminates the pointer load, making the access sequence to EG/CG just a single mov (x64) / mrs + add + ldr (aarch64) under local-exec. initial-exec likewise loses the pointer load so 3 -> 2 instructions (x64).
cc @arnaud-lb

What I'm adding here to counter the global-dynamic fallback slowdown is the option to explicitly opt-in to initial-exec, under the knowledge that host programs loading it will need to increase the static tls surplus. This is not an issue for package providers.

PS: Actually figured out that the explicit model choice doesn't happen for musl, not sure about IE (static tls surplus on musl?), but LE should work just fine. That's for another PR though.

@arnaud-lb arnaud-lb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the --with-tsrm-tls-model idea.

Did a first pass, but I will take the time to review carefully.

jit->tls = ir_TLS(
tsrm_ls_cache_tcb_offset ? tsrm_ls_cache_tcb_offset : tsrm_tls_index,
tsrm_ls_cache_tcb_offset ? IR_NULL : tsrm_tls_offset);
jit->tls = ir_TLS(tsrm_tls_index, tsrm_tls_offset + offsetof(zend_tsrm_ls_cache, self));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JIT changes seem wrong.

Can we postpones these changes to a separate PR? I believe that ir_TLS() should just work, as *(void**)_tsrm_ls_cache is still valid due to the cache field in _zend_tsrm_ls_cache. We just need to change the zend_jit_get_tsrm_ls_cache() function?

In the separate PR we could change ir_TLS() so that it returns the address of _tsrm_ls_cache instead of loading it.

Comment thread TSRM/TSRM.h
Comment on lines +191 to +192
/* Windows can't dllexport the TLS struct, so outside Zend each module
* keeps a per-module `void *` pointer and reaches EG/CG via the resource-id indirection. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you expand on why it can not work on Windows?

Comment thread Zend/zend.c
{
if (alloc) {
zend_win_tsrm_cache_slot = TlsAlloc();
ZEND_ASSERT(zend_win_tsrm_cache_slot < 64); /* must be a direct TEB TlsSlot */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a zend_error_noreturn() or a print/abort if this can happen because of the environment, not only because of programming errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants