Bytecode cache: numbered strings with stored hashes, so repeat lookups skip the atom table
Merged
JA
3mo ago+186+186+012345678901234567890123456789-19-19-01234567890123456789
Atomizing identifiers and string constants was ~23% of decoding a code block (hash, probe, compare on every reference; the table regrowing as they trickle in). Now:
- Each distinct non-symbol string record stores its
StringImpl::hash()and a sequence number assigned in encode order. Decode adds viaAtomStringImpl::add(HashTranslatorCharBuffer{chars, hash})(no rehash) and remembers the atom per number in aVectoron theDecoder; later references are an index. 1- and 2-character strings that live inline in their slot (#499) are memoized in two flat tables keyed by their characters. - String constants (
CachedJSValue) decode as plainStringImpls (aliasing the payload when long) — only identifiers need to be atoms — so half the string references never touch the table. HashTable::reserveCapacity/AtomStringImpl::reserveCapacityForCurrentThread: a block reserves for its identifiers+constants before adding them.- Update payloads appended by
CachedBytecode::addFunctionUpdate(jsc shell) continue the base payload's numbering:Encodertakes a first number,CachedBytecoderecords the end,encodeFunctionCodeBlocktakes it.
30 MB / 58-library bundle, jsc shell, interleaved runs on pinned cores (box under load, so medians):
| main (aff53044) | this | |
|---|---|---|
| decode every top-level function (60k), cycles:u | 1170 M | 874 M (−25%) |
| decode everything (128k blocks), cycles:u | 2164 M | 1781 M (−18%) |
| cache size | 87.7 MB | 89.2 MB (+8 B per string record) |
JSTests/stress through the disk cache (persistent + non-persistent): unchanged vs main.
For scale: on the same bundle compiled with bun build --compile --bytecode, samply puts bytecode decode at ~21% of main-thread startup (150 ms of 0.72 s), and the atom table is over a third of that.