From 693a2f16aacd939115cee87674c9c0417bc6635a Mon Sep 17 00:00:00 2001 From: zinc Date: Thu, 15 Oct 2026 14:20:07 +0330 Subject: actors: large per-thread compiler tables mapped on first use instead of static tls (13 MB of every worker's stack mapping) --- src/core/core.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 18 deletions(-) (limited to 'src/core') diff --git a/src/core/core.c b/src/core/core.c index af7bba7..bf48b19 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -626,7 +626,38 @@ static VM_TLS Enh enh[NENH]; static VM_TLS int need_flush; #define MAXUOP 4096 -static VM_TLS Uop ub[MAXUOP]; static VM_TLS int un, unv, uerr; /* the program being built */ +/* large per-thread tables live behind a pointer and are mapped on first use, so a + worker thread that never compiles anything pays nothing for them (as static tls + they were zeroed into every thread's stack mapping: about 13 MB per actor) */ +static VM_TLS void **tls_big_slots[48]; static VM_TLS size_t tls_big_sizes[48]; static VM_TLS int tls_big_n; +static void *tls_big(size_t bytes, void **slot) +{ + void *p; +#if !defined(_WIN32) && !defined(__wasm__) + p = mmap(0, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (p == MAP_FAILED) p = 0; +#else + p = calloc(1, bytes); +#endif + if (!p) { fprintf(stderr, "zy: out of memory for compiler tables\n"); exit(2); } + if (tls_big_n < 48) { tls_big_slots[tls_big_n] = slot; tls_big_sizes[tls_big_n++] = bytes; } + *slot = p; return p; +} +static void tls_big_free(void) +{ + while (tls_big_n > 0) { + tls_big_n--; +#if !defined(_WIN32) && !defined(__wasm__) + munmap(*tls_big_slots[tls_big_n], tls_big_sizes[tls_big_n]); +#else + free(*tls_big_slots[tls_big_n]); +#endif + *tls_big_slots[tls_big_n] = 0; + } +} +#define TLS_BIG(p) (*((p) ? (p) : (__typeof__(p))tls_big(sizeof *(p), (void **)&(p)))) +static VM_TLS Uop (*ub_tls)[MAXUOP]; +#define ub TLS_BIG(ub_tls) +static VM_TLS int un, unv, uerr; /* the program being built */ static int unew(int op, int a, int b, int c, Cell imm) { Uop *u; @@ -838,7 +869,9 @@ static void umagic(UCell d, UCell *mul, int *add, int *sh) /* hacker's delight, *mul = q2 + 1; *sh = p - 64; } -static VM_TLS Uop ob[MAXUOP]; static VM_TLS int on, onv; +static VM_TLS Uop (*ob_tls)[MAXUOP]; +#define ob TLS_BIG(ob_tls) +static VM_TLS int on, onv; static VM_TLS int *omap, *isk; static VM_TLS Cell *kv; /* old value -> new value; constness of new values */ static int pure(int op) { return !is_memop(op); } static int ispow2(UCell x) { return x && !(x & (x - 1)); } @@ -939,8 +972,8 @@ static int oe(int op, int a, int b, int c, Cell imm) return raw(op, a, b, c, 0); } static void optimizer_storage(void) { - static VM_TLS int omap_[MAXUOP + 16], isk_[MAXUOP * 4]; static VM_TLS Cell kv_[MAXUOP * 4]; - omap = omap_; isk = isk_; kv = kv_; + static VM_TLS int (*omap_)[MAXUOP + 16], (*isk_)[MAXUOP * 4]; static VM_TLS Cell (*kv_)[MAXUOP * 4]; + omap = TLS_BIG(omap_); isk = TLS_BIG(isk_); kv = TLS_BIG(kv_); } /* runs the optimizer over ub[0..un) with nin inputs; leaves the result in e */ static int optimize(Enh *e, int nin, int *out, int nout) @@ -1142,12 +1175,18 @@ static VM_TLS int nfn; #define NPG ((code_words >> PGSH) + 1) #define MAXPGN (1 << 19) #define MAXDEP (1 << 18) -static VM_TLS struct { int a, b; } ix_blk[MAXB]; +static VM_TLS struct { int a, b; } (*ix_blk_tls)[MAXB]; +#define ix_blk TLS_BIG(ix_blk_tls) + static VM_TLS int ix_n; static VM_TLS int *pg_head, pg_free, pg_top, pg_live; /* node numbers are stored plus one: 0 is none */ -static VM_TLS struct { int blk, next; } pg_node[MAXPGN]; +static VM_TLS struct { int blk, next; } (*pg_node_tls)[MAXPGN]; +#define pg_node TLS_BIG(pg_node_tls) + static VM_TLS int dep_head[NENH + 1], dep_n; /* slot NENH: depends on every install */ -static VM_TLS struct { int blk, next; } dep_node[MAXDEP]; +static VM_TLS struct { int blk, next; } (*dep_node_tls)[MAXDEP]; +#define dep_node TLS_BIG(dep_node_tls) + static VM_TLS int drop_epoch; /* changes whenever any translation dies */ static void be_drop(int j); /* the backend forgets block j's code and coverage */ static void runmark(Rg *R); /* the backend stops watching the region's words */ @@ -1878,8 +1917,12 @@ static VM_TLS int lv_[RMAXV], lw_[RMAXV]; #define SB 1100 static VM_TLS int skv[2 * SB], stp, slo, shi; static VM_TLS unsigned char skk[2 * SB]; -static VM_TLS int afp[MAXUOP * 4 + 16]; /* value -> 1 + its offset from fp, if it is fp + k */ -static VM_TLS int emap[MAXUOP * 4 + 16]; +static VM_TLS int (*afp_tls)[MAXUOP * 4 + 16]; +#define afp TLS_BIG(afp_tls) +/* value -> 1 + its offset from fp, if it is fp + k */ +static VM_TLS int (*emap_tls)[MAXUOP * 4 + 16]; +#define emap TLS_BIG(emap_tls) + static int rvar(int v) { @@ -2360,7 +2403,11 @@ enum { C_UNSEEN, C_CONST, C_VAR, C_COPY }; /* C_COPY: the variable holds the same value as variable v (both at the same point) */ static int copy_ok(const Rg *R, int j, int k) { return j > 0 && j < RMAXV && k > 0 && k < R->nv && !R->fv[j] && !R->fv[k]; } typedef struct { unsigned char st[RMAXV]; Cell v[RMAXV]; } CIn; -static VM_TLS int g_isk[MAXUOP * 4]; static VM_TLS Cell g_kv[MAXUOP * 4]; static VM_TLS int g_omap[MAXUOP + 16]; +static VM_TLS int (*g_isk_tls)[MAXUOP * 4]; +#define g_isk TLS_BIG(g_isk_tls) +static VM_TLS Cell (*g_kv_tls)[MAXUOP * 4]; +#define g_kv TLS_BIG(g_kv_tls) +static VM_TLS int g_omap[MAXUOP + 16]; static int cjoin(CIn *d, const CIn *s) { int j, ch = 0; @@ -3963,7 +4010,7 @@ static int spec_enh(int s, Cell *ks, int nk, Enh *te) { Enh *e = &enh[s]; int nin = e->nin - nk, i, j, out[8], na; - static VM_TLS int mp[MAXUOP * 4 + 16]; + static VM_TLS int (*mp_tls)[MAXUOP * 4 + 16]; int *mp = TLS_BIG(mp_tls); if (e->nv > MAXUOP * 4) return -1; un = 0; unv = nin; uerr = 0; for (j = 0; j < nin; j++) mp[j] = j; @@ -5237,9 +5284,13 @@ static Blk lcompile(int p) #if !defined(ZYLLVM) && !defined(ZYJIT_X64) static VM_TLS unsigned char *rcov; /* 1 on the words of a lifted region */ #endif -static VM_TLS Cell rvals[MAXUOP * 4 + 16]; +static VM_TLS Cell (*rvals_tls)[MAXUOP * 4 + 16]; +#define rvals TLS_BIG(rvals_tls) + #define RPOOL 40 -static VM_TLS Cell rpool[RPOOL][MAXUOP * 4 + 16]; +static VM_TLS Cell (*rpool_tls)[RPOOL][MAXUOP * 4 + 16]; +#define rpool TLS_BIG(rpool_tls) + static VM_TLS int rpool_n; static int rdrop(int a) /* a store landed on region code: drop those regions */ { @@ -6090,7 +6141,11 @@ static void float_op(int i, int k) static const int xreg[] = { r_si, r_di, r_8, r_9, r_10, r_11, r_bp }; #define XNREG 7 #define XMAXOP 400 -static VM_TLS int xkc[MAXUOP * 4 + 16]; static VM_TLS Cell xkv[MAXUOP * 4 + 16]; +static VM_TLS int (*xkc_tls)[MAXUOP * 4 + 16]; +#define xkc TLS_BIG(xkc_tls) +static VM_TLS Cell (*xkv_tls)[MAXUOP * 4 + 16]; +#define xkv TLS_BIG(xkv_tls) + static VM_TLS signed char *xr; static int x64_op_ok(int op) { @@ -6110,7 +6165,7 @@ static int x64_spec_enh(int s, Cell *ks, int nk, Enh *te) { Enh *e = &enh[s]; int nin = e->nin - nk, i, j, out[8], na; - static VM_TLS int mp[MAXUOP * 4 + 16]; + static VM_TLS int (*mp_tls)[MAXUOP * 4 + 16]; int *mp = TLS_BIG(mp_tls); if (e->nv > MAXUOP * 4 || e->n > XMAXOP) return -1; for (i = 0; i < e->n; i++) if (!x64_op_ok(e->u[i].op)) return -1; un = 0; unv = nin; uerr = 0; @@ -6131,7 +6186,9 @@ static int x64_spec_enh(int s, Cell *ks, int nk, Enh *te) return 0; } /* the register value v lives in, or scratch with the constant v loaded into it */ -static VM_TLS int xvh[MAXUOP * 4 + 16]; /* regions: 1 + the var a value reads at block entry, 0 if none */ +static VM_TLS int (*xvh_tls)[MAXUOP * 4 + 16]; +#define xvh TLS_BIG(xvh_tls) +/* regions: 1 + the var a value reads at block entry, 0 if none */ static VM_TLS Rg *xR; static VM_TLS Cell xsyn[RMAXV]; /* the home of a var the lifter made (a hoisted value), which has no frame slot */ static int xv(int v, int scratch) @@ -6258,7 +6315,7 @@ static Cell xev(long op, Cell a, Cell b, Cell c) { return ev((int)op, a, b, c); /* a value's last use, registers on the six, -1 if the block doesn't fit or can't be compiled */ static int x64_ralloc(RB *B) { - static VM_TLS int last[MAXUOP * 4 + 16]; + static VM_TLS int (*last_tls)[MAXUOP * 4 + 16]; int *last = TLS_BIG(last_tls); int i, j, k, na, fr = (1 << RXNREG) - 1, n = B->n; if (B->nv > MAXUOP * 4) return -1; for (i = 0; i < B->nv; i++) { last[i] = -1; xkc[i] = 0; xvh[i] = 0; rxr[i] = -1; } @@ -7506,7 +7563,7 @@ static void context_engine_free(void) { free(lcov); free(lt); free(hot); #endif #endif - if (memory_mapped) vm_memory_unmap(m); else free(m); m = 0; snap_drop(); atomic_fetch_sub(&context_memory_live, 1); + if (memory_mapped) vm_memory_unmap(m); else free(m); m = 0; snap_drop(); tls_big_free(); atomic_fetch_sub(&context_memory_live, 1); } static void *context_worker(void *opaque) { VmContext *context = opaque; -- cgit v1.2.3