Goose

slice_pool versus reusable[]

reusable and reusable[] remain deprecated but available. These comparisons keep the builtins as baselines; removal is a separate change. Application measurements and migration decisions are in pool_workloads.md.

The library keeps its free spans in the builtin's own augmented B+ tree: the runtime's span tree, reached through three externs on its nodes and tree fields. Its copy of the tree functions differs from reusable[]'s only in taking the header apart from the nodes and in checking what it reads from them, which the program can write. Differential tests check placement as well as contents.

Current measurements

Measured 2026-10-28 on Windows, Ryzen 9 9950X3D, CPUs 16–31. Ratios are library / builtin; smaller is better. Clang compiles the generated C at O2. Each cell is the ratio of median process-cycle counts from seven interleaved samples after two warmups, with identical checksums. The six-case means give equal weight to each workload/size pair. These allocation-heavy loops have no application work between operations.

Workload Bytes O1 Clang O1 TinyCC O1 JIT total O2 Clang O2 TinyCC O2 JIT total
append 8 0.934 1.033 1.100 0.960 1.017 1.069
append 128 1.007 1.027 1.017 1.009 1.036 1.009
fragmented 8 0.994 1.084 1.205 0.995 1.087 1.196
fragmented 128 0.999 1.080 1.140 1.006 1.077 1.140
resize 8 1.011 1.143 1.229 1.014 1.116 1.194
resize 128 0.995 1.157 1.253 1.003 1.112 1.140
Geometric mean 0.990 1.086 1.154 0.998 1.073 1.123

The same run measured the previous library, whose tree was checked Goose code, as a third side. The new library takes this fraction of its cycles:

Workload Bytes O1 Clang O1 TinyCC O1 JIT total O2 Clang O2 TinyCC O2 JIT total
append 8 1.016 1.007 0.879 1.019 1.005 0.849
append 128 1.002 1.010 0.967 1.004 1.011 0.975
fragmented 8 0.892 0.730 0.457 0.901 0.731 0.453
fragmented 128 0.925 0.754 0.507 0.923 0.751 0.506
resize 8 0.918 0.729 0.470 0.872 0.708 0.460
resize 128 0.882 0.793 0.615 0.910 0.762 0.541
Geometric mean 0.938 0.829 0.620 0.937 0.819 0.601

Standalone TinyCC measures execution only. JIT total includes Goose compilation, TinyCC compilation and execution at the stated optimization level; it is not an allocation-only measurement. Process startup, initialization and final checksumming are included on both sides. Wall times are also retained, but were more sensitive to background activity than process cycles.

Reproduce

python bench/slice_pool.py --exe build/Release/goose.exe --levels 1 2 --calls natural --widths 1 16 --compilers clang tcc jit --reps 7 --warmups 2 --affinity 0xffff0000 --previous-library <old>/slice_pool.goose --out build/slice-pool-bench-final

<old> holds stdlib/slice_pool.goose and stdlib/slice_pool_internal.goose from 06a6300f. The harness saves Goose/C snapshots, logs, checksums, compiler and library fingerprints, sizes and all samples under --out. The checked-in current results contain this matrix. --levels 0 is also supported. --calls natural leaves the emitted C unchanged; boundary adds GS_NOINLINE to the native exercise function, while inline requests inlining. JIT always uses the compiler's unmodified output. TinyCC is discovered beside the Goose build or on PATH. MSVC is available via --compilers msvc; timings here use Clang, standalone TinyCC and the JIT.

Interpretation and remaining work

With the tree in C, Clang is at parity. Under the JIT the tree runs natively, as reusable[]'s does, which is where the old library lost most (2.0–2.7×); under standalone TinyCC both trees are TinyCC code. What remains under TinyCC and the JIT is the Goose code around the tree calls, which TinyCC compiles as written. At O2 the resize loop runs about 60 cycles per step more than the builtin's under both. Of those, the checks the tree copy makes take about 1.5% of the run under TinyCC. Three 16-byte slice copies that inlining realloc_slice and finish binds as parameters take about 12 million of 58 million cycles. Snapshotting the gs_rref argument, writing back cached tops around the call, and the library's own bounds and span checks take about 1% each. Calling the tree through a wrapper made no measurable difference. JIT totals also pay for compiling the inlined library code, about 7–15 million cycles a program here, which is most of the append rows' gap. Not inlining alloc_slice slowed append without helping reuse.

reusable[] keeps the unchecked tree functions. Sharing the checked copy measured 3–5% slower for it on fragmented and resize under TinyCC and the JIT. With its own copy it measures at master's speed: geometric means of 1.001 Clang, 0.991 TinyCC and 1.017 JIT over the same 12 workload/level pairs.