Reuse scratch subject buffer in processMsgResults to eliminate heap allocation on hot

perfloop/nats-server · ALLOCATION HOT LOOP

https://perfloop.ai/t/oss/case_75tfr1qge9

Verdict

VALIDATED BY A DIFFERENT CHANGE · settled 2026-10-03

What happened: The paired measurements met the required improvement.

Hypothesis

Using `go build -gcflags=-m`, we observed: `server/client.go:5170:6: moved to heap: _dsubj`. Go memory allocation profiling on `BenchmarkPublish/MsgSz=32b/Subs=1xAsync` confirms that `_dsubj [128]byte` is heap-allocated on every message delivery, contributing 6,497,049 object allocations (~31.98% of the total). Because connection message processing is single-threaded per connection, we can safely persist and reuse this scratch buffer on the `client.in` or `client` struct to achieve allocation-free execution on this hot path.

Change to test: Move the scratch subject byte array `_dsubj` to be a persistent field on the `readCache` (or `client`) struct so it can be reused across calls without escaping to the heap on every message delivery.

Where it lives

perfloop/nats-server · server/client.go

Evidence

BenchmarkPublish/MsgSz=32b/Subs=1xAsync · 10 sample pairs

metric baseline candidate paired median change confidence range required result
ns/op 351.1 316.4 −9.2% (−32.35) −46.8 to −12.8 ≤ 0 PASSED
MB/s 91.16 101.2 +10.2% (+9.34) +3.43 to +14.21 ≥ 0 PASSED
%error 0 0 0 0 to 0 ≤ 0 PASSED
B/op 269.5 146 −45.6% (−123) −124 to −122 < −13.48 PASSED
allocs/op 2 1 −50% (−1) −1 to −1 < −0.1 PASSED

Checks: 6 of 6 passed. Verification: no defect found.

Timeline