.nornir/assets/static-tree-vs-ann.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1180 760" font-family="'DejaVu Sans', 'Segoe UI', sans-serif">
<defs>
<linearGradient id="board" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#0b1c2e"/>
<stop offset="1" stop-color="#06111d"/>
</linearGradient>
<linearGradient id="chip" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#14384f"/>
<stop offset="1" stop-color="#0e2a3e"/>
</linearGradient>
<linearGradient id="rail" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#173d57"/>
<stop offset="1" stop-color="#0c2438"/>
</linearGradient>
<linearGradient id="ice" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#e4f5ff" stop-opacity="0.95"/>
<stop offset="0.55" stop-color="#bfe6ff" stop-opacity="0.6"/>
<stop offset="1" stop-color="#bfe6ff" stop-opacity="0.08"/>
</linearGradient>
</defs>
<!-- PCB substrate -->
<rect x="0" y="0" width="1180" height="760" fill="url(#board)"/>
<g stroke="#143a52" stroke-width="1">
<line x1="0" y1="76" x2="1180" y2="76"/>
<line x1="0" y1="712" x2="1180" y2="712"/>
</g>
<g fill="#16415c">
<circle cx="40" cy="40" r="2"/><circle cx="1140" cy="40" r="2"/>
<circle cx="40" cy="732" r="2"/><circle cx="1140" cy="732" r="2"/>
</g>
<!-- title -->
<text x="40" y="42" fill="#eaf6ff" font-size="22" font-weight="700">Two static structures, one Ragnar</text>
<text x="392" y="42" fill="#9fc9e0" font-size="14">exact-key <tspan fill="#bfe6ff" font-weight="700">S+tree</tspan> · approximate-NN <tspan fill="#bfe6ff" font-weight="700">IVF</tspan> — same cache-aware DNA</text>
<!-- legend -->
<g font-size="12.5">
<g stroke="#dff4ff" stroke-width="1.4" stroke-linecap="round" transform="translate(842,34)">
<line x1="0" y1="-6" x2="0" y2="8"/><line x1="-7" y1="1" x2="7" y2="1"/>
<line x1="-5" y1="-4" x2="5" y2="6"/><line x1="5" y1="-4" x2="-5" y2="6"/>
</g>
<text x="854" y="42" fill="#cfe6f5">static (build-once, query-many)</text>
<line x1="1044" y1="38" x2="1082" y2="38" stroke="#6fd3f5" stroke-width="3"/>
<text x="1090" y="42" fill="#cfe6f5">hot query</text>
</g>
<!-- ==================== LEFT PANEL: STree32 (exact) ==================== -->
<rect x="34" y="96" width="540" height="470" rx="12" fill="none" stroke="#24506e" stroke-width="1.5" stroke-dasharray="3 4"/>
<text x="54" y="126" fill="#eaf6ff" font-size="17" font-weight="700">stree32 · Ragnar S+tree</text>
<text x="54" y="146" fill="#9fc9e0" font-size="12" font-family="'DejaVu Sans Mono', monospace">find_exact(q: u32) → Option<usize></text>
<text x="54" y="163" fill="#86b3cc" font-size="11">1-D ordered keys · implicit tree · 100% exact</text>
<!-- query pin -->
<circle cx="300" cy="188" r="6" fill="#6fd3f5" stroke="#0a1622"/>
<text x="312" y="192" fill="#a9d8ee" font-size="11.5" font-family="'DejaVu Sans Mono', monospace">q</text>
<!-- implicit S+tree: root -> level1 -> leaf line, cache-line nodes (B=16 keys) -->
<!-- root node -->
<rect x="214" y="200" width="176" height="34" rx="6" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2"/>
<text x="302" y="222" fill="#eaf6ff" font-size="11.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">node = 64 B line · 16×u32</text>
<path d="M300 188 V200" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<!-- level 1: three nodes -->
<g>
<rect x="70" y="270" width="132" height="30" rx="6" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.5"/>
<rect x="236" y="270" width="132" height="30" rx="6" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2"/>
<rect x="402" y="270" width="132" height="30" rx="6" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.5"/>
<text x="136" y="290" fill="#9fc9e0" font-size="11" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">16 keys</text>
<text x="302" y="290" fill="#eaf6ff" font-size="11" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">16 keys</text>
<text x="468" y="290" fill="#9fc9e0" font-size="11" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">16 keys</text>
</g>
<!-- child arithmetic (no pointers) -->
<g stroke="#5aa6cf" stroke-width="1.4" fill="none" stroke-dasharray="4 3">
<path d="M260 234 L136 270"/>
<path d="M344 234 L468 270"/>
</g>
<path d="M302 234 V270" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<text x="404" y="252" fill="#86b3cc" font-size="10">child = k·(B+1)+i — no pointers</text>
<!-- leaf layer (sorted array) -->
<rect x="70" y="336" width="464" height="30" rx="6" fill="url(#rail)" stroke="#7fc8ea" stroke-width="1.5"/>
<text x="302" y="356" fill="#dff1ff" font-size="11.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">sorted leaf array — the keys themselves</text>
<path d="M302 300 V336" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<circle cx="302" cy="336" r="4" fill="#bfecff"/>
<!-- SIMD compare callout -->
<rect x="70" y="392" width="230" height="52" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="185" y="412" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">branchless SIMD compare</text>
<text x="185" y="429" fill="#9fc9e0" font-size="10.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">count_lt: _mm256_cmpgt_epi32</text>
<text x="185" y="440" fill="#86b3cc" font-size="9.5" text-anchor="middle">8×u32 lanes/instr · popcount(movemask)/4</text>
<!-- depth note -->
<rect x="316" y="392" width="218" height="52" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="425" y="412" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">log₁₇(N) dependent loads</text>
<text x="425" y="429" fill="#9fc9e0" font-size="10.5" text-anchor="middle">one cache miss per level</text>
<text x="425" y="440" fill="#86b3cc" font-size="9.5" text-anchor="middle">B=16 → shallow → fewer misses</text>
<!-- output -->
<rect x="180" y="470" width="244" height="34" rx="8" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2"/>
<text x="302" y="492" fill="#eaf6ff" font-size="12.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">Some(index) · EXACT</text>
<path d="M302 444 V470" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<!-- static icicles under the leaf -->
<g>
<path d="M96 366 L104 366 L100 388 Z" fill="url(#ice)"/>
<path d="M180 366 L188 366 L184 382 Z" fill="url(#ice)"/>
<path d="M300 366 L308 366 L304 390 Z" fill="url(#ice)"/>
<path d="M420 366 L428 366 L424 380 Z" fill="url(#ice)"/>
<path d="M500 366 L508 366 L504 386 Z" fill="url(#ice)"/>
</g>
<text x="54" y="536" fill="#86b3cc" font-size="10.5">40× faster than binary search in the RAM-bound regime</text>
<!-- ==================== RIGHT PANEL: RagnarsEmb IVF (approx) ==================== -->
<rect x="606" y="96" width="540" height="470" rx="12" fill="none" stroke="#24506e" stroke-width="1.5" stroke-dasharray="3 4"/>
<text x="626" y="126" fill="#eaf6ff" font-size="17" font-weight="700">vann · RagnarsEmb IVF</text>
<text x="626" y="146" fill="#9fc9e0" font-size="12" font-family="'DejaVu Sans Mono', monospace">search(q: &[f32], k) → [(id, score)]</text>
<text x="626" y="163" fill="#86b3cc" font-size="11">768-D vectors · k-means + posting lists · approximate</text>
<!-- query pin -->
<circle cx="876" cy="188" r="6" fill="#6fd3f5" stroke="#0a1622"/>
<text x="888" y="192" fill="#a9d8ee" font-size="11.5" font-family="'DejaVu Sans Mono', monospace">q (768-D)</text>
<!-- centroid row -->
<rect x="642" y="200" width="468" height="40" rx="8" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.5"/>
<text x="876" y="216" fill="#eaf6ff" font-size="11.5" text-anchor="middle" font-weight="700">nlist centroids (√N)</text>
<g>
<rect x="660" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="690" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="720" y="222" width="22" height="12" rx="2" fill="#173d57" stroke="#7fc8ea" stroke-width="1.6"/>
<rect x="750" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="780" y="222" width="22" height="12" rx="2" fill="#173d57" stroke="#7fc8ea" stroke-width="1.6"/>
<rect x="810" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="840" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="870" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="900" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="930" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="960" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="990" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="1020" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="1050" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
<rect x="1080" y="222" width="22" height="12" rx="2" fill="#0e2a3e" stroke="#5aa6cf"/>
</g>
<path d="M876 188 V200" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<text x="626" y="262" fill="#86b3cc" font-size="10.5">scan centroids → pick <tspan fill="#bfe6ff" font-weight="700">nprobe</tspan> nearest (the two lit)</text>
<!-- posting lists (CSR) for the two probed clusters -->
<rect x="700" y="290" width="180" height="46" rx="6" fill="url(#rail)" stroke="#7fc8ea" stroke-width="1.6"/>
<text x="790" y="308" fill="#dff1ff" font-size="10.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">posting[c1] — int8 rows</text>
<text x="790" y="324" fill="#86b3cc" font-size="9.5" text-anchor="middle">contiguous · cluster order</text>
<rect x="900" y="290" width="180" height="46" rx="6" fill="url(#rail)" stroke="#7fc8ea" stroke-width="1.6"/>
<text x="990" y="308" fill="#dff1ff" font-size="10.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">posting[c2] — int8 rows</text>
<text x="990" y="324" fill="#86b3cc" font-size="9.5" text-anchor="middle">one sequential scan each</text>
<path d="M732 234 V290" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<path d="M792 234 L980 290" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<!-- int8 VNNI dot callout -->
<rect x="642" y="392" width="230" height="52" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="757" y="412" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">int8 cosine (shared kernel)</text>
<text x="757" y="429" fill="#9fc9e0" font-size="10.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">score_i8_batch: vpdpbusd</text>
<text x="757" y="440" fill="#86b3cc" font-size="9.5" text-anchor="middle">64 int8 MACs/instr (AVX-512 VNNI)</text>
<!-- traversal note -->
<rect x="888" y="392" width="222" height="52" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="999" y="412" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">2 levels, not O(N)</text>
<text x="999" y="429" fill="#9fc9e0" font-size="10.5" text-anchor="middle">centroids + nprobe postings</text>
<text x="999" y="440" fill="#86b3cc" font-size="9.5" text-anchor="middle">nprobe = recall/speed knob</text>
<!-- output -->
<rect x="754" y="470" width="244" height="34" rx="8" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2"/>
<text x="876" y="492" fill="#eaf6ff" font-size="12.5" text-anchor="middle" font-family="'DejaVu Sans Mono', monospace">top-k (id,score) · APPROX</text>
<path d="M876 444 V470" stroke="#6fd3f5" stroke-width="3" fill="none"/>
<!-- static icicles under the postings -->
<g>
<path d="M720 336 L728 336 L724 358 Z" fill="url(#ice)"/>
<path d="M800 336 L808 336 L804 350 Z" fill="url(#ice)"/>
<path d="M920 336 L928 336 L924 356 Z" fill="url(#ice)"/>
<path d="M1000 336 L1008 336 L1004 348 Z" fill="url(#ice)"/>
<path d="M1060 336 L1068 336 L1064 354 Z" fill="url(#ice)"/>
</g>
<text x="626" y="536" fill="#86b3cc" font-size="10.5">~60–1000× faster than flat scan at ≥0.95 recall (data-dependent)</text>
<!-- ==================== SHARED DNA RAIL ==================== -->
<rect x="34" y="590" width="1112" height="104" rx="12" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2"/>
<text x="54" y="618" fill="#dff1ff" font-size="15" font-weight="700">shared Ragnar DNA</text>
<text x="214" y="618" fill="#9fc9e0" font-size="12">both are build-once static structures that beat the naive scan by respecting the memory hierarchy</text>
<g>
<rect x="54" y="632" width="346" height="48" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="227" y="652" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">1 · contiguous memory in traversal order</text>
<text x="227" y="669" fill="#86b3cc" font-size="10" text-anchor="middle">implicit tree layout · CSR cluster order — no pointer chasing</text>
<rect x="418" y="632" width="346" height="48" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="591" y="652" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">2 · branchless SIMD inner loop</text>
<text x="591" y="669" fill="#86b3cc" font-size="10" text-anchor="middle">count_lt (u32) ⋅ vpdpbusd int8 dot — many lanes/instr, no mispredicts</text>
<rect x="782" y="632" width="364" height="48" rx="8" fill="#0f2c40" stroke="#4f8aab" stroke-width="1.5"/>
<text x="964" y="652" fill="#eaf6ff" font-size="12" text-anchor="middle" font-weight="700">3 · software-pipelined batch (P in flight)</text>
<text x="964" y="669" fill="#86b3cc" font-size="10" text-anchor="middle">batch_stream::<P> prefetch — hides the cache→RAM cliff via MLP</text>
</g>
</svg>