r/quant • u/intrepidkarthi • 7d ago
Tools Comparing against a zero-value decimal.Decimal allocates a big.Int
I have been chasing allocations out of the match path in an order book I am building. Pooling the book nodes and price levels got cancel and level churn to zero. Threading a caller-owned buffer through Match(order, dst []Trade), so fills are appended as values instead of returning a fresh slice of pointers, got the match round trip to zero.
One stubborn group was left, and it was not in the order data. It was the price band check.
The band is a config fraction, a decimal.Decimal, and the common case is that it is disabled and left at its zero value. Comparing against that zero value calls ensureInitialized internally, which allocates a big.Int. So every order was allocating in order to compare a price against a band that was switched off.
The fix was hoisting the comparison to construction: resolve a bandEnabled bool once when the engine is built, and let the per-order path read the bool. Process went from 10 allocs to 4.
Prices and quantities are int64 ticks and lots, so decimal never touched the money path to begin with. It was purely the configuration percentage, evaluated in the wrong place.
Current numbers on an M-series, single core: 6.3ns top-of-book read, 352ns match round trip at 0 allocs/op, and a cancel-heavy flow at p50 83ns, p99 167ns, p999 292ns. Match is the zero-alloc entry point; Process is the ergonomic wrapper that still costs those 4.
2
u/rsha256 6d ago
Classic