Repository navigation
[memory] layout: hold fixed leaf values in their leaf nodes - #74
Conversation
ukaratay
left a comment
There was a problem hiding this comment.
The encoding is right: closing the gap and the arithmetic shift restore the packed value, sign included.
Before this merges I want one paired A/B on an AVX-512 Google Cloud machine. Every latency number in the stack is from an M4 Pro, and this PR changes list_avx512, which the M4 never runs: a masked load and a widening per 8 items on the hot path. Across #67, #68 and this PR, 1 candidate pays +1.3%, +0.7% and +2.4%, consistent in every round. 9.4% of the loaded heap is worth it for users who load into their own memory. But every user pays the latency and can't opt out, so measure it where production runs.
bd7b0e0 to
6f5c3f0
Compare
|
Measured on a GCP c4-standard-8 (Intel Xeon Platinum 8581C, AVX-512, SMT off, all-core turbo, one pinned core), on the private model, with six paired rounds (A B B A) per pair. Every build gives bitwise equal outputs there, equal to the M4's.
"noise" marks a cell whose rounds disagree in sign; in every other cell, all six rounds agree. On AVX-512, #67 is faster at every size. So the three together are faster than #66 at every served size and at 512 and 1,024 rows, and within noise at 128. The description has the ranges. |
ukaratay
left a comment
There was a problem hiding this comment.
Thanks for the c4 numbers. I was wrong about list_avx512: the 14-row cap keeps served requests on the cursor loop. +2% at 1 and 32 candidates for 9.4% of the loaded heap, with the three PRs together faster than #66 at every served size, is a trade I'll take.
Hold each Fixed leaf in its node instead of an eight-byte entry in a separate table. Its packed signed significand and shift go in word 1, with a gap at the leaf flag; removing the gap restores the packed value exactly. Word 0 stays zero, slot included, because lanes evaluate the bin test before checking the flag. Keep the walks and their split steps unchanged. Constant passes decode the word the walk returns; grouped Fixed entries carry the stop link and the leaf pass reads the node through it. Resolve selects the all-rows mask before reading a Fixed leaf as a mask field. Keep separate Fixed and Wide resolve instantiations, so Wide retains its table indices and Fixed keeps its generated hot paths. The plan selects the leaf source: Wide plans keep one vector of values, empty under Fixed. Introspection counts no Fixed leaf table. Tests round-trip the encoding through leaf nodes; the existing split and reference checks cover both prediction paths.
6f5c3f0 to
0d79175
Compare
Motivation
After #68, a fixed-point leaf's value is an eight-byte entry in a table beside the nodes, though the leaf's node already spends 16 bytes on two words, most of whose bits a leaf doesn't use. Holding the value in the node removes the table and its index.
Design
layout: hold fixed leaf values in their leaf nodesFixedplan, a leaf's packed value ([memory] layout: fixed-point leaf values take 8 bytes #68's signed odd significand and shift) is in its node's word 1, with a gap at the leaf flag; removing the gap restores the packed value exactly. Word 0 stays zero, slot included, because lanes evaluate the bin test before checking the flag. A Fixed leaf's mask-field bits hold significand bits, so readers check the leaf flag first.Wideplans keep their table of limbs, one vector that is empty under Fixed. Resolve stays instantiated separately for Fixed and Wide: folding them adds instructions to Fixed's x86-64 hot paths (resolve 41 to 43, the leaf pass 552 to 554).Evidence
predict_fixedper request on the generated requests, one thread, Apple M4 Pro: p50 change from [memory] layout: fixed-point leaf values take 8 bytes #68, median of four paired rounds (A B B A, 1 s per cell), with the rounds' range:On AVX-512 (GCP c4-standard-8, Intel Xeon Platinum 8581C, SMT off, all-core turbo, one pinned core; six paired rounds), for the served requests and for groups of 128 to 1,024 rows (
max_group_width1024): like any group of 65 to 1,024 rows, these takeHandlecarriers and reachlist_avx512:A served request's 14-row group is
W32, withu32carriers, so resolve takes its cursor loop there; onlyHandlecarriers (65 to 1,024 rows) go throughresolve_listedandlist_avx512. On those groups, this PR costs 1.1% at 128 rows and is 1.7% faster at 512 and 1,024. With #67 and #68, the stack is faster than #66 on AVX-512 at every served size.Where the rounds disagree in sign, only their range is given.
cargo test --release --features researchpasses; clippy (workspace, research, library alone) and fmt.