← Playground · Strategy experiments
Profile → change → verify → measure

More search.
The same decisions.

The MC-taught linear bot now runs substantially faster. Fixed-work comparisons preserve every sampled result and recommendation. All intensive measurements ran inside a Linux ARM container limited to two CPU cores and 2 GiB RAM, with no swap.

8.8×Native fixed-work speedup
1 worker · 5 repeats
7.0×Complete-bot throughput gain
2 workers · 3 repeats
31.0 MiBWeb-server process peak RSS
2 simultaneous clients
1,838Identical full-game decisions
32 fresh regression deals
Complete-bot throughput before and after optimization, and 100 ms simulation gains by game stage

On small screens, scroll the chart or tables sideways. Whiskers show observed repeat ranges, not statistical confidence intervals.

What the measurements include

The native comparison includes the JSON boundary and precomputed linear priors. The complete-bot comparison also includes public feature calculation, linear inference, recommendation explanations, worker communication, and batch scheduling. Immediate-win shortcuts were disabled in these position benchmarks so both versions had to simulate; normal play still uses those shortcuts. Each version handles the same 96 positions × 3 repeats × 1,024 futures below.

WorkersVersionFutures / secondMean decision (ms)p95 decision (ms)Decisions
1Before14,61969.95144.67288
1Optimized103,3959.8218.52288
2Before28,24471.61148.67288
2Optimized198,03110.0219.16288

Throughput uses total futures divided by batch wall time. Decision latency uses individual calls; two workers improve aggregate capacity, not the latency of a single search. Each search remains single-threaded.

What fits in 100 ms?

With two workers, the optimized bot completed 2.78× as many futures overall. The median gain across individual positions was 9.32×. Both are useful: easy late positions contribute far more futures to the aggregate count, while openings and middle games benefit more from cheaper move scoring.

StageBefore: mean futuresOptimized: mean futuresRatio
Opening7237,1229.85×
Middle1,74415,8729.10×
Late58,344146,0182.50×

The stages are the first, middle, and last recorded position from each collected game. Last snapshots have 5–11 defeated enemies (median 10), and can precede either a win or a loss. Each stage contains 32 positions, repeated three times per version. The simulation cap was raised to 1,000,000 solely to keep it from ending these timed searches; no call reached it. The final bot’s two-worker p95 was 104.33 ms: the allowance is checked between futures, and serialization/explanations add a small overhead.

This is a speed study. More futures do not establish a higher win rate. The corpus comes from development/training deals, not held-out strength evaluation. Repeated positions are not independent games. These are host-CPU measurements in a constrained ARM Linux container, not measurements on the deployment box.

Where the work disappeared

The initial instruction profile was dominated by legal-move construction and allocation/free operations. The same profiling workload fell from 17.07 billion to 1.99 billion instructions (88.4% fewer). Callgrind counts instructions; these percentages are not CPU-time shares.

Experiments we did not keep

Collapsing additional defensive cache keys showed no clear speed gain. A 65,536-entry cache was slower than the existing 16,384-entry default on the tested 4,096-future workload; 2,048 entries performed about the same. Fat LTO did not beat thin LTO. These trials and their exact outputs are retained in the experiment folder.

Correctness and resource checks

Remaining limits

This is not proof that further optimization is impossible. Candidate scoring and legal-subset traversal still dominate the hot loop. The next defensible step is profiling the actual deployment CPU and checking a broader state distribution. Parallelizing one adaptive search or changing rollout policies would be a separate algorithm experiment, requiring new strength comparisons.

Reproduce and inspect the evidence

The frozen binaries, model, public positions, source snapshots, raw timings, profiles, regression replays, and rejected trials are in experiments/linear-optimization/. See docs/NATIVE.md for constrained-container commands.

Download audited summary and source hashes ↓

python3 tools/compare_linear.py --libraries '{"before":"experiments/linear-optimization/baseline.so","optimized":"experiments/linear-optimization/final.so"}' --repeat 5 --output /tmp/native-repeat.json
python3 tools/bench_linear_full.py --libraries '{"before":"experiments/linear-optimization/baseline.so","optimized":"experiments/linear-optimization/final.so"}' --workers 2 --repeat 3 --output /tmp/full-repeat.json

Run the Linux binaries inside the documented container; do not load them into macOS Python. The normal app uses a separately rebuilt native library for your operating system.

Model SHA-256: dc1c0b485eb7ec14cab5…
Source fingerprint: 0595762fade80e7dec57…