.nornir/assets/gatling_variants.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 940" 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="newchip" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#3a3416"/>
<stop offset="1" stop-color="#241f0c"/>
</linearGradient>
<marker id="arrow" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">
<path d="M0,0 L9,4.5 L0,9 Z" fill="#6fd3f5"/>
</marker>
</defs>
<!-- PCB substrate -->
<rect x="0" y="0" width="1200" height="940" fill="url(#board)"/>
<g stroke="#143a52" stroke-width="1">
<line x1="0" y1="78" x2="1200" y2="78"/>
<line x1="0" y1="892" x2="1200" y2="892"/>
</g>
<g fill="#16415c">
<circle cx="40" cy="40" r="2"/><circle cx="1160" cy="40" r="2"/>
<circle cx="40" cy="912" r="2"/><circle cx="1160" cy="912" r="2"/>
</g>
<!-- title -->
<text x="40" y="40" fill="#eaf6ff" font-size="22" font-weight="700">gatling</text>
<text x="128" y="40" fill="#9fc9e0" font-size="15">the no-barrier <tspan fill="#bfe6ff" font-weight="700">1 → N → 1</tspan> worker pool — pick a row by the shape of the work, a card by what you need back</text>
<!-- legend -->
<g font-size="12.5">
<rect x="864" y="54" width="16" height="12" rx="2" fill="url(#newchip)" stroke="#ffd479" stroke-width="1.5"/>
<text x="888" y="64" fill="#cfe6f5">newly added</text>
<line x1="1000" y1="60" x2="1036" y2="60" stroke="#6fd3f5" stroke-width="3" marker-end="url(#arrow)"/>
<text x="1046" y="64" fill="#cfe6f5">flow / result</text>
</g>
<!-- the soul strip -->
<rect x="36" y="96" width="1128" height="50" rx="9" fill="url(#rail)" stroke="#2c6485" stroke-width="1.5"/>
<text x="52" y="118" fill="#bfe6ff" font-size="13" font-weight="700">THE SOUL (every variant)</text>
<g font-size="12.5" fill="#cfe6f5">
<text x="250" y="118">• no barrier</text>
<text x="372" y="118">• atomic self-dispatch</text>
<text x="556" y="118">• rayon-free <tspan fill="#9fc9e0">std::thread::scope</tspan></text>
<text x="792" y="118">• disjoint, zero-copy output</text>
</g>
<text x="52" y="136" fill="#7fa8bf" font-size="11.5">the reader only reads · the collector only stitches in order · all heavy work happens in the workers (never make the ends cook)</text>
<!-- ============ BAND 1 — STREAMING ============ -->
<rect x="36" y="166" width="1128" height="150" rx="10" fill="none" stroke="#1d4865" stroke-width="1.2"/>
<text x="52" y="190" fill="#8fd6c9" font-size="14" font-weight="700">STREAMING</text>
<text x="52" y="210" fill="#7fa8bf" font-size="11.5">work arrives a</text>
<text x="52" y="226" fill="#7fa8bf" font-size="11.5">chunk at a time</text>
<text x="52" y="246" fill="#6b93ab" font-size="11">Read → split →</text>
<text x="52" y="261" fill="#6b93ab" font-size="11">N → collector</text>
<text x="52" y="276" fill="#6b93ab" font-size="11">→ sink</text>
<text x="52" y="298" fill="#5d7d92" font-size="10.5">gatling/src/gatling.rs</text>
<!-- run -->
<rect x="270" y="180" width="280" height="122" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="288" y="206" fill="#bfe6ff" font-size="15" font-weight="700">gatling::run</text>
<text x="288" y="228" fill="#9fc9e0" font-size="12">byte mode · Codec + Sink</text>
<text x="288" y="252" fill="#cfe6f5" font-size="11.5">USE WHEN: bytes need reassembly at</text>
<text x="288" y="269" fill="#cfe6f5" font-size="11.5">boundaries — bz2/gz decode → XML.</text>
<text x="288" y="290" fill="#7fa8bf" font-size="11">collector cuts at Sink::safe_end</text>
<!-- run_typed -->
<rect x="582" y="180" width="280" height="122" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="600" y="206" fill="#bfe6ff" font-size="15" font-weight="700">gatling::run_typed</text>
<text x="600" y="228" fill="#9fc9e0" font-size="12">typed mode · TypedCodec + Sink</text>
<text x="600" y="252" fill="#cfe6f5" font-size="11.5">USE WHEN: workers emit finished</text>
<text x="600" y="269" fill="#cfe6f5" font-size="11.5">records, split cuts logical bounds.</text>
<text x="600" y="290" fill="#7fa8bf" font-size="11">parse · nornir deep_scan stages</text>
<!-- Config note -->
<rect x="894" y="180" width="252" height="122" rx="9" fill="url(#rail)" stroke="#2c6485" stroke-width="1.4"/>
<text x="912" y="206" fill="#bfe6ff" font-size="13" font-weight="700">Config knobs</text>
<text x="912" y="228" fill="#cfe6f5" font-size="11.5">ring_slots = pipeline width</text>
<text x="912" y="246" fill="#cfe6f5" font-size="11.5">carry_headroom = tail carry</text>
<text x="912" y="264" fill="#cfe6f5" font-size="11.5">SlotFill::Incremental (default)</text>
<text x="912" y="282" fill="#cfe6f5" font-size="11.5">SlotFill::Big (planet-scale)</text>
<!-- ============ BAND 2 — FORK-JOIN ============ -->
<rect x="36" y="332" width="1128" height="190" rx="10" fill="none" stroke="#1d4865" stroke-width="1.2"/>
<text x="52" y="356" fill="#8fd6c9" font-size="14" font-weight="700">FORK-JOIN</text>
<text x="52" y="376" fill="#7fa8bf" font-size="11.5">the whole work set</text>
<text x="52" y="392" fill="#7fa8bf" font-size="11.5">is known upfront</text>
<text x="52" y="412" fill="#6b93ab" font-size="11">a fixed 0..n,</text>
<text x="52" y="427" fill="#6b93ab" font-size="11">a Vec, or a grid</text>
<text x="52" y="447" fill="#6b93ab" font-size="11">N workers drain</text>
<text x="52" y="462" fill="#6b93ab" font-size="11">one atomic counter</text>
<text x="52" y="510" fill="#5d7d92" font-size="10.5">gatling/src/gatling_forkjoin.rs</text>
<!-- for_each -->
<rect x="270" y="346" width="205" height="162" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="286" y="372" fill="#bfe6ff" font-size="14" font-weight="700">gatling_for_each</text>
<text x="286" y="392" fill="#9fc9e0" font-size="11.5">0..n → Vec<T>, in order</text>
<text x="286" y="416" fill="#cfe6f5" font-size="11">USE WHEN: a fixed set of</text>
<text x="286" y="432" fill="#cfe6f5" font-size="11">independent CPU units,</text>
<text x="286" y="448" fill="#cfe6f5" font-size="11">all known now, similar</text>
<text x="286" y="464" fill="#cfe6f5" font-size="11">cost — and you want the</text>
<text x="286" y="480" fill="#cfe6f5" font-size="11">results back. (also: run,</text>
<text x="286" y="496" fill="#cfe6f5" font-size="11">map_owned, map_balanced)</text>
<!-- gatling_run -->
<rect x="495" y="346" width="205" height="162" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="511" y="372" fill="#bfe6ff" font-size="14" font-weight="700">gatling_run</text>
<text x="511" y="392" fill="#9fc9e0" font-size="11.5">0..n, side-effects only</text>
<text x="511" y="416" fill="#cfe6f5" font-size="11">USE WHEN: same, but no</text>
<text x="511" y="432" fill="#cfe6f5" font-size="11">collected return — each</text>
<text x="511" y="448" fill="#cfe6f5" font-size="11">worker writes into per-i</text>
<text x="511" y="464" fill="#cfe6f5" font-size="11">state the caller owns.</text>
<!-- for_each_balanced (NEW) -->
<rect x="720" y="346" width="205" height="162" rx="9" fill="url(#newchip)" stroke="#ffd479" stroke-width="2.2"/>
<rect x="843" y="335" width="46" height="20" rx="6" fill="#ffd479"/>
<text x="866" y="349" fill="#241f0c" font-size="11.5" font-weight="700" text-anchor="middle">NEW</text>
<text x="736" y="372" fill="#ffe6a6" font-size="13.5" font-weight="700">gatling_for_each</text>
<text x="736" y="389" fill="#ffe6a6" font-size="13.5" font-weight="700">_balanced</text>
<text x="736" y="409" fill="#e7cf93" font-size="11">LPT + batched, in order</text>
<text x="736" y="431" fill="#f0e3c4" font-size="11">USE WHEN: costs are</text>
<text x="736" y="447" fill="#f0e3c4" font-size="11">heavily skewed. weight(i)</text>
<text x="736" y="463" fill="#f0e3c4" font-size="11">schedules biggest-first;</text>
<text x="736" y="479" fill="#f0e3c4" font-size="11">idle workers steal the</text>
<text x="736" y="495" fill="#f0e3c4" font-size="11">cheap tail in batches.</text>
<!-- scanlines -->
<rect x="945" y="346" width="201" height="162" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="961" y="372" fill="#bfe6ff" font-size="14" font-weight="700">gatling_scanlines</text>
<text x="961" y="392" fill="#9fc9e0" font-size="11.5">rows × stride buffer</text>
<text x="961" y="416" fill="#cfe6f5" font-size="11">USE WHEN: a 2D grid</text>
<text x="961" y="432" fill="#cfe6f5" font-size="11">where each row is</text>
<text x="961" y="448" fill="#cfe6f5" font-size="11">independent — each</text>
<text x="961" y="464" fill="#cfe6f5" font-size="11">worker owns a disjoint</text>
<text x="961" y="480" fill="#cfe6f5" font-size="11">row slice (rendering).</text>
<!-- ============ BAND 3 — ASYNC I/O ============ -->
<rect x="36" y="538" width="1128" height="178" rx="10" fill="none" stroke="#1d4865" stroke-width="1.2"/>
<text x="52" y="562" fill="#8fd6c9" font-size="14" font-weight="700">ASYNC I/O</text>
<text x="52" y="582" fill="#7fa8bf" font-size="11.5">latency-bound, not</text>
<text x="52" y="598" fill="#7fa8bf" font-size="11.5">CPU-bound</text>
<text x="52" y="618" fill="#6b93ab" font-size="11">tokio tasks, not</text>
<text x="52" y="633" fill="#6b93ab" font-size="11">threads · ≤ max</text>
<text x="52" y="648" fill="#6b93ab" font-size="11">_in_flight at once</text>
<text x="52" y="704" fill="#5d7d92" font-size="10.5">gatling/src/gatling_io.rs</text>
<!-- io::run -->
<rect x="270" y="552" width="280" height="150" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="288" y="578" fill="#bfe6ff" font-size="15" font-weight="700">gatling::io::run</text>
<text x="288" y="600" fill="#9fc9e0" font-size="12">results fastest-first</text>
<text x="288" y="624" fill="#cfe6f5" font-size="11.5">USE WHEN: I/O fan-out where</text>
<text x="288" y="641" fill="#cfe6f5" font-size="11.5">order doesn't matter — S3</text>
<text x="288" y="658" fill="#cfe6f5" font-size="11.5">PUT/GET, fsync, network calls.</text>
<text x="288" y="682" fill="#7fa8bf" font-size="11">Bytes moved, never cloned</text>
<!-- io::run_ordered -->
<rect x="582" y="552" width="280" height="150" rx="9" fill="url(#chip)" stroke="#5aa6cf" stroke-width="1.8"/>
<text x="600" y="578" fill="#bfe6ff" font-size="15" font-weight="700">gatling::io::run_ordered</text>
<text x="600" y="600" fill="#9fc9e0" font-size="12">results in submission order</text>
<text x="600" y="624" fill="#cfe6f5" font-size="11.5">USE WHEN: same, but you need</text>
<text x="600" y="641" fill="#cfe6f5" font-size="11.5">the outputs back in the order</text>
<text x="600" y="658" fill="#cfe6f5" font-size="11.5">you submitted them.</text>
<text x="600" y="682" fill="#7fa8bf" font-size="11">admission control = backpressure</text>
<!-- the law -->
<rect x="894" y="552" width="252" height="150" rx="9" fill="url(#rail)" stroke="#cf5a5a" stroke-width="1.6"/>
<text x="912" y="578" fill="#ffc9c9" font-size="13" font-weight="700">THE LAW</text>
<text x="912" y="602" fill="#f0d6d6" font-size="11.5">No rayon. Ever.</text>
<text x="912" y="624" fill="#e9cccc" font-size="11">Its hidden global pool is</text>
<text x="912" y="640" fill="#e9cccc" font-size="11">exactly what serialized</text>
<text x="912" y="656" fill="#e9cccc" font-size="11">osm-katana to ~3 cores.</text>
<text x="912" y="680" fill="#e9cccc" font-size="11">One core busy = you broke it.</text>
<!-- ============ BAND 4 — PARALLEL WRITER (NEW) ============ -->
<rect x="36" y="732" width="1128" height="140" rx="10" fill="none" stroke="#7a6a1d" stroke-width="1.3"/>
<rect x="843" y="721" width="46" height="20" rx="6" fill="#ffd479"/>
<text x="866" y="735" fill="#241f0c" font-size="11.5" font-weight="700" text-anchor="middle">NEW</text>
<text x="52" y="756" fill="#e7cf93" font-size="14" font-weight="700">PARALLEL WRITER</text>
<text x="52" y="776" fill="#7fa8bf" font-size="11.5">no collector, no</text>
<text x="52" y="792" fill="#7fa8bf" font-size="11.5">single writer thread</text>
<text x="52" y="812" fill="#6b93ab" font-size="11">each worker writes</text>
<text x="52" y="827" fill="#6b93ab" font-size="11">its OWN disjoint</text>
<text x="52" y="842" fill="#6b93ab" font-size="11">output</text>
<text x="52" y="862" fill="#5d7d92" font-size="10.5">gatling/src/parwrite.rs</text>
<!-- extract_entries_unordered -->
<rect x="270" y="746" width="280" height="112" rx="9" fill="url(#newchip)" stroke="#ffd479" stroke-width="2"/>
<text x="288" y="770" fill="#ffe6a6" font-size="13.5" font-weight="700">extract_entries_unordered</text>
<text x="288" y="790" fill="#e7cf93" font-size="11.5">independent files</text>
<text x="288" y="812" fill="#f0e3c4" font-size="11">USE WHEN: archive entries</text>
<text x="288" y="828" fill="#f0e3c4" font-size="11">(ZIP/JAR) — pread + decode +</text>
<text x="288" y="844" fill="#f0e3c4" font-size="11">write own file. No ordering.</text>
<!-- write_segments/slices_positional -->
<rect x="582" y="746" width="280" height="112" rx="9" fill="url(#newchip)" stroke="#ffd479" stroke-width="2"/>
<text x="600" y="770" fill="#ffe6a6" font-size="13.5" font-weight="700">write_*_positional</text>
<text x="600" y="790" fill="#e7cf93" font-size="11.5">disjoint regions of one stream</text>
<text x="600" y="812" fill="#f0e3c4" font-size="11">USE WHEN: offsets known (prefix-</text>
<text x="600" y="828" fill="#f0e3c4" font-size="11">sum), out pre-sized. pwrite at</text>
<text x="600" y="844" fill="#f0e3c4" font-size="11">offset → exact order, no collector.</text>
<!-- numbers card -->
<rect x="894" y="746" width="252" height="112" rx="9" fill="url(#rail)" stroke="#2c6485" stroke-width="1.4"/>
<text x="912" y="770" fill="#bfe6ff" font-size="12.5" font-weight="700">the lift (cores_busy/12)</text>
<g font-size="11" font-family="'DejaVu Sans Mono', monospace">
<text x="912" y="792" fill="#cfe6f5">ljar 3.5→<tspan fill="#7CFFB2">11.5</tspan> lzip 4.0→<tspan fill="#7CFFB2">11.5</tspan></text>
<text x="912" y="812" fill="#cfe6f5">lgz 3.1→<tspan fill="#ffcf6f">7.8</tspan> lbz2 ~10.4 held</text>
</g>
<text x="912" y="834" fill="#7fa8bf" font-size="10">lgz: single-stream tail; -o files</text>
<text x="912" y="848" fill="#7fa8bf" font-size="10">only (stdout can't be offset).</text>
<!-- footer -->
<text x="40" y="912" fill="#5d7d92" font-size="11">znippy-zoomies · gatling — see gatling-design.md / gatling-guide.md / gatling-reasoning.md / gatling-for-idiots.md</text>
</svg>