From 8dd7e2041ea7a85dd11bba51df0d7b3fe8beaabb Mon Sep 17 00:00:00 2001 From: Valiantsin Dzerakh Date: Sat, 31 Oct 2026 18:10:26 +0300 Subject: [PATCH] Cache the thread id per thread on Linux (#236) plat_thread_id made a gettid syscall on every call, and plat_rmutex_lock calls it on every acquire: about 685,000 syscalls a second in the open world on 1.4.3. A thread's id never changes and the runtime starts child processes with posix_spawn only, so the id now comes from a __thread cache after the first call. Windows is unchanged: GetCurrentThreadId reads the TEB. Found, measured and fixed by H.A. Forster in issue #236 (RTX 2060 laptop: 36.3 to 46.0 fps with replay_async). SchulzCode confirmed it on an RTX 2070 Super with a 7800X3D: 98 to 120 fps at game resolution, 76 to 90 fps at render_scale 150. Co-Authored-By: H.A. Forster <154697759+h-a-forster@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 --- runtime/platform/linux.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/platform/linux.c b/runtime/platform/linux.c index 0b9f572a..3520f91e 100644 --- a/runtime/platform/linux.c +++ b/runtime/platform/linux.c @@ -1172,7 +1172,14 @@ size_t plat_getenv(const char *name, char *out, size_t cap) { return n; } -uint32_t plat_thread_id(void) { return (uint32_t)syscall(SYS_gettid); } +// Cached per thread: plat_rmutex_lock asks on every acquire, hundreds of thousands of times a second in play, and +// gettid is a full syscall on Linux (Windows reads GetCurrentThreadId from the TEB). A thread's id never changes, and +// the runtime starts child processes with posix_spawn only, so no forked copy inherits a stale id. +uint32_t plat_thread_id(void) { + static __thread uint32_t tid; + if (!tid) tid = (uint32_t)syscall(SYS_gettid); + return tid; +} // The first set variable of names ("ru_RU.UTF-8") as a BCP-47 tag ("ru-RU") into tag; "" for C and POSIX. static void locale_tag(const char *const *names, int n, char *tag, size_t cap) {