Skip to content

Read the GC's thread context from a __thread pointer on Linux - #429

Open
Tutez64 wants to merge 1 commit into
openfl:mainfrom
Tutez64:perf/thread-local-tls
Open

Tutez64 wants to merge 1 commit into
openfl:mainfrom
Tutez64:perf/thread-local-tls

Conversation

@Tutez64

@Tutez64 Tutez64 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

On Linux, the fast thread-local slot (DECLARE_FAST_TLS_DATA, used for the
GC's stack context tlsStackContext) is read with pthread_getspecific: a
call through the PLT on each allocation that leaves the inline path, each time
a String or a number is boxed, and wherever HX_CTX_GET is used. In the
profile of an OpenFL game, pthread_getspecific took 3 to 4% of the main
thread's samples, spread over allocations everywhere.

With GCC and Clang on Linux, the fast slot is now a __thread pointer, read
with one load from the thread pointer. __thread rather than thread_local:
for an extern thread_local, C++ calls a wrapper in case another unit
initializes the variable dynamically. Only the fast slot changes, and only on
Linux, the platform this was measured on; defining HXCPP_PTHREAD_FAST_TLS
keeps pthread_getspecific. WinRT already declares the slot the same way.

Benchmarks (bash bench/ci.sh fb7f8045, Linux, Ryzen 7 3700X, gcc 14.2,
3 processes x 5 samples): 20 improvements past the noise, among them
alloc_strings 1.14x, anon_field 1.22x, array_pop 1.16x, sort_int 1.12x and
1.07 to 1.16x on most CPPIA workloads. The two regressions it flagged
(cffi_string_view 0.86x, the tail pause of large_cache_purge) were level on a
rerun of those two with 5 processes x 10 samples (0.94x ±12%, 0.88x ±78%).

Tests: test/haxe, test/native and test/gc_stress (10 s with 4 threads, 60 s
with 8) pass on Linux 64-bit.

On Linux the fast thread-local slot, the GC's stack context, was read with pthread_getspecific: a call through the PLT on each allocation that leaves the inline path, each time a String or a number is boxed, and wherever HX_CTX_GET is used. In an OpenFL game it was 3 to 4% of the main thread's samples. With GCC and Clang on Linux it is now a __thread pointer, read with one load from the thread pointer. __thread rather than thread_local: for an extern thread_local, C++ calls a wrapper in case another unit initializes it dynamically.

Defining HXCPP_PTHREAD_FAST_TLS keeps pthread_getspecific.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant