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 <noreply@anthropic.com>
This commit is contained in:
committed 2026-10-31 18:10:26 +03:00
1 parent 7d62cbfa85
commit 8dd7e2041e
1 file changed
+8 -1
+8 -1
View File
@@ -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) {