<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>zLLM · Building the Best Inference Engine</title>
    <subtitle>zLLM is building the best inference engine: Rust-native, architecturally unified, and scalable from embedded devices to hyperscale clusters.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://zhuai.tech/en/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://zhuai.tech"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-09-17T00:00:00+00:00</updated>
    <id>https://zhuai.tech/en/atom.xml</id>
    <entry xml:lang="en">
        <title>DeepSeek V4.1 Flash Engineering, Part 4: Long Prefill and the Final Optimization Mile</title>
        <published>2026-09-17T00:00:00+00:00</published>
        <updated>2026-09-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/deepseek-v41-flash-final-optimization/"/>
        <id>https://zhuai.tech/en/blog/deepseek-v41-flash-final-optimization/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/deepseek-v41-flash-final-optimization/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;en&#x2F;blog&#x2F;deepseek-v41-flash-vision-tensor-alignment&#x2F;&quot;&gt;Part 3&lt;&#x2F;a&gt; aligned image preprocessing, the vision tower, the aligner, and dual-bias routing through intermediate tensors. At that point, DeepSeek V4.1 Flash could run its text path, Engram, eight-GPU language backbone, and multimodal path.&lt;&#x2F;p&gt;
&lt;p&gt;The final article asks one remaining question: &lt;strong&gt;when an input grows from a few thousand tokens to 261K and then one million, how can the complete request keep making progress while cold-prefill time actually falls?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This was not a matter of replacing one matrix kernel. Long context amplifies sparse indexing, temporary tensors, VRAM fragmentation, stage handoffs, and chunk scheduling at the same time. A microbenchmark can improve by several times without moving the complete model. A configuration can touch 1,600 token&#x2F;s yet leave only a few dozen MiB of VRAM, making it unsuitable as the retained deployment path.&lt;&#x2F;p&gt;
&lt;p&gt;We therefore accepted three kinds of evidence: operator comparison against the official reference, complete-output gates on fixed inputs, and end-to-end timing with profiling disabled.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;deepseek-v41-flash&#x2F;prefill-evolution-en.svg&quot; alt=&quot;Performance evolution of the 261K cold-prefill path from recovery baseline to the retained result&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;restore-the-official-cross-layer-sparse-semantics-first&quot;&gt;Restore the official cross-layer sparse semantics first&lt;&#x2F;h2&gt;
&lt;p&gt;The first long-prefill step was a correctness fix.&lt;&#x2F;p&gt;
&lt;p&gt;DeepSeek V4.1 Flash DSA does more than select Top-K positions from all history independently at every layer. Layer 20 first aggregates index scores into blocks and chooses a candidate region. Layers 24, 28, 32, and 36 then apply their own scores within those candidates. Sparsity therefore extends across depth: one source layer publishes a bounded search region and later consumers reuse it.&lt;&#x2F;p&gt;
&lt;p&gt;An early local configuration disabled this chain and let later layers select directly from full history. When the context was short, candidate capacity covered all visible history and the paths looked equivalent. Beyond roughly 16K tokens, candidates began to exclude positions and the difference became real.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM restored three pieces of state:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Candidate blocks produced by the source layer remain alive across stages and travel with the hidden state.&lt;&#x2F;li&gt;
&lt;li&gt;Consumer layers score only candidate slots and map local selections back to global positions.&lt;&#x2F;li&gt;
&lt;li&gt;The newest visible block, causal prefixes, and partial tail blocks follow the official boundaries.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Real-shape tests extended to 1,048,576 historical positions. Candidate blocks and both Top-512 stages matched the official PyTorch result. The fix also reshaped the performance problem: the four downstream consumers are capped at 16,384 scored positions, while the earlier source index layers still traverse history that grows with context.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-latest-prefill-path-stop-decoding-the-same-index-data-repeatedly&quot;&gt;The latest prefill path: stop decoding the same index data repeatedly&lt;&#x2F;h2&gt;
&lt;p&gt;After candidate semantics were fixed, profiling pointed to front-end FP4 index scoring. The straightforward implementation repeatedly decoded the same query and key values inside every score tile. The cost became increasingly visible with longer history.&lt;&#x2F;p&gt;
&lt;p&gt;The final path has four parts.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-a-provably-lossless-integer-representation&quot;&gt;1. A provably lossless integer representation&lt;&#x2F;h3&gt;
&lt;p&gt;When the official FP4 mantissas and E8M0 scales satisfy a checked exponent range, they can be aligned into small integers. INT8 inputs and an INT32 dot product then produce the exact integer sum. The implementation restores the power-of-two scale and preserves the original head order for ReLU, weighting, and reduction.&lt;&#x2F;p&gt;
&lt;p&gt;This is not approximate quantization. Each tile is checked at runtime. If any proof condition fails, the tile falls back to the original FP32 accumulation path. Tests cover different head counts, dimensions, tails, causal prefixes, and million-position history; GPU scores and Top-K results remain bit-identical.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-expand-each-query-and-key-once&quot;&gt;2. Expand each query and key once&lt;&#x2F;h3&gt;
&lt;p&gt;An integer dot product alone is insufficient if every tile still parses FP4 codes and scales repeatedly. The new path expands the query batch and historical keys once, after which scoring reads prepared data directly.&lt;&#x2F;p&gt;
&lt;p&gt;In isolated same-shape measurements, 128 queries over 131,072 historical positions fell from about 47.6 ms to 6.55 ms. Thirty-two queries over 524,288 positions fell from about 47.6 ms to 6.62 ms. These are index-score microbenchmarks, not complete-model speedups, but they clearly identify repeated decode as removable work.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-dynamic-lds-follows-the-actual-head-count&quot;&gt;3. Dynamic LDS follows the actual head count&lt;&#x2F;h3&gt;
&lt;p&gt;An early kernel reserved shared memory for as many as 64 heads even though the front index used 32. Dynamic LDS computes shared-memory use from the actual head count, allowing more workgroups to remain active on a CU.&lt;&#x2F;p&gt;
&lt;p&gt;On the same 261,933-token cold input, this reduced TTFT from 357.10 to 306.66 seconds and raised input processing from 733.51 to 854.15 token&#x2F;s. The complete 1,024-token output remained identical to the control.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-recompute-only-the-heads-that-fail-the-integer-conditions&quot;&gt;4. Recompute only the heads that fail the integer conditions&lt;&#x2F;h3&gt;
&lt;p&gt;If one head in a query falls outside the exponent proof, falling back an entire tile to scalar FP32 discards most of the gain. The final implementation builds a warp mask for exceptional heads and recomputes only those dot products in the original order. It uses full-tile fallback only when the exceptional share is too large.&lt;&#x2F;p&gt;
&lt;p&gt;After one-time expansion, candidate-score reuse, and local fallback, the same 261K request reached 1,188.42 token&#x2F;s at chunk 256 while preserving the complete output hash.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dense-and-moe-keep-only-improvements-visible-in-the-complete-model&quot;&gt;Dense and MoE: keep only improvements visible in the complete model&lt;&#x2F;h2&gt;
&lt;p&gt;Once indexing improved, the bottleneck moved into routed MoE, attention projections, and dense temporaries.&lt;&#x2F;p&gt;
&lt;p&gt;The dense path gained direct fragment-load branches for 16, 64, and 128 rows while preserving the original accumulation order for each 16-element K segment. It raised the 261K cold-prefill result from 1,023.58 to 1,075.85 token&#x2F;s, about 5.1%.&lt;&#x2F;p&gt;
&lt;p&gt;For MoE, we tried larger K tiles, input conversion, merged LDS, route-block changes, and early weight reads. Most changes did not survive complete-model testing or helped only very small route groups. The retained path preserves matrix accumulation order and passes CPU&#x2F;GPU oracles. We do not present a local 3%–5% microbenchmark gain as an end-to-end result.&lt;&#x2F;p&gt;
&lt;p&gt;This became a recurring rule of the final optimization cycle: &lt;strong&gt;hotspot order changes whenever the previous bottleneck is removed.&lt;&#x2F;strong&gt; The top item in an old profile may no longer be the best target in the current build.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-larger-chunk-is-useful-only-while-its-working-set-fits&quot;&gt;A larger chunk is useful only while its working set fits&lt;&#x2F;h2&gt;
&lt;p&gt;After operators and allocation became stable, we retested prefill chunks on the same 261,933-token input:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: right&quot;&gt;Chunk&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;TTFT&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Cold-prefill rate&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Minimum sampled VRAM headroom&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;256&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;220.40 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,188.42 token&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;about 5.05 GiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;512&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;177.29 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,477.44 token&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;about 5.00 GiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;173.45 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,510.12 token&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;about 2.63 GiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;2,048&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;170.67 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,534.76 token&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;about 0.26 GiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;A larger chunk lets one weight load serve more tokens and reduces pipeline fill and drain cycles. It also enlarges activations, routed output, RoPE, and index workspaces. Reducing the work window from 32 to 16 changed throughput by only about 0.31% and did not restore VRAM headroom, so it was not retained as an optimization.&lt;&#x2F;p&gt;
&lt;p&gt;Different chunk sizes enter different BF16 row-count dispatches, and long generated text diverged across batch shapes. Runs with the same chunk, build, and fixed input reproduce; operator oracles also remain aligned. We still do not claim that the cross-batch-shape full-text difference is resolved. These numbers describe the current engineering path and do not replace a full model-quality evaluation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-final-arena-step-split-returned-large-blocks-again&quot;&gt;The final arena step: split returned large blocks again&lt;&#x2F;h2&gt;
&lt;p&gt;Long prefill continuously changes temporary-tensor sizes. The old L1 reuse layer could use a large completed buffer for a small request while lending out the entire block. The unused remainder could not immediately rejoin the arena, so a later large request fell through to &lt;code&gt;hipMalloc&lt;&#x2F;code&gt;, potentially introducing an implicit device synchronization.&lt;&#x2F;p&gt;
&lt;p&gt;The fix applies when a completed block came from the arena, is at least twice the requested size, and leaves a useful remainder. Under the same arena lock, the reuse layer returns the complete block and allocates the requested size again. The remainder becomes immediately available for coalescing and reuse.&lt;&#x2F;p&gt;
&lt;p&gt;With a 3 GiB arena and chunk 2,048, the complete request reached &lt;strong&gt;1,553.91 token&#x2F;s&lt;&#x2F;strong&gt; with a &lt;strong&gt;168.56-second TTFT&lt;&#x2F;strong&gt;. One 5 GiB arena experiment reached &lt;strong&gt;1,600.04 token&#x2F;s&lt;&#x2F;strong&gt;, but the tightest GPU had only about &lt;strong&gt;38 MiB&lt;&#x2F;strong&gt; of sampled headroom. We did not retain that capacity-edge configuration. The final reporting point uses 3 GiB instead of selecting the one run that barely crossed 1,600.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-performance-data&quot;&gt;Final performance data&lt;&#x2F;h2&gt;
&lt;p&gt;The test system used two AMD EPYC 9334 processors, 64 physical cores in total, roughly 1 TiB of host memory, and eight Radeon Pro W7900D GPUs with about 48 GiB each. The model used the official safetensors checkpoint on the ROCm backend. Every row below is a complete HTTP request; model loading is excluded.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Scenario&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Input &#x2F; output&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Result&lt;&#x2F;th&gt;&lt;th&gt;Scope&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;261K cold-prefill recovery baseline&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;261,933 &#x2F; 1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;332.73 token&#x2F;s, TTFT 787.22 s&lt;&#x2F;td&gt;&lt;td&gt;Same input after restoring a runnable production configuration, before optimization&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Retained 261K cold prefill&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;261,933 &#x2F; 1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;1,553.91 token&#x2F;s, TTFT 168.56 s&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;3 GiB arena; complete SSE termination&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Complete single-request 1M context&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,000,411 &#x2F; 1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;732.59 token&#x2F;s, TTFT 1,365.59 s&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Complete generation; output matched the previous 1M run&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Single-request DSpark decode&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;about 2K generated tokens&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;25.82 &#x2F; 26.14 token&#x2F;s&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Two no-profiler runs&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Eight-request aggregate DSpark decode&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;eight long requests&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;107.61 &#x2F; 109.47 token&#x2F;s&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Current reliable two-run baseline&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Historical eight-request best range&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;eight-request steady state&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;111.15–114.43 token&#x2F;s&lt;&#x2F;td&gt;&lt;td&gt;Shows the observed ceiling; not the default result&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;From the recovery baseline to the final retained 261K result, TTFT fell by about &lt;strong&gt;78.6%&lt;&#x2F;strong&gt; and cold-input throughput rose by about &lt;strong&gt;4.67×&lt;&#x2F;strong&gt;. No single kernel produced this result. It came from the official sparse semantics, index representation, shared-memory sizing, dense tiling, chunk size, and memory lifetime working together.&lt;&#x2F;p&gt;
&lt;p&gt;TTFT runs from the HTTP request to the first non-empty text event. Cold-prefill rate is input tokens divided by TTFT. It includes request handling, eight-GPU prefill, and first-output cost; it is not pure GPU-kernel throughput. VRAM figures are whole-card samples taken every five seconds and may miss instantaneous peaks. Prefill and decode rows come from different requests and cannot be added into one run&#x27;s total throughput.&lt;&#x2F;p&gt;
&lt;p&gt;The article includes the raw summary: &lt;a href=&quot;&#x2F;data&#x2F;deepseek-v41-20260917&#x2F;final-performance.json&quot;&gt;final performance JSON&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-remains-after-the-optimization-series&quot;&gt;What remains after the optimization series&lt;&#x2F;h2&gt;
&lt;p&gt;The four articles began with Engram, continued through weight assembly, eight-GPU state transfer, and vision-tensor alignment, and end with long-context performance. Together they show that model capability no longer lives only in dense Transformer computation.&lt;&#x2F;p&gt;
&lt;p&gt;Knowledge can be retrieved through Engram. Cross-layer candidates can restrict attention history. Visual semantics can enter the language backbone as typed rows. Speculative decoding can reduce the number of full target-model rounds. An inference engine now manages a combination of computation, retrieval, state, and resources.&lt;&#x2F;p&gt;
&lt;p&gt;The effective optimization order followed the same structure: restore the model&#x27;s semantics, locate repeated work, pass operator oracles, verify complete output, and only then decide from end-to-end TTFT, throughput, and memory whether a change should remain.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Reaching a peak shows that an experiment worked. Repeatedly completing requests within correctness, capacity, and stability boundaries shows that the engineering path works.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DeepSeek V4.1 Flash Engineering, Part 3: Integrating Vision—Finding Differences with Intermediate Tensors</title>
        <published>2026-09-16T00:00:00+00:00</published>
        <updated>2026-09-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/deepseek-v41-flash-vision-tensor-alignment/"/>
        <id>https://zhuai.tech/en/blog/deepseek-v41-flash-vision-tensor-alignment/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/deepseek-v41-flash-vision-tensor-alignment/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;en&#x2F;blog&#x2F;deepseek-v41-flash-eight-gpu&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; followed weight assembly into an eight-GPU text pipeline, preserving compressed KV, selections, and mHC state across device boundaries. This article connects images to that same language backbone.&lt;&#x2F;p&gt;
&lt;p&gt;Multimodal integration can create a convincing false positive. The HTTP request succeeds, the vision tower returns without an error, and the model generates text. Yet a one-pixel resize difference, a different RoPE ordering, or an incorrectly padded aligner grid gives every later layer a tensor with the right shape and the wrong meaning.&lt;&#x2F;p&gt;
&lt;p&gt;We therefore did not treat a plausible image description as sufficient evidence. zLLM first connected the end-to-end path, then used the official PyTorch implementation as an oracle and divided the vision pipeline into observable boundaries. The first boundary with a material difference identified where to investigate.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;multimodal-basics-turn-an-image-into-tokens-the-language-model-can-process&quot;&gt;Multimodal basics: turn an image into tokens the language model can process&lt;&#x2F;h2&gt;
&lt;p&gt;A language model consumes rows of hidden vectors, while a raw image is a two-dimensional pixel array. A multimodal model first converts both modalities into a shared representation space and then sends them through the same inference path.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;deepseek-v41-flash&#x2F;multimodal-principle-en.svg&quot; alt=&quot;Multimodal inference: image patching, vision encoding, language-space alignment, and joint inference&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The first step is &lt;strong&gt;patching&lt;&#x2F;strong&gt;. The image is divided into fixed-size regions; DeepSeek V4.1 Flash, for example, uses 14×14 RGB patches. Each patch becomes one row of numbers while retaining its grid coordinates. A continuous image becomes a sequence of visual tokens with two-dimensional positions:&lt;&#x2F;p&gt;
&lt;p&gt;The second step is &lt;strong&gt;vision-tower encoding&lt;&#x2F;strong&gt;. A patch projection maps pixel rows into a vision hidden space. ViT attention, positional encoding, and MLPs then give each patch information from the whole image. A small orange region in a carrot image can come to represent shape, object category, neighboring objects, and spatial location as well as local color. The result is not one vector summarizing the entire image, but a set of semantic vectors that preserve spatial structure.&lt;&#x2F;p&gt;
&lt;p&gt;The third step is &lt;strong&gt;alignment with the language space&lt;&#x2F;strong&gt;. Vision hidden width usually differs from language hidden width, and the patch sequence may be too long. An aligner or merger combines nearby visual positions and projects them to the language model&#x27;s hidden size. This module is trained with the model. It performs more than reshaping a matrix: it maps visual features into a representation the language model has learned to interpret.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, the model inserts these visual tokens into reserved image positions in the text sequence. Text tokens still come from vocabulary embeddings, while vision vectors replace the image-placeholder rows. The language model now sees one unified hidden sequence:&lt;&#x2F;p&gt;
&lt;p&gt;Language attention lets question tokens read relevant visual tokens and fuses visual information with surrounding text layer by layer. The model still predicts the next text token through its LM head, so image recognition, description, and visual question answering all appear as ordinary text generation at the output.&lt;&#x2F;p&gt;
&lt;p&gt;In an inference engine, the vision tower normally computes image semantics once during prefill. These visual tokens build language-model KV cache together with the rest of the prompt. During token-by-token decode, later tokens read the encoded image context through that cache; the engine does not rerun the full vision tower for every generated token.&lt;&#x2F;p&gt;
&lt;p&gt;DeepSeek V4.1 Flash and Qwen3-VL share this overall design. They differ in patch construction, spatial or temporal positions, grid reduction, and whether visual features enter the language stack once or at multiple depths. With this common principle established, intermediate-tensor comparison has a precise goal: every boundary must preserve the visual meaning and spatial layout used during training.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;connect-the-complete-image-to-text-path-first&quot;&gt;Connect the complete image-to-text path first&lt;&#x2F;h2&gt;
&lt;p&gt;An OpenAI-compatible &lt;code&gt;image_url&lt;&#x2F;code&gt; part passes through much more than a Vision Encoder:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;deepseek-v41-flash&#x2F;deepseek-v41-vision-pipeline-en.svg&quot; alt=&quot;The complete DeepSeek V4.1 Flash image-to-text path from image_url to the eight-GPU language backbone&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The zLLM node preserves the original order of text and images and writes image placeholders into the prompt. After preprocessing, each placeholder expands into a two-dimensional token span:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;[IMAGE_START]
&lt;&#x2F;span&gt;&lt;span&gt;[IMAGE] [IMAGE] ... [IMAGE] [IMAGE_NEWLINE]
&lt;&#x2F;span&gt;&lt;span&gt;[IMAGE] [IMAGE] ... [IMAGE] [IMAGE_NEWLINE]
&lt;&#x2F;span&gt;&lt;span&gt;...
&lt;&#x2F;span&gt;&lt;span&gt;[IMAGE_END]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All these positions carry the same &lt;code&gt;image_token_id&lt;&#x2F;code&gt; in &lt;code&gt;input_ids&lt;&#x2F;code&gt;. Additional type and embedding assembly rules distinguish START, IMAGE, NEWLINE, and END. Aligner output replaces regular IMAGE rows, while learned vectors fill the boundary and newline positions.&lt;&#x2F;p&gt;
&lt;p&gt;This representation must also survive chunked prefill. An image span may cross a chunk boundary, so the engine cannot replace rows only once in a complete prompt. zLLM retains each image&#x27;s token range and overlay rows. When it assembles an embedding chunk, it replaces only the intersecting part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-1-reproduce-the-discrete-image-processing-rules&quot;&gt;Step 1: Reproduce the discrete image-processing rules&lt;&#x2F;h2&gt;
&lt;p&gt;V4.1 Flash uses 14×14 patches, a 1,024-dimensional vision hidden state, 16 attention heads, and a 32-layer ViT. The aligner merges a 3×3 spatial neighborhood and projects the result into the language model&#x27;s 5,120-dimensional hidden state. A single image may use at most 1,024 language tokens, while images below the minimum pixel budget are enlarged proportionally first.&lt;&#x2F;p&gt;
&lt;p&gt;Preprocessing contains a chain of discrete decisions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Compute the largest grid that fits the aspect ratio and token budget.&lt;&#x2F;li&gt;
&lt;li&gt;Constrain target dimensions to the patch and downsampling grid.&lt;&#x2F;li&gt;
&lt;li&gt;Apply PIL &lt;code&gt;contain&lt;&#x2F;code&gt; semantics with bicubic resizing.&lt;&#x2F;li&gt;
&lt;li&gt;Center the result on RGB 127 gray padding.&lt;&#x2F;li&gt;
&lt;li&gt;Flatten patches in &lt;code&gt;(grid_y, grid_x, channel, y, x)&lt;&#x2F;code&gt; order.&lt;&#x2F;li&gt;
&lt;li&gt;Map pixels from &lt;code&gt;[0, 255]&lt;&#x2F;code&gt; to &lt;code&gt;[-1, 1]&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The first discrepancy appeared in step three. A common contain helper may use &lt;code&gt;ceil&lt;&#x2F;code&gt; to cover a destination edge. The official path branches on aspect ratio, computes the other dimension, and applies &lt;code&gt;round&lt;&#x2F;code&gt;. A one-pixel difference changes padding offsets and boundary patches, after which every corresponding row differs.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM therefore implements this planning and rounding rule explicitly instead of applying a generic resize helper. Image preprocessing is part of the model&#x27;s numerical definition, even though it occurs before the vision tower.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-locate-the-first-divergence-with-five-tensor-boundaries&quot;&gt;Step 2: Locate the first divergence with five tensor boundaries&lt;&#x2F;h2&gt;
&lt;p&gt;Final generated text cannot tell us whether an error began in preprocessing, visual attention, the aligner, or the language model. We made the official implementation dump intermediate tensors and exported the same boundaries from zLLM:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Boundary&lt;&#x2F;th&gt;&lt;th&gt;What it can isolate&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Patches&lt;&#x2F;td&gt;&lt;td&gt;Resize, padding, normalization, and patch row order&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2D RoPE cos&#x2F;sin&lt;&#x2F;td&gt;&lt;td&gt;Height and width coordinates, frequency order, and rotation layout&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Block 0 output&lt;&#x2F;td&gt;&lt;td&gt;Patch projection, QKV bias, attention, and residual order&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Final norm output&lt;&#x2F;td&gt;&lt;td&gt;Error growth across 32 blocks and RMSNorm semantics&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Aligner output&lt;&#x2F;td&gt;&lt;td&gt;Right&#x2F;bottom padding, 3×3 unfold, channel order, and two linear projections&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The method is useful because it always looks for the &lt;strong&gt;earliest material divergence&lt;&#x2F;strong&gt;. If patches differ, investigation stops at preprocessing. If patches match and block 0 diverges, positional encoding and the first vision block become the search area. This avoids guessing about an error dozens of layers earlier from final text.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM added optional vision debug dumps and a focused real-weight test for this purpose. It runs only the vision tower and aligner and takes about one second on one GPU, avoiding a full eight-GPU language run for every comparison.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-height-and-width-occupy-blocks-in-2d-rope&quot;&gt;Step 3: Height and width occupy blocks in 2D RoPE&lt;&#x2F;h2&gt;
&lt;p&gt;Visual attention uses two-dimensional positions. For a grid coordinate &lt;code&gt;(h, w)&lt;&#x2F;code&gt;, the official implementation builds height and width frequencies and flattens them as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;[h·f0, h·f1, ..., h·fn, w·f0, w·f1, ..., w·fn]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our early implementation interpreted the layout as interleaved:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;[h·f0, w·f0, h·f1, w·f1, ...]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The shapes are identical, and basic invariants such as &lt;code&gt;cos² + sin² = 1&lt;&#x2F;code&gt; pass for both. Each channel nevertheless receives a different spatial coordinate. Comparing cos&#x2F;sin directly led us to use height and width blocks and to preserve the official half-vector rotation convention.&lt;&#x2F;p&gt;
&lt;p&gt;This shows why tensor comparison reveals more than shape checks. The incorrect ordering causes no out-of-bounds access or NaN; it simply presents another geometry to the model.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-4-the-aligner-pads-a-two-dimensional-grid&quot;&gt;Step 4: The aligner pads a two-dimensional grid&lt;&#x2F;h2&gt;
&lt;p&gt;After 32 ViT blocks, the aligner pads the right and bottom edges to multiples of three and unfolds each 3×3 window. An early implementation simplified padding to appending zero rows to the flattened tensor.&lt;&#x2F;p&gt;
&lt;p&gt;Appending rows happens to work for bottom padding. It fails on the right edge. If each source row contains five patches and must be padded to six, the sixth position of every row should be zero. Appending zeros only at the end places the first valid patch of the second row in the first row&#x27;s padding slot, shifting the rest of the image.&lt;&#x2F;p&gt;
&lt;p&gt;The corrected implementation maps each &lt;code&gt;(source_y, source_x)&lt;&#x2F;code&gt; to a 3×3 destination window and an offset inside that window. It also follows the official channel-major unfold order:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;[the 9 positions of channel 0,
&lt;&#x2F;span&gt;&lt;span&gt; the 9 positions of channel 1,
&lt;&#x2F;span&gt;&lt;span&gt; ...]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The current path downloads final-norm output to the host, assembles this grid, and uploads it again. The transfer is at most about 15 MB per image. This keeps the semantics explicit and easy to compare; moving assembly into a device kernel remains a separate optimization.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-5-pad-588-columns-to-592-without-changing-the-math&quot;&gt;Step 5: Pad 588 columns to 592 without changing the math&lt;&#x2F;h2&gt;
&lt;p&gt;One 14×14 RGB patch contains:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;3 × 14 × 14 = 588
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The current ROCm matrix path requires input columns aligned to 16, and 588 is not a multiple of 16. zLLM pads four zeros onto both the patch input and the matching projection-weight rows, extending the operation to 592 columns:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;[x0 ... x587, 0, 0, 0, 0]
&lt;&#x2F;span&gt;&lt;span&gt;×
&lt;&#x2F;span&gt;&lt;span&gt;[w0 ... w587, 0, 0, 0, 0]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The new columns always contribute zero to the dot product. The result is mathematically equivalent to the original 588-dimensional linear layer while satisfying the kernel&#x27;s tile constraint. This adaptation belongs at the backend preparation boundary and does not become a new model dimension.&lt;&#x2F;p&gt;
&lt;p&gt;After correcting contain rounding, RoPE layout, aligner grid assembly, and patch alignment, the recorded maximum relative difference at the aligner was about 5%. We treat that as an error range to continue monitoring on the BF16 execution path, rather than claiming elementwise identity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;comparing-the-deepseek-v4-1-flash-and-qwen3-vl-vision-towers&quot;&gt;Comparing the DeepSeek V4.1 Flash and Qwen3-VL vision towers&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM also supports Qwen3-VL. Both models turn dynamically sized images into patches, encode them with a ViT, and project the result into the language hidden state, but their tensor contracts differ substantially. The comparison below uses zLLM&#x27;s current &lt;strong&gt;Qwen3-VL-32B&lt;&#x2F;strong&gt; configuration; other sizes and MoE variants in the Qwen3-VL family may use different language-layer specifications.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;deepseek-v41-flash&#x2F;deepseek-qwen3vl-vision-comparison-en.svg&quot; alt=&quot;Vision-tower architecture comparison between DeepSeek V4.1 Flash and Qwen3-VL-32B&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stage&lt;&#x2F;th&gt;&lt;th&gt;DeepSeek V4.1 Flash&lt;&#x2F;th&gt;&lt;th&gt;Current zLLM Qwen3-VL-32B path&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Patch&lt;&#x2F;td&gt;&lt;td&gt;14×14; one image row has &lt;code&gt;3×14×14=588&lt;&#x2F;code&gt; columns&lt;&#x2F;td&gt;&lt;td&gt;16×16 with temporal patch=2; one row has &lt;code&gt;3×2×16×16=1536&lt;&#x2F;code&gt; columns&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Vision tower&lt;&#x2F;td&gt;&lt;td&gt;32 layers, hidden 1,024, 16 heads, RMSNorm and SwiGLU&lt;&#x2F;td&gt;&lt;td&gt;27 layers, hidden 1,152, 16 heads, LayerNorm and GELU MLP&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Image sizing&lt;&#x2F;td&gt;&lt;td&gt;Targets at most 1,024 LLM image tokens, then uses contain and gray padding&lt;&#x2F;td&gt;&lt;td&gt;Uses min&#x2F;max-pixel smart resize, aligning dimensions to &lt;code&gt;patch×merge=32&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Vision position&lt;&#x2F;td&gt;&lt;td&gt;Height&#x2F;width-blocked 2D RoPE&lt;&#x2F;td&gt;&lt;td&gt;Interpolated learned position embeddings plus visual 2D RoPE&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Spatial reduction&lt;&#x2F;td&gt;&lt;td&gt;3×3 aligner with optional right&#x2F;bottom zero padding&lt;&#x2F;td&gt;&lt;td&gt;2×2 merger after preprocessing guarantees a divisible grid&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Language input&lt;&#x2F;td&gt;&lt;td&gt;Final aligner output replaces IMAGE rows, with learned START&#x2F;NEWLINE&#x2F;END vectors&lt;&#x2F;td&gt;&lt;td&gt;Merger output replaces &lt;code&gt;&amp;lt;image_pad&amp;gt;&lt;&#x2F;code&gt; between &lt;code&gt;&amp;lt;vision_start&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;vision_end&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Language position&lt;&#x2F;td&gt;&lt;td&gt;The serialized image grid advances through the text sequence&lt;&#x2F;td&gt;&lt;td&gt;M-RoPE assigns temporal&#x2F;height&#x2F;width positions and uses &lt;code&gt;rope_delta&lt;&#x2F;code&gt; to continue text positions&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Multi-level vision injection&lt;&#x2F;td&gt;&lt;td&gt;Main path injects once at the embedding boundary&lt;&#x2F;td&gt;&lt;td&gt;DeepStack takes ViT layers 8, 16, and 24 through separate mergers and injects them into the early language model&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Current language backbone&lt;&#x2F;td&gt;&lt;td&gt;MoE with row-level &lt;code&gt;bias_vl&lt;&#x2F;code&gt; routing for image rows&lt;&#x2F;td&gt;&lt;td&gt;The integrated 32B path uses dense MLPs and has no image-row MoE dual bias&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The clearest common requirement is that vision-output rows must exactly equal the image-placeholder rows in the language model. A difference of one row shifts all following text positions. The models enforce this through different protocols. DeepSeek inserts an explicit NEWLINE after each span row and learns vectors for START, NEWLINE, and END. Qwen3-VL uses separate vision boundary tokens, a continuous &lt;code&gt;&amp;lt;image_pad&amp;gt;&lt;&#x2F;code&gt; range, and three-axis position IDs for the complete sequence.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;patch-order-reflects-the-spatial-reducer&quot;&gt;Patch order reflects the spatial reducer&lt;&#x2F;h3&gt;
&lt;p&gt;DeepSeek lays patches out in ordinary two-dimensional row-major order. Its aligner groups 3×3 windows only after the vision tower, so an edge may require zero padding. Qwen3-VL preprocessing makes patches from the same 2×2 merge group contiguous, allowing a direct spatial merge after the vision tower. For a still image, the same frame fills both temporal-patch slots; for video, two adjacent frames form one temporal patch. The Qwen3-VL patch input therefore carries a time dimension from the beginning.&lt;&#x2F;p&gt;
&lt;p&gt;This also explains why DeepSeek&#x27;s 588 columns need padding to 592 while Qwen3-VL&#x27;s 1,536 columns already satisfy 16-column alignment. Alignment is a result of the model&#x27;s patch contract combined with a kernel tile requirement. One vision tower&#x27;s adjustment cannot be copied into another model.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;qwen3-vl-has-two-layers-of-positional-meaning&quot;&gt;Qwen3-VL has two layers of positional meaning&lt;&#x2F;h3&gt;
&lt;p&gt;The DeepSeek vision tower uses 2D RoPE directly, after which its serialized span participates in the language sequence. Inside the Qwen3-VL vision tower, zLLM first interpolates a learned 48×48 position table to the actual image grid and then applies visual 2D RoPE. At the language boundary, it constructs temporal, height, and width M-RoPE positions for visual tokens. Text tokens use equal positions on all three axes, while &lt;code&gt;rope_delta&lt;&#x2F;code&gt; keeps decode positions continuous after the visual range.&lt;&#x2F;p&gt;
&lt;p&gt;Tensor alignment for Qwen3-VL therefore needs more than a cos&#x2F;sin check inside the vision tower. It must also verify learned-position interpolation, three-axis position IDs, and &lt;code&gt;rope_delta&lt;&#x2F;code&gt; after the image. The height&#x2F;width error found in this DeepSeek integration was confined to the vision tower; Qwen3-VL can also diverge where visual output enters the language model.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deepstack-means-that-vision-output-is-plural&quot;&gt;DeepStack means that “vision output” is plural&lt;&#x2F;h3&gt;
&lt;p&gt;DeepSeek&#x27;s main path places final aligner embeddings into the image span and then lets the language model process them layer by layer. Qwen3-VL also extracts features from ViT layers 8, 16, and 24. Three DeepStack mergers project those intermediate features into the language hidden size and add them again in the early language model.&lt;&#x2F;p&gt;
&lt;p&gt;This preserves visual information from multiple depths. The final merger provides the input embedding, while intermediate visual features continue to refine early language hidden states. An implementation must manage all four results as one unit: one primary embedding and three DeepStack feature tensors. Connecting only the final merger can preserve valid shapes and text generation while dropping information the model expects for spatial localization and fine detail. Qwen describes DeepStack as a core architecture update that fuses multi-level ViT features. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;QwenLM&#x2F;Qwen3-VL&quot;&gt;Official Qwen3-VL repository&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;zllm-shares-capabilities-while-preserving-model-orchestration&quot;&gt;zLLM shares capabilities while preserving model orchestration&lt;&#x2F;h3&gt;
&lt;p&gt;The two paths can share backend capabilities for image decoding, dynamic sizing, patch tensors, visual attention, linear layers, spatial merge, and embedding scatter. Their resize rules, patch order, positional encoding, merger layout, span protocol, and injection depth remain explicit in each model runtime.&lt;&#x2F;p&gt;
&lt;p&gt;This boundary follows the format-independent design discussed in &lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;en&#x2F;blog&#x2F;deepseek-v41-flash-eight-gpu&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt;: stable execution capabilities are shared, while model semantics live in preparation and orchestration. Two towers may both look like “ViT plus projector,” yet their actual data contracts still need independent alignment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-6-image-spans-change-engram-sequence-semantics&quot;&gt;Step 6: Image spans change Engram sequence semantics&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;en&#x2F;blog&#x2F;deepseek-v41-flash-engram&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; described how Engram retrieves memory from hashes of consecutive token n-grams. Every position in an image span shares one token ID. Feeding these positions into the hash window would create many repeated n-grams with no linguistic meaning and could incorrectly connect text on opposite sides of an image.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM follows the official semantics by pushing image positions into a &lt;code&gt;DEAD&lt;&#x2F;code&gt; state, preventing n-grams from crossing an image span. Engram injection layers also receive a row-level &lt;code&gt;image_mask&lt;&#x2F;code&gt;: text rows perform retrieval and gating, while image rows bypass the Engram gate so their visual embeddings pass through. If an internal image token is sampled as output, generation stops immediately to keep it from leaking through the protocol or feeding back into the next step.&lt;&#x2F;p&gt;
&lt;p&gt;Vision integration therefore affects more than embedding replacement. Every sequence-dependent state machine, including n-grams, KV caches, positions, and output filtering, must know the row type.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-7-image-and-text-rows-use-different-moe-correction-biases&quot;&gt;Step 7: Image and text rows use different MoE correction biases&lt;&#x2F;h2&gt;
&lt;p&gt;Once visual features enter the 40-layer language model, they pass through MoE layers. Each token executes only a Top-K subset of the experts selected by a router, after which the selected expert outputs are combined with route weights.&lt;&#x2F;p&gt;
&lt;p&gt;The “dual” in dual-bias routing means that the checkpoint provides two expert-calibration tables for the same router: text rows use &lt;code&gt;bias&lt;&#x2F;code&gt;, and image-span rows use &lt;code&gt;bias_vl&lt;&#x2F;code&gt;. The two biases are not added together, and the model does not run two routers. There is one router weight matrix, one expert set, and one Top-K procedure; the runtime chooses a bias according to the row type.&lt;&#x2F;p&gt;
&lt;p&gt;For expert &lt;code&gt;e&lt;&#x2F;code&gt; and one hidden-state row &lt;code&gt;h&lt;&#x2F;code&gt;, the V4.1 route can be simplified into five steps:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;raw logit:          l_e = h · W_router[e]
&lt;&#x2F;span&gt;&lt;span&gt;unbiased score:     r_e = sqrt(softplus(l_e))
&lt;&#x2F;span&gt;&lt;span&gt;row-specific bias:  b_e = text ? bias[e] : bias_vl[e]
&lt;&#x2F;span&gt;&lt;span&gt;expert selection:   TopK(r_e + b_e)
&lt;&#x2F;span&gt;&lt;span&gt;expert route weight: r_e &#x2F; sum(r_selected) × route_scale
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The bias participates only in Top-K ranking. The final mixture weight still comes from the unbiased score &lt;code&gt;r_e&lt;&#x2F;code&gt; of each selected expert. This allows the model to change which experts a class of tokens tends to select without injecting the correction bias into the numerical weight of their outputs.&lt;&#x2F;p&gt;
&lt;p&gt;A four-expert, Top-2 example makes the distinction concrete:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Expert&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Raw score &lt;code&gt;r&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Text bias&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Image &lt;code&gt;bias_vl&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;E0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.60&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.55&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.10&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E2&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.50&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.20&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;E3&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.45&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.00&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The text row selects E1 and E0 by &lt;code&gt;r + bias&lt;&#x2F;code&gt;; the image row selects E2 and E0 by &lt;code&gt;r + bias_vl&lt;&#x2F;code&gt;. E2&#x27;s actual mixture weight on the image row still uses its raw 0.50 rather than its biased 0.70. Dual-bias routing therefore &lt;strong&gt;calibrates the expert set by modality&lt;&#x2F;strong&gt; rather than scaling expert outputs by modality.&lt;&#x2F;p&gt;
&lt;p&gt;Using the text bias for an image row leaves every matrix shape, expert count, and output dimension valid, but sends visual tokens to a different expert set. Like an incorrect RoPE ordering, the system can run reliably while following a path different from training.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM carries &lt;code&gt;router_bias_vl&lt;&#x2F;code&gt; and a row-level &lt;code&gt;image_rows&lt;&#x2F;code&gt; mask from model weights through its generic MoE interface. The ROCm kernel then selects a bias for each row. A single prefill batch may contain both text and image rows, so this decision is row-level rather than one setting for the whole batch. Text-only models and paths without a second bias retain the existing interface semantics. A CPU reference and the HIP kernel were compared on interleaved text and image rows, checking both expert IDs and the rule that route weights use unbiased normalization.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;end-to-end-results-and-their-limits&quot;&gt;End-to-end results and their limits&lt;&#x2F;h2&gt;
&lt;p&gt;After the vision tensor fixes and dual-bias routing, the eight-GPU path produced several concrete results:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Input and prompt&lt;&#x2F;th&gt;&lt;th&gt;Observed result&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Carrot example, open description&lt;&#x2F;td&gt;&lt;td&gt;Identified “a pile of carrots”&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Corn example, English identification&lt;&#x2F;td&gt;&lt;td&gt;Included “fresh corn”&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Detailed corn description, before and after dual bias&lt;&#x2F;td&gt;&lt;td&gt;Output grew from 10 to 46 tokens&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Carrot image, Chinese counting question&lt;&#x2F;td&gt;&lt;td&gt;Correctly answered “five carrots”&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;These results establish a working connection across HTTP input, preprocessing, the vision tower, image spans, the language backbone, and routing. They also show that &lt;code&gt;bias_vl&lt;&#x2F;code&gt; materially affects expert selection for visual tokens. They are engineering acceptance examples, rather than a quantitative visual evaluation across OCR, charts, spatial relations, and fine-grained recognition.&lt;&#x2F;p&gt;
&lt;p&gt;Three boundaries remain explicit. Some images still produce short answers under open-ended English prompts. Aligner grid assembly still crosses host memory. zLLM currently uses the tanh GELU approximation while the official vision path uses erf, with local differences around &lt;code&gt;1e-3&lt;&#x2F;code&gt;. Full-model statistical alignment and stable multimodal throughput require further validation.&lt;&#x2F;p&gt;
&lt;p&gt;The difficult part of multimodal integration is that every detail that appears approximately equivalent can be amplified by the following network. A reliable process turns the pipeline into mathematical boundaries: align pixels and patches first, then positions and vision blocks, then the grid and language embeddings, and finally row-level routing inside MoE. The result is an engineering baseline that can support later optimization, not only a demonstration that produces text.&lt;&#x2F;p&gt;
&lt;p&gt;The next article will collect the current performance data, explain the measurement conventions for 50K prefill, continuous decode, and early projection, and identify which figures cannot be placed in the same comparison table.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Engineering basis: zLLM&#x27;s DeepSeek V4.1 Flash integration record, the current Qwen3-VL runtime, and the actual changes in &lt;code&gt;69ff88ad&lt;&#x2F;code&gt;, &lt;code&gt;ae6248a5&lt;&#x2F;code&gt;, and &lt;code&gt;51f83af8&lt;&#x2F;code&gt;. Model semantics follow DeepSeek&#x27;s official &lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;deepseek-ai&#x2F;DeepSeek-V4.1-Flash&#x2F;blob&#x2F;main&#x2F;inference&#x2F;vision.py&quot;&gt;vision.py&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;deepseek-ai&#x2F;DeepSeek-V4.1-Flash&#x2F;blob&#x2F;main&#x2F;inference&#x2F;image_processor.py&quot;&gt;image_processor.py&lt;&#x2F;a&gt;, and the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;QwenLM&#x2F;Qwen3-VL&quot;&gt;official Qwen3-VL repository&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DeepSeek V4.1 Flash Engineering, Part 2: From Weight Loading to an Eight-GPU Text Pipeline</title>
        <published>2026-09-15T00:00:00+00:00</published>
        <updated>2026-09-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/deepseek-v41-flash-eight-gpu/"/>
        <id>https://zhuai.tech/en/blog/deepseek-v41-flash-eight-gpu/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/deepseek-v41-flash-eight-gpu/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;en&#x2F;blog&#x2F;deepseek-v41-flash-engram&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; covered Engram: keeping learned memory in host RAM and using lookup, AVX-512 BF16, and CPU&#x2F;GPU overlap to make it part of inference. This article returns to the GPU backbone.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Splitting a model into eight stages assigns the work to devices. Correct text generation also requires preserving the model&#x27;s state.&lt;&#x2F;strong&gt; DeepSeek V4.1 Flash shares compressed KV across layers, reuses sparse selections, and passes mHC mixing coefficients between sublayers. Device boundaries cut through these dependencies; they cannot disappear with a stage&#x27;s local variables.&lt;&#x2F;p&gt;
&lt;p&gt;We integrated the official safetensors weights on eight AMD Radeon Pro W7900D GPUs, fixing weight interpretation, ROCm execution, session state, and the output head along the way. This account follows that implementation and its subsequent corrections. It does not fold other ongoing work into the conclusions of those integration tests.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;start-with-the-complete-inference-round&quot;&gt;Start with the complete inference round&lt;&#x2F;h2&gt;
&lt;p&gt;The language backbone has 40 layers, a hidden dimension of 5,120, and four mHC residual streams. The eight-GPU configuration assigns consecutive complete layers to each device. Each &lt;code&gt;layer_ends&lt;&#x2F;code&gt; entry is an exclusive upper bound:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;layer_ends&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;15&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;25&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;30&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;35&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;40&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;GPU&lt;&#x2F;th&gt;&lt;th&gt;Backbone layers&lt;&#x2F;th&gt;&lt;th&gt;State encountered in this stage&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0&lt;&#x2F;td&gt;&lt;td&gt;L0–L4&lt;&#x2F;td&gt;&lt;td&gt;Engram at L1; L2 publishes compressed KV and selections&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;&#x2F;td&gt;&lt;td&gt;L5–L9&lt;&#x2F;td&gt;&lt;td&gt;Continues with L2 state, then L8 publishes a new group&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;&#x2F;td&gt;&lt;td&gt;L10–L14&lt;&#x2F;td&gt;&lt;td&gt;Continues with L8 state; L14 applies Engram and publishes new state&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;&#x2F;td&gt;&lt;td&gt;L15–L19&lt;&#x2F;td&gt;&lt;td&gt;Uses L14&#x27;s shared compressed KV and selections&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;&#x2F;td&gt;&lt;td&gt;L20–L24&lt;&#x2F;td&gt;&lt;td&gt;L20 publishes KV for the remaining layers; L24 updates selections&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&lt;&#x2F;td&gt;&lt;td&gt;L25–L29&lt;&#x2F;td&gt;&lt;td&gt;Uses L20 KV; L28 updates selections&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;6&lt;&#x2F;td&gt;&lt;td&gt;L30–L34&lt;&#x2F;td&gt;&lt;td&gt;Uses L20 KV; L32 updates selections&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;7&lt;&#x2F;td&gt;&lt;td&gt;L35–L39&lt;&#x2F;td&gt;&lt;td&gt;Uses L20 KV; L36 updates selections, then the backbone finishes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Layer numbers are zero-based. This is a pipeline of consecutive layers: the device responsible for a layer runs its attention, router, and expert computation. Five layers per GPU describes placement; it does not mean eight independent pieces of the same token execute simultaneously.&lt;&#x2F;p&gt;
&lt;p&gt;With speculative decoding disabled, the complete round is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;text → tokenizer &#x2F; conversation encoding → embedding
&lt;&#x2F;span&gt;&lt;span&gt;     → GPU0: L0–L4 → GPU1: L5–L9 → … → GPU7: L35–L39
&lt;&#x2F;span&gt;&lt;span&gt;     → final mHC reduction → final norm → LM head → sampling
&lt;&#x2F;span&gt;&lt;span&gt;     → next token returns to the entry point
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Prefill can place multiple input chunks into the pipeline. Ordinary single-request decode must wait for the sampled token before starting the next token&#x27;s round. Eight GPUs provide capacity and a division of work; throughput still depends on the dependency chain, chunk size, and stage times.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-1-assemble-file-tensors-into-usable-model-weights&quot;&gt;Step 1: Assemble file tensors into usable model weights&lt;&#x2F;h2&gt;
&lt;p&gt;Weight loading starts with three questions: what is the tensor called, what is its logical shape, and how should its bytes be interpreted?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;safetensors-and-gguf-solve-different-layers-of-the-problem&quot;&gt;Safetensors and GGUF solve different layers of the problem&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Safetensors&lt;&#x2F;strong&gt; is primarily a safe, simple tensor container. It records tensor names, dtypes, shapes, and byte ranges, either in one file or across shards described by an index JSON. It does not execute deserialization code, works well for official training checkpoints, and supports tensor-level or row-level reads. The model configuration and implementation must still explain what a tensor such as &lt;code&gt;layers.2.attn.wq_a.weight&lt;&#x2F;code&gt; means in DeepSeek V4.1 inference.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;GGUF&lt;&#x2F;strong&gt; combines a tensor directory with inference metadata that commonly describes the model architecture, context parameters, and tokenizer, while also defining quantization types used by the GGML ecosystem. It is closer to a distribution package for local inference: one file can tell an engine both what model it contains and how packed weight bytes are represented. Conversion has already chosen naming, quantization, and metadata conventions, however, so a third-party GGUF still needs validation that it fully represents a new architecture.&lt;&#x2F;p&gt;
&lt;p&gt;The distinction is not “Safetensors means high precision, GGUF means low precision.” Safetensors can store FP8, MXFP4, BF16, and other dtypes. GGUF can contain F32, F16, BF16, or a mixture of quantized tensors. Their fundamental differences concern container contracts, metadata conventions, and quantized representations.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Dimension&lt;&#x2F;th&gt;&lt;th&gt;Safetensors&lt;&#x2F;th&gt;&lt;th&gt;GGUF&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Primary role&lt;&#x2F;td&gt;&lt;td&gt;General, safe tensor storage&lt;&#x2F;td&gt;&lt;td&gt;Inference-oriented model and quantization container&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Model structure&lt;&#x2F;td&gt;&lt;td&gt;Usually interpreted with external configuration and model code&lt;&#x2F;td&gt;&lt;td&gt;Usually described by file metadata&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Tokenizer&lt;&#x2F;td&gt;&lt;td&gt;Usually stored in external files&lt;&#x2F;td&gt;&lt;td&gt;Commonly carried in GGUF metadata&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Typical organization&lt;&#x2F;td&gt;&lt;td&gt;Official checkpoint, often sharded&lt;&#x2F;td&gt;&lt;td&gt;Single-file or size-sharded distribution package&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;zLLM access&lt;&#x2F;td&gt;&lt;td&gt;Read tensors or selected rows by name, shape, and dtype&lt;&#x2F;td&gt;&lt;td&gt;Parse metadata, tensor directory, and GGML quantization types, then access matrices on demand&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;format-independence-begins-at-zllm-s-model-orchestration-boundary&quot;&gt;Format independence begins at zLLM&#x27;s model-orchestration boundary&lt;&#x2F;h3&gt;
&lt;p&gt;zLLM does not force Safetensors and GGUF to share a byte layout. The implementation has four layers:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;File containers
&lt;&#x2F;span&gt;&lt;span&gt;  SafetensorsStore &#x2F; GgufReader
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;Model weight assembly
&lt;&#x2F;span&gt;&lt;span&gt;  tensor naming, shape validation, logical weight roles
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;Unified prepared weights and backend capabilities
&lt;&#x2F;span&gt;&lt;span&gt;  LinearWeight &#x2F; PreparedLayer &#x2F; ExpertSource
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;DeepSeek V4.1 runtime
&lt;&#x2F;span&gt;&lt;span&gt;  attention → shared KV &#x2F; selections → MoE → mHC
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The container layer handles metadata, tensor indexing, and raw byte access. The Safetensors path assembles BlockFP8, MXFP8, MXFP4, and dense tensors into model weights with explicit logical shapes. The GGUF path interprets its own quantization types and prepares linear weights and expert sources consumable by the same backend.&lt;&#x2F;p&gt;
&lt;p&gt;The paths may differ before weight preparation. The current DeepSeek implementation has separate Safetensors and GGUF layer-preparation functions because tensor naming, fusion, and quantized slicing differ. After preparation, both produce the same &lt;code&gt;DeepSeekV4PreparedLayer&lt;&#x2F;code&gt; and enter the same layer runtime. Attention, MoE, KV lifecycle, and mHC ordering are not reimplemented for each file extension.&lt;&#x2F;p&gt;
&lt;p&gt;Format independence therefore does not mean that any GGUF can automatically replace the official checkpoint. It means that &lt;strong&gt;model algorithms do not depend on a file container; container differences converge at the reading and weight-assembly boundary.&lt;&#x2F;strong&gt; A new format must still supply complete architecture metadata, tensor mappings, and backend support. If Engram, mHC, shared compressed KV, or a required quantized kernel is missing, the engine should reject it explicitly rather than generate under incorrect assumptions.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM detects the top-level namespace. For example, the current loader uses a &lt;code&gt;language_model.&lt;&#x2F;code&gt; prefix when it finds &lt;code&gt;language_model.embed.weight&lt;&#x2F;code&gt;, and unprefixed names otherwise. This accommodates checkpoint organizations encountered during integration. The tensor directory determines the prefix, not the name of the download folder.&lt;&#x2F;p&gt;
&lt;p&gt;Likewise, knowing that weights are quantized does not determine their execution path:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Representation&lt;&#x2F;th&gt;&lt;th&gt;What loading must establish&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;BlockFP8&lt;&#x2F;td&gt;&lt;td&gt;Data matrix, two-dimensional scale grid, and block sizes along both axes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MXFP8&lt;&#x2F;td&gt;&lt;td&gt;Codes and scales grouped along each row&#x27;s input dimension, rather than a two-dimensional block layout&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MXFP4 experts&lt;&#x2F;td&gt;&lt;td&gt;Packed data, grouped scales, expert index, and logical gate&#x2F;up&#x2F;down shapes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Unquantized tensors&lt;&#x2F;td&gt;&lt;td&gt;dtype, shape, and their role in normalization, mixing, or projection&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Experts can also be stored separately or in merged tensors. The implementation reads individually named expert matrices or selects the appropriate row range from a merged rank-three expert tensor. For the FFN, gate&#x2F;up have shape &lt;code&gt;[intermediate, hidden]&lt;&#x2F;code&gt;, while down has shape &lt;code&gt;[hidden, intermediate]&lt;&#x2F;code&gt;. The relationship between packed columns and logical columns must remain intact.&lt;&#x2F;p&gt;
&lt;p&gt;The weight layer handles these differences. Model orchestration receives interpretable matrices and expert sources, and the backend selects the corresponding execution path.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-the-fp8-bug-was-in-the-two-dimensional-scale-layout&quot;&gt;Step 2: The FP8 bug was in the two-dimensional scale layout&lt;&#x2F;h2&gt;
&lt;p&gt;A concrete integration fix concerned &lt;strong&gt;32×32 BlockFP8&lt;&#x2F;strong&gt; in the official V4.1 backbone linear weights. The official inference implementation explicitly uses this block layout. &lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;deepseek-ai&#x2F;DeepSeek-V4.1-Flash&#x2F;blob&#x2F;main&#x2F;inference&#x2F;model.py&quot;&gt;Official model implementation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For an &lt;code&gt;[M, N]&lt;&#x2F;code&gt; matrix, the scale grid has shape:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;[ceil(M &#x2F; 32), ceil(N &#x2F; 32)]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Element &lt;code&gt;(r, c)&lt;&#x2F;code&gt; uses the scale at &lt;code&gt;(r &#x2F; 32, c &#x2F; 32)&lt;&#x2F;code&gt;, with integer division. Both axes determine the scale. Row-independent MXFP8 groups values within each row; a group size of 32 does not make it the same representation.&lt;&#x2F;p&gt;
&lt;p&gt;We added a small test: all codes in a 64×64 matrix encode the same value, while its four 32×32 blocks receive scales corresponding to multipliers of 1, 2, 4, and 8. The decoded quadrants must contain those four values. This directly tests scale transitions along both rows and columns.&lt;&#x2F;p&gt;
&lt;p&gt;Small matrices introduce another subtlety: their scale shapes may not distinguish 32×32 from the older 128×128 layout. The loader must use model-version information as well as tensor shape.&lt;&#x2F;p&gt;
&lt;p&gt;The correction had two parts. &lt;code&gt;80b8d3e5&lt;&#x2F;code&gt; fixed weight assembly; &lt;code&gt;07cf53f1&lt;&#x2F;code&gt; completed the ROCm 32×32 BlockFP8 path. Correct CPU decoding does not automatically establish that a GPU kernel uses the same scale indices.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-separate-local-kv-shared-compressed-kv-and-selections&quot;&gt;Step 3: Separate local KV, shared compressed KV, and selections&lt;&#x2F;h2&gt;
&lt;p&gt;The familiar idea of one KV cache per layer needs to be expanded into several distinct objects.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Sliding-window KV&lt;&#x2F;strong&gt; belongs to each layer and serves its local history. The configured window is 128.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Compressed KV&lt;&#x2F;strong&gt; is produced by a designated source layer and reused by a group of layers. zLLM&#x27;s V4.1 mapping is:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Source layer&lt;&#x2F;th&gt;&lt;th&gt;Consumers&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Ratio&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;L2&lt;&#x2F;td&gt;&lt;td&gt;L2–L7&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;L8&lt;&#x2F;td&gt;&lt;td&gt;L8–L13&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;L14&lt;&#x2F;td&gt;&lt;td&gt;L14–L19&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;L20&lt;&#x2F;td&gt;&lt;td&gt;L20–L39&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;L0 and L1 do not use this compressed branch. From L20 onward, &lt;code&gt;ratio=1&lt;&#x2F;code&gt; still means consuming the 1:1 representation produced at L20. It does not disable the branch or make each layer produce an independent replacement. Ratio describes granularity; the source mapping describes provenance.&lt;&#x2F;p&gt;
&lt;p&gt;This can be understood as a further engineering step beyond DSA, or DeepSeek Sparse Attention. DSA first makes the &lt;strong&gt;history dimension sparse&lt;&#x2F;strong&gt;. As the context grows, the indexer builds a lighter representation of the history, scores it against the current query, and selects the Top-K positions. Attention then reads KV only from those positions instead of visiting the entire history. The longer the context, the more unnecessary history this mechanism can skip.&lt;&#x2F;p&gt;
&lt;p&gt;V4.1 extends sparsity from the time axis into network depth. Adjacent layers do not have to construct another compressed history, nor does every layer need to repeat the full selection process. A KV source layer publishes compressed KV and index keys for a group; index source layers recompute selections at a configured cadence; the other layers consume the latest published result. The design therefore has two independent dimensions of sparsity:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Dimension&lt;&#x2F;th&gt;&lt;th&gt;Problem it addresses&lt;&#x2F;th&gt;&lt;th&gt;Runtime behavior&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;History sparsity&lt;&#x2F;td&gt;&lt;td&gt;A query does not need to visit every previous token&lt;&#x2F;td&gt;&lt;td&gt;The indexer selects Top-K history positions and attention reads only those positions&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Cross-layer sparsity&lt;&#x2F;td&gt;&lt;td&gt;Adjacent layers need not repeatedly build similar history representations and selections&lt;&#x2F;td&gt;&lt;td&gt;Source layers publish compressed KV, index keys, or selections for later layers in the group&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This sharing preserves each layer&#x27;s own attention computation. Every layer derives its own query from its hidden state and maintains its own sliding-window KV. Layers reuse the compressed representation of distant history and, within its valid range, the selected history positions. Layer-specific computation remains, while highly repetitive history preparation and retrieval move from once per layer to once per group with periodic refreshes.&lt;&#x2F;p&gt;
&lt;p&gt;The L20–L39 range makes this separation especially clear. Its compressed KV continues to come from L20, while selections are refreshed at L20, L24, L28, L32, and L36. In other words, &lt;strong&gt;which layer supplies history&lt;&#x2F;strong&gt; and &lt;strong&gt;when the model reassesses which history is worth reading&lt;&#x2F;strong&gt; follow separate schedules. The larger, more stable compressed history can span more layers, while the lighter selection result can be refreshed more often.&lt;&#x2F;p&gt;
&lt;p&gt;This follows a path similar to our GLM 5.3 DSA implementation. A full indexer layer in GLM 5.3 builds the history index and publishes a selection; subsequent IndexShare layers reuse the latest valid selection, and consecutive MTP steps can reuse it when their state is complete and continuous. The weights and layer maps differ between the models, but the engineering goal is the same: &lt;strong&gt;find the worthwhile positions in a long history, then let subsequent computation reuse that retrieval instead of rescanning the entire history at every layer.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Selections&lt;&#x2F;strong&gt; are the history positions chosen by the indexer for each query. The configuration updates them at L2, L8, L14, L20, L24, L28, L32, and L36; other layers reuse the latest published result. Selections and compressed KV are different state: L24 can publish new selections while continuing to read L20&#x27;s compressed history.&lt;&#x2F;p&gt;
&lt;p&gt;Separating these objects lets the runtime express what a layer writes, whose history it reads, and whether it performs a new selection.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-4-do-not-reset-shared-state-at-a-gpu-boundary&quot;&gt;Step 4: Do not reset shared state at a GPU boundary&lt;&#x2F;h2&gt;
&lt;p&gt;Consider the transition from GPU0 to GPU1.&lt;&#x2F;p&gt;
&lt;p&gt;L2 on GPU0 has already produced shared history and selections. GPU1 starts at L5, and L5–L7 need those results until L8 publishes the next group. Creating fresh local shared state at every stage entrance gives L5 its hidden tensor but loses the selection it should reuse.&lt;&#x2F;p&gt;
&lt;p&gt;Fix &lt;code&gt;cc0be412&lt;&#x2F;code&gt; made selections part of the pipeline work item. A stage restores the preceding stage&#x27;s published selections, updates them as needed, and passes them onward. Compressed-KV sources are resolved through the whole model&#x27;s layer mapping instead of whichever source happened to appear inside the current stage.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Data&lt;&#x2F;th&gt;&lt;th&gt;How the pipeline manages it&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Hidden state&lt;&#x2F;td&gt;&lt;td&gt;Travels through stages with the current input chunk&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Selections&lt;&#x2F;td&gt;&lt;td&gt;Continue with the chunk and update at indexer layers&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Compressed KV history&lt;&#x2F;td&gt;&lt;td&gt;Lives in the session cache table; consumers resolve it by source layer&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Layer-local sliding KV&lt;&#x2F;td&gt;&lt;td&gt;Maintained by the session state responsible for that layer&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Continuing with a chunk is a logical dependency, not a requirement to download the data to the CPU. Subsequent optimization changed its physical placement.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-5-share-across-stages-within-a-session-isolate-across-sessions&quot;&gt;Step 5: Share across stages within a session, isolate across sessions&lt;&#x2F;h2&gt;
&lt;p&gt;Another lifecycle bug remained after fixing source resolution: each stage independently forked a full-layer cache table from its template.&lt;&#x2F;p&gt;
&lt;p&gt;Every table could be valid, but they were different instances. GPU0 wrote L2 history into one instance; GPU1 looked up L2 in another and found empty state.&lt;&#x2F;p&gt;
&lt;p&gt;Fix &lt;code&gt;307c5e85&lt;&#x2F;code&gt; introduced a coordinated session-opening path: &lt;strong&gt;fork one full-layer cache table for the session, then distribute references to that same table across all eight stages.&lt;&#x2F;strong&gt; Engram sequence state is also created at session scope and shared among that session&#x27;s stages.&lt;&#x2F;p&gt;
&lt;p&gt;This defines the boundary of sharing. Model objects and read-only weights can be reused between sessions. Mutable sequence history belongs to a particular session. Resetting a session must handle shared history and Engram token state as well as layer-local caches.&lt;&#x2F;p&gt;
&lt;p&gt;Sharing only works when ownership, consumers, and write visibility are explicit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-6-mhc-pre-mix-also-crosses-layers-and-gpus&quot;&gt;Step 6: mHC pre-mix also crosses layers and GPUs&lt;&#x2F;h2&gt;
&lt;p&gt;Once weights and KV are connected, checking the hidden tensor&#x27;s shape is still insufficient.&lt;&#x2F;p&gt;
&lt;p&gt;V4.1 mHC carries four residual streams and reduces them with mixing coefficients for sublayer inputs. Our early path treated sublayers more independently; the correction passed pre-mix state continuously:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;pre published by the preceding layer&amp;#39;s FFN
&lt;&#x2F;span&gt;&lt;span&gt;  → input reduction for this layer&amp;#39;s attention
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;pre published by this layer&amp;#39;s attention
&lt;&#x2F;span&gt;&lt;span&gt;  → input reduction for this layer&amp;#39;s FFN
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;pre published by this layer&amp;#39;s FFN
&lt;&#x2F;span&gt;&lt;span&gt;  → the next layer&amp;#39;s attention, or final output-head reduction
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The first layer uses the defined initialization behavior when no upstream pre is available. After the final layer, the last FFN&#x27;s pre reduces the expanded state before final norm and the LM head.&lt;&#x2F;p&gt;
&lt;p&gt;Fix &lt;code&gt;8919f797&lt;&#x2F;code&gt; made layer execution return both hidden state and the pre to propagate, and carried pre across stage boundaries. When GPU0 finishes L4, GPU1&#x27;s L5 needs both L4&#x27;s hidden tensor and the corresponding pre.&lt;&#x2F;p&gt;
&lt;p&gt;Shape checks alone are poor at catching this error: coefficients from different sublayers can have identical shapes while representing different points in execution. Numerical correctness requires checking who produced an input and who should consume it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;after-correctness-keep-shared-data-near-its-consumers&quot;&gt;After correctness: keep shared data near its consumers&lt;&#x2F;h2&gt;
&lt;p&gt;The first goal was ensuring that shared state existed and reached the right consumer. We then optimized physical access.&lt;&#x2F;p&gt;
&lt;p&gt;One change added &lt;strong&gt;local mirrors of compressed KV&lt;&#x2F;strong&gt;. Reading source-GPU history works, but long prefill repeatedly scans the remote data. Later code maintains a local copy at the consumer, transferring only appended rows while the layout and existing prefix match. Growth, layout changes, or a different prefix require rebuilding the necessary contents. This uses additional local VRAM in exchange for local reads during computation.&lt;&#x2F;p&gt;
&lt;p&gt;Another change &lt;strong&gt;keeps selections on GPUs&lt;&#x2F;strong&gt;. The early path downloaded indices into host vectors and uploaded them to later layers. The optimized ROCm selection holds a device-buffer reference, reuses it on the same GPU, and uses ordered P2P transfers across GPUs rather than repeatedly passing through the CPU.&lt;&#x2F;p&gt;
&lt;p&gt;Neither change is merely a pointer substitution. Asynchronous consumers must retain source data until completion. Production, copying, and consumption need correct ordering, and temporary allocations need a lifetime suitable for crossing stages. Separating logical sharing from physical placement creates room to move from working inference to faster inference.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-we-verified-the-chain&quot;&gt;How we verified the chain&lt;&#x2F;h2&gt;
&lt;p&gt;Validation operated at several levels; producing text did not replace the other checks:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Format:&lt;&#x2F;strong&gt; independent scale-quadrant tests for BlockFP8 interpretation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Orchestration:&lt;&#x2F;strong&gt; a small full-forward model test covering compressed sharing, candidate selection, and Engram, including prefill, decode, and the effect of enabling Engram.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Real weights:&lt;&#x2F;strong&gt; loading the official checkpoint and executing all 40 layers, final reduction, head, and sampling.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Optimization regression:&lt;&#x2F;strong&gt; fixed inputs and output budgets, complete output-text hashes, and separate first-token and generation timing.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One scope limit matters. The official model includes candidate-block filtering followed by a second selection stage. In the ROCm integration configuration inspected for this article, &lt;code&gt;candidate_source_layer=None&lt;&#x2F;code&gt; and &lt;code&gt;candidate_topk_blocks=0&lt;&#x2F;code&gt; select a single-stage Top-K path. A CPU reference for the two-stage logic does not establish that the ROCm path reproduces every official selection step. Nor should the two strategies be called equivalent for all inputs without validation.&lt;&#x2F;p&gt;
&lt;p&gt;Part 1&#x27;s approximately 26.06-second TTFT for 50K input and 20.45 tokens&#x2F;s single-request generation came from later optimized fixed-input text tests with DSpark disabled. They are not the isolated performance benefit of an FP8 or state-management fix, and this writing session did not rerun those benchmarks.&lt;&#x2F;p&gt;
&lt;p&gt;The essential work from checkpoint to eight-GPU generation was preserving one model across locations: consistent byte interpretation, explicit history sources, correct session boundaries, and the right order of mixing state. Performance work can then change where data lives and when it moves while preserving those relationships.&lt;&#x2F;p&gt;
&lt;p&gt;The next article follows the image path: finding differences in preprocessing, vision-tower, and aligner intermediates, then connecting image-and-text inputs to this language backbone.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Engineering evidence: zLLM&#x27;s V4.1 integration notes and changes in &lt;code&gt;80b8d3e5&lt;&#x2F;code&gt;, &lt;code&gt;07cf53f1&lt;&#x2F;code&gt;, &lt;code&gt;cc0be412&lt;&#x2F;code&gt;, &lt;code&gt;307c5e85&lt;&#x2F;code&gt;, &lt;code&gt;8919f797&lt;&#x2F;code&gt;, and &lt;code&gt;e9b539e4&lt;&#x2F;code&gt;. Model configuration reference: &lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;deepseek-ai&#x2F;DeepSeek-V4.1-Flash&#x2F;blob&#x2F;main&#x2F;inference&#x2F;config.json&quot;&gt;official inference configuration&lt;&#x2F;a&gt;.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Engram in DeepSeek V4.1 Flash: Knowledge Without Recomputing It Every Time</title>
        <published>2026-09-14T00:00:00+00:00</published>
        <updated>2026-09-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/deepseek-v41-flash-engram/"/>
        <id>https://zhuai.tech/en/blog/deepseek-v41-flash-engram/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/deepseek-v41-flash-engram/">&lt;p&gt;&lt;strong&gt;We consider DeepSeek V4.1 Flash a remarkable feat of model engineering.&lt;&#x2F;strong&gt; It brings together image and text understanding, sparse experts, compressed sparse attention, and Engram conditional memory, giving knowledge capacity, computation, and runtime resources more distinct roles.&lt;&#x2F;p&gt;
&lt;p&gt;The interesting questions are concrete: which information needs contextual computation, which local patterns can come from learned memory, and which intermediate results can be shared across layers? These decisions determine how much weight data inference reads, how much history it retains, and which work can start early. Implementing the model in an inference engine reveals how those details fit together.&lt;&#x2F;p&gt;
&lt;p&gt;This series follows our actual integration work in zLLM. Our first placement decision was to &lt;strong&gt;keep Engram&#x27;s large tables and projections on the host, while the backbone runs on GPUs&lt;&#x2F;strong&gt;. Gating initially ran on the CPU as well; later optimization moved prefill gating onto the GPU.&lt;&#x2F;p&gt;
&lt;p&gt;That decision shaped the rest of the work: weight access, the moment each token enters the memory state, state propagation across GPUs, and whether image positions participate in text lookup all had to become explicit parts of the inference path.&lt;&#x2F;p&gt;
&lt;p&gt;As of September 14, 2026, zLLM has run the complete image-and-text-to-generation path on eight W7900D GPUs using the official safetensors weights. Verified text performance is &lt;strong&gt;about 26.06 seconds to the first token for a 50K input, and 20.45 tokens&#x2F;s for single-request generation&lt;&#x2F;strong&gt;. Both figures are from text tests with DSpark disabled: the former is average time to first token for 50,032 input tokens; the latter is average generation speed for 700 output tokens following a 25-token prompt. This first article covers Engram&#x27;s mechanism and engineering optimizations in full. Other model components and the complete benchmark tables follow in later articles.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;knowledge-in-memory-engram&quot;&gt;Knowledge in memory: Engram&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;deepseek-v41-flash&#x2F;engram-lookup-en.png&quot; alt=&quot;Engram uses token-derived addresses to retrieve memory from host RAM, then applies WKV projection and context-dependent gating to update the hidden state&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The upper path does not depend on the current layer&#x27;s hidden state, so lookup and projection can run ahead. The lower path waits for that state before gating. The key&#x2F;value boxes in both panels refer to the same projection outputs.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-remember-something-instead-of-reconstructing-it-every-time&quot;&gt;Why remember something instead of reconstructing it every time?&lt;&#x2F;h3&gt;
&lt;p&gt;Some language-model computation interprets new context, combines information, and reasons. Some reconstructs familiar local patterns, such as representations of common phrases, fixed expressions, and entity names. A conventional Transformer encodes these capabilities together in its weights and reconstructs the relevant representations through network computation for each input.&lt;&#x2F;p&gt;
&lt;p&gt;Engram provides a lookup path for the part suited to memory. During training, reusable local patterns are learned in n-gram memory tables. At inference time, token combinations identify rows, retrieve learned vectors, and let the current context determine how to incorporate them. The official mechanistic analysis suggests this relieves early layers of static-pattern reconstruction, potentially preserving more effective depth for complex reasoning. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;deepseek-ai&#x2F;Engram&quot;&gt;Official Engram research overview&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Consider a familiar proper noun spanning several tokens. A memory table can supply learned features associated with that local combination; subsequent computation determines its role in this particular sentence. This is an illustration of the mechanism, not a claim that each row stores a readable fact or that we have traced a particular phrase to its exact bucket.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Some representations that the network would otherwise reconstruct now have a direct lookup path.&lt;&#x2F;strong&gt; More precisely, training assigns some knowledge and patterns to dedicated memory parameters. zLLM loads those weights and executes retrieval; our integration does not extract a separate knowledge base from an older model.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;computational-savings-and-vram-savings-happen-at-different-levels&quot;&gt;Computational savings and VRAM savings happen at different levels&lt;&#x2F;h3&gt;
&lt;p&gt;At the model-design level, Engram assigns some static-pattern reconstruction to conditional memory. The table is large, but each step accesses a fixed number of rows. Adding memory capacity does not require scanning the entire table for every token. This improves the relationship between memory capacity and computation; it does not establish that disabling Engram in the existing V4.1 model would make inference slower, or that enabling it automatically skips Transformer layers.&lt;&#x2F;p&gt;
&lt;p&gt;At the engine level, zLLM uses that sparse access pattern to place the large tables in host memory. VRAM can then hold backbone weights, experts, KV cache, and temporary tensors without also holding the entire Engram table. This saves the capacity that a fully GPU-resident copy of the same table would require. The machine still has to store that memory somewhere.&lt;&#x2F;p&gt;
&lt;p&gt;At approximately &lt;code&gt;384 million rows × 256 dimensions × 2 layers&lt;&#x2F;code&gt;, the one-byte codes alone occupy about &lt;strong&gt;183 GiB&lt;&#x2F;strong&gt;, before scales. This is a calculation from tensor dimensions, not a measured RSS value or measured VRAM reduction. By contrast, retrieving 24 rows in each of two layers requires only 12 KiB of codes per token. Actual operating-system access happens in pages, and projection reads more weights afterward, so 12 KiB is not the total memory traffic of an inference step.&lt;&#x2F;p&gt;
&lt;p&gt;Lookup does not produce a complete answer. Retrieved vectors still pass through WKV projection and contextual gating, and backbone inference continues. Our optimizations therefore address distinct stages: small-row access overhead, projection efficiency, thread and data locality, and early execution of work that does not depend on the hidden state.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;turning-knowledge-into-addressable-vectors&quot;&gt;Turning knowledge into addressable vectors&lt;&#x2F;h3&gt;
&lt;p&gt;Engram adds a memory-read path triggered by the token sequence. Short sequences around the current position determine addresses in a learned table. The current hidden state then determines how much of the retrieved information to inject.&lt;&#x2F;p&gt;
&lt;p&gt;This knowledge exists as vectors in model parameters. There are no directly editable factual entries, and a conversation does not automatically write new facts back into the weights. Input determines where to look; context determines how to use what was retrieved.&lt;&#x2F;p&gt;
&lt;p&gt;Our configuration places Engram at &lt;strong&gt;L1 and L14&lt;&#x2F;strong&gt;, using zero-based layer numbers. Each position produces 2-grams, 3-grams, and 4-grams, with eight hash heads per length: 24 retrieved rows, each 256 dimensions wide.&lt;&#x2F;p&gt;
&lt;p&gt;For one token at one Engram layer, that is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;3 n-gram lengths × 8 heads × 256 dimensions = 6,144 values
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each layer&#x27;s table has roughly 384 million rows, but a lookup touches only 24. &lt;strong&gt;Large total capacity with sparse per-step access&lt;&#x2F;strong&gt; motivated host placement.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-get-the-addresses-right&quot;&gt;Step 1: Get the addresses right&lt;&#x2F;h3&gt;
&lt;p&gt;The first requirement is matching the token-to-address mapping. The official implementation normalizes token text and maps tokens with identical normalized forms to a shared compressed ID. Case, accents, and whitespace affect the resulting address; the compressed vocabulary size also affects hash-multiplier generation. &lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;deepseek-ai&#x2F;DeepSeek-V4.1-Flash&#x2F;blob&#x2F;main&#x2F;inference&#x2F;engram.py&quot;&gt;Official Engram implementation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;zLLM follows the official generation rules, exports the compressed token map offline as &lt;code&gt;engram_token_map.bin&lt;&#x2F;code&gt;, and reads it at runtime. Per-layer multipliers, prime bucket sizes, and offsets are expanded into static tables. The map currently contains 129,280 &lt;code&gt;u32&lt;&#x2F;code&gt; entries.&lt;&#x2F;p&gt;
&lt;p&gt;The runtime path is straightforward:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;token ID
&lt;&#x2F;span&gt;&lt;span&gt;  → compressed token ID
&lt;&#x2F;span&gt;&lt;span&gt;  → current token and the preceding three positions
&lt;&#x2F;span&gt;&lt;span&gt;  → 24 hashes across 2 &#x2F; 3 &#x2F; 4-grams
&lt;&#x2F;span&gt;&lt;span&gt;  → 24 row addresses in the corresponding layer&amp;#39;s table
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An incorrect mapping can still return a correctly shaped vector—it just retrieves different memory. We aligned hashing before validating projection and gating.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-2-read-selected-rows-and-compute-the-projection-on-the-host&quot;&gt;Step 2: Read selected rows and compute the projection on the host&lt;&#x2F;h3&gt;
&lt;p&gt;The weight layer&#x27;s &lt;code&gt;engram_embedding_rows&lt;&#x2F;code&gt; reads only selected rows. The official tables use MXFP8: E4M3 codes with E8M0 scales in groups of 32 values. The corresponding scales are read with the codes, and the selected rows are decoded on the CPU.&lt;&#x2F;p&gt;
&lt;p&gt;The initial integration read each row at a file offset. Hot data could be served by the operating-system page cache, while cold accesses could reach storage. Later optimization switched to read-only mmap and page-by-page warmup during loading, described below. Warmup is not page locking: memory pressure can still evict pages.&lt;&#x2F;p&gt;
&lt;p&gt;Projection weights are explicitly held in memory. The retrieved 6,144-dimensional vector passes through &lt;code&gt;wkv&lt;&#x2F;code&gt; to produce the key and value used for gating. Each layer&#x27;s matrix has shape &lt;code&gt;25,600 × 6,144&lt;&#x2F;code&gt;; converting it to BF16 during preparation takes roughly 300 MiB. The initial implementation used AVX2&#x2F;FMA with Rayon row parallelism. Later work replaced that with AVX-512 BF16 and persistent worker teams.&lt;&#x2F;p&gt;
&lt;p&gt;Reading only 24 table rows therefore does not make Engram free. Projection still reads a substantial matrix. Keeping it on the CPU saves VRAM but puts pressure on host memory bandwidth. Cold reads, scheduling, and CPU&#x2F;GPU synchronization also affect latency and must be measured separately.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-inject-memory-at-the-correct-layer-boundary&quot;&gt;Step 3: Inject memory at the correct layer boundary&lt;&#x2F;h3&gt;
&lt;p&gt;Engram modifies the hidden state at the entrance to L1 and L14, while it still contains the multiple streams of the expanded mHC representation.&lt;&#x2F;p&gt;
&lt;p&gt;The CPU implementation computes key and value, then gates each hidden-state stream separately. Hidden state and key have their own normalization factors. After computing their correlation score &lt;code&gt;dot&lt;&#x2F;code&gt;, it applies:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;gate = sigmoid(copysign(sqrt(max(abs(dot), 1e-6)), dot))
&lt;&#x2F;span&gt;&lt;span&gt;hidden += gate × value
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The gate controls how much memory the current context absorbs. Our implementation stores the elementwise product of the q and k weights in advance; normalization and gate computation remain separate for each stream.&lt;&#x2F;p&gt;
&lt;p&gt;The initial layer-entry hook invoked CPU Engram and returned the updated hidden state to the GPU backbone. The optimized implementation separates lookup, projection, and gating: tables and projection stay on the CPU, precomputed prefill results go to GPU gating, and decode can retain CPU gating. Placement depends on the execution path.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-4-keep-memory-history-local-to-the-session&quot;&gt;Step 4: Keep memory history local to the session&lt;&#x2F;h3&gt;
&lt;p&gt;Each token must enter the hash history exactly once. Both Engram layers read the history at that same position using their own hash parameters. Appending the token again at each layer would shift subsequent n-grams.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM therefore separates appending tokens from applying Engram. Prefill appends a sequence; decode appends one token at a time. The L1 and L14 hooks use the positions already established.&lt;&#x2F;p&gt;
&lt;p&gt;Weights can be shared, but sequence histories must remain isolated. The implementation shares projection weights through &lt;code&gt;Arc&lt;&#x2F;code&gt;, creates fresh hash state when forking a session, and clears history on reset. Session-isolation fixes were necessary for serving the model correctly.&lt;&#x2F;p&gt;
&lt;p&gt;Images introduce another boundary. Image spans insert &lt;code&gt;DEAD&lt;&#x2F;code&gt; markers to prevent text n-grams from crossing those positions, and image rows skip Engram gating. We completed both behaviors during multimodal numerical validation so image placeholders would not be treated as ordinary text-memory inputs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;read-optimization-from-per-row-pread-to-mmap-warmup&quot;&gt;Read optimization: from per-row pread to mmap warmup&lt;&#x2F;h2&gt;
&lt;p&gt;The first row reader was sufficient to establish correctness. But each row contains only 256 quantized values, and separately reading codes and scales creates many small file operations. Prefill repeats this across the whole input, making fixed per-call costs increasingly visible.&lt;&#x2F;p&gt;
&lt;p&gt;Optimization commit &lt;code&gt;e9b539e4&lt;&#x2F;code&gt; added read-only mmap to the safetensors path. Inference copies selected rows from the mapped region, reducing per-row &lt;code&gt;pread&lt;&#x2F;code&gt; system calls. The large table remains quantized; it is not expanded wholesale into F32.&lt;&#x2F;p&gt;
&lt;p&gt;Loading also warms the Engram codes and scales: it advises sequential access and readahead, touches each page, then restores &lt;code&gt;MADV_RANDOM&lt;&#x2F;code&gt; for runtime lookup. This moves some first-touch faults and storage reads into loading. The costs are startup time and host-memory occupancy; the benefit needs to be measured after warmup.&lt;&#x2F;p&gt;
&lt;p&gt;This path does not use &lt;code&gt;mlock&lt;&#x2F;code&gt;. Mapping a file alone does not ensure its pages are in RAM; explicit page touches perform the warmup. Memory pressure and page residency still matter when describing the running system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;avx-512-bf16-reuse-loaded-weights-across-tokens&quot;&gt;AVX-512 BF16: reuse loaded weights across tokens&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;the-initial-bottleneck-repeated-wkv-scans&quot;&gt;The initial bottleneck: repeated WKV scans&lt;&#x2F;h3&gt;
&lt;p&gt;Initially, each input token required a separate GEMV. BF16 already reduced weight bytes, but a token-by-token prefill loop still repeatedly scanned the same approximately 300 MiB matrix.&lt;&#x2F;p&gt;
&lt;p&gt;The main optimization was to put multiple tokens into one matrix multiplication and reuse loaded weights across those inputs. AVX-512 BF16 provides the instructions; batching and layout determine how effectively they work.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pack-bf16-pairs-across-16-outputs&quot;&gt;Pack BF16 pairs across 16 outputs&lt;&#x2F;h3&gt;
&lt;p&gt;The optimized WKV layout is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;[output block][input-dimension pair][16 output channels]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each &lt;code&gt;u32&lt;&#x2F;code&gt; packs two BF16 weights. The inner loop loads weight pairs for 16 output channels, broadcasts a token&#x27;s two input values, and calls &lt;code&gt;_mm512_dpbf16_ps&lt;&#x2F;code&gt; to accumulate into 16 F32 outputs. Multiple tokens keep independent accumulators while sharing that weight load.&lt;&#x2F;p&gt;
&lt;p&gt;The inspected version handles input tiles and tails in groups of 30, 16, 8, 4, 2, and 1 rows. A 30-row tile can reuse one load across as many as 30 tokens. Single-row decode does not receive that batching benefit.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;match-input-layout-to-the-inner-loop&quot;&gt;Match input layout to the inner loop&lt;&#x2F;h3&gt;
&lt;p&gt;Inputs use a pair-major layout, &lt;code&gt;[input-dimension pair][token row]&lt;&#x2F;code&gt;. Values for the same dimension pair across tokens sit next to each other, avoiding large token-stride jumps. Official MXFP8 table rows can also decode directly into this packed BF16 input, avoiding a full intermediate F32 expansion followed by conversion back to BF16.&lt;&#x2F;p&gt;
&lt;p&gt;This change exposed a real layout bug. Each token&#x27;s input contains 24 heads, and packing must preserve the head-to-pair mapping. Changing the packer without checking the consuming indices can produce valid dimensions with incorrectly ordered content. We added comparisons between packed MXFP8 and the F32 decoding path, and between batched BF16 computation and a scalar reference.&lt;&#x2F;p&gt;
&lt;p&gt;Runtime checks for &lt;code&gt;avx512f&lt;&#x2F;code&gt; and &lt;code&gt;avx512bf16&lt;&#x2F;code&gt; select the supported kernel. Changes to input precision handling and accumulation require numerical validation, not just a faster microbenchmark. Local reference tests, real-input generation, and end-to-end performance are separate checks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numa-placement-put-threads-and-weights-together&quot;&gt;NUMA placement: put threads and weights together&lt;&#x2F;h2&gt;
&lt;p&gt;Projection reads large weight regions continuously, so the relationship between a thread&#x27;s CPU and the memory node holding its pages matters. If a worker runs on one socket while much of its data resides on another, remote access can offset vectorization gains.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;identify-the-physical-cores-available-to-the-process&quot;&gt;Identify the physical cores available to the process&lt;&#x2F;h3&gt;
&lt;p&gt;The implementation first calls &lt;code&gt;sched_getaffinity&lt;&#x2F;code&gt; and considers only allowed CPUs. It then reads &lt;code&gt;physical_package_id&lt;&#x2F;code&gt; and &lt;code&gt;core_id&lt;&#x2F;code&gt; from sysfs, groups by physical package, and removes duplicate SMT siblings. With two packages, L1 and L14 use different CPU groups.&lt;&#x2F;p&gt;
&lt;p&gt;The code groups by &lt;strong&gt;package&lt;&#x2F;strong&gt;. That matches the placement strategy for this dual-socket machine, but it is not a complete NUMA-topology solution for arbitrary hardware: one socket can expose several NUMA nodes. Moving to another machine requires checking its topology rather than assuming socket and NUMA node always mean the same thing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;persistent-workers-with-fixed-partitions&quot;&gt;Persistent workers with fixed partitions&lt;&#x2F;h3&gt;
&lt;p&gt;Each layer creates a persistent &lt;code&gt;EngramTeam&lt;&#x2F;code&gt;. Workers bind to selected CPUs at startup and process fixed output-block ranges. They park when idle, wake for work, and notify the submitter when finished. This stabilizes thread placement and work partitioning, reducing the migration and locality changes possible with general-purpose dynamic scheduling.&lt;&#x2F;p&gt;
&lt;p&gt;The selection leaves cores available for other work. When a package offers at least 16 physical cores, Engram uses three quarters of them. A fully available 32-core package therefore supplies 24 workers, leaving scheduling headroom for GPU submission, transfers, and serving. This is not a cpuset guarantee that those other threads run exclusively on the remaining cores.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-loading-and-first-touch-follow-computation&quot;&gt;Make loading and first-touch follow computation&lt;&#x2F;h3&gt;
&lt;p&gt;The fixed workers repack WKV directly into its final buffer. Initial writes therefore occur on the CPUs that will later consume those partitions. Table warmup also temporarily binds to a CPU from the corresponding layer&#x27;s group before touching mapped pages.&lt;&#x2F;p&gt;
&lt;p&gt;This combines thread affinity with first-touch placement. The implementation does not use &lt;code&gt;mbind&lt;&#x2F;code&gt; to forcibly migrate existing pages. If another process has already loaded file pages, or the system applies a different memory policy, touching pages from a bound thread does not guarantee relocation. Actual page distribution and remote accesses still need inspection.&lt;&#x2F;p&gt;
&lt;p&gt;AVX-512 improves computation after data arrives. NUMA placement determines where those bytes come from. Leaving CPU headroom helps projection avoid starving GPU submission. All three serve the same complete pipeline.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;upload-near-the-gpu-that-consumes-the-projection&quot;&gt;Upload near the GPU that consumes the projection&lt;&#x2F;h3&gt;
&lt;p&gt;After moving prefill gating onto the GPU, projection results still needed an upload. Measurements found about 15 ms for L1 and 90 ms for an equally sized L14 result: the L14 CPU team and its consuming GPU were on different NUMA nodes.&lt;&#x2F;p&gt;
&lt;p&gt;We added pinned staging near the destination GPU so the transfer could use a local page-locked buffer. In that round of adjacent experiments, 50K time to first token fell from roughly 26.67–26.84 seconds to 25.87–25.88 seconds, with matching output hashes. Those figures describe that experiment; later measurements provide the final benchmark table.&lt;&#x2F;p&gt;
&lt;p&gt;Binding also needs measurement. Moving all GPU submission threads to the other NUMA node produced 28.47 seconds and was reverted. Successful data placement and upload staging do not imply that binding every thread according to PCIe topology will always help.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;run-projection-early-overlap-cpu-work-with-the-gpu&quot;&gt;Run projection early: overlap CPU work with the GPU&lt;&#x2F;h2&gt;
&lt;p&gt;Engram addresses depend only on token history, and WKV projection depends only on retrieved vectors. Only gating needs the current layer&#x27;s hidden state. That dependency allows the substantial computation to start early.&lt;&#x2F;p&gt;
&lt;p&gt;The optimized path prepares both layers&#x27; Engram batches at stage 0 and launches projection tasks. GPUs advance through the backbone concurrently, then obtain the results at L1 or L14. If projection is complete, only the remaining work is exposed at layer entry; otherwise, execution still waits. L14 has more preceding backbone work and therefore a larger potential overlap window. Profiling must establish how much latency is actually hidden.&lt;&#x2F;p&gt;
&lt;p&gt;For precomputed prefill, projected key&#x2F;value results go to the GPU for gating, keeping the large hidden-state batch on the device. Subsequent decode commit &lt;code&gt;153598f6&lt;&#x2F;code&gt; also starts projection early but retains CPU gating. This reduces exposed waits and some transfers while leaving the tables and WKV computation on the CPU.&lt;&#x2F;p&gt;
&lt;p&gt;In the adjacent decode A&#x2F;B test, a fixed 25-token prompt and 700-token output improved from an average &lt;strong&gt;19.417 to 20.454 tokens&#x2F;s, a 5.34% gain&lt;&#x2F;strong&gt;, with the complete output-text hash unchanged. The full benchmark table follows in the performance article. AVX-512, NUMA placement, warmup, and pipeline changes all contributed to prefill optimization; the overall gain cannot be attributed to one change, and batching gains do not directly translate into single-token decode gains.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coming-next&quot;&gt;Coming next&lt;&#x2F;h2&gt;
&lt;p&gt;We plan to continue with one article per day on the remaining engineering work:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Part 2: From weight loading to an eight-GPU text pipeline&lt;&#x2F;li&gt;
&lt;li&gt;Part 3: Adding vision—finding differences through intermediate tensors&lt;&#x2F;li&gt;
&lt;li&gt;Part 4: Benchmarks—prefill, decode, and before-and-after comparisons&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Essential App for Time Travelers: Modern Civilization in One Phone</title>
        <published>2026-09-09T00:00:00+00:00</published>
        <updated>2026-09-09T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/zllm-app-minicpm5-2b-qualcomm-npu/"/>
        <id>https://zhuai.tech/en/blog/zllm-app-minicpm5-2b-qualcomm-npu/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/zllm-app-minicpm5-2b-qualcomm-npu/">&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;zllm-app&#x2F;traveler-hero.webp&quot; alt=&quot;A time traveler holding a local-LLM phone while solar power lights up ancient farms, waterwheels, granaries, and workshops&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Suppose you wake up tomorrow in the ancient world.&lt;&#x2F;p&gt;
&lt;p&gt;There is no power grid, no internet, no search engine, and certainly no cloud LLM. You cannot recite an encyclopedia of preindustrial technology from memory. You vaguely remember crop rotation from a video, but when a harvest depends on it, all that remains is one terrifying question: &lt;strong&gt;how exactly was that done?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Other time travelers bring a lighter. You bring a phone running &lt;strong&gt;zllm-app&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Inside it is the complete MiniCPM5-2B model. The weights are the knowledge. No network, no server, and no prewritten question list are required. Growing grain, making fertilizer, brewing alcohol, translating languages, building waterwheels, organizing workshops, training a disciplined force—if you can ask it, you can start working on it.&lt;&#x2F;p&gt;
&lt;p&gt;Add a foldable solar panel and a power bank: harvest sunlight by day, summon modern civilization by night.&lt;&#x2F;p&gt;
&lt;p&gt;This is not time travel. This is administrator access.&lt;&#x2F;p&gt;
&lt;video controls playsinline preload=&quot;metadata&quot; poster=&quot;&#x2F;videos&#x2F;zllm-traveler-guide-poster.png&quot; style=&quot;width: 100%; border-radius: 16px;&quot;&gt;
  &lt;source src=&quot;&#x2F;videos&#x2F;zllm-traveler-guide.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
&lt;&#x2F;video&gt;
&lt;p&gt;First we type a time-traveler opening question. MiniCPM5-2B immediately lays out a path through farming, fertilizer, brewing, and disciplined organization. Then we type one Chinese sentence and get English, Japanese, and Korean on the spot. Airplane mode cannot stop it, because every answer is generated inside the phone.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;put-the-artifact-in-your-pocket&quot;&gt;Put the artifact in your pocket&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;downloads&#x2F;zllm-app-0.1-sm8635-preview.apk&quot;&gt;Download zllm-app 0.1 SM8635 Preview APK (8.7 MB)&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;SHA-256: &lt;code&gt;d3cbc48b928da4932cea37c4538dc0ed687336b1c4d5d93d18692b3f92d2cb5a&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is a directly installable technical preview. The APK does not cram a 2B model into the installer. On first launch, it races Hugging Face against ModelScope and downloads the faster copy of the roughly 2.6 GiB core model, with resumable transfers and SHA-256 verification. Once the model is in place, conversations work without a network.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;current-device-restrictions&quot;&gt;Current device restrictions&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Item&lt;&#x2F;th&gt;&lt;th&gt;Current support boundary&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Fully validated phone&lt;&#x2F;td&gt;&lt;td&gt;Motorola XT2451-4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SoC &#x2F; NPU&lt;&#x2F;td&gt;&lt;td&gt;Qualcomm SM8635 (Snapdragon 8s Gen 3) &#x2F; HTP V73&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Memory&lt;&#x2F;td&gt;&lt;td&gt;12 GB validated; lower-memory devices are not currently supported&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;OS and ABI&lt;&#x2F;td&gt;&lt;td&gt;Android 16 and arm64-v8a validated&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Storage&lt;&#x2F;td&gt;&lt;td&gt;Core model is about 2.6 GiB; reserve at least 4 GiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The current model package and QNN context graphs are built specifically for &lt;strong&gt;SM8635 &#x2F; HTP V73&lt;&#x2F;strong&gt;. Other Snapdragon chips and HTP versions, as well as MediaTek, Samsung, and Kirin platforms, are outside the current support boundary. The artifact has arrived—but this release answers to one NPU only.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;civilization-is-not-in-the-cloud-it-is-in-the-weights&quot;&gt;Civilization is not in the cloud. It is in the weights.&lt;&#x2F;h2&gt;
&lt;p&gt;Most AI apps become an empty input box when the network disappears. zllm-app is different: the MiniCPM5-2B weights, tokenizer, chat template, and inference engine all live on the phone.&lt;&#x2F;p&gt;
&lt;p&gt;You ask; the phone computes. The model answers; the text is generated locally. Nothing needs to be sent to a server, and no reply has to travel back from an internet connection a thousand years in the future.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Text ───────────────────────┐
&lt;&#x2F;span&gt;&lt;span&gt;                            ↓
&lt;&#x2F;span&gt;&lt;span&gt;Voice → local SenseVoice → MiniCPM5-2B
&lt;&#x2F;span&gt;&lt;span&gt;                            ↓
&lt;&#x2F;span&gt;&lt;span&gt;                    Qualcomm HTP&#x2F;NPU
&lt;&#x2F;span&gt;&lt;span&gt;                            ↓
&lt;&#x2F;span&gt;&lt;span&gt;              Streaming output and saved chats
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As long as the phone can light up, the knowledge is still there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;we-actually-ran-it-on-a-qualcomm-npu&quot;&gt;We actually ran it on a Qualcomm NPU&lt;&#x2F;h2&gt;
&lt;p&gt;“An LLM on a phone” is easy to put on a slide. Making it real is a different kind of battle.&lt;&#x2F;p&gt;
&lt;p&gt;Our test device is a Motorola XT2451-4 with an SM8635, 12 GB of RAM, and HTP V73, running QAIRT 2.38. We did not dump the model onto the CPU and wait. We integrated Qualcomm QNN directly: dynamically loading &lt;code&gt;libQnnHtp.so&lt;&#x2F;code&gt;, creating the backend, device, context, and execution graphs, then sending MiniCPM5-2B&#x27;s core computation to the phone&#x27;s NPU.&lt;&#x2F;p&gt;
&lt;p&gt;The embedding, all 42 Transformer layers, final normalization, and vocabulary projection now run on HTP. The SenseVoice-Small encoder and CTC head run there as well: hold the microphone, speech becomes text locally, MiniCPM5-2B receives the question, and the answer streams onto the screen.&lt;&#x2F;p&gt;
&lt;p&gt;The integration produced enough traps to fill a book:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Activation outliers in the fixed prefix distorted the quantization range, driving K&#x2F;V errors across all 42 layers to unacceptable levels.&lt;&#x2F;li&gt;
&lt;li&gt;On this hardware and SDK combination, QNN could not use the S32&#x2F;F16&#x2F;F32 output paths we originally expected, so we redesigned the path around S8 input, S8 output, and scale restoration.&lt;&#x2F;li&gt;
&lt;li&gt;Full INT4 was fast but drifted during long generations; full INT8 preserved quality but reached only 8.97 tok&#x2F;s.&lt;&#x2F;li&gt;
&lt;li&gt;DSpark speculative decoding ran end to end, but weak draft acceptance reduced it to 5.723 tok&#x2F;s—slower than ordinary decoding.&lt;&#x2F;li&gt;
&lt;li&gt;Reloading 43 graphs for every request cost several seconds, so decode and ASR received resident runners that load the graphs once and reuse them.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The final 2B INT8 configuration sustains roughly &lt;strong&gt;8.7–9.8 tok&#x2F;s&lt;&#x2F;strong&gt; on long answers. Reusing resident graphs reduced the second in-app question from 14.1 seconds to 9.65 seconds. For long inputs, batched AR8 prefill takes over: a fresh 831-token conversation fell from 95–143 seconds to 36.9 seconds.&lt;&#x2F;p&gt;
&lt;p&gt;This is not “NPU support” in a presentation. It is a real phone, real QNN graphs, and a complete path from the text box to streaming output.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;question-one-how-do-you-survive-the-first-week&quot;&gt;Question one: how do you survive the first week?&lt;&#x2F;h2&gt;
&lt;p&gt;While everyone else is composing poetry, tracing family trees, and looking for a patron, you open zllm-app and solve the problems that actually matter: water, food, shelter, safety, local climate, and the local power structure.&lt;&#x2F;p&gt;
&lt;p&gt;Seconds later, the phone gives you a plan for day one, the first three days, and the first week.&lt;&#x2F;p&gt;
&lt;p&gt;They are still trying to identify the dynasty. You are already building a base.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;question-two-how-do-you-double-the-harvest&quot;&gt;Question two: how do you double the harvest?&lt;&#x2F;h2&gt;
&lt;p&gt;In the ancient world, the real hard currency is not gold. It is grain.&lt;&#x2F;p&gt;
&lt;p&gt;You do not need to memorize an entire agronomy library. Tell zllm-app the climate, the crop, the sowing date, the color of the leaves, and last year&#x27;s yield. It breaks the problem apart: seed selection, seedlings, rotation, compost, drainage, spacing, weeds, pests, harvest, drying, and storage.&lt;&#x2F;p&gt;
&lt;p&gt;In year one, farmers keep control plots and record seed, fertilizer, and yield for every field. In year two, “the elders say so” becomes a set of comparable records. Which seed produces more? Which compost works? Which field floods? Now you have evidence.&lt;&#x2F;p&gt;
&lt;p&gt;Everyone else depends on the weather. You begin agricultural experiments.&lt;&#x2F;p&gt;
&lt;p&gt;Once the harvest grows reliably, you are no longer feeding only yourself. You can support craftsmen, caravans, guards, and an expanding city.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;question-three-how-do-you-make-fertilizer-without-a-chemical-plant&quot;&gt;Question three: how do you make fertilizer without a chemical plant?&lt;&#x2F;h2&gt;
&lt;p&gt;A time traveler cannot build a chemical factory on day one. With a model, however, you know where the technology tree begins.&lt;&#x2F;p&gt;
&lt;p&gt;Start with mature compost, green manure, crop rotation, nitrogen-fixing legumes, wood ash, and drainage. Then distinguish nitrogen, phosphorus, and potassium deficiencies from plant symptoms. Change one variable at a time, keep small control plots, and record inputs against yield.&lt;&#x2F;p&gt;
&lt;p&gt;The frightening advantage is not memorizing one recipe. It is knowing how to experiment, debug, and turn a lucky accident into a repeatable method.&lt;&#x2F;p&gt;
&lt;p&gt;One village learns it; ten villages copy it. Ten villages prove it; the yield of an entire region begins to rise.&lt;&#x2F;p&gt;
&lt;p&gt;That is what knowledge can do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;question-four-what-comes-after-surplus-grain&quot;&gt;Question four: what comes after surplus grain?&lt;&#x2F;h2&gt;
&lt;p&gt;Once agriculture creates a surplus, fermentation is the next branch of the technology tree.&lt;&#x2F;p&gt;
&lt;p&gt;Temperature, sanitation, containers, timing, batch records—zllm-app turns mystical “ancestral intuition” into a process. Record the ingredients, the day each jar was filled, when bubbling began, and when an off smell appeared.&lt;&#x2F;p&gt;
&lt;p&gt;Reliable alcohol becomes trade goods. Vinegar seasons and preserves food. The same fermentation discipline extends to sauces, pickles, and long-term storage.&lt;&#x2F;p&gt;
&lt;p&gt;Other households brew by luck. Your workshop begins quality control.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;question-five-no-common-language-your-phone-is-the-foreign-office&quot;&gt;Question five: no common language? Your phone is the foreign office.&lt;&#x2F;h2&gt;
&lt;p&gt;You may not land in the imperial heartland. You might appear on a trade route, at a frontier town, in a port, or among people whose language you cannot understand at all.&lt;&#x2F;p&gt;
&lt;p&gt;Translation is not a luxury there. It is survival.&lt;&#x2F;p&gt;
&lt;p&gt;MiniCPM5-2B&#x27;s on-device quality regression includes translation samples. The SenseVoice-Small front end supports Chinese, English, Japanese, Korean, and Cantonese; Chinese, English, and Cantonese all passed the phone&#x27;s HTP transcription gates. A visitor can speak to the phone, while zllm-app extracts the meaning, translates it, and rewrites your reply to sound more polite, formal, or suitable for trade.&lt;&#x2F;p&gt;
&lt;p&gt;Others meet a foreign caravan and resort to gestures. You negotiate prices, ask for routes, read contracts, and compose replies.&lt;&#x2F;p&gt;
&lt;p&gt;One phone becomes translator, scribe, and diplomatic adviser.&lt;&#x2F;p&gt;
&lt;p&gt;Trade routes stop being lines on a map. They become your intelligence network, commercial network, and talent pipeline.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;question-six-how-do-you-build-a-formidable-ancient-force&quot;&gt;Question six: how do you build a formidable ancient force?&lt;&#x2F;h2&gt;
&lt;p&gt;Weapons alone have never determined the strength of an army.&lt;&#x2F;p&gt;
&lt;p&gt;zllm-app gives you an organizational system: teams of ten with clear responsibility; standard commands, formations, and assembly times; regular conditioning, load carrying, marching, watch duty, first aid, and evacuation drills; inventories for provisions and equipment; records for illness and injury; consistent rewards and discipline; a mandatory review after every operation.&lt;&#x2F;p&gt;
&lt;p&gt;Other commanders train according to their mood. You use a standard schedule.&lt;&#x2F;p&gt;
&lt;p&gt;Other columns scatter on the march, lose track of supplies, and garble commands. Your people know who is responsible for whom, where the supplies are, and where to report a problem. In peace they repair levees, fight fires, guard grain, and move cargo. In crisis they act together instead of dissolving into chaos at the first alarm.&lt;&#x2F;p&gt;
&lt;p&gt;What you brought is not a legendary weapon. It is modern organizational science.&lt;&#x2F;p&gt;
&lt;p&gt;That is more powerful than a legendary weapon.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;one-phone-is-an-entire-technology-tree&quot;&gt;One phone is an entire technology tree&lt;&#x2F;h2&gt;
&lt;p&gt;The classic time-traveler mistake is trying to build a steam engine immediately.&lt;&#x2F;p&gt;
&lt;p&gt;The real sequence is different: secure water and food, then establish records and measurement. Once the food supply is stable, organize craftsmen. With craftsmen, improve tools, kilns, waterwheels, and workshops. Only after surplus capacity exists do you advance metallurgy, machinery, transport, and education.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;What you have&lt;&#x2F;th&gt;&lt;th&gt;What comes next&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A field and some seed&lt;&#x2F;td&gt;&lt;td&gt;Rotation, seedlings, compost, control plots, grain storage&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Surplus grain&lt;&#x2F;td&gt;&lt;td&gt;Brewing, vinegar, food processing, warehouses&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Contact with other languages&lt;&#x2F;td&gt;&lt;td&gt;Translation, trade, diplomacy, technical intelligence&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Carpenters and blacksmiths&lt;&#x2F;td&gt;&lt;td&gt;Better tools, waterwheels, standardized dimensions&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Reliable tax grain&lt;&#x2F;td&gt;&lt;td&gt;Support craftsmen, open schools, build roads, train teams&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Mature workshops&lt;&#x2F;td&gt;&lt;td&gt;Metallurgy, machinery, printing, scaled production&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The model does not conjure machines from thin air. It ensures that you always know the objective, the principle, the missing prerequisites, and the first step.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-time-traveler-s-real-cheat-code&quot;&gt;The time traveler&#x27;s real cheat code&lt;&#x2F;h2&gt;
&lt;p&gt;One phone. MiniCPM5-2B. zllm-app. A Qualcomm NPU. One foldable solar panel.&lt;&#x2F;p&gt;
&lt;p&gt;No network, yet questions still receive answers. No cloud, yet text still appears. Speak once and SenseVoice recognizes it locally. Leave mid-conversation and the chat and KV state remain saved. Start another topic and it receives a completely independent conversation.&lt;&#x2F;p&gt;
&lt;p&gt;Other time travelers rely on whatever fragments they can recall.&lt;&#x2F;p&gt;
&lt;p&gt;You carry an agronomist, process engineer, translator, historian, drillmaster, and chief technology officer who never sleeps.&lt;&#x2F;p&gt;
&lt;p&gt;From the first cup of clean water to the first high-yield field; from the first reliable jar of alcohol to the first disciplined organization; from the first cross-language trade to a technological system that keeps compounding—it all begins with one sentence typed onto a phone.&lt;&#x2F;p&gt;
&lt;p&gt;This is zllm-app.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Completely offline. Modern civilization in your pocket.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If we must find one missing piece, this time-travel artifact still lacks TTS. Once it can speak an answer aloud, the modern strategist inside the phone will not merely write your plans. It will have a voice.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps that is for the best.&lt;&#x2F;p&gt;
&lt;p&gt;An artifact this powerful should not be revealed lightly.&lt;&#x2F;p&gt;
&lt;p&gt;We will save that voice for the next release.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>From 1M Total to 15+ × 1M Contexts: GLM-5.3 KV Cache Offloading</title>
        <published>2026-09-08T00:00:00+00:00</published>
        <updated>2026-09-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/glm53-cpu-kv-offload/"/>
        <id>https://zhuai.tech/en/blog/glm53-cpu-kv-offload/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/glm53-cpu-kv-offload/">&lt;p&gt;&lt;strong&gt;The biggest benefit of this optimization is capacity: moving from roughly 1M tokens of total context toward 15+ concurrent decoding sessions, each carrying a 1M-token history.&lt;&#x2F;strong&gt; The same GPUs can support a projected 15+ times as much active history.&lt;&#x2F;p&gt;
&lt;p&gt;Previously, one million-token conversation could consume almost the entire system&#x27;s context budget. Now, each machine&#x27;s 1 TiB of host RAM holds MLA history, while the GPUs keep a 32K hot cache for current computation. The aim is to let multiple users keep generating against their own million-token histories, beyond simply archiving completed sessions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;15+ × 1M is a capacity estimate, not a completed concurrency benchmark.&lt;&#x2F;strong&gt; We show the memory accounting below. The nearly lossless speed result comes from completed 50K comparisons. These answer two separate questions: how much history the system can hold, and how much speed offloading costs.&lt;&#x2F;p&gt;
&lt;p&gt;Our measured result has a specific scope: &lt;strong&gt;with 50,023 input tokens, 1,024 output tokens, and a 32K GPU hot cache, storing MLA history in host RAM reduced throughput by only 0.15%–1.30% across three MTP=3 pairs. Without MTP, three pairs were about 2% faster. Every complete output matched its corresponding baseline.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Getting there took several reversals. We first tried moving indexing to the CPU, then backed away. The hot-cache path once lost roughly 30% of throughput. Several convincing local optimizations failed to improve the full model. The remaining instability eventually led us from attention kernels into Linux memory management and the GPU driver.&lt;&#x2F;p&gt;
&lt;p&gt;This article follows those decisions: why we tried each path, which evidence justified keeping it, and what forced a change of direction.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;separate-historical-capacity-from-the-current-working-set&quot;&gt;Separate historical capacity from the current working set&lt;&#x2F;h2&gt;
&lt;p&gt;We used the two-node system from our &lt;a href=&quot;https:&#x2F;&#x2F;zhuai.tech&#x2F;en&#x2F;blog&#x2F;rocm-glm53-single-pipeline&#x2F;&quot;&gt;previous GLM-5.3 optimization article&lt;&#x2F;a&gt;: eight AMD Radeon PRO W7900D GPUs per machine, 48 GiB per card, an EPYC 9334 platform, and 1 TiB of RAM per node. Intra-node communication uses HIP P2P; TCP carries activations across node boundaries. There is no XGMI.&lt;&#x2F;p&gt;
&lt;p&gt;This work changes KV placement. Weights and layer computation stay on their assigned devices, and historical data stays in the host memory of the node responsible for those layers.&lt;&#x2F;p&gt;
&lt;p&gt;The opportunity comes from GLM-5.3&#x27;s sparse attention path. The DSA indexer selects the top 2,048 historical positions; MLA attention consumes their KV. Some subsequent layers reuse the selection, while retaining their own KV contents.&lt;&#x2F;p&gt;
&lt;p&gt;The amount of history that must be preserved can therefore be managed separately from the amount consumed by one attention operation.&lt;&#x2F;p&gt;
&lt;p&gt;In this Q8G64 MLA representation, each row contains 512 B of latent data, 16 B of scales, and 128 B of RoPE data: &lt;strong&gt;656 B in total&lt;&#x2F;strong&gt;. The engineering records give the following payload sizes. These cover MLA only, excluding DSA, MTP layers, metadata, alignment, and scratch space.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Object&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;MLA payload&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;50,000 tokens, one layer, one replica&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;31.281 MiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;50,000 tokens, 78 main-model layers, both replicas&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.765 GiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2,048 selected rows, one layer, one replica&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.28125 MiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;32,768 hot-cache rows, one layer, one replica&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;20.5 MiB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;One 50K history is manageable, but its cost accumulates with conversation length and session count. We wanted the main GPU MLA history allocation to be bounded by the hot-cache size, with growth absorbed by RAM.&lt;&#x2F;p&gt;
&lt;p&gt;This hot cache is not a sliding window that discards everything older than 32K tokens. &lt;strong&gt;Old positions remain in RAM, DSA can still select them, and a cache miss reads them back.&lt;&#x2F;strong&gt; Physical placement changes without deliberately truncating accessible history.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;decision-one-should-the-cpu-store-history-and-run-the-indexer&quot;&gt;Decision one: should the CPU store history and run the indexer?&lt;&#x2F;h2&gt;
&lt;p&gt;The first proposal was natural: the host has abundant memory and CPU cores, so let it score the full history and select Top-K, then send the selected KV to the GPU.&lt;&#x2F;p&gt;
&lt;p&gt;There was also a scheduling opportunity. Once selection was known, layers reusing it could prefetch historical KV while independent GPU work continued.&lt;&#x2F;p&gt;
&lt;p&gt;We already had CPU Q8 scoring, blocked layouts, and vectorized implementations. We added a fixed worker team, local histograms, reusable Top-K workspaces, incremental mirrors, rollback, and prefetching. This deserved a test rather than dismissal based solely on CPU versus GPU arithmetic speed.&lt;&#x2F;p&gt;
&lt;p&gt;Hardware testing produced two independent reasons to reject it for this iteration.&lt;&#x2F;p&gt;
&lt;p&gt;First, performance: the 50K GPU baseline delivered about &lt;strong&gt;14.03 tokens&#x2F;s&lt;&#x2F;strong&gt;, while CPU DSA candidates reached only &lt;strong&gt;10.92–11.32 tokens&#x2F;s&lt;&#x2F;strong&gt;. Query readback, thread coordination, selection upload, and GPU waiting joined CPU arithmetic on the critical path. Older CPU measurements at 131K could not simply be multiplied by &lt;code&gt;50&#x2F;131&lt;&#x2F;code&gt;, because the number of active workers changed with context length too.&lt;&#x2F;p&gt;
&lt;p&gt;Second, correctness: final selection using CPU Q8 queries differed around the cutoff from the GPU BF16-query exact path. Fixed greedy generation diverged. Internal CPU consistency did not establish equivalence to the original GPU path.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Decision: keep exact DSA on the GPU, disable CPU selection, and continue moving MLA history into RAM.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This narrowed the problem. Storage expansion did not need to wait for CPU indexing to succeed. Prefetching remained a useful overlap opportunity, but could not be credited as zero overhead in advance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;decision-two-let-the-cpu-manage-the-gpu-hot-cache-first&quot;&gt;Decision two: let the CPU manage the GPU hot cache first&lt;&#x2F;h2&gt;
&lt;p&gt;With selection still computed on the GPU, the early hot-cache path sent it back to the host. The CPU looked up mappings, checked hits, packed missing rows, and submitted uploads and attention.&lt;&#x2F;p&gt;
&lt;p&gt;It established the capacity direction, but performance was poor. During the September 7 experiments, the real 32K hot-cache configuration, after packed replication and batched-mirror fixes, still had a three-run median of about &lt;strong&gt;10.41 tokens&#x2F;s&lt;&#x2F;strong&gt;. That phase reported roughly a 29% loss against its reference. Those early results included different diagnostic conditions and must not be combined with the final tests as one strict A&#x2F;B comparison.&lt;&#x2F;p&gt;
&lt;p&gt;Two concrete problems appeared first.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The offload branch had lost the existing compressed replication path.&lt;&#x2F;strong&gt; Previously, the owner quantized once and copied 656 B of packed KV per row to its peer. In hot-cache mode, fallback appended and requantized on both sides, also losing existing channel overlap. We restored packed replication to the hot-cache path.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The number of small transfers mattered.&lt;&#x2F;strong&gt; Updating host mirrors per layer and token could create 156 stream operations across 78 layers and their owner&#x2F;peer replicas. Splitting each row into three transfers and increasing outstanding work raised that to 468 operations. Throughput fell to roughly 8.8 tokens&#x2F;s.&lt;&#x2F;p&gt;
&lt;p&gt;That experiment rejected the assumption that more asynchronous concurrency would solve transfer overhead. The payload was already small; additional submissions could cost more than additional bytes.&lt;&#x2F;p&gt;
&lt;p&gt;Copying back in batches of 64 rows sharply reduced operation count and improved the full-cache phase. A substantial gap remained once the cache actually entered hot mode.&lt;&#x2F;p&gt;
&lt;p&gt;We then moved peer-mirror maintenance into the worker&#x27;s locked section and made the append kernel also write a contiguous log. The three-run medians were about &lt;strong&gt;10.39&lt;&#x2F;strong&gt; and &lt;strong&gt;10.06 tokens&#x2F;s&lt;&#x2F;strong&gt;, respectively. Neither delivered the expected improvement.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Decision: retain batched logs and necessary lifecycle fixes, but withdraw the claim that moving locks and adding log writes would close the main gap.&lt;&#x2F;strong&gt; Completing an implementation and validating its performance hypothesis are separate milestones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-mistaken-diagnosis-the-largest-share-does-not-identify-the-regression&quot;&gt;A mistaken diagnosis: the largest share does not identify the regression&lt;&#x2F;h2&gt;
&lt;p&gt;We added synchronized tracing around hot-cache attention. Kernels accounted for 77.4% of measured time, and host locking plus remapping for about 22%. We initially concluded that the remaining problem was primarily the window-attention kernel.&lt;&#x2F;p&gt;
&lt;p&gt;Reviewing the evidence invalidated that method of attribution.&lt;&#x2F;p&gt;
&lt;p&gt;The profile described &lt;strong&gt;the candidate&#x27;s absolute time breakdown&lt;&#x2F;strong&gt;. It did not establish where the difference between GPU residency and offloading arose. The resident path also runs attention. Moreover, synchronization and tracing reduced full-run throughput to about 4.21 tokens&#x2F;s, substantially changing submission and overlap behavior.&lt;&#x2F;p&gt;
&lt;p&gt;Source inspection also found that selected-gather functionality, proposed as a next step, already existed. We needed to verify the actual path and its dependencies rather than treat an existing mechanism as missing work.&lt;&#x2F;p&gt;
&lt;p&gt;We changed the measurement procedure: diagnostics locate problems; formal throughput runs disable profiling, tracing, and extra synchronization. Each candidate gets a same-version GPU baseline, and complete outputs must match before comparing speed. The quantity to explain is the difference between the two paths.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;decision-three-let-the-gpu-manage-the-hot-cache-directly&quot;&gt;Decision three: let the GPU manage the hot cache directly&lt;&#x2F;h2&gt;
&lt;p&gt;New evidence brought us back to the submission chain. The GPU had already computed selection, yet the CPU waited for readback and performed mapping and packing before attention could proceed. Host-observed selection readback waits reached about 0.7–1.2 ms. They included earlier queued GPU work, so they were not pure copy times, but they exposed the control round trip.&lt;&#x2F;p&gt;
&lt;p&gt;We moved cache management. Selection stays on the GPU, which directly checks hot slots. Hits read VRAM; misses read the registered RAM mirror. Gather produces a compact selection for attention.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;GPU exact DSA ──→ selected historical positions
&lt;&#x2F;span&gt;&lt;span&gt;                              │
&lt;&#x2F;span&gt;&lt;span&gt;                              ▼
&lt;&#x2F;span&gt;&lt;span&gt;                       GPU hot gather
&lt;&#x2F;span&gt;&lt;span&gt;                       ├─ hit: read VRAM hot slot
&lt;&#x2F;span&gt;&lt;span&gt;Local RAM history ─────└─ miss: read registered host memory
&lt;&#x2F;span&gt;&lt;span&gt;                              │
&lt;&#x2F;span&gt;&lt;span&gt;                              ▼
&lt;&#x2F;span&gt;&lt;span&gt;                       compact KV → GPU attention
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;New KV → GPU recent ring → batched copyback → RAM history
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Host memory supplies historical capacity while indexing, cache lookup, and attention continue on the GPU. The CPU still manages resources and lifetimes, but no longer coordinates lookup and transfer for every selection.&lt;&#x2F;p&gt;
&lt;p&gt;PCIe reads still have a cost. Some early sampling windows had less than 1% misses, showing reuse in this workload; that does not promise the same hit rate for every input.&lt;&#x2F;p&gt;
&lt;p&gt;The first GPU-managed version reached about &lt;strong&gt;11.72 tokens&#x2F;s&lt;&#x2F;strong&gt;, with an initial registration stall. Moving registration earlier and completing multirow handling even produced a run at &lt;strong&gt;5.13 tokens&#x2F;s&lt;&#x2F;strong&gt;. We kept investigating the direction without calling near-baseline steady-state event spacing a passing full-run result.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gpu-cache-management-has-hazards-too&quot;&gt;GPU cache management has hazards too&lt;&#x2F;h2&gt;
&lt;p&gt;MTP exposed problems that were less visible on the single-row path. Verification processes multiple rows, and several queries can select the same missing position.&lt;&#x2F;p&gt;
&lt;p&gt;An early implementation allowed one block within a gather invocation to read a slot another block had just filled. A real MTP test produced abnormal scales, followed by a failure to find a finite selectable token.&lt;&#x2F;p&gt;
&lt;p&gt;We tightened visibility: &lt;strong&gt;slots filled in the current round become hit sources only in the next round; duplicate misses within the same round read RAM directly.&lt;&#x2F;strong&gt; Slots used by the current round are pinned, and a recent ring protects new tokens that have not yet reached RAM.&lt;&#x2F;p&gt;
&lt;p&gt;The complete 1,024-token MTP output then matched the same-version resident baseline, but throughput was &lt;strong&gt;16.175 versus 27.530 tokens&#x2F;s&lt;&#x2F;strong&gt;, a loss of about 41.2%. Both accepted 665 draft tokens, ruling out acceptance-rate differences as the explanation.&lt;&#x2F;p&gt;
&lt;p&gt;Restoring owner-to-peer packed KV replication for small verification batches raised the candidate to &lt;strong&gt;20.035 tokens&#x2F;s&lt;&#x2F;strong&gt;. This was a full execution-path repair worth keeping.&lt;&#x2F;p&gt;
&lt;p&gt;Replacement policy brought another surprise. With clock replacement and reference bits, a synthetic case with a full hot cache, all reference bits set, and only 1% misses took about &lt;strong&gt;1.03–1.07 ms&lt;&#x2F;strong&gt; per gather. A few miss blocks had to scan long stretches. More misses could actually run faster because more blocks participated in scanning.&lt;&#x2F;p&gt;
&lt;p&gt;Replacing it with circular eviction that skips currently pinned slots reduced the same probe to about &lt;strong&gt;0.032 ms for 2,048 rows&lt;&#x2F;strong&gt;. Yet full MTP throughput was only &lt;strong&gt;19.767 tokens&#x2F;s&lt;&#x2F;strong&gt;. The probe&#x27;s large gain did not materialize in the complete model.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Decision: keep the simpler replacement policy and remove the demonstrated pathological case, but reject the inference that this local hotspot explained the full performance gap.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;decision-four-inspect-the-submission-threads-beyond-attention&quot;&gt;Decision four: inspect the submission threads beyond attention&lt;&#x2F;h2&gt;
&lt;p&gt;CPU sampling restricted to active decode threads found about 21.1% of CPU cycles in host memcpy. The call chain ended in &lt;code&gt;clone&lt;&#x2F;code&gt; on read-only MoE weights: submitting work to a worker could deep-copy the dense router.&lt;&#x2F;p&gt;
&lt;p&gt;This was unrelated to KV semantics, but affected the same execution chain. We shared the read-only weight group through &lt;code&gt;Arc&lt;&#x2F;code&gt;, preserving operators and numerical values.&lt;&#x2F;p&gt;
&lt;p&gt;The resident baseline received the same fix. The v15 MTP comparison reached &lt;strong&gt;25.425 versus 26.832 tokens&#x2F;s&lt;&#x2F;strong&gt;, narrowing the loss to &lt;strong&gt;5.24%&lt;&#x2F;strong&gt;. That was substantial progress, but still just outside the 5% gate. The 21.1% CPU sampling share could not be translated into an equal end-to-end speed gain.&lt;&#x2F;p&gt;
&lt;p&gt;Further work reused the recent ring for peer logging, removed duplicate copies, transferred existing prefill buffers into hot-cache ownership, and used compute copies and packing for small logs to reduce DMA queue operations.&lt;&#x2F;p&gt;
&lt;p&gt;These changes did not solve everything at once. One no-MTP pair reached a 4.80% loss; a later MTP pair lost 9.87%. Logs kept showing &lt;strong&gt;0.6–1.2 second stalls&lt;&#x2F;strong&gt;, even when most event intervals were near the resident baseline.&lt;&#x2F;p&gt;
&lt;p&gt;We did not discard the first 128 tokens or declare victory using p50 alone. Those stalls were part of the user&#x27;s wait and remained in the original complete-decode timing window.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;decision-five-follow-the-stalls-into-os-page-migration&quot;&gt;Decision five: follow the stalls into OS page migration&lt;&#x2F;h2&gt;
&lt;p&gt;Device timelines showed that the long stalls did not necessarily occur in hot gather. One capture showed maximum gather times of only about 0.054–0.056 ms, alongside a roughly 792 ms synchronization wait and almost no device work across the pipeline.&lt;&#x2F;p&gt;
&lt;p&gt;Blaming whichever kernel appeared stretched in the profiler would repeat our earlier error. We began comparing system settings and driver behavior across the two machines.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;disabling-automatic-numa-balancing-helped-but-was-insufficient&quot;&gt;Disabling automatic NUMA balancing helped, but was insufficient&lt;&#x2F;h3&gt;
&lt;p&gt;Amd-1 had &lt;code&gt;kernel.numa_balancing=0&lt;&#x2F;code&gt;; Amd-2 had it set to &lt;code&gt;1&lt;&#x2F;code&gt;. Long stalls concentrated on the latter. Disabling it consistently produced a first MTP pair with only &lt;strong&gt;0.51%&lt;&#x2F;strong&gt; loss.&lt;&#x2F;p&gt;
&lt;p&gt;But the third interleaved pair lost &lt;strong&gt;5.49%&lt;&#x2F;strong&gt;, stopping the gate. We retained consistent settings and continued investigating, instead of averaging away the failed pair.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pre-touching-registered-tail-pages-did-not-solve-the-problem&quot;&gt;Pre-touching registered tail pages did not solve the problem&lt;&#x2F;h3&gt;
&lt;p&gt;Another hypothesis was that unused tail pages in the RAM mirror&#x27;s reserved capacity had not yet been written, and first-touch writes during append disrupted GPU mappings. We zeroed spare capacity before registration without changing valid KV bytes or logical length.&lt;&#x2F;p&gt;
&lt;p&gt;The candidate still showed &lt;strong&gt;7.35%&lt;&#x2F;strong&gt; loss and second-scale gaps. Pre-touching did not resolve the instability and could not be presented as the final cause or solution.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;proactive-compaction-finally-yielded-call-chain-evidence&quot;&gt;Proactive compaction finally yielded call-chain evidence&lt;&#x2F;h3&gt;
&lt;p&gt;BPF tracing tied the actual inference process to invalidation addresses and restore workers, capturing this path:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;kcompactd
&lt;&#x2F;span&gt;&lt;span&gt;  → proactive_compact_node
&lt;&#x2F;span&gt;&lt;span&gt;  → migrate_pages
&lt;&#x2F;span&gt;&lt;span&gt;  → try_to_migrate_one
&lt;&#x2F;span&gt;&lt;span&gt;  → amdgpu_hmm_invalidate_hsa
&lt;&#x2F;span&gt;&lt;span&gt;  → related queue restoration work
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This established a path through proactive page compaction, HMM invalidation, and queue restoration. Many sampled invalidation addresses did not intersect the explicitly registered KV mirrors, so they could not be explained as first writes to KV tail pages. This capture did not reproduce the earlier full one-second stall; we did not claim to have attributed every long pause individually.&lt;&#x2F;p&gt;
&lt;p&gt;We changed &lt;code&gt;vm.compaction_proactiveness&lt;&#x2F;code&gt; from 20 to 0 on both machines, with automatic NUMA balancing still disabled. Linux documents that zero disables proactive compaction and that page migration can cause application latency spikes. The system-specific conclusion still required complete paired runs under identical settings. &lt;a href=&quot;https:&#x2F;&#x2F;docs.kernel.org&#x2F;admin-guide&#x2F;sysctl&#x2F;vm.html#compaction-proactiveness&quot;&gt;Linux kernel documentation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;These were runtime changes on the experimental machines, with original values and restoration commands recorded. Boot configuration was unchanged. These settings are part of the test conditions, not a universal tuning prescription for every GPU system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;six-interleaved-pairs-establish-the-nearly-lossless-result&quot;&gt;Six interleaved pairs establish the nearly lossless result&lt;&#x2F;h2&gt;
&lt;p&gt;The final gate used the same v23 binary, 50,023 input tokens, 1,024 output tokens, greedy decoding, and fixed seed01. The offloaded configuration used a 32,768-row hot cache; the baseline kept KV on the GPU. Both disabled automatic NUMA balancing, proactive compaction, profiling, tracing, attach support, and CPU DSA.&lt;&#x2F;p&gt;
&lt;p&gt;We restarted the experimental runtime for each run and interleaved resident and offloaded ordering. Throughput retained the original first-text-event-to-SSE-DONE window, with no stalls removed.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Mode&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Pair&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;RAM history + GPU hot cache (tokens&#x2F;s)&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;GPU resident (tokens&#x2F;s)&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Offload throughput loss&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;MTP=3&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;27.839&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;27.883&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.157%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MTP=3&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;27.923&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;27.966&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.150%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MTP=3&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;27.738&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;28.102&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.297%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No MTP&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;14.042&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;13.788&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;−1.846%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No MTP&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;14.029&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;13.753&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;−2.007%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No MTP&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;14.029&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;13.747&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;−2.056%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;All 12 requests completed, and full output-file hashes matched the reference for their respective modes. Negative loss means offloading was slightly faster. We interpret that as essentially matching the baseline on this workload, not a guarantee that offloading accelerates inference.&lt;&#x2F;p&gt;
&lt;p&gt;This validates single-session decoding with long history. Initial prefill, appended inputs, multi-session capacity, and concurrent throughput each need their own tests.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;capacity-optimization-must-survive-the-session-lifecycle&quot;&gt;Capacity optimization must survive the session lifecycle&lt;&#x2F;h2&gt;
&lt;p&gt;One uninterrupted generation is not enough for real conversations. Users append inputs, MTP rejects drafts and rolls back, and sessions are saved and restored.&lt;&#x2F;p&gt;
&lt;p&gt;The successful path has an explicit consistency boundary. New KV may still be in the GPU recent ring or an in-flight copyback. &lt;strong&gt;Before exporting or restoring full history, submit outstanding logs, wait for copyback, and verify that CPU row count has caught up with logical row count.&lt;&#x2F;strong&gt; An enqueued asynchronous copy is not yet a completed RAM copy.&lt;&#x2F;p&gt;
&lt;p&gt;Large append prefill also differs from decode. To process many new rows, the current implementation restores the required full-history GPU MLA KV from RAM, performs the append, and returns to hot-cache mode for decode. Small prefill tail chunks must not be mistaken for verification, and owner and peer must remain consistent.&lt;&#x2F;p&gt;
&lt;p&gt;The first real large-append test failed on MTP terminal hidden state. Request completion had saved a raw BF16 residual where the later path expected F32 hidden state after final normalization. The fix aligned normalization boundaries and addressed old-cache restoration.&lt;&#x2F;p&gt;
&lt;p&gt;The subsequent v25 same-version comparison reused 50,085 historical rows, appended 9,854 tokens, reached a final prompt of 59,939 tokens, and generated 1,024 tokens. Complete outputs matched.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Large append comparison&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Offloaded&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;GPU resident&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Time to first token&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;17.905 s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;18.350 s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Decode&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;26.850 tokens&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;27.004 tokens&#x2F;s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Decode loss was &lt;strong&gt;0.57%&lt;&#x2F;strong&gt; in this pair. It established that an offloaded long conversation could continue with a large append, but remained a single append test rather than a replacement for the six single-session pairs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ten-sessions-exposed-a-physical-allocation-problem&quot;&gt;Ten sessions exposed a physical-allocation problem&lt;&#x2F;h2&gt;
&lt;p&gt;While preparing ten independent 50K histories, the sixth prefill ran out of memory on the tail node. The highest 1 Hz VRAM sample was about &lt;strong&gt;47.98 GiB&lt;&#x2F;strong&gt;. Ten-session simultaneous decoding had not yet begun.&lt;&#x2F;p&gt;
&lt;p&gt;The problem was the general GPU memory pool. Small long-lived KV, hot-cache, and session buffers could receive large allocations returned by prefill through best-fit reuse, then hold them indefinitely. A logical allocation of tens of KiB or MiB could retain much more physical space. Accounting reported logical bytes, further understating usage.&lt;&#x2F;p&gt;
&lt;p&gt;We enforced exact capacity limits for long-lived caches and changed primary KV&#x2F;DSA accounting to actual allocation size, while retaining the existing pool policy for temporary activations. Tests first populated the pool with oversized blocks, then checked that small hot-cache buffers did not retain them.&lt;&#x2F;p&gt;
&lt;p&gt;After the fix, v28 completed all ten histories and subsequent concurrent appends, delivering &lt;strong&gt;98.56 tokens&#x2F;s&lt;&#x2F;strong&gt; over the full request group. The highest observed VRAM sample was about &lt;strong&gt;45.12 GiB&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Concurrency tuning followed. Reducing MTP depth to 1 raised full-request throughput to &lt;strong&gt;125.26 tokens&#x2F;s&lt;&#x2F;strong&gt;. Reducing each stage&#x27;s decode batch limit from 4 to 1 reached &lt;strong&gt;145.67 tokens&#x2F;s&lt;&#x2F;strong&gt;, with matching inputs, complete outputs, and usage between those latter two configurations. Multiple requests could keep the pipeline moving; deeper speculation and larger batches that help one session need not help ten.&lt;&#x2F;p&gt;
&lt;p&gt;Separately increasing the A0 ready-work batch limit produced &lt;strong&gt;145.28 tokens&#x2F;s&lt;&#x2F;strong&gt;, essentially unchanged. With no demonstrated benefit, we removed that change.&lt;&#x2F;p&gt;
&lt;p&gt;These concurrent results use actual completion-token totals divided by wall time for the entire append request group, including request boundaries. SSE text-event counts were not treated as token counts. There was no corresponding fully GPU-resident concurrency comparison, so these runs do not establish lossless concurrent offloading. The latest concurrency candidate still needed single-session regression gates.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-capacity-gain-from-roughly-1m-total-toward-15-sessions-of-1m-each&quot;&gt;The capacity gain: from roughly 1M total toward 15+ sessions of 1M each&lt;&#x2F;h2&gt;
&lt;p&gt;Consider the user-facing change. With an original total budget of about 1M tokens, one user carrying a million-token history could effectively occupy it. After offloading, the target is 15+ such users decoding concurrently, each retaining their own million-token history.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Dimension&lt;&#x2F;th&gt;&lt;th&gt;Original capacity basis&lt;&#x2F;th&gt;&lt;th&gt;Estimated offloaded capacity target&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Total history across active sessions&lt;&#x2F;td&gt;&lt;td&gt;About 1M tokens&lt;&#x2F;td&gt;&lt;td&gt;15M+ tokens&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Sessions carrying 1M history each&lt;&#x2F;td&gt;&lt;td&gt;About 1&lt;&#x2F;td&gt;&lt;td&gt;15+ decoding concurrently&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Main storage for full MLA history&lt;&#x2F;td&gt;&lt;td&gt;GPU VRAM&lt;&#x2F;td&gt;&lt;td&gt;1 TiB host RAM per node&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MLA history payload on GPUs&lt;&#x2F;td&gt;&lt;td&gt;Grows with history length&lt;&#x2F;td&gt;&lt;td&gt;32K hot cache per session; old positions read on demand&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This is the practical meaning of opening up capacity: targeting &lt;strong&gt;15+ times&lt;&#x2F;strong&gt; as much active long history on the same GPUs. It expands multi-session historical capacity. Per-session speed at that concurrency still depends on compute, indexing, transfers, and scheduling.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;does-ram-fit-fifteen-million-token-histories&quot;&gt;Does RAM fit fifteen million-token histories?&lt;&#x2F;h3&gt;
&lt;p&gt;Using the same main-model MLA encoding, &lt;code&gt;1M = 1,048,576 tokens&lt;&#x2F;code&gt;, 78 layers, and both owner&#x2F;peer replicas:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;One 1M history = 1,048,576 × 656 B × 78 × 2
&lt;&#x2F;span&gt;&lt;span&gt;               ≈ 99.94 GiB
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;15 sessions    ≈ 1,499.06 GiB (about 1.464 TiB) of main-model MLA history
&lt;&#x2F;span&gt;&lt;span&gt;Total host RAM across both machines = 2 TiB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The budget is &lt;strong&gt;2 TiB across two machines&lt;&#x2F;strong&gt;. All history does not reside on one 1 TiB host. MLA history is distributed according to layer ownership. The owner&#x2F;peer replicas are already included above; they do not mean each host stores the entire model&#x27;s history.&lt;&#x2F;p&gt;
&lt;p&gt;For 15 sessions, the main-model MLA history totals about 1.464 TiB; roughly equal distribution would put about 0.732 TiB on each node. Actual placement follows each node&#x27;s layer count and requires checking headroom separately. &lt;strong&gt;The main-model MLA payload for 15 sessions does not exhaust the two-node RAM budget, leaving room to explore 15+ sessions.&lt;&#x2F;strong&gt; This is an encoding-based capacity calculation, not a measured whole-system memory peak or maximum concurrency.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, fifteen 32K hot caches require about &lt;strong&gt;46.85 GiB&lt;&#x2F;strong&gt; of main-model payload across all GPUs, including both replicas. That component does not grow proportionally when each history expands from 50K to 1M. This is the central capacity advantage.&lt;&#x2F;p&gt;
&lt;p&gt;The complete service budget must also cover GPU DSA history, token mappings, MTP layers, per-session workspaces, allocation and registration overhead, and transient VRAM during full-history restoration for prefill or large append. Fitting the MLA bytes in RAM provides a basis for the 15+ session target, but does not constitute whole-system validation at that scale.&lt;&#x2F;p&gt;
&lt;p&gt;Completed measurements cover nearly lossless 50K single-session decoding, a large append reaching about 60K, and complete concurrent requests over ten 50K histories. &lt;strong&gt;The next meaningful capacity test is to extend each history to 1M and active concurrency to 15+, recording real memory usage, time to first token, and sustained decode throughput.&lt;&#x2F;strong&gt; The current 1M admission configuration must also be aligned with that larger aggregate target.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-decisions-that-survived&quot;&gt;The decisions that survived&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Approach or claim&lt;&#x2F;th&gt;&lt;th&gt;Outcome&lt;&#x2F;th&gt;&lt;th&gt;Decisive evidence&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Final DSA selection on CPU&lt;&#x2F;td&gt;&lt;td&gt;Rejected for this iteration&lt;&#x2F;td&gt;&lt;td&gt;Lower speed and divergent fixed greedy output&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MLA history in RAM with GPU hot working sets&lt;&#x2F;td&gt;&lt;td&gt;Retained&lt;&#x2F;td&gt;&lt;td&gt;Six passing single-session pairs with matching complete outputs&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CPU coordinates hot slots for every selection&lt;&#x2F;td&gt;&lt;td&gt;Main path moved to GPU management&lt;&#x2F;td&gt;&lt;td&gt;Readback waiting interrupted submission&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;More per-row asynchronous copies&lt;&#x2F;td&gt;&lt;td&gt;Rejected&lt;&#x2F;td&gt;&lt;td&gt;More operations reduced throughput&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Batched logs and packed owner&#x2F;peer replication&lt;&#x2F;td&gt;&lt;td&gt;Retained&lt;&#x2F;td&gt;&lt;td&gt;Reduced redundant work and repaired single-row&#x2F;MTP paths&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Moving locks or dual-writing logs closes the gap&lt;&#x2F;td&gt;&lt;td&gt;Rejected&lt;&#x2F;td&gt;&lt;td&gt;No substantial complete-run improvement&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;High kernel share proves the regression is in kernels&lt;&#x2F;td&gt;&lt;td&gt;Withdrawn&lt;&#x2F;td&gt;&lt;td&gt;Absolute shares did not explain the resident&#x2F;offload difference&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Simpler GPU replacement&lt;&#x2F;td&gt;&lt;td&gt;Retained with limited claims&lt;&#x2F;td&gt;&lt;td&gt;Large probe improvement without matching full-model gain&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Share read-only weights instead of deep-copying&lt;&#x2F;td&gt;&lt;td&gt;Retained&lt;&#x2F;td&gt;&lt;td&gt;Same-version full-run gap narrowed&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Disabling automatic NUMA balancing is sufficient&lt;&#x2F;td&gt;&lt;td&gt;Sufficiency rejected&lt;&#x2F;td&gt;&lt;td&gt;Repeated gate still failed&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Pre-touching registered tail pages fixes the stalls&lt;&#x2F;td&gt;&lt;td&gt;Rejected&lt;&#x2F;td&gt;&lt;td&gt;Second-scale gaps and a 7.35% failed pair remained&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Consistent NUMA settings and disabled proactive compaction&lt;&#x2F;td&gt;&lt;td&gt;Retained for these machines&lt;&#x2F;td&gt;&lt;td&gt;Kernel-path evidence and six interleaved pairs&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Small logical buffers imply small VRAM occupancy&lt;&#x2F;td&gt;&lt;td&gt;Rejected&lt;&#x2F;td&gt;&lt;td&gt;Preparation OOM and oversized physical allocations&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Increase A0 batching under concurrency&lt;&#x2F;td&gt;&lt;td&gt;Removed in this iteration&lt;&#x2F;td&gt;&lt;td&gt;Full-request throughput was essentially unchanged&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The reusable lesson is to manage historical capacity, current working sets, and control dependencies separately. RAM provides capacity; GPU hot caches preserve local access; the GPU consumes selection directly; batched logs maintain new history. Host and driver memory behavior also belong in end-to-end measurement.&lt;&#x2F;p&gt;
&lt;p&gt;Every change of direction used the same standard: did complete requests improve, did outputs match, and could failures be reproduced? That standard turned spare host RAM into usable long-history capacity, with a path from roughly 1M total toward 15+ simultaneous million-token sessions.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Sources and measurement scope: based on zLLM engineering records in &lt;code&gt;docs&#x2F;glm53-cpu-kv-decode.md&lt;&#x2F;code&gt; (September 5–8, 2026), the two-node baseline in &lt;code&gt;docs&#x2F;glm53-rocm-amd12-baseline-20260906.md&lt;&#x2F;code&gt;, and inspection of the local GPU hot-cache implementation. Formal single-session results are the six &lt;code&gt;cp0-v23&lt;&#x2F;code&gt; pairs; append results are from &lt;code&gt;append-v25&lt;&#x2F;code&gt;; concurrency results are from &lt;code&gt;concurrency-v28&lt;&#x2F;code&gt; and &lt;code&gt;concurrency-tuning&lt;&#x2F;code&gt;. Historical exploration, diagnostic probes, formal pairs, and later concurrency candidates are identified separately and must not be merged into a single strict cross-version performance curve. No remote benchmarks were rerun for this article.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Vintage Machine Can Run a Top-Tier LLM</title>
        <published>2026-09-07T00:00:00+00:00</published>
        <updated>2026-09-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/old-xeon-rtx3060-qwen38-flash-next/"/>
        <id>https://zhuai.tech/en/blog/old-xeon-rtx3060-qwen38-flash-next/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/old-xeon-rtx3060-qwen38-flash-next/">&lt;p&gt;Two old Xeons, one RTX 3060, and about 125 GiB of system memory: that is the machine used to run Qwen3.8-Flash-Next.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM now runs the complete &lt;strong&gt;48-layer text model&lt;&#x2F;strong&gt; on this system and has connected it to a resident service. With CUDA-driver-owned pinned host memory, the latest service regression delivers about &lt;strong&gt;6.0–6.4 tokens&#x2F;s&lt;&#x2F;strong&gt;, passing 50 requests and four long generations without failure.&lt;&#x2F;p&gt;
&lt;p&gt;The current highest result is a separate, single-process MTP benchmark: &lt;strong&gt;13.158 tokens&#x2F;s&lt;&#x2F;strong&gt;. It reproduces the original 42-token template prompt and 128-token output with a 5.25 GiB expert cache, confidence 0.6, prefetch 0, chunk size 64, a 0.25 GiB MTP cache, four draft tokens, Q8G64 KV, frequency cache and &lt;code&gt;expert_transfer_group=1&lt;&#x2F;code&gt;. Acceptance was 97.09%. The previous 12.708-token&#x2F;s run used the same workload and parameters; pinned target and draft sources reduced verify time from 8.960 s to 8.662 s and draft time from 0.758 s to 0.713 s, with identical upload bytes and token sequence. This is a benchmark record, not the resident service baseline, and it excludes model preloading.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-hardware-and-model&quot;&gt;The hardware and model&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Component&lt;&#x2F;th&gt;&lt;th&gt;Specification&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;CPU&lt;&#x2F;td&gt;&lt;td&gt;Dual Intel Xeon E5-2696 v4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;GPU&lt;&#x2F;td&gt;&lt;td&gt;NVIDIA GeForce RTX 3060, 12 GiB VRAM&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Memory&lt;&#x2F;td&gt;&lt;td&gt;125 GiB RAM&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;OS&lt;&#x2F;td&gt;&lt;td&gt;Fedora 44&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;GPU stack&lt;&#x2F;td&gt;&lt;td&gt;NVIDIA driver 610.57.04, CUDA 13.3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Link&lt;&#x2F;td&gt;&lt;td&gt;PCIe 3.0 x16&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Model&lt;&#x2F;td&gt;&lt;td&gt;Qwen3.8-Flash-Next, GGUF architecture &lt;code&gt;qwen4exp&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Weights&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;unsloth&#x2F;Qwen3.8-Flash-Next-GGUF&lt;&#x2F;code&gt;, four UD-Q4_K_XL shards&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The model&#x27;s quantized expert weights alone occupy 77,017,907,200 bytes, about 71.73 GiB. A 12 GiB GPU cannot hold them all. The model is a mixture-of-experts network, however: each layer routes a token to 10 experts out of 512, plus shared experts. System memory holds the complete expert set while the GPU receives only the experts needed for the current layer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-the-model-fits-in-12-gib&quot;&gt;How the model fits in 12 GiB&lt;&#x2F;h2&gt;
&lt;p&gt;Non-expert weights remain quantized in VRAM. The 71.73 GiB of experts stay resident in host memory and are uploaded on demand. Large PLE tables and embeddings are accessed by selecting only the compressed rows needed for the current token history.&lt;&#x2F;p&gt;
&lt;p&gt;The runtime keeps experts in their GGUF formats instead of expanding everything to F16. CUDA kernels consume Q4_K, Q5_K, Q5_1 and Q8_0 data directly. A fixed-size VRAM arena tracks ownership and reuse, so long generations cannot exhaust the card through allocator fragmentation.&lt;&#x2F;p&gt;
&lt;p&gt;“On demand” here means host-to-device transfer: the experts are already in RAM. PLE and embedding access can still cause limited disk reads.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-the-architecture-makes-new-models-easier-to-add&quot;&gt;Why the architecture makes new models easier to add&lt;&#x2F;h2&gt;
&lt;p&gt;Qwen3.8-Flash-Next combines Hyper-Connection, GDN, PLE, sparse QSA attention and 512-way expert routing. zLLM can still add it without cloning an entire inference stack because model semantics and device execution are separate.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Describe the model.&lt;&#x2F;strong&gt; &lt;code&gt;src&#x2F;model_spec&#x2F;qwen4exp.rs&lt;&#x2F;code&gt; declares the 48-layer layout, tensor shapes, routing constants, QSA&#x2F;PLE&#x2F;HC parameters and GGUF metadata.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Implement the model.&lt;&#x2F;strong&gt; &lt;code&gt;src&#x2F;runtime&#x2F;qwen4exp&#x2F;mod.rs&lt;&#x2F;code&gt; loads weights and assembles the forward pass. &lt;code&gt;cpu.rs&lt;&#x2F;code&gt; is the reference implementation; &lt;code&gt;cuda.rs&lt;&#x2F;code&gt; and &lt;code&gt;cuda_mtp.rs&lt;&#x2F;code&gt; compose the required CUDA and MTP paths.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Register the service.&lt;&#x2F;strong&gt; Configuration and Node registration add &lt;code&gt;Qwen4ExpNodeModelConfig&lt;&#x2F;code&gt; and &lt;code&gt;Qwen4ExpCudaEngine&lt;&#x2F;code&gt;, while reusing chat templates, streaming, cancellation, stop&#x2F;EOS handling and the scheduler protocol.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Those five model-specific files total about &lt;strong&gt;2,228 lines of Rust&lt;&#x2F;strong&gt;. They reuse the existing GGUF loader, KV cache, CUDA context and streams, fixed VRAM arena, expert LRU, quantized kernels, service protocol and lifecycle management. Expert transfer lives in &lt;code&gt;backend&#x2F;cuda&#x2F;expert.rs&lt;&#x2F;code&gt;; packed Q4&#x2F;Q5&#x2F;Q8, attention, routing and tensor kernels are shared modules. New models define how weights and computation are interpreted, rather than rebuilding the runtime.&lt;&#x2F;p&gt;
&lt;p&gt;Correctness and optimization are verified separately. CPU reference comparisons, tensor-format checks, operator tests and token-by-token output comparisons answer “is it correct?” Arena reuse, grouped DMA, cache policy and MTP draft length are then measured with complete requests to answer “is it fast?” A faster microbenchmark is not enough to ship a change; it must also pass reference comparisons, long-output stability and end-to-end throughput checks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mtp-results-and-their-limits&quot;&gt;MTP results and their limits&lt;&#x2F;h2&gt;
&lt;p&gt;Shared MTP uses a draft layer to propose tokens and the full 48-layer target to verify them.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Workload&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Prompt &#x2F; output&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Ordinary&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;MTP&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Coding task&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;42 &#x2F; 128 tokens&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;10.779&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;12.708&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Coding task&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;42 &#x2F; 256 tokens&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;10.682&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.239&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Chinese chat&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;43 &#x2F; 256 tokens&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.371&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.238&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;A later run used a different 59-token Chinese coding prompt and reached 8.32 tokens&#x2F;s with 74.6% acceptance. It is not comparable with the 13.158 result. MTP works in the standalone benchmark loop, but the resident Node MTP path is still converging, so the current service runs without MTP.&lt;&#x2F;p&gt;
&lt;p&gt;The bottleneck is data movement. In one 256-token test, each output token corresponded to about 632.5 MB of expert uploads. At a measured 11.90 GB&#x2F;s transfer rate, the lower bound is already about 53.2 ms per token, before computation and synchronization. That is why simply increasing draft depth does not guarantee 20 tokens&#x2F;s.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;service-lessons&quot;&gt;Service lessons&lt;&#x2F;h2&gt;
&lt;p&gt;The first persistent-service failures came from two independent bugs: an incorrect PLE row width and an unsafe asynchronous DMA path reading registered ordinary heap memory. The final implementation loads experts into CUDA-driver-allocated pinned host memory and uploads from there. It restores the 6.0–6.4 tokens&#x2F;s range and passed the 50-request regression.&lt;&#x2F;p&gt;
&lt;p&gt;Pinned memory needs operational care. A normal exit can return roughly 95 GiB to the driver&#x27;s host pool without immediately restoring the operating system&#x27;s &lt;code&gt;MemAvailable&lt;&#x2F;code&gt;; the next allocation can reuse it, but a memory preflight may reject the run. A forced &lt;code&gt;SIGKILL&lt;&#x2F;code&gt; can leave NVIDIA driver accounting behind and require a reboot. Production shutdown should therefore be graceful.&lt;&#x2F;p&gt;
&lt;p&gt;Long-context prefill is a separate cost. A recorded 10.6K-token input took 351.2 seconds after sparse QSA integration, so first-token latency can be measured in minutes. The tested context configuration reaches 65,536 tokens, but that is not a claim of a full 65,536-token benchmark. Vision support and complete logits alignment remain future work.&lt;&#x2F;p&gt;
&lt;p&gt;This is what the vintage machine provides: enough RAM to hold a modern MoE model, a consumer GPU to execute its active experts, and a runtime that keeps compressed weights, transfers and lifetimes under control. The result is practical text inference on hardware that appears far too small on a VRAM specification sheet.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Based on the zLLM Qwen3.8-Flash-Next integration record and its 2026-09-07 follow-up. Historical benchmark records and current service validation are reported separately; benchmarks were not rerun for this article.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How Good Is K2-Horizon from Abu Dhabi? On an M5 It Solves the Problems, Then Stumbles over Chinese Prose</title>
        <published>2026-09-06T00:00:00+00:00</published>
        <updated>2026-09-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/k2-horizon-mova/"/>
        <id>https://zhuai.tech/en/blog/k2-horizon-mova/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/k2-horizon-mova/">&lt;p&gt;How good is K2-Horizon, the new model family from Abu Dhabi? Give it a reasoning problem and then
ask it to write an essay, and the two results tell very different stories.&lt;&#x2F;p&gt;
&lt;p&gt;After adding K2-Horizon-MoVA-36B-A4B to zLLM, I ran the IQ3_XS quantization on an Apple M5 Mac
with 24 GiB of unified memory. It correctly solved a snail-climbing problem and the classic
cats-and-mice rate problem. When asked for a Chinese essay about a market after the rain, however,
it began mixing English, Japanese, and Arabic into the prose and eventually hit repetition detection.&lt;&#x2F;p&gt;
&lt;p&gt;Lowering the temperature to zero improved the output, but did not eliminate the problem. Before
evaluating the model, though, there is a more basic engineering question to answer: what did zLLM
actually have to implement to run it end to end on Metal?&lt;&#x2F;p&gt;
&lt;p&gt;K2-Horizon is not a conventional Transformer that can be supported by swapping in a new set of
matrix dimensions. It places sparse experts in the value path of attention, creating two independent
routing systems inside a layer. The first half of this article explains how zLLM connects the GGUF,
MoVA, MoE, KV cache, and complete decode path. The second half examines the distance between
&lt;strong&gt;solving a problem and writing consistently good Chinese&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;start-with-the-source-ifm-is-headquartered-in-abu-dhabi&quot;&gt;Start with the source: IFM is headquartered in Abu Dhabi&lt;&#x2F;h2&gt;
&lt;p&gt;K2-Horizon was developed by the Institute of Foundation Models, or IFM. According to IFM, the
institute is part of Mohamed bin Zayed University of Artificial Intelligence (MBZUAI), headquartered
in Abu Dhabi, UAE, with research centers in Paris and Silicon Valley. The K2 Horizon family was
released on September 3, 2026. See the &lt;a href=&quot;https:&#x2F;&#x2F;ifm.ai&#x2F;about&#x2F;&quot;&gt;IFM overview&lt;&#x2F;a&gt; and
&lt;a href=&quot;https:&#x2F;&#x2F;ifm.ai&#x2F;k2&#x2F;press-release&#x2F;&quot;&gt;official announcement&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Calling it simply a European model would therefore be inaccurate. A better description is a model
released by a global research team headquartered in Abu Dhabi, with research centers in Paris and
Silicon Valley.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;36b-parameters-of-capacity-about-4b-active-per-token&quot;&gt;36B parameters of capacity, about 4B active per token&lt;&#x2F;h2&gt;
&lt;p&gt;The complete model name is &lt;strong&gt;K2-Horizon-MoVA-36B-A4B&lt;&#x2F;strong&gt;. IFM describes it as a 36B-parameter model
that activates about 4B parameters per token, with a native context length of 524,288 tokens. Those
are model specifications; this local test did not attempt a 512K context. See the
&lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;IFM&#x2F;K2-Horizon-MoVA-36B-A4B&quot;&gt;official model card&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Conventional MoE models apply sparse routing mainly to the feed-forward network. K2-Horizon&#x27;s MoVA,
or Mixture-of-Value Attention, extends expert routing into the value path of attention. IFM describes
the design in its &lt;a href=&quot;https:&#x2F;&#x2F;ifm.ai&#x2F;blog&#x2F;k2&#x2F;&quot;&gt;K2 Horizon architecture overview&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The local GGUF metadata describes 48 layers, a hidden size of 2,560, 32 query heads, and 8 KV heads.
The value path selects 4 of 64 experts. After three leading dense layers, the feed-forward path selects
8 of 100 routed experts and also executes a shared expert. zLLM now supports the corresponding grouped
RMSNorm, attention output gate, MoVA, MoE, complete Metal prefill and decode, and Q8g64 KV cache.&lt;&#x2F;p&gt;
&lt;p&gt;The file used here was:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;K2-Horizon-MoVA-36B-A4B-IQ3_XS.gguf
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It is 15,695,083,968 bytes, or about &lt;strong&gt;14.62 GiB&lt;&#x2F;strong&gt;. This is a third-party low-bit quantization rather
than an official IFM BF16 checkpoint. Its tensors mix IQ3_S, IQ3_XXS, Q3_K, Q6_K, and F32; the file
is not made entirely from one “three-bit” tensor format.&lt;&#x2F;p&gt;
&lt;p&gt;Activating only a subset of experts reduces computation. It does not mean that only 4B parameters
need to be loaded. The quality results below belong to this exact GGUF and runtime configuration and
should not be treated as measurements of the official BF16 model.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;integration-step-one-k2-cannot-be-treated-as-ordinary-gqa&quot;&gt;Integration step one: K2 cannot be treated as ordinary GQA&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM does not bake K2 dimensions into Metal kernels. Model dimensions, layer counts, normalization,
attention geometry, expert counts, and Top-K values live in a dedicated &lt;code&gt;K2HorizonConfig&lt;&#x2F;code&gt;. When a
GGUF is opened, zLLM reads and validates the metadata field by field.&lt;&#x2F;p&gt;
&lt;p&gt;The validation goes beyond hidden size. The current implementation confirms sigmoid expert routing,
normalization of selected expert weights, one shared expert per sparse layer, matching key and value
head dimensions, and MoE in every layer after the dense prefix. A structurally incompatible GGUF
fails during loading instead of carrying an invalid shape into GPU execution.&lt;&#x2F;p&gt;
&lt;p&gt;The model schedule lives in &lt;code&gt;runtime&#x2F;k2_horizon&#x2F;&lt;&#x2F;code&gt;. It describes the actual flow of one K2 layer:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;hidden
&lt;&#x2F;span&gt;&lt;span&gt;  → grouped RMSNorm
&lt;&#x2F;span&gt;&lt;span&gt;  → query + attention gate
&lt;&#x2F;span&gt;&lt;span&gt;  → key
&lt;&#x2F;span&gt;&lt;span&gt;  → dense value (first 3 layers) or 4 of 64 value experts (last 45 layers)
&lt;&#x2F;span&gt;&lt;span&gt;  → RoPE + GQA + KV append
&lt;&#x2F;span&gt;&lt;span&gt;  → attention gate + output projection + residual
&lt;&#x2F;span&gt;&lt;span&gt;  → grouped RMSNorm
&lt;&#x2F;span&gt;&lt;span&gt;  → dense FFN (first 3 layers) or 8 of 100 routed experts + shared expert
&lt;&#x2F;span&gt;&lt;span&gt;  → residual
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The value path is the easiest part to wire incorrectly. In ordinary GQA, value is a single linear
projection. From layer four onward, K2 first applies a sigmoid router and bias, chooses 4 of 64 value
experts, computes their outputs, and combines them with normalized routing weights. The result has
8 KV heads of 128 dimensions each, for a 1,024-dimensional value vector, and only then enters attention.&lt;&#x2F;p&gt;
&lt;p&gt;The FFN has a separate router. It selects 8 of 100 routed experts, executes a shared expert, normalizes
the selected weights, and applies a scaling factor of 2.5. The two routers own different weights and
serve different dataflows. Treating both as one generic “expert” branch would hide a real dependency.&lt;&#x2F;p&gt;
&lt;p&gt;The third-party GGUF quantizes the small but sensitive MoVA router as IQ3_S. zLLM decodes only that
router to resident F32 during loading to keep routing scores stable. The much larger value-expert
weights remain quantized and are consumed directly by Metal. This keeps the exception local instead
of weakening the general weight-loading contract or expanding all expert weights.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;integration-step-two-iq3-xs-is-not-one-kernel&quot;&gt;Integration step two: IQ3_XS is not one kernel&lt;&#x2F;h2&gt;
&lt;p&gt;Opening the tensor directory reveals five storage types: 322 IQ3_S tensors, 240 IQ3_XXS tensors,
3 Q3_K tensors, 1 Q6_K tensor, and 232 F32 tensors. A single generic “IQ3” entry point is not enough.&lt;&#x2F;p&gt;
&lt;p&gt;To execute the whole file, the Metal path needs ordinary quantized GEMV, fused gate&#x2F;up projection,
expert-indexed gate&#x2F;up&#x2F;down projection, the 4-of-64 MoVA value operation, and a Q6_K LM head over a
250,624-token vocabulary. These kernels read packed GGUF weights directly. Fully dequantizing the
weights before every generated token would throw away much of the capacity and bandwidth advantage
of the 14.62 GiB file.&lt;&#x2F;p&gt;
&lt;p&gt;Prefill and decode also need different execution shapes. Prefill processes multiple token rows,
routes the batch, groups work by expert, and then executes each group. Decode has one row and uses
device-resident Top-K results with indexed expert kernels. The last 45 layers contain 100 FFN experts
each: 4,500 expert groups totaling about 10.00 GiB. The zero-configuration run preloads them into
Metal resources visible through unified memory so that generation does not stop for per-layer disk I&#x2F;O.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;integration-step-three-a-24-gib-machine-still-needs-room-for-kv&quot;&gt;Integration step three: a 24 GiB machine still needs room for KV&lt;&#x2F;h2&gt;
&lt;p&gt;A 14.62 GiB weight file does not leave every other byte available for context. Execution also needs
resident expert resources, activations, scratch space, Metal objects, and memory for the operating
system. At startup, zLLM reads the model geometry and machine budget, then computes KV cost per token.
The M5 run reported:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Weights                  14.62 GiB
&lt;&#x2F;span&gt;&lt;span&gt;KV budget                 0.96 GiB
&lt;&#x2F;span&gt;&lt;span&gt;KV cost per token       ~101,376 bytes
&lt;&#x2F;span&gt;&lt;span&gt;Selected context length   9,216 tokens
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For K2 on a 24 GiB machine, zLLM permits a working-set ceiling of three quarters of physical memory
while preserving at least 2 GiB for the system. KV cache defaults to Q8g64, with one quantization group
per 64 values. Short contexts use direct attention. Beyond 256 tokens, split-KV divides history into
parallel segments and merges the partial results. This is why the model may advertise 524,288 tokens
while the zero-configuration entry point chooses 9,216 on this machine.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;flattening-complete-decode-into-replay&quot;&gt;Flattening complete decode into replay&lt;&#x2F;h2&gt;
&lt;p&gt;One decode round crosses all 48 layers. An early profile showed roughly 1,100 Metal commands per token.
Rebuilding and submitting that graph from the CPU for every round would repeat substantial scheduling
work.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM therefore implements complete single-token decode replay for K2. The first pass records the
sequence from an uploaded token embedding through 48 layers of attention, both expert routers, MoE,
final norm, LM head, and argmax. Later rounds update the token embedding, position, and KV write slots,
then replay the same plan. Q8 attention replay selects its direct or split-KV pipeline from the current
position, so crossing the 256-token boundary does not silently preserve the short-context path.&lt;&#x2F;p&gt;
&lt;p&gt;There is an important limitation behind the performance figures. Greedy generation can replay the
complete round. Temperature&#x2F;top-p sampling must retrieve logits and maintain sampling state, so the
current K2 path does not enter the same replay fast path. The roughly 30 tok&#x2F;s greedy result therefore
does not describe the sampled writing tests later in this article.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;prove-the-numerical-path-before-judging-the-text&quot;&gt;Prove the numerical path before judging the text&lt;&#x2F;h2&gt;
&lt;p&gt;Integration did not stop when the model began emitting tokens. The validation record includes:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;K2 Q8 direct and split KV append comparisons against the reference;&lt;&#x2F;li&gt;
&lt;li&gt;a BF16 split-KV vectorized-kernel argument-binding regression, fixed and rechecked against the CPU oracle;&lt;&#x2F;li&gt;
&lt;li&gt;Q6_K GEMV compared with a dot product over decoded weights;&lt;&#x2F;li&gt;
&lt;li&gt;Metal top-p sampling over a 250,624-token vocabulary checked point by point against the CPU reference;&lt;&#x2F;li&gt;
&lt;li&gt;full real-weight loading, prefill, decode, Q8 KV cache, and replay on the M5.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Under low-reasoning greedy settings, zLLM and llama.cpp produced the same four wrong answers for the
same GGUF. With high reasoning and enough token budget, the archived llama.cpp test answered all four
questions correctly across three seeds, for 12&#x2F;12. This comparison does not prove that every numerical
path is identical, but it rules out the simple explanation that those four mistakes occurred only in zLLM.&lt;&#x2F;p&gt;
&lt;p&gt;The console exposed another problem that was subtler than a kernel bug. The shared client initially
sent &lt;code&gt;enable_thinking=false&lt;&#x2F;code&gt; explicitly. K2&#x27;s chat template interprets that as an empty thinking fence,
forcing a direct answer and making mixed-language degradation more likely. The corrected client leaves
that field unset for K2, preserving the template&#x27;s default high-thinking mode. MiniCPM5 and Qwen retain
their separately validated settings. Model support includes template semantics as well as matrix math.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;current-speed-and-the-remaining-bottlenecks&quot;&gt;Current speed and the remaining bottlenecks&lt;&#x2F;h2&gt;
&lt;p&gt;In an archived run with the same 55-token prompt, greedy decoding, and a 256-token completion,
llama.cpp used F16 KV and measured 0.322 seconds for prefill plus 7.800 seconds for decode, or
32.691 tok&#x2F;s. zLLM used Q8 KV and its best recorded run measured 5.999 seconds for prefill plus
8.623 seconds for decode, or &lt;strong&gt;29.690 tok&#x2F;s&lt;&#x2F;strong&gt;, 9.18% slower.&lt;&#x2F;p&gt;
&lt;p&gt;This is not a strict same-KV-format comparison, but it makes the current position clear: decode is
close, while short-prompt prefill remains much slower. In a single-token profile, the two sigmoid
routers—the 8-of-100 FFN router and the 4-of-64 value router—ranked among the largest GPU costs.
Split-KV beyond 256 tokens and replay submission also leave room for improvement.&lt;&#x2F;p&gt;
&lt;p&gt;The machine eventually thermal-throttled during the last stress run, increasing direct decode from
about 30 ms&#x2F;token to 80–100 ms&#x2F;token. The article therefore uses the reproducible pre-throttling
measurements instead of presenting an overheated transient result as normal performance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;give-it-enough-reasoning-budget-then-inspect-the-answer&quot;&gt;Give it enough reasoning budget, then inspect the answer&lt;&#x2F;h2&gt;
&lt;p&gt;The quality test used high reasoning, temperature 1.0, and top-p 0.95, matching IFM&#x27;s
&lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;IFM&#x2F;K2-Horizon-MoVA-36B-A4B#best-practices&quot;&gt;recommended settings&lt;&#x2F;a&gt;.
Each request had a 9,216-token context limit and up to 4,096 generated tokens. The questions were
independent and did not share chat history.&lt;&#x2F;p&gt;
&lt;p&gt;Reasoning mode is not merely a display option. The local template changes its thinking marker based
on the selected effort; disabling thinking inserts an empty thinking fence. If a run spends a tiny
completion budget reasoning and ends with &lt;code&gt;finish=length&lt;&#x2F;code&gt;, it should be recorded as incomplete rather
than graded as if its intermediate text were the final answer.&lt;&#x2F;p&gt;
&lt;p&gt;Both reasoning problems below reached a normal stop:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Problem&lt;&#x2F;th&gt;&lt;th&gt;Correct answer&lt;&#x2F;th&gt;&lt;th&gt;Observed answer&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Generated tokens, including reasoning&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A snail climbs 3 m by day and slips 2 m at night. On which day does it leave a 10 m well?&lt;&#x2F;td&gt;&lt;td&gt;Day 8&lt;&#x2F;td&gt;&lt;td&gt;Day 8&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,344&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Three cats catch three mice in three minutes. How long do nine cats need for nine mice?&lt;&#x2F;td&gt;&lt;td&gt;3 minutes&lt;&#x2F;td&gt;&lt;td&gt;3 minutes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;700&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The final climb has no following slip, which makes day eight correct. The cats work in parallel, so
scaling both cats and mice by three leaves the time unchanged. The model got both conclusions right.&lt;&#x2F;p&gt;
&lt;p&gt;Its Chinese explanation of the snail problem nevertheless included this sentence:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;每天 daytime 爬升 3 米， nighttime 回滑 2 米&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It means “climbs three meters in the daytime and slips two at night,” but inserts the English words
&lt;code&gt;daytime&lt;&#x2F;code&gt; and &lt;code&gt;nighttime&lt;&#x2F;code&gt; into an otherwise Chinese sentence. It also produced the malformed phrase
&lt;code&gt;白天下爬到&lt;&#x2F;code&gt;. A correct numeric answer did not make the explanation naturally written.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-market-essay-makes-the-problem-obvious&quot;&gt;The market essay makes the problem obvious&lt;&#x2F;h2&gt;
&lt;p&gt;The first writing prompt, in Chinese, asked for a restrained 400–600-character slice-of-life essay
titled &lt;em&gt;The Market after the Rain&lt;&#x2F;em&gt;, organized around concrete people, actions, sounds, and smells.
It explicitly requested only the title and body, in Chinese.&lt;&#x2F;p&gt;
&lt;p&gt;The generated body began:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;雨刚停，菜市场还不全是人。薄荷黑布遮在丑毛さらに码头上，粪水从竹筐的缝隙里滴着Becoming一条条银白色的短河。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The sentence starts in Chinese, inserts Japanese &lt;code&gt;さらに&lt;&#x2F;code&gt; and English &lt;code&gt;Becoming&lt;&#x2F;code&gt;, and is semantically
broken even if those words are removed. Later text includes &lt;code&gt;Granny&lt;&#x2F;code&gt;, Japanese &lt;code&gt;へえ&lt;&#x2F;code&gt;, Arabic
&lt;code&gt;السجائر&lt;&#x2F;code&gt;, and ends with &lt;code&gt;ポリ袋ákááááááááá&lt;&#x2F;code&gt;. The runtime stopped it with &lt;code&gt;finish=repetition&lt;&#x2F;code&gt;.
Counting only Han characters after the title, it produced 88—far short of the requested essay.&lt;&#x2F;p&gt;
&lt;p&gt;The second prompt requested a 400–600-character explanatory essay titled &lt;em&gt;If a Small Model Can Solve
Problems, Can It Write Well?&lt;&#x2F;em&gt; It asked the model to distinguish answer correctness, reliable reasoning,
and natural language, with one concrete example.&lt;&#x2F;p&gt;
&lt;p&gt;This time the output formed complete paragraphs, but still contained lines such as:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Such表述语言自然。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;最后把推理过程写成一段连贯的文字 describing the solving journey, 语言自然。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;And the conclusion inserted the word &lt;code&gt;fingernail&lt;&#x2F;code&gt; where it had no meaningful role. The argument also
treated natural language as exclusive to creative writing and tried to infer writing ability from one
correct equation solution. Its &lt;code&gt;finish=stop&lt;&#x2F;code&gt; means generation stopped normally; it does not mean the
essay passed review.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-much-does-temperature-zero-help&quot;&gt;How much does temperature zero help?&lt;&#x2F;h2&gt;
&lt;p&gt;I kept high reasoning and the same two prompts, changed temperature to 0, and ran each writing task
once more.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Task&lt;&#x2F;th&gt;&lt;th&gt;temperature=1.0&lt;&#x2F;th&gt;&lt;th&gt;temperature=0&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Market essay&lt;&#x2F;td&gt;&lt;td&gt;Mixed languages, repetition stop; 88 Han characters&lt;&#x2F;td&gt;&lt;td&gt;Normal stop, 489 Han characters; still contains English&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Evaluation essay&lt;&#x2F;td&gt;&lt;td&gt;Normal stop, 481 Han characters; meaningless English insertions&lt;&#x2F;td&gt;&lt;td&gt;Normal stop, 652 Han characters; much better Chinese, but over the requested length&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The counts include only Han characters in the body after the title, excluding punctuation, Latin
letters, and Markdown markers.&lt;&#x2F;p&gt;
&lt;p&gt;The zero-temperature market essay has a complete scene, but still writes &lt;code&gt;freshly 捕来的鲫鱼&lt;&#x2F;code&gt;,
&lt;code&gt;Somehow 却不显得刺鼻&lt;&#x2F;code&gt;, and &lt;code&gt;女孩 grabs 住鱼&lt;&#x2F;code&gt;. It also renders the sound of bargaining as a
camera-like “click.” The evaluation essay improves much more and has no comparable meaningless
foreign-language insertion; &lt;code&gt;AI&lt;&#x2F;code&gt; is an ordinary abbreviation. It still misses the requested length.&lt;&#x2F;p&gt;
&lt;p&gt;The result should not be compressed into “the model cannot write Chinese.” A more accurate conclusion
is that &lt;strong&gt;under these prompts and this runtime configuration, completion, language consistency, and
instruction following in Chinese writing are unstable, and both genre and generation settings affect
the result&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;One engineering question remains unresolved: where does the degradation originate? The writing prompts
were not repeated on the BF16 checkpoint or another runtime, so this test cannot separate quantization
loss, runtime numerics, template behavior, and the model itself. Temperature zero also changes sampling
state and eligibility for the current zLLM replay path, so it is not a clean one-variable ablation.&lt;&#x2F;p&gt;
&lt;p&gt;The evidence is enough to describe this local configuration. It is not enough to judge the entire
model family.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;running-k2-horizon-with-zllm&quot;&gt;Running K2-Horizon with zLLM&lt;&#x2F;h2&gt;
&lt;p&gt;From the zLLM repository, pass the GGUF path to the compiled release client:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;sh&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-sh &quot;&gt;&lt;code class=&quot;language-sh&quot; data-lang=&quot;sh&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.&#x2F;target&#x2F;release&#x2F;zllm-metal &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F;Volumes&#x2F;ORICO&#x2F;models&#x2F;K2-Horizon-MoVA-36B-A4B-IQ3_XS.gguf
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;--release&lt;&#x2F;code&gt; is a Cargo build option and is not passed to &lt;code&gt;zllm-metal&lt;&#x2F;code&gt;. Once loading completes, enter a
message directly; &lt;code&gt;&#x2F;reset&lt;&#x2F;code&gt; clears the conversation and &lt;code&gt;&#x2F;exit&lt;&#x2F;code&gt; quits. On this machine the client selected
a 9,216-token context automatically. The advertised 512K model limit is not the usable capacity of this
24 GiB configuration.&lt;&#x2F;p&gt;
&lt;p&gt;This integration gives zLLM a complete executable path for a MoVA model. The writing tests add the
other half of the story: an engine can run every layer correctly while a particular quantization and
generation setup still produces poor prose. After looking at reasoning scores, it is worth asking a
model for one concrete slice-of-life essay. Tasks without a single correct answer often reveal whether
the language holds together, whether details make sense, and whether the result can actually be handed
to a reader.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;test-record&quot;&gt;Test record&lt;&#x2F;h2&gt;
&lt;p&gt;The tests ran on September 6, 2026, on an Apple M5 with 24 GiB of unified memory, using the IQ3_XS GGUF,
Metal, and Q8g64 KV cache. There were six independent requests: four at temperature 1.0 without a fixed
seed and two zero-temperature writing comparisons. Every request allowed up to 4,096 generated tokens,
including reasoning and answer text. The sampled runs were not replay performance measurements.&lt;&#x2F;p&gt;
&lt;p&gt;The production entry point is &lt;code&gt;zllm-metal&lt;&#x2F;code&gt;, which identifies and loads K2 from the supplied GGUF path.
Quality tests reused the same production inference path and an existing release build. The recorded
source HEAD describes the workspace at test time and is not a verified build commit for that binary.
The runtime returned a combined text stream rather than separate reasoning and content fields. Essay
excerpts begin after an explicit title; problem grading uses the final answer at the end. English inside
hidden reasoning was not counted as an error in the Chinese essay body.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a href=&quot;&#x2F;data&#x2F;k2-horizon-20260906&#x2F;results.json&quot;&gt;JSON record&lt;&#x2F;a&gt; contains every prompt, all six raw outputs,
finish reasons, and a runtime fingerprint. The &lt;a href=&quot;&#x2F;data&#x2F;k2-horizon-20260906&#x2F;high.log&quot;&gt;sampled log&lt;&#x2F;a&gt;,
&lt;a href=&quot;&#x2F;data&#x2F;k2-horizon-20260906&#x2F;greedy.log&quot;&gt;zero-temperature log&lt;&#x2F;a&gt;,
&lt;a href=&quot;&#x2F;data&#x2F;k2-horizon-20260906&#x2F;high.yaml&quot;&gt;sampled configuration&lt;&#x2F;a&gt;, and
&lt;a href=&quot;&#x2F;data&#x2F;k2-horizon-20260906&#x2F;greedy.yaml&quot;&gt;zero-temperature configuration&lt;&#x2F;a&gt; are also available.
The raw model output remains in Chinese and has not been rewritten. Paths in the configuration files
belong to the test machine and must be changed for reproduction elsewhere.&lt;&#x2F;p&gt;
&lt;p&gt;Operator validation and earlier performance measurements are preserved in the
&lt;a href=&quot;&#x2F;data&#x2F;k2-horizon-20260906&#x2F;prior-integration-report.md&quot;&gt;integration report&lt;&#x2F;a&gt;. Its measurements and the
writing tests above are separate records.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Running GLM-5.3 on Two 8-GPU AMD W7900D Machines</title>
        <published>2026-09-05T00:00:00+00:00</published>
        <updated>2026-09-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/rocm-glm53-single-pipeline/"/>
        <id>https://zhuai.tech/en/blog/rocm-glm53-single-pipeline/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/rocm-glm53-single-pipeline/">&lt;p&gt;Sixteen GPUs, 768 GiB of total VRAM—and GLM-5.3 initially generated just &lt;strong&gt;4.08 tokens&#x2F;s&lt;&#x2F;strong&gt;, taking roughly 245 milliseconds per token.&lt;&#x2F;p&gt;
&lt;p&gt;Having enough memory to fit the model does not make compute scale automatically. Each of our two machines has eight 48 GiB W7900D cards. Communication uses PCIe 4.0 within a node and TCP between nodes, with no XGMI high-speed interconnect available to this execution path. The model has to span multiple GPUs, but every additional collaboration point can add another wait to each token.&lt;&#x2F;p&gt;
&lt;p&gt;This constraint shaped our ROCm optimization work in zLLM: reduce communication inside layers, improve the work performed on each GPU, then use MTP speculative decoding to reduce the number of full-model execution rounds.&lt;&#x2F;p&gt;
&lt;p&gt;Checking the original result files on Amd-1 gives &lt;strong&gt;11.321 tokens&#x2F;s&lt;&#x2F;strong&gt; for a complete run with one GPU per stage and MTP disabled. The latest two-GPU operator-pair version achieved &lt;strong&gt;13.842–14.043 tokens&#x2F;s across three runs, with a median of 13.968—about 14 tokens&#x2F;s&lt;&#x2F;strong&gt;. The single-GPU-stage path is about 2.77 times faster than the initial 4.08 tokens&#x2F;s baseline.&lt;&#x2F;p&gt;
&lt;p&gt;A complete run of an older paired version with MTP also recorded &lt;strong&gt;25.275 tokens&#x2F;s&lt;&#x2F;strong&gt;. This establishes that the combination has been tested; it does not establish that MTP has been validated on the latest version delivering approximately 14 tokens&#x2F;s without it.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Measurement notes: the main results come from JSON files and runtime logs on Amd-1. The corpus is named &lt;code&gt;50k-allgpu.txt&lt;&#x2F;code&gt;, but the actual requests contained 46,152–46,158 input tokens and generated 1,024 tokens each. Decode throughput is the remaining 1,023 generated tokens divided by generation time after the first text event. “50K” below retains the corpus name; 50K in operator profiles describes their workload size. Results with different commits, nonces, or MTP settings are not strict same-version A&#x2F;B comparisons.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;decide-where-data-lives-before-deciding-how-to-parallelize&quot;&gt;Decide where data lives before deciding how to parallelize&lt;&#x2F;h2&gt;
&lt;p&gt;Single-request autoregressive generation has a hard dependency: the next token must wait for the current token to finish sampling.&lt;&#x2F;p&gt;
&lt;p&gt;For the 78-layer model tested here, a token passes through every layer, the final output projection (LM Head), and sampling before the next round can begin. Distributing those layers across 16 GPUs divides the execution chain into stages. It does not let one token pass through all layers simultaneously.&lt;&#x2F;p&gt;
&lt;p&gt;We started by assigning each GPU a &lt;strong&gt;contiguous range of complete layers&lt;&#x2F;strong&gt;. Weights, KV cache, sparse-attention index state, expert weights, and scratch buffers remain local. Only the hidden activation at a stage boundary needs to be handed off:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;images&#x2F;rocm-glm53&#x2F;execution-map.en.svg&quot;&gt;&lt;img src=&quot;&#x2F;images&#x2F;rocm-glm53&#x2F;execution-map.en.svg&quot; alt=&quot;GLM-5.3 execution map: capacity, 16-stage pipeline, DSA&#x2F;MLA&#x2F;MoE workloads, traffic and latency ratios&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Click to enlarge. Traffic and latency comparisons have separate scales. Layer profiles contain nested scopes and must not be added up into a whole-token time breakdown.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;“Single-GPU stage” means one GPU executes each stage; the complete model still uses all 16 GPUs.&lt;&#x2F;p&gt;
&lt;p&gt;An earlier design used finer-grained collaboration between two GPUs, repeatedly exchanging queries, selections, and expert partial results—four to six handoffs per layer. The tensors were not necessarily large, but event waits after each exchange put the difference in GPU progress onto the critical path. Across 78 layers, coordination costs absorbed the savings from parallel computation.&lt;&#x2F;p&gt;
&lt;p&gt;The approximate latency budget for single-request decode is therefore:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Per-token latency
&lt;&#x2F;span&gt;&lt;span&gt;  ≈ sum of serial execution time across all stages
&lt;&#x2F;span&gt;&lt;span&gt;  + stage handoffs
&lt;&#x2F;span&gt;&lt;span&gt;  + LM Head, sampling, and the autoregressive return path
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This distinction also matters for load balancing. When multiple requests or prefill chunks overlap, the slowest stage limits pipeline throughput. For single-request, token-by-token decode, total chain time comes first. Moving layer boundaries helps only if it reduces waits, improves execution efficiency, or relieves resource constraints.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;from-4-08-to-11-32-where-the-time-went&quot;&gt;From 4.08 to 11.32: where the time went&lt;&#x2F;h2&gt;
&lt;p&gt;The archived tests used GLM-5.3 UD-IQ4_XS GGUF, 78 layers, Q8G64 KV, the “50K” corpus—actually about 46.15K input tokens—and 1,024 generated tokens. MTP, DSpark, and the old cooperative-pair path were disabled.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stage&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Decode (tokens&#x2F;s)&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Latency per token&lt;&#x2F;th&gt;&lt;th&gt;Main changes&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Initial single pipeline&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.084&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;244.8 ms&lt;&#x2F;td&gt;&lt;td&gt;Topology corrected; single-row operators still unoptimized&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;One GPU per stage, no intra-layer pairing&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.321&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;About 88.3 ms&lt;&#x2F;td&gt;&lt;td&gt;Dedicated IQ kernels, MLA tuning, Q8 shared expert, direct W8 reads, scheduling improvements&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;At 11.321 tokens&#x2F;s, throughput is about 2.77 times the initial baseline and per-token latency is about 63.9% lower. Several changes account for the improvement.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-single-decode-row-is-not-a-large-matrix-multiplication&quot;&gt;A single decode row is not a large matrix multiplication&lt;&#x2F;h3&gt;
&lt;p&gt;Prefill processes multiple input rows, allowing matrix multiplication to amortize weight reads. Ordinary decode processes one row at a time. It is closer to matrix-vector multiplication, or GEMV: a large set of weights is read to produce one token&#x27;s output.&lt;&#x2F;p&gt;
&lt;p&gt;Weight traffic, dequantization instructions, thread mapping, and kernel launch overhead become prominent. We wrote HIP&#x2F;ROCm operators specifically for gfx1100, with dedicated paths for IQ3_S, IQ4_XS, and Q8&#x2F;W8A16.&lt;&#x2F;p&gt;
&lt;p&gt;For example, IQ3_S gate&#x2F;up uses an 8-lane subgroup, followed by wider &lt;code&gt;u16&lt;&#x2F;code&gt; loads to reduce loading overhead. With layout and output bit patterns unchanged, probe time fell from approximately 207–211 microseconds to 188–190 microseconds.&lt;&#x2F;p&gt;
&lt;p&gt;Similar code does not guarantee similar gains. IQ4_XS down already achieved about 465 GB&#x2F;s of effective bandwidth. Porting a different codebook-reading implementation increased its time from 102 to 144 microseconds. For this shape, further work on codebook caching did not address the main bottleneck.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;keep-quantized-weights-compressed-in-memory&quot;&gt;Keep quantized weights compressed in memory&lt;&#x2F;h3&gt;
&lt;p&gt;Multi-head Latent Attention, or MLA, is a major aggregate hotspot executed in every layer. The resident representation of &lt;code&gt;kv_b&lt;&#x2F;code&gt; was wasting substantial bandwidth.&lt;&#x2F;p&gt;
&lt;p&gt;The old path decoded W8 weights into F16 and then kept a dense F32 representation resident for absorb&#x2F;PV operations. Although the stored weights were quantized, execution expanded them again: those stages read approximately 58.6 MB per layer.&lt;&#x2F;p&gt;
&lt;p&gt;The new path consumes W8 weights and scales directly, reducing traffic to about 14.7 MB, roughly one quarter of the original. Complete runs improved from about 9.76 to 10.50&#x2F;10.28 tokens&#x2F;s, an average gain of roughly 6.5% across the two runs.&lt;&#x2F;p&gt;
&lt;p&gt;This was more direct than another tile adjustment: &lt;strong&gt;if the hot path ultimately reads F32, quantization has saved file size without fully delivering its runtime bandwidth advantage.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Other MLA changes also helped. Vectorizing contiguous PV loads reduced the device scope from approximately 0.484 to 0.439 ms&#x2F;layer, yielding about 2.5%–3.5% for the full model. Selected scan distributed QK work previously handled mainly by one wave across eight waves, cutting partial-plus-merge time from approximately 90.6 to 57–59 microseconds.&lt;&#x2F;p&gt;
&lt;p&gt;These local savings do not automatically translate into equal reductions in token latency.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-faster-kernel-can-still-leave-the-full-chain-slower&quot;&gt;A faster kernel can still leave the full chain slower&lt;&#x2F;h2&gt;
&lt;p&gt;The DSA sparse-attention indexer selects historical positions for subsequent attention computation. At a 50K workload, score computation took about 91 microseconds, already reaching roughly 68% of the profile&#x27;s estimated compute ceiling. The serial tail of radix selection was a more promising target.&lt;&#x2F;p&gt;
&lt;p&gt;The original last two levels rescanned serially in one thread block. Splitting the work into multiple kernels reduced the relevant kernel time from about 105 to 96 microseconds. But two additional launches, each costing about 12 microseconds, left complete selection at approximately 139 microseconds. The expected end-to-end gain did not materialize.&lt;&#x2F;p&gt;
&lt;p&gt;These experiments expanded our unit of optimization: first a kernel, then an entire operator, and ultimately a stage&#x27;s contribution to the token critical path.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Main constraint&lt;&#x2F;th&gt;&lt;th&gt;Examples&lt;&#x2F;th&gt;&lt;th&gt;Useful direction&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Weight-read bandwidth&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;q_b&lt;&#x2F;code&gt;, &lt;code&gt;kv_b&lt;&#x2F;code&gt;, MoE weight streams&lt;&#x2F;td&gt;&lt;td&gt;Compressed residency, wide contiguous loads, shared weight reads&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Compute and dequantization instructions&lt;&#x2F;td&gt;&lt;td&gt;DSA score, IQ decoding&lt;&#x2F;td&gt;&lt;td&gt;Wave mapping, shorter instruction dependencies&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Synchronization and scheduling&lt;&#x2F;td&gt;&lt;td&gt;Multi-level radix selection, launches, pair joins&lt;&#x2F;td&gt;&lt;td&gt;Fewer handoffs, fused dependency chains, no bridge copies&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The updated source notes provide the following bandwidth breakdown:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;images&#x2F;rocm-glm53&#x2F;bandwidth-roof.en.svg&quot;&gt;&lt;img src=&quot;&#x2F;images&#x2F;rocm-glm53&#x2F;bandwidth-roof.en.svg&quot; alt=&quot;Effective bandwidth and compute or synchronization bottlenecks of major operators&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Effective bandwidth here is weight bytes divided by kernel time. It is an analytical metric. Usable underlying memory-traffic and occupancy counters were not available in this investigation, so these values are not hardware-counter measurements.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-graph-experiment-faster-launches-but-4-5-lower-throughput&quot;&gt;The Graph experiment: faster launches, but 4%–5% lower throughput&lt;&#x2F;h2&gt;
&lt;p&gt;HIP Graph records a repeated kernel dependency chain and replays it, reducing the cost of submitting kernels individually. This looks attractive for single-row decode with many short kernels. Our probes confirmed launch savings, but integrating the graph into the production path did not deliver an end-to-end gain.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-probe-improved-the-complete-request-did-not&quot;&gt;The probe improved; the complete request did not&lt;&#x2F;h3&gt;
&lt;p&gt;A chain of 64 small kernels took approximately 0.76–0.80 ms on the eager device timeline, compared with 0.23–0.27 ms for warmed static Graph replay. That is roughly 12 versus 4 microseconds per node. Graph reduced dispatch gaps; it did not accelerate the computation or weight reads inside each kernel.&lt;&#x2F;p&gt;
&lt;p&gt;After correctness fixes, complete-request tests of the five-node MoE Graph produced:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Same binary, “50K” corpus, 1,024-token decode&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Run 1&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Run 2&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Graph disabled&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;9.509 tokens&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;9.607 tokens&#x2F;s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Graph enabled&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;9.119 tokens&#x2F;s&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;9.124 tokens&#x2F;s&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Throughput change&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;−4.1%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;−5.0%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This was a historical Graph experiment, fixed in &lt;code&gt;b4433700&lt;&#x2F;code&gt; and recorded in &lt;code&gt;51a4f30d&lt;&#x2F;code&gt;. These figures describe a same-version Graph toggle comparison. &lt;strong&gt;The verified single-GPU-stage result used elsewhere in this article is 11.321 tokens&#x2F;s.&lt;&#x2F;strong&gt; The historical regression percentage must not be applied directly to the latest version.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-saving-launches-still-lost-time&quot;&gt;Why saving launches still lost time&lt;&#x2F;h3&gt;
&lt;p&gt;The crucial cost was bridging into fixed addresses. This implementation&#x27;s graph nodes recorded device pointers, while upstream outputs had to be moved into the graph&#x27;s fixed slots. That added &lt;strong&gt;three device-to-device copies per layer&lt;&#x2F;strong&gt;, plus their host submission overhead.&lt;&#x2F;p&gt;
&lt;p&gt;The graph covered only five nodes, limiting the dispatch gaps it could eliminate. The bridge operations, meanwhile, recurred every layer. Applying the per-node probe difference gives only about 40 microseconds of potential savings for five nodes. This is a scale estimate, not the production graph&#x27;s net benefit. The real budget must include bridging, replay, and subsequent dependency waits; complete-request measurements showed a loss.&lt;&#x2F;p&gt;
&lt;p&gt;The small MoE graph also left the long serial attention chain outside its scope. It did not reduce expert weight reads, dequantization, or inter-GPU waits. It optimized a small part of the critical path while adding fixed costs to enter that part.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;correctness-was-fixed-the-performance-problem-remained&quot;&gt;Correctness was fixed; the performance problem remained&lt;&#x2F;h3&gt;
&lt;p&gt;Earlier versions also suffered hangs, NaNs, and illegal memory accesses. Intermediate buffers used while building the graph were returned to the memory pool when construction finished, but graph nodes retained their device pointers. Once the pool reused those addresses, replay could access another object&#x27;s memory.&lt;&#x2F;p&gt;
&lt;p&gt;The isolated probe did not reproduce this because it retained its buffers throughout the test. Production execution had a different resource lifetime. The fix made the graph object retain the required buffers, keeping their addresses valid during replay. Additional synchronization temporarily hid symptoms, but long-context tests still failed: synchronization cannot repair a dangling pointer.&lt;&#x2F;p&gt;
&lt;p&gt;The negative results in the table were measured after the fix. Stable replay and lower end-to-end latency require separate validation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when-another-attempt-is-worthwhile&quot;&gt;When another attempt is worthwhile&lt;&#x2F;h3&gt;
&lt;p&gt;The production path remained at &lt;code&gt;decode_graph=false&lt;&#x2F;code&gt;. A worthwhile next experiment is a whole-layer graph covering a longer dependency chain: upstream operators write directly into fixed workspaces, eliminating extra bridge copies, and the graph explicitly retains its referenced resources. Workspace growth and memory-pool reuse must not invalidate captured pointers.&lt;&#x2F;p&gt;
&lt;p&gt;Even with broader coverage, the final test is a complete request using the same binary, input, and output-correctness requirements, with the profiler disabled. &lt;strong&gt;The acceptance metric is token latency—not node count or replay-probe speed.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-two-gpu-parallelism-did-not-come-close-to-doubling-speed&quot;&gt;Why two-GPU parallelism did not come close to doubling speed&lt;&#x2F;h2&gt;
&lt;p&gt;After optimizing the single-GPU path, we revisited paired execution. The new &lt;code&gt;parallel_operator_pairs&lt;&#x2F;code&gt; path partitions MLA by query heads and MoE by intermediate dimension, merging results at larger attention and FFN boundaries.&lt;&#x2F;p&gt;
&lt;p&gt;We also changed the exchange format. The owner quantizes KV once, transmitting only newly appended Q8 latent values, scales, and RoPE information. Per-token, per-layer KV exchange fell from 2,304 B to 656 B, a 71.5% reduction. Each side recomputes the router to avoid another routing-table handoff.&lt;&#x2F;p&gt;
&lt;p&gt;The non-MTP results are:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Execution&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Decode (tokens&#x2F;s)&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Latency per token&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;One GPU per stage, no intra-layer pairing&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.321&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;About 88.3 ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Two-GPU operator pairs, median of three runs&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;13.968, about 14&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;About 71.6 ms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The paired median is approximately 23.4% faster than 11.321 tokens&#x2F;s, reducing latency by about 16.7 milliseconds per token. This compares different versions, rather than a strict same-version parallelism toggle. Profiles explain why the gain is far below ideal halving.&lt;&#x2F;p&gt;
&lt;p&gt;First, &lt;strong&gt;splitting query heads did not split the historical scan&lt;&#x2F;strong&gt;. Both GPUs still gathered Top-2048 positions and scanned the complete logical KV. The paired MLA scope took approximately 0.35–0.45 ms&#x2F;layer versus 0.35–0.36 ms&#x2F;layer on one GPU, providing essentially no speedup.&lt;&#x2F;p&gt;
&lt;p&gt;Second, splitting large weight operations did not halve the entire dependency chain. Norms, routing, DSA selection, some shared-expert work, and merges remained. The complete FFN&#x2F;MoE section became only about 30%–40% faster.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, attention and MoE each exchanged partial results in both directions every layer. Peer-copy kernels alone totaled about 3.8 ms&#x2F;token across 78 layers, with additional costs from events, joins, and unequal progress.&lt;&#x2F;p&gt;
&lt;p&gt;These bottlenecks come from profiles of an earlier paired version. They explain the optimization direction, not the exact breakdown of the latest &lt;code&gt;da1e40be&lt;&#x2F;code&gt; version. In those profiles, raw inter-node TCP transmission took only tens of microseconds per token. Most losses arose from layer-by-layer collaboration within a node. Reducing message size alone was insufficient; duplicate scans and synchronization waits mattered more.&lt;&#x2F;p&gt;
&lt;p&gt;Amdahl&#x27;s law limits overall acceleration when serial work remains, and added communication further reduces the gain. Two busy GPUs do not imply that the token critical path has been cut in half.&lt;&#x2F;p&gt;
&lt;p&gt;Further work aims to make the owner hold the canonical hidden state, reduce partials toward it in one direction, and remove reverse exchanges and duplicate residual work. Partitioning DSA&#x2F;MLA across history would then allow the GPUs to scan different portions of that history. The next performance step requires changing the division of data and work.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mtp-saves-full-model-execution-rounds&quot;&gt;MTP saves full-model execution rounds&lt;&#x2F;h2&gt;
&lt;p&gt;Once ordinary decode reaches about 88.3 milliseconds per token, shaving off a few more microseconds has diminishing returns. MTP takes another approach: propose several candidate tokens, then verify them with the target model so that one expensive verification round can commit several valid tokens.&lt;&#x2F;p&gt;
&lt;p&gt;Autoregressive semantics remain intact. Candidate prefixes turn some sequential work into multi-row verification. The benefit depends on accepted tokens per round and the combined cost of drafting, verification, rollback, and state catch-up.&lt;&#x2F;p&gt;
&lt;p&gt;Amd-1&#x27;s &lt;code&gt;paired-mtp5-g2-50k-d1024-r1.json&lt;&#x2F;code&gt; records an actual paired-plus-MTP run: 46,152 input tokens, 1,024 output tokens, 40.475 seconds of decode, and &lt;strong&gt;25.275 tokens&#x2F;s&lt;&#x2F;strong&gt;. The configuration uses &lt;code&gt;parallel_operator_pairs=true&lt;&#x2F;code&gt;, draft depth 5, and verify group rows 2. Runtime logs confirm that MTP L78 also uses an operator pair.&lt;&#x2F;p&gt;
&lt;p&gt;The corresponding complete-request log records &lt;strong&gt;315 target rounds&lt;&#x2F;strong&gt;, &lt;strong&gt;1,567 verified candidates&lt;&#x2F;strong&gt;, and &lt;strong&gt;709 accepted candidates&lt;&#x2F;strong&gt;, for a &lt;strong&gt;45.25% acceptance rate&lt;&#x2F;strong&gt;. That is approximately &lt;strong&gt;3.25 committed tokens per target round&lt;&#x2F;strong&gt; under the log&#x27;s accounting, or 1,024 &#x2F; 315. Accepted counts by draft depth were 241, 179, 130, 92, and 67, showing declining useful output at deeper candidate positions.&lt;&#x2F;p&gt;
&lt;p&gt;Other complete runs on the same general path reached 23.793 tokens&#x2F;s with depth 3 &#x2F; group 2, and 20.647 tokens&#x2F;s with depth 5 &#x2F; group 1. Verification organization affects end-to-end behavior, but because settings and request nonces differ, we do not attribute the entire difference to one factor.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;25.275 is one complete measurement of an older paired configuration. It is neither a result for MTP on the latest &lt;code&gt;da1e40be&lt;&#x2F;code&gt; paired version nor a stable multi-run baseline.&lt;&#x2F;strong&gt; The 1,024-token request completed before a later rocprof-attached diagnostic run appeared in the log. We do not mix those profiler results into this throughput figure. The latest paired version still requires separate validation of MTP speed and correctness.&lt;&#x2F;p&gt;
&lt;p&gt;The next priority is not simply increasing draft depth. Multi-row verification should share weight reads across projections, MLA, and MoE. The cheaper each extra verification row becomes, the more valuable additional accepted candidates are.&lt;&#x2F;p&gt;
&lt;p&gt;Correctness must include state. After a mismatch, target KV, DSA selection, and MTP caches must return to the correct position. After accepting a prefix, the corresponding caches must catch up. Throughput is meaningful only when these states remain consistent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-4-bit-today-room-for-5-6-bit-but-not-fp8&quot;&gt;Why 4-bit today: room for 5&#x2F;6-bit, but not FP8&lt;&#x2F;h2&gt;
&lt;p&gt;We currently choose 4-bit quantization to preserve VRAM headroom for long contexts, MTP, and multi-GPU execution. &lt;strong&gt;Four bits is the starting point for this deployment; five or six bits are possible upgrades. Our assessment of this execution path is that they can improve model quality noticeably with essentially no performance loss.&lt;&#x2F;strong&gt; For quality-sensitive use, that is more attractive than pushing weight precision lower.&lt;&#x2F;p&gt;
&lt;p&gt;Increasing bit width does not increase end-to-end time proportionally. Single-request decode also includes DSA, attention, kernel submission, inter-GPU synchronization, and sampling. Low-bit formats themselves incur codebook, scale, sign-handling, and dequantization costs. More weight traffic at 5&#x2F;6-bit therefore does not directly imply the same percentage increase in token latency. This assessment applies to this model, hardware, and execution path; this article does not present separate 5&#x2F;6-bit speed or quality measurements.&lt;&#x2F;p&gt;
&lt;p&gt;FP8 crosses a different boundary: &lt;strong&gt;the current hardware cannot meet the memory budget of the complete deployment described here.&lt;&#x2F;strong&gt; The high-quality 4-bit checkpoint is approximately 362 GiB. Roughly doubling weight storage gives about 724 GiB at 8-bit, against 768 GiB of physical VRAM across all 16 GPUs. The remaining approximately 44 GiB must hold long-context KV, DSA state, MTP, peer replicas, workspaces, HIP runtime allocations, and fragmentation. That is insufficient for this deployment. This is a capacity-and-headroom conclusion, not a claim that an API cannot express FP8.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Weight format&lt;&#x2F;th&gt;&lt;th&gt;Choice and assessment for this project&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;4-bit&lt;&#x2F;td&gt;&lt;td&gt;Current path; preserves context and execution headroom&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5&#x2F;6-bit&lt;&#x2F;td&gt;&lt;td&gt;Possible upgrade with little expected performance loss and noticeable quality gains on this path; requires recalculating memory headroom&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;FP8&lt;&#x2F;td&gt;&lt;td&gt;Current hardware cannot meet the full deployment&#x27;s VRAM budget&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Execution does not require every tensor to use the same bit width. Routed experts currently use IQ3_S&#x2F;IQ4_XS, suitable projections retain W8A16, KV uses Q8G64, and LM Head uses Q8G128. A precision upgrade should still choose formats per operator and validate quality on the intended tasks.&lt;&#x2F;p&gt;
&lt;p&gt;Whether using four, five, or six bits, the hot path should consume compressed weights directly instead of keeping an expanded F32 representation resident. Additional memory should improve model quality, rather than pay for unnecessary intermediate representations.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keep-measuring-complete-requests&quot;&gt;Keep measuring complete requests&lt;&#x2F;h2&gt;
&lt;p&gt;A historical cooperative prefill path has an archived &lt;strong&gt;1331.62 tokens&#x2F;s&lt;&#x2F;strong&gt; result under the 50K test setting. It is a different execution configuration and must not be combined with current decode figures as if they came from one run. The next goal is to complete multi-row operators and improve chunk and stage scheduling toward 1500 tokens&#x2F;s, validated through complete-prompt time to first token.&lt;&#x2F;p&gt;
&lt;p&gt;For decode, the next step is to revalidate MTP on the latest paired version: check output and cache state first, then measure acceptance, multi-row verification cost, and complete-request throughput across repeated runs. The older configuration&#x27;s 25.275 tokens&#x2F;s provides experimental evidence, but 30+ remains a target. Gains from different versions cannot simply be multiplied.&lt;&#x2F;p&gt;
&lt;p&gt;The latest notes also report effective activity above 70% during a GPU&#x27;s local stage execution window. That does not mean all 16 GPUs sustain 70% utilization throughout single-request decode, and it is not interchangeable with hardware occupancy. Local work is more compact; end-to-end timing still determines whether the user waits less.&lt;&#x2F;p&gt;
&lt;p&gt;We retained direct W8 consumption because it reduces weight reads, and rejected experiments whose better microbenchmarks did not improve the complete chain. The standard remains the same: use a reference to check values and state, use profiling to locate waits, then disable the profiler and measure complete requests with matching inputs and settings.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The path every token must finish is the thing we ultimately have to optimize.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Sources and accounting: algorithms and historical experiments follow the project notes updated on September 5, 2026. Current speeds were cross-checked against original JSON files, configurations, and logs on Amd-1. The complete requests cited below were inspected, not rerun for this article.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Path &#x2F; result file&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Input tokens&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Output tokens&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Decode (tokens&#x2F;s)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Single-GPU stage: &lt;code&gt;c343909e-current-routepair-no-mtp-strict1024-r1.json&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;46,158&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.321&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Paired: &lt;code&gt;paired-directjoin-da1e40be-formal-no-mtp.json&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;46,153&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;14.043&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Paired recheck: &lt;code&gt;paired-directjoin-da1e40be-formal-recheck-no-mtp.json&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;46,153&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;13.842&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Paired recheck: &lt;code&gt;paired-directjoin-da1e40be-formal-recheck-no-mtp-r2.json&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;46,153&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;13.968&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Older paired + MTP: &lt;code&gt;paired-mtp5-g2-50k-d1024-r1.json&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;46,152&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,024&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;25.275&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The files are in &lt;code&gt;&#x2F;workspace&#x2F;zllm-kv-eval-run&#x2F;runs&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;results&#x2F;&lt;&#x2F;code&gt; on Amd-1. Their base-corpus SHA-256 matches, while nonces are not all identical. The three latest paired runs have identical output SHA-256 values. This establishes output consistency across those runs, not an independent model-quality evaluation. Some runner &lt;code&gt;topology&lt;&#x2F;code&gt; fields retained a “16 serial stages” template; runtime logs confirm four owner-peer pairs per node, or eight logical stages across both nodes, for paired execution.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The Algorithm Layer: From Attention and KV Cache to FFN and MoE</title>
        <published>2026-09-04T00:00:00+00:00</published>
        <updated>2026-09-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/algorithm-layer/"/>
        <id>https://zhuai.tech/en/blog/algorithm-layer/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/algorithm-layer/">&lt;p&gt;The algorithm layer answers not “which GPU runs this?” but “what result should this operation
produce mathematically?” In zLLM it begins with device-independent specifications, dataflow,
and CPU &lt;code&gt;f32&lt;&#x2F;code&gt; references. The CPU oracle provides a readable and testable correctness anchor;
Metal, CUDA, ROCm, Vulkan, and NPU kernels are accelerated implementations of the same
semantics.&lt;&#x2F;p&gt;
&lt;p&gt;This distinction matters. A fast kernel may change layouts, fuse operations, lower precision,
or submit work in parallel, but it must not silently change the causal mask, RoPE, Top-K
routing, KV write position, or numerical definition. New algorithms first establish shapes,
boundaries, and results in CPU&#x2F;reference code, then device implementations are compared against
that oracle. Passing an operator oracle establishes local semantic agreement; complete
prefill&#x2F;decode execution still has to validate output and performance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-from-a-token-to-the-next-token&quot;&gt;1. From a token to the next token&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;algorithm-layer&#x2F;thinking-flow.en.svg&quot; alt=&quot;The algorithm flow from tokenization and vectorization through attention and FFN to the next token&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A Transformer layer can be compared to one structured step of thought, but the analogy is not
a biological claim. “Angle, shape, vision, semantics, history, and relationships” are not six
predefined slots. After training, such features are distributed across directions in a
high-dimensional representation, and an individual coordinate rarely has one stable human
interpretation.&lt;&#x2F;p&gt;
&lt;p&gt;The complete path is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Tokenization.&lt;&#x2F;strong&gt; The tokenizer maps text or multimodal placeholders to token IDs. A token
may be a word, subword, byte fragment, or special symbol; it need not equal one written word.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Vectorization.&lt;&#x2F;strong&gt; An embedding table maps each discrete ID to a &lt;code&gt;hidden_size&lt;&#x2F;code&gt;-dimensional
vector. Positional encoding then lets attention distinguish order and relative position.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Layer-by-layer transformation.&lt;&#x2F;strong&gt; Each layer uses attention to collect relevant information
from current and historical tokens, then an FFN or MoE transforms each token representation.
Normalization and residual connections run throughout the block.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Output.&lt;&#x2F;strong&gt; Final normalization and the LM head project hidden state to vocabulary logits.
Sampling or greedy selection chooses a token, and decode feeds it into the next round until
EOS or a length limit is reached.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;what-expansion-matrix-multiplication-merging-and-reduction-mean&quot;&gt;What expansion, matrix multiplication, merging, and reduction mean&lt;&#x2F;h3&gt;
&lt;p&gt;For one hidden row &lt;code&gt;x ∈ R^d&lt;&#x2F;code&gt;, a linear layer is fundamentally &lt;code&gt;y = xW&lt;&#x2F;code&gt;. Matrix multiplication
forms weighted combinations of the old coordinates in a new coordinate system. It may project
&lt;code&gt;d&lt;&#x2F;code&gt; dimensions into a wider &lt;code&gt;d_ff&lt;&#x2F;code&gt; space, form Q&#x2F;K&#x2F;V, or map concatenated head outputs back to
&lt;code&gt;d&lt;&#x2F;code&gt; dimensions.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Expansion&lt;&#x2F;strong&gt; does not create facts from nothing. It supplies a wider intermediate workspace
in which gates and nonlinearities can express more feature combinations.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Multiple heads&lt;&#x2F;strong&gt; project the same hidden state into several subspaces. Heads may learn
locality, semantics, position, reference, or visual relations, but no rule says “head 3 is
shape.”&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Merging&lt;&#x2F;strong&gt; usually means a weighted sum of values followed by concatenation across heads. It
aggregates information rather than copying the source sentence.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reduction&lt;&#x2F;strong&gt; uses an output projection or FFN down projection to return to &lt;code&gt;hidden_size&lt;&#x2F;code&gt;, so
the result can join the residual stream and enter the next layer. It is learned projection,
not truncating the last few coordinates.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A layer&#x27;s “thinking” is therefore better described as alternating cross-token retrieval in
attention with per-token feature transformation in the FFN. Depth provides successive revisions
of the representation; width provides the workspace available to each revision.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-attention-choosing-what-information-to-read-now&quot;&gt;2. Attention: choosing what information to read now&lt;&#x2F;h2&gt;
&lt;p&gt;Classic scaled dot-product attention is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Attention(Q,K,V) = softmax(QKᵀ &#x2F; √dₖ + mask)V&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Q asks “what am I looking for now?”, K is the index under which each piece of history can be
found, and V is the content retrieved after a match. A causal mask prevents token N from seeing
the future. &lt;a href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1706.03762&quot;&gt;“Attention Is All You Need”&lt;&#x2F;a&gt; replaced the recurrent
backbone common in earlier sequence models with global self-attention, but modern models now use
several compute&#x2F;storage trade-offs.&lt;&#x2F;p&gt;
&lt;p&gt;These techniques are not all forms of “gated grouping.” They differ in where compression occurs:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Design&lt;&#x2F;th&gt;&lt;th&gt;Core idea&lt;&#x2F;th&gt;&lt;th&gt;Primarily reduces&lt;&#x2F;th&gt;&lt;th&gt;zLLM mapping&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;MHA, multi-head attention&lt;&#x2F;td&gt;&lt;td&gt;Every Q head has its own K&#x2F;V head&lt;&#x2F;td&gt;&lt;td&gt;Baseline; expressive but KV-heavy&lt;&#x2F;td&gt;&lt;td&gt;General multi-head geometry and block&#x2F;reference semantics&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MQA&lt;&#x2F;td&gt;&lt;td&gt;All Q heads share one K&#x2F;V pair&lt;&#x2F;td&gt;&lt;td&gt;KV cache and K&#x2F;V bandwidth&lt;&#x2F;td&gt;&lt;td&gt;The &lt;code&gt;num_kv_heads = 1&lt;&#x2F;code&gt; limit of GQA&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;GQA, grouped-query attention&lt;&#x2F;td&gt;&lt;td&gt;A group of Q heads shares one K&#x2F;V head&lt;&#x2F;td&gt;&lt;td&gt;KV cache, K&#x2F;V projection, bandwidth&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;gqa.rs&lt;&#x2F;code&gt;, including full&#x2F;sliding windows and hybrid layers&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MLA &#x2F; Gated MLA&lt;&#x2F;td&gt;&lt;td&gt;Compress KV into a low-rank latent and reconstruct it when needed; optionally gate output&lt;&#x2F;td&gt;&lt;td&gt;KV representation and projection cost&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;mla.rs&lt;&#x2F;code&gt;, composed by GLM-5.2, DeepSeek-V3, Kimi-K3, and other runtimes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Sliding &#x2F; block attention&lt;&#x2F;td&gt;&lt;td&gt;Read only a recent window or explicitly visible blocks&lt;&#x2F;td&gt;&lt;td&gt;Long-context attention compute&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;gqa::CausalWindow&lt;&#x2F;code&gt;, &lt;code&gt;attention&#x2F;block.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;DSA &#x2F; MSA&lt;&#x2F;td&gt;&lt;td&gt;A learned indexer or block index selects Top-K tokens&#x2F;blocks per query&lt;&#x2F;td&gt;&lt;td&gt;Long-context QK and AV work&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;dsa.rs&lt;&#x2F;code&gt;, &lt;code&gt;attention&#x2F;msa.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Compressed sparse attention&lt;&#x2F;td&gt;&lt;td&gt;Keep a recent window and pool older history before selection or full reading&lt;&#x2F;td&gt;&lt;td&gt;Distant-history storage and compute&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;compressed_sparse.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Gated DeltaNet &#x2F; KDA&lt;&#x2F;td&gt;&lt;td&gt;Recursively summarize history in short-convolution and fixed-size recurrent state&lt;&#x2F;td&gt;&lt;td&gt;Avoid a full KV cache that grows with context&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;gated_delta_net.rs&lt;&#x2F;code&gt;, &lt;code&gt;attention&#x2F;kda.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Gating, grouping, low rank, and sparse selection are four different mechanisms. A gate controls
how much information passes; GQA shares KV among query heads; MLA compresses representation;
DSA&#x2F;MSA let the current query focus on only part of history. They resemble selective human
attention: a question need not examine all memories at equal strength. Engineering still has to
ask whether selection itself scans all history—sparse output does not automatically imply sparse
computation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-kv-cache-a-reusable-representation-of-history&quot;&gt;3. KV cache: a reusable representation of history&lt;&#x2F;h2&gt;
&lt;p&gt;Autoregressive generation adds only one token per round. Recomputing K and V for every earlier
token each time would repeat a large amount of matrix multiplication. A KV cache stores the
historical K&#x2F;V representation—or equivalent state—already produced by each layer. Decode only
computes the new token, queries the saved history, and appends a new record.&lt;&#x2F;p&gt;
&lt;p&gt;It is useful to compare this with an index and contents in working memory, but it is not a
lossless copy of the original conversation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;GQA stores fewer KV heads; MHA&#x2F;GQA capacity still generally grows linearly with token count.&lt;&#x2F;li&gt;
&lt;li&gt;MLA stores a normalized low-rank latent plus a RoPE component and reconstructs the required
representation when read.&lt;&#x2F;li&gt;
&lt;li&gt;zLLM&#x27;s MLA cache supports &lt;code&gt;F16&lt;&#x2F;code&gt; and also defines a layout with per-group INT8 latent values
while retaining RoPE in F16. Quantization saves capacity at the cost of measurable error.&lt;&#x2F;li&gt;
&lt;li&gt;Sliding windows retain only the effective window; DSA&#x2F;MSA also maintain indexing information.&lt;&#x2F;li&gt;
&lt;li&gt;Gated DeltaNet and KDA retain fixed-size recurrent and short-convolution state. Their update
semantics differ from full-attention KV and must not be forced into one storage abstraction.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;kv_cache&#x2F;mod.rs&lt;&#x2F;code&gt; owns device-independent semantics such as logical-layer-to-slot mapping,
GQA&#x2F;MLA shapes, formats, strides, capacity, and valid length. The backend owns physical buffer
allocation, placement, quantization kernels, and synchronization. KV stays with the device that
computes its layer; when multi-node execution partitions consecutive complete layers, per-layer
KV does not travel across the network.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-ffn-feature-processing-inside-each-layer&quot;&gt;4. FFN: feature processing inside each layer&lt;&#x2F;h2&gt;
&lt;p&gt;Attention answers “what should I retrieve from other tokens?” The FFN answers “how should this
token transform the information it now has?” A mainstream gated MLP can be summarized as:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;y = W_down(act(xW_gate) ⊙ (xW_up))&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Gate and up projections expand hidden state into a wider intermediate space; activation and
elementwise multiplication make a nonlinear selection; down projection returns to hidden size.
zLLM&#x27;s &lt;code&gt;moe&#x2F;dense_mlp.rs&lt;&#x2F;code&gt; preserves this device-independent dataflow. Its current activation
specifications cover SiLU, clamped SiLU, SiTU, OpenAI-style SwiGLU, and GELU-Tanh, while the
backend capability implements the matrix operations.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dense-ffn-and-moe&quot;&gt;Dense FFN and MoE&lt;&#x2F;h3&gt;
&lt;p&gt;In a dense FFN, the complete gate&#x2F;up&#x2F;down weight set participates for every token. It is casually
described as “all experts working in a layer,” but in source terms a dense MLP is one complete
network block, not a collection of pre-existing experts that were all selected.&lt;&#x2F;p&gt;
&lt;p&gt;MoE instead provides many expert FFNs. A router scores them and activates only the Top-K routed
experts for each token; shared experts may always run. This resembles the idea that different
brain regions become active for different tasks: total parameter capacity can be large while
active computation per token remains smaller. The analogy explains sparse activation only; it
does not mean Transformer experts map to fixed brain regions or possess named personalities.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM divides FFN&#x2F;MoE responsibility as follows:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Module&lt;&#x2F;th&gt;&lt;th&gt;Responsibility&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;dense_mlp.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Dense gated-MLP specification, activations, and &lt;code&gt;gate&#x2F;up → activation → down&lt;&#x2F;code&gt; dataflow&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;routing.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Routing scores, Top-K, grouped assignments, and active-expert accounting&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;topk_moe.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Routed&#x2F;shared expert composition, post-routing scaling, and output accumulation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;latent_moe.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Route from original hidden state while routed experts consume a low-dimensional latent; shared MLP still reads original hidden&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;prefill.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Grouped expert execution and merging for multi-token prefill&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;expert_predictor.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Predict later experts from real route history for asynchronous prefetch; owns neither weights nor I&#x2F;O&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;MoE primarily reduces computation per token; it does not automatically reduce total model
weights. High-throughput, low-latency deployments generally keep many or all expert weights
resident in memory&#x2F;VRAM, so memory usage remains close to that of the complete MoE model. The
router saves FLOPs but does not remove unselected experts from the checkpoint.&lt;&#x2F;p&gt;
&lt;p&gt;When capacity is tight and performance requirements are lower, cold experts can reside in CPU
memory or on SSD and be streamed after routing. zLLM accordingly places expert source, prefetch,
and prediction feedback at backend&#x2F;runtime boundaries. This introduces I&#x2F;O latency, prefetch hit
rate, concurrent working-set, and jitter problems. Streaming trades residency for acceptable
latency only when loading genuinely overlaps current-layer compute and prediction hits often
enough.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-a-map-of-zllm-s-algorithm-modules&quot;&gt;5. A map of zLLM&#x27;s algorithm modules&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Directory&#x2F;module&lt;&#x2F;th&gt;&lt;th&gt;What it defines&lt;&#x2F;th&gt;&lt;th&gt;What it does not own&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;mod.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Device-independent entry point for attention-family specifications&lt;&#x2F;td&gt;&lt;td&gt;Device buffers or command submission&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;rope.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Positional rotation, layouts, and reference behavior&lt;&#x2F;td&gt;&lt;td&gt;Model tokenization&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;gqa.rs&lt;&#x2F;code&gt;, &lt;code&gt;mla.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;GQA&#x2F;MLA geometry, windows, projection relationships, and f32 references&lt;&#x2F;td&gt;&lt;td&gt;Platform-specific fused kernels&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;dsa.rs&lt;&#x2F;code&gt;, &lt;code&gt;msa.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Token&#x2F;block scoring, causal Top-K, and sparse-selection semantics&lt;&#x2F;td&gt;&lt;td&gt;A claim that every selection path is inherently O(K)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;compressed_sparse.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Recent window, compressed history, visibility, and selection plans&lt;&#x2F;td&gt;&lt;td&gt;Physical placement of compression buffers&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;gated_delta_net.rs&lt;&#x2F;code&gt;, &lt;code&gt;kda.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Recurrent&#x2F;conv state shapes, update semantics, and references&lt;&#x2F;td&gt;&lt;td&gt;Pretending this state is ordinary KV cache&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;hybrid.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Combined full-attention and linear&#x2F;recurrent layer state&lt;&#x2F;td&gt;&lt;td&gt;Device-placement policy&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;attention&#x2F;hyper_connection.rs&lt;&#x2F;code&gt;, &lt;code&gt;attn_res.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Multi-residual-stream and attention-residual semantics&lt;&#x2F;td&gt;&lt;td&gt;HTTP sessions or cross-node transport&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;moe&#x2F;*&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Dense FFN, Top-K&#x2F;latent MoE, routing, shared experts, and prefetch feedback semantics&lt;&#x2F;td&gt;&lt;td&gt;SSD&#x2F;VRAM allocation and DMA&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;kv_cache&#x2F;*&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Logical layouts, formats, capacity, valid length, and persistence boundaries&lt;&#x2F;td&gt;&lt;td&gt;Platform allocation, synchronization, and kernels&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;runtime&#x2F;prefill.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Generic chunk, batch, stage, and complete-layer loops&lt;&#x2F;td&gt;&lt;td&gt;A specific model&#x27;s layer order&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;runtime&#x2F;generation.rs&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Token generation, EOS, and position-advance lifecycle&lt;&#x2F;td&gt;&lt;td&gt;Attention&#x2F;FFN mathematics&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;runtime&#x2F;&amp;lt;model&amp;gt;&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Compose embedding, per-layer attention + FFN, normalization, LM head from model specifications&lt;&#x2F;td&gt;&lt;td&gt;Duplicating backend algorithms&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Production execution has only three complete task forms: &lt;code&gt;NewPrefill&lt;&#x2F;code&gt; creates a session and its
cache, &lt;code&gt;AppendPrefill&lt;&#x2F;code&gt; appends a token span after existing history, and &lt;code&gt;DecodeRound&lt;&#x2F;code&gt; consumes one
token, runs every layer, appends state, and produces the next token. The algorithm layer defines
the correct order, runtime composes a complete model, backend manages resources and submission,
and kernels accelerate local computation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;6-how-the-cpu-oracle-protects-correctness&quot;&gt;6. How the CPU oracle protects correctness&lt;&#x2F;h2&gt;
&lt;p&gt;The CPU oracle is valuable not because “the CPU can just about run the model,” but because it
turns complex device optimization into testable problems:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Use small but real shapes to verify post-tokenization positions, masks, RoPE, matrix
dimensions, and cache append behavior.&lt;&#x2F;li&gt;
&lt;li&gt;Compare deterministic CPU &lt;code&gt;f32&lt;&#x2F;code&gt; references with device kernels. Check shape, finite values,
absolute&#x2F;relative error, and discrete results such as sparse indices and MoE expert IDs.&lt;&#x2F;li&gt;
&lt;li&gt;Compare hidden state, routing, and logits layer by layer to find where error first grows.&lt;&#x2F;li&gt;
&lt;li&gt;Run prefill&#x2F;decode regressions on fixed token sequences, including cache boundaries, appended
prefixes, and long context.&lt;&#x2F;li&gt;
&lt;li&gt;Finally run the complete model task. Successful compilation, a passing operator oracle,
fixed-token agreement, and acceptable on-device performance are four distinct states.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Tolerance must follow dtype, quantization method, and error propagation; one generous global
threshold is not correctness. Top-K routing, causal selection, cache length, and commit position
often require exact equality. Evidence-based &lt;code&gt;atol&#x2F;rtol&lt;&#x2F;code&gt; belongs on F16&#x2F;BF16&#x2F;quantized matrix
outputs. Even a kernel that passes its local oracle may hurt end-to-end performance through extra
conversion, synchronization, or unsuitable shapes, so complete prefill&#x2F;decode throughput, peak
memory, and stability still need validation.&lt;&#x2F;p&gt;
&lt;p&gt;That is the role of zLLM&#x27;s algorithm layer: not another device implementation, but the
mathematical contract shared by every implementation. CPU references make the contract
executable and regression-testable; attention&#x2F;KV define how history is read, FFN&#x2F;MoE define how
the current representation is processed, and runtime composes them into one complete,
verifiable generation.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Quantization Is Not a Zip File: Model Layers and the Reality of 4-bit Inference</title>
        <published>2026-09-03T00:00:00+00:00</published>
        <updated>2026-09-03T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/model-layer-quantization/"/>
        <id>https://zhuai.tech/en/blog/model-layer-quantization/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/model-layer-quantization/">&lt;p&gt;LLM quantization is often reduced to one sentence: turn 16-bit weights into 4-bit weights and the model becomes four times smaller. That is true, but it omits nearly every difficult part.&lt;&#x2F;p&gt;
&lt;p&gt;Quantization first solves a &lt;strong&gt;capacity problem&lt;&#x2F;strong&gt;: the weights do not fit in VRAM or unified memory. It also offers a possible second benefit—&lt;strong&gt;higher speed&lt;&#x2F;strong&gt;. Decode is frequently memory-bandwidth-bound, and the weights must be read again for every token. Yet a smaller file does not automatically run faster. If the runtime expands 4-bit weights to F16&#x2F;F32, or the device lacks a packed kernel, much of the capacity and bandwidth advantage disappears.&lt;&#x2F;p&gt;
&lt;p&gt;Quantization is therefore not a file-conversion option. It is an execution path spanning model specification, weight assembly, device residency, kernels, and quality validation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-start-with-layers-model-architecture-and-weight-encoding-are-different-concerns&quot;&gt;1. Start with layers: model architecture and weight encoding are different concerns&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM does not put everything behind one opaque &lt;code&gt;Model&lt;&#x2F;code&gt; object:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;model_spec&#x2F;&amp;lt;model&amp;gt;  architecture constants: layers, hidden size, attention, MoE
&lt;&#x2F;span&gt;&lt;span&gt;runtime&#x2F;&amp;lt;model&amp;gt;     LayerSpec expansion and complete prefill&#x2F;decode orchestration
&lt;&#x2F;span&gt;&lt;span&gt;weight&#x2F;model        checkpoint names, shape validation, model assembly
&lt;&#x2F;span&gt;&lt;span&gt;weight&#x2F;format       byte layouts such as FP8, W4A16, and GGUF K-quants
&lt;&#x2F;span&gt;&lt;span&gt;weight&#x2F;codec        IO-free reference decoding and layout conversion
&lt;&#x2F;span&gt;&lt;span&gt;backend + kernel    residency, format dispatch, and direct device computation
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The important consequence is that &lt;strong&gt;model architecture is not weight encoding&lt;&#x2F;strong&gt;. The same linear layer may come from BF16, FP8, NVFP4, W4A16, or Q4_K. The format layer interprets bytes, the model adapter knows names and shapes, runtime describes dataflow, and the backend selects a kernel according to device capability.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;model-quantization&#x2F;format-paths.en.svg&quot; alt=&quot;Common numeric encodings and the W4A16&#x2F;W4A8 execution paths&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-what-quantization-actually-solves&quot;&gt;2. What quantization actually solves&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;capacity-comes-first&quot;&gt;Capacity comes first&lt;&#x2F;h3&gt;
&lt;p&gt;Ignoring scales, metadata, and tensors retained at higher precision, an N-parameter model needs approximately:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;BF16 &#x2F; FP16: 2N bytes
&lt;&#x2F;span&gt;&lt;span&gt;INT8:        1N bytes
&lt;&#x2F;span&gt;&lt;span&gt;INT4:        0.5N bytes
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A 70B model is therefore about 140 GB in BF16 and ideally 35 GB at 4-bit. Real Q4_K_M files are larger because block scales, minima, alignment, and protected tensors also consume space. Quantization only shrinks the data it covers: KV cache, activations, scratch buffers, and runtime memory still need separate budgets.&lt;&#x2F;p&gt;
&lt;p&gt;On discrete GPUs this determines whether weights fit in VRAM. On Apple UMA it determines whether the model, KV cache, OS, and application can coexist without severe memory pressure. Eliminating a traditional host-to-VRAM staging copy does not make capacity infinite.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;moving-fewer-bytes-can-make-decode-faster&quot;&gt;Moving fewer bytes can make decode faster&lt;&#x2F;h3&gt;
&lt;p&gt;Single-token decode is dominated by GEMV-like operations with little weight reuse. If weights stay packed and a kernel decodes each local block directly into accumulation, 4-bit storage offers close to four times the effective weight density of BF16.&lt;&#x2F;p&gt;
&lt;p&gt;That speedup requires a native device kernel, low enough decode&#x2F;scale&#x2F;activation overhead, and end-to-end measurement. In one real zLLM Qwen3.8 CPU path, packed Q4_K AVX2&#x2F;FMA dot products reduced a 16-token decode from 107.483 to 7.578 seconds, and paired gate&#x2F;up traversal reached 7.183 seconds. This roughly 15× change combined removal of repeated F32 expansion with specialized vectorization; it is &lt;strong&gt;not&lt;&#x2F;strong&gt; a universal 4-bit-versus-BF16 ratio. Conversely, a Gemma 4 Q4_K dual-output kernel passed its CPU oracle but reduced end-to-end throughput from 35.9 to 35.5 tok&#x2F;s and was reverted.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-the-core-idea-local-structure-not-global-rounding&quot;&gt;3. The core idea: local structure, not global rounding&lt;&#x2F;h2&gt;
&lt;p&gt;Naive quantization shares one range across an entire matrix:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;q = round(w &#x2F; scale)
&lt;&#x2F;span&gt;&lt;span&gt;w_hat = scale * q
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Outliers stretch that range and leave too few useful grid points for ordinary values. Modern weight quantization therefore makes three local decisions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Block locality.&lt;&#x2F;strong&gt; Groups of 32, 64, 128, or 256 weights receive their own scale. Smaller groups fit local distributions better but add metadata and kernel cost.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Tensor and channel sensitivity.&lt;&#x2F;strong&gt; Attention value&#x2F;output projections, FFN down projections, embeddings, and the LM head need not tolerate the same error. AWQ similarly uses activation statistics to identify and protect salient weights.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Layer locality.&lt;&#x2F;strong&gt; Error propagates through depth: a hidden-state shift changes later routing, attention, and token ranking. Layerwise calibration controls accumulated error better than one global conversion. MoE calibration must also exercise a representative expert-routing distribution.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;model-quantization&#x2F;scale-layouts.en.svg&quot; alt=&quot;Per-tensor, per-channel, group-wise, and GGUF K-quant scale hierarchies&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-to-read-common-gguf-quantization-names&quot;&gt;How to read common GGUF quantization names&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;GGUF is a container, not a quantization algorithm.&lt;&#x2F;strong&gt; It stores metadata, tokenizer data, a tensor directory, and tensors that may independently use F32, F16, Q4_K, Q6_K, IQ4_XS, and other types. A &lt;code&gt;Q4_K_M&lt;&#x2F;code&gt; filename does not mean every tensor uses an identical 4-bit block.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;model-quantization&#x2F;gguf-formats.en.svg&quot; alt=&quot;Common GGUF quantization families from aggressive compression to high fidelity&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Family&lt;&#x2F;th&gt;&lt;th&gt;Common names&lt;&#x2F;th&gt;&lt;th&gt;Structure and use&lt;&#x2F;th&gt;&lt;th&gt;Practical guidance&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Classic blocks&lt;&#x2F;td&gt;&lt;td&gt;Q4_0, Q5_0, Q8_0&lt;&#x2F;td&gt;&lt;td&gt;Integer codes plus a block scale; simple and widely implemented&lt;&#x2F;td&gt;&lt;td&gt;Q4_0 favors compatibility; Q8_0 is a useful quality baseline or activation format&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;K-quants&lt;&#x2F;td&gt;&lt;td&gt;Q3_K, Q4_K, Q5_K, Q6_K&lt;&#x2F;td&gt;&lt;td&gt;Sub-block scales&#x2F;minima inside 256-weight super-blocks&lt;&#x2F;td&gt;&lt;td&gt;Start with Q4_K_M; use Q5_K_M&#x2F;Q6_K when memory permits&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;I-quants&lt;&#x2F;td&gt;&lt;td&gt;IQ2_&lt;em&gt;, IQ3_&lt;&#x2F;em&gt;, IQ4_NL, IQ4_XS&lt;&#x2F;td&gt;&lt;td&gt;Importance-aware or nonlinear codebooks&lt;&#x2F;td&gt;&lt;td&gt;Strong quality per bit, but verify native backend kernels&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Mixed recipes&lt;&#x2F;td&gt;&lt;td&gt;Q3_K_S&#x2F;M&#x2F;L, Q4_K_S&#x2F;M, Q5_K_S&#x2F;M, UD-*&lt;&#x2F;td&gt;&lt;td&gt;Different tensor types according to sensitivity&lt;&#x2F;td&gt;&lt;td&gt;Read the tensor directory; never infer support from the filename alone&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;code&gt;S&#x2F;M&#x2F;L&lt;&#x2F;code&gt; usually means Small, Medium, or Large mixed-precision policy, not three integer widths. &lt;code&gt;IQ4_XS&lt;&#x2F;code&gt; and &lt;code&gt;Q4_K_M&lt;&#x2F;code&gt; also are not interchangeable: the former uses nonlinear&#x2F;codebook ideas, while the latter uses hierarchical K-quant scales. A custom name such as &lt;code&gt;UD-Q4_K_XL&lt;&#x2F;code&gt; may mix Q4_K, Q5_K, Q6_K, and IQ4_XS across layers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-why-4-bit-is-often-the-sweet-spot&quot;&gt;4. Why 4-bit is often the sweet spot&lt;&#x2F;h2&gt;
&lt;p&gt;The sweet spot is where three curves meet: capacity falls sharply, devices can still decode efficiently, and quality has not entered the steep decline often seen at 2–3 bits.&lt;&#x2F;p&gt;
&lt;p&gt;For Mistral 7B on WikiText-2 at context 512, one same-method llama.cpp comparison reported:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Format&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Perplexity&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Increase over FP16&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Q3_K_S&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;6.0021&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.44%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Q3_K_M&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.8489&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2.75%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Q4_K_S&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.7349&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.75%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Q4_K_M&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.7259&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.59%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Q5_K_S&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.7100&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.31%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Moving from Q3_K_M to Q4_K_M cuts the relative increase from 2.75% to 0.59%; moving again to Q5_K_S only reaches 0.31% while consuming more memory bandwidth. Llama 33B results show a similar curve: FP16, Q6_K, Q5_K_M, Q4_K_M, and Q3_K_M scored 4.1557, 4.1598, 4.1675, 4.2081, and 4.3594 respectively.&lt;&#x2F;p&gt;
&lt;p&gt;These numbers establish a trend only for that model, dataset, context, and quantizer. Perplexity is not synonymous with chat quality, code correctness, or long-reasoning stability. Small local errors can flip tokens near a decision boundary or change MoE Top-K routing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;5-why-models-can-be-quantized-at-all&quot;&gt;5. Why models can be quantized at all&lt;&#x2F;h2&gt;
&lt;p&gt;Neural networks are not bit-exact symbolic programs. Many parameters form redundant statistical representations, while residual connections, normalization, and overparameterization tolerate small perturbations. Quantization exploits that redundancy.&lt;&#x2F;p&gt;
&lt;p&gt;Generation is probabilistic as well. Logits pass through softmax, temperature, top-k&#x2F;top-p, and sampling. At nonzero temperature, identical weights can produce different answers; even greedy decoding can flip when two logits are extremely close.&lt;&#x2F;p&gt;
&lt;p&gt;This does not mean errors are harmless. Open-ended generation often distinguishes good from better rather than one exact string, so quality must be evaluated statistically and by task. Mathematics, code, tool arguments, and JSON schemas still have hard correctness boundaries.&lt;&#x2F;p&gt;
&lt;p&gt;A quantized model may occasionally score above the floating-point baseline. Quantization noise may break an existing bad preference, but finite samples, sampling variance, and evaluator noise can do the same. Only repeated, multi-seed, multi-dataset results with uncertainty justify a claim of genuine improvement.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;6-what-training-and-calibration-compensate-for&quot;&gt;6. What training and calibration compensate for&lt;&#x2F;h2&gt;
&lt;p&gt;Post-training quantization (PTQ) uses representative samples to estimate scales, clipping, channel importance, or Hessian approximations. GPTQ compensates layerwise for induced weight error; AWQ uses activations to protect salient weights. The target is not merely “each weight remains close,” but “the layer output remains close on real inputs.”&lt;&#x2F;p&gt;
&lt;p&gt;Quantization-aware training (QAT) simulates rounding and clipping during training so parameters adapt to the low-precision grid. It costs more but becomes increasingly important for 3&#x2F;2-bit weights, activation quantization, and sensitive tasks. Calibration data must match deployment: short prose is a poor calibration set for long reasoning, code, or broad MoE expert routing.&lt;&#x2F;p&gt;
&lt;p&gt;Weight, activation, and KV-cache quantization are separate choices. W4A16 keeps activations at 16-bit; W4A8 adds dynamic-range and fused-kernel constraints; KV quantization changes long-context capacity and continuously perturbs attention. Calling all three a “4-bit model” hides essential information.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;7-fences-constrain-structure-they-do-not-manufacture-correctness&quot;&gt;7. Fences constrain structure; they do not manufacture correctness&lt;&#x2F;h2&gt;
&lt;p&gt;Three engineering fences are needed:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Operator fence:&lt;&#x2F;strong&gt; reference codecs, CPU oracles, layerwise hidden&#x2F;logit comparisons, and real shape validation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Generation fence:&lt;&#x2F;strong&gt; token-level JSON Schema, tool-name, closing-tag, stop-token constraints, plus an independent repetition guard.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Quality fence:&lt;&#x2F;strong&gt; greedy regression prompts, perplexity, task suites, long contexts, multi-seed evaluation, and human review for critical tasks.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;A generation fence can guarantee allowed structure, not correct semantics. A JSON repair pass must not hide tool-selection drift. Chain-of-thought should not be judged only by verbatim equality either: visible reasoning is sampling-sensitive and is not the model&#x27;s internal computation. Evaluate final answers and tool outcomes, key intermediate constraints, finish reasons, loop rate, length distribution, and success-rate changes under the same decoding policy.&lt;&#x2F;p&gt;
&lt;p&gt;Short greedy prompts can produce a dangerous false pass. Small logit shifts accumulate over thousands of reasoning tokens. Long tests should track correctness, semantic restarts, repetition, premature EOS, and pathological long tails. Fences prevent protocol disasters; evaluations discover semantic regression.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;8-engineering-pitfalls&quot;&gt;8. Engineering pitfalls&lt;&#x2F;h2&gt;
&lt;p&gt;“Support” must be split into four states:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;text&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-text &quot;&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;&lt;span&gt;file can be parsed
&lt;&#x2F;span&gt;&lt;span&gt;≠ CPU reference can decode it
&lt;&#x2F;span&gt;&lt;span&gt;≠ device has a packed direct kernel
&lt;&#x2F;span&gt;&lt;span&gt;≠ model uses it in the default end-to-end path
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Packed data must remain packed until the kernel. Expanding it to F32 at load time erases the memory and bandwidth advantage. The desired lifecycle is container lookup → format interpretation → name&#x2F;shape validation → backend residency → block-local decode and accumulation.&lt;&#x2F;p&gt;
&lt;p&gt;Do not forget non-weight memory. KV cache, prefill activations, MoE working sets, vision encoders, logits, and command buffers can become the new peak. Measure TTFT, prefill and decode throughput, peak memory, SSD wait, GPU utilization, and thermal steady state. Finally, “the output looks fluent” is not correctness: validate codecs, operator oracles, layerwise differences, fixed-token regressions, and full task suites.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Quantization reallocates a budget among precision, capacity, bandwidth, and compute. Four bits is often attractive because it crosses the capacity threshold, retains near-baseline statistical quality, and maps well to packed device kernels. It is not a magic number independent of model, data, hardware, and task.&lt;&#x2F;p&gt;
&lt;p&gt;A trustworthy deployment answers four questions: what is quantized at what granularity; whether the device consumes the packed representation directly; whether speed improves end to end; and whether quality fences cover long reasoning, structured output, and real tasks. Only then does “the model runs” become “the model is deployable.”&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2210.17323&quot;&gt;GPTQ&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2306.00978&quot;&gt;AWQ&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ggml-org&#x2F;llama.cpp&#x2F;discussions&#x2F;4364&quot;&gt;llama.cpp Mistral 7B quantization comparison&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ggml-org&#x2F;llama.cpp&#x2F;discussions&#x2F;406&quot;&gt;llama.cpp K-quant perplexity records&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ggml-org&#x2F;llama.cpp&#x2F;blob&#x2F;master&#x2F;tools&#x2F;quantize&#x2F;quantize.cpp&quot;&gt;llama.cpp quantization formats&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Decoupling: Let Models, Algorithms, Backends, and Kernels Evolve Independently</title>
        <published>2026-09-01T00:00:00+00:00</published>
        <updated>2026-09-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/zllm-decoupled-architecture/"/>
        <id>https://zhuai.tech/en/blog/zllm-decoupled-architecture/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/zllm-decoupled-architecture/">&lt;p&gt;First drafted on 2026-08-28; rewritten on 2026-09-01. This article follows the current
&lt;code&gt;model_spec&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;runtime&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;backend&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;kernel&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;weight&#x2F;&lt;&#x2F;code&gt;, and &lt;code&gt;server&#x2F;&lt;&#x2F;code&gt; source trees.
It describes boundaries that exist in the implementation rather than presenting a generic
planner or remote Expert RPC as completed architecture.&lt;&#x2F;p&gt;
&lt;p&gt;Decoupling in zLLM is not about making the directory tree look tidy. It is about making each
kind of change travel along the right axis: model architecture changes belong mainly in the
spec and runtime, new mathematical semantics enter the domain layer first, device differences
stop at the backend, and local performance work stays in kernels. A complete inference request
still uses all of these regions, but no region needs to know every implementation below it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;1-the-architecture-at-a-glance&quot;&gt;1. The Architecture at a Glance&lt;&#x2F;h2&gt;
&lt;p&gt;The main dependency direction runs from top to bottom. Weight formats form a side data path:
&lt;code&gt;model_spec&lt;&#x2F;code&gt; supplies shapes, &lt;code&gt;weight&lt;&#x2F;code&gt; parses containers and encodings, and the backend selects
the final resident representation. This side path does not control model execution.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;zllm-decoupling&#x2F;architecture.en.png&quot; alt=&quot;zLLM decoupled architecture: platform backends and parameterized kernels are peer tracks inside device implementation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The five-layer spine and the device implementation region are responsibility boundaries, not
six runtime processes. Inside device implementation, &lt;code&gt;backend&#x2F;&amp;lt;platform&amp;gt;&lt;&#x2F;code&gt; and
&lt;code&gt;kernel&#x2F;&amp;lt;platform&amp;gt;&lt;&#x2F;code&gt; are peers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;the platform backend owns tensors, weights, caches, placement, submission, and transfer;&lt;&#x2F;li&gt;
&lt;li&gt;parameterized kernels own local computation;&lt;&#x2F;li&gt;
&lt;li&gt;together they implement the capabilities required by the upper layers.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Production requests still execute a complete New Prefill, Append Prefill, or Decode Round.
Decoupling changes code dependencies and resource ownership; it does not turn full-model
inference into disconnected partial demonstrations.&lt;&#x2F;p&gt;
&lt;p&gt;The responsibility of each region is concise:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;bin&#x2F;runtime&lt;&#x2F;code&gt; and &lt;code&gt;bin&#x2F;tools&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; compose platform entry points driven by
&lt;code&gt;--config &amp;lt;yaml&amp;gt;&lt;&#x2F;code&gt;. Model-specific validation and profiling binaries live outside the main
runtime.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;embedded&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; exposes an in-process &lt;code&gt;Engine&lt;&#x2F;code&gt; with the same structured JSON semantics as
service mode, without starting HTTP, the scheduler, or iroh.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;server&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; owns HTTP&#x2F;H3 protocols, Chat&#x2F;Anthropic&#x2F;Responses compatibility, nodes, the
scheduler, and iroh stage transport.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;model_spec&#x2F;&amp;lt;model&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; contains execution- and platform-independent architecture data. It
is the single specification shared by runtime orchestration and weight assembly.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;runtime&#x2F;&amp;lt;model&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; expands layer specs and owns platform-independent full-model
orchestration. Platform composition files remain beside the model runtime.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;attention&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;moe&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;kv_cache&#x2F;&lt;&#x2F;code&gt;, and &lt;code&gt;norm.rs&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; contain domain specs, reusable
algorithms, and reference semantics.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;backend&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; contains capability contracts and platform resource implementations,
including cache, completion, streams or command buffers, residency, and scheduling.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;kernel&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; contains parameterized local computation. Model dimensions may not be hard
coded. ROCm HIP bodies are stored in separate &lt;code&gt;source.hip&lt;&#x2F;code&gt; files; Rust launchers load,
validate, and submit them.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;weight&#x2F;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; parses standard containers and encodings. Model-specific code is limited to
naming and shape adaptation.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;2-dependency-rules&quot;&gt;2. Dependency Rules&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;Shared scheduling such as &lt;code&gt;runtime::prefill&lt;&#x2F;code&gt; does not depend on a concrete model or backend.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;model_spec&lt;&#x2F;code&gt; contains architecture data only and does not depend on other crate modules.
Runtime and weight assembly depend on it without depending on each other.&lt;&#x2F;li&gt;
&lt;li&gt;Platform-independent code in &lt;code&gt;runtime&#x2F;&amp;lt;model&amp;gt;&#x2F;mod.rs&lt;&#x2F;code&gt; depends on capability traits. Only
adjacent platform composition files may depend on a concrete backend.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;backend&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;kernel&#x2F;&lt;&#x2F;code&gt; do not name or branch on concrete models. Cooperative two-GPU MoE
and Metal replay express device capabilities; they may not encode GLM layer numbers.&lt;&#x2F;li&gt;
&lt;li&gt;Domain modules contain specs, algorithms, and references. Concrete storage, kernels, and
synchronization belong to device implementation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;weight&#x2F;&lt;&#x2F;code&gt; parses and loads formats. Algorithms do not enter the weight tree, and platform
resources do not enter format parsing.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;A violation usually indicates dependency inversion. Any exception must be a concrete platform
composition file or a measurable production execution path. It must not be an empty flow,
factory, or protocol introduced for hypothetical reuse.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-the-three-decoupling-boundaries&quot;&gt;3. The Three Decoupling Boundaries&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Boundary&lt;&#x2F;th&gt;&lt;th&gt;Upstream expression&lt;&#x2F;th&gt;&lt;th&gt;Downstream responsibility&lt;&#x2F;th&gt;&lt;th&gt;Current constraint&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A · Model × Domain&lt;&#x2F;td&gt;&lt;td&gt;Layer dataflow and complete prefill&#x2F;decode ordering&lt;&#x2F;td&gt;&lt;td&gt;Mathematical semantics for attention, MoE, KV, and their references&lt;&#x2F;td&gt;&lt;td&gt;Define and test new semantics before accelerating them&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;B · Domain × Capability&lt;&#x2F;td&gt;&lt;td&gt;Required capabilities declared by generic bounds&lt;&#x2F;td&gt;&lt;td&gt;Tensor&#x2F;cache&#x2F;weight residency, submission, and transfer contracts&lt;&#x2F;td&gt;&lt;td&gt;Runtime does not touch HIP events or Metal command buffers&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;C · Capability × Implementation&lt;&#x2F;td&gt;&lt;td&gt;Capability and resource-lifecycle contracts&lt;&#x2F;td&gt;&lt;td&gt;Peer platform-backend and parameterized-kernel tracks&lt;&#x2F;td&gt;&lt;td&gt;Both tracks implement capabilities; kernels contain no model names or constants&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;boundary-a-model-x-domain&quot;&gt;Boundary A: Model × Domain&lt;&#x2F;h3&gt;
&lt;p&gt;Model orchestration calls domain operations and passes a generic backend through the dataflow.
The domain describes what a computation means; device implementation decides how it runs. A
new operation such as kpool first receives a reference definition and tests. GPU kernels are
accelerated substitutes for that definition rather than independent sources of semantics.&lt;&#x2F;p&gt;
&lt;p&gt;CPU remains the primary correctness oracle, but it is not limited to that role. Current
GLM-5.2 paths also use CPU work in production for DSA selection and the DSpark drafter. The
boundary is semantic ownership, not a rule that all CPU code must be diagnostic.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;boundary-b-domain-x-capability&quot;&gt;Boundary B: Domain × Capability&lt;&#x2F;h3&gt;
&lt;p&gt;The base &lt;code&gt;Backend&lt;&#x2F;code&gt; trait contains operations shared across models. Domain capabilities such as
&lt;code&gt;KdaKernel&lt;&#x2F;code&gt;, &lt;code&gt;HyperConnectionKernel&lt;&#x2F;code&gt;, &lt;code&gt;DsaPrefillBackend&lt;&#x2F;code&gt;, and
&lt;code&gt;ExpertPrefillBackend&lt;&#x2F;code&gt; remain separate. A model&#x27;s generic bounds state exactly what it needs.
New capabilities may use explicit unsupported defaults so existing backends can adopt them
incrementally without pretending to support the new semantics.&lt;&#x2F;p&gt;
&lt;p&gt;Stage execution uses the same principle. Runtime sees completion, available resources, and
submission classes; it does not see HIP events or queue handles. The ROCm backend may choose
latency or background streams and retire completions out of order without leaking those
mechanics into model code.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;boundary-c-capability-x-device-implementation&quot;&gt;Boundary C: Capability × Device Implementation&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;backend&#x2F;&amp;lt;platform&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;kernel&#x2F;&amp;lt;platform&amp;gt;&lt;&#x2F;code&gt; sit on the same side of this boundary as peer
tracks. The backend owns resources, resident representations, shape&#x2F;format dispatch, and
submission semantics. Kernels own parameterized local computation.&lt;&#x2F;p&gt;
&lt;p&gt;Weights enter device implementation through &lt;code&gt;LinearWeight&lt;&#x2F;code&gt; variants such as F32, F16,
Bf16Bytes, FP8, MXFP8, MXFP4, NVFP4, W4A16, W8A16, and GGUF. A platform backend selects a
kernel using weight format and shape. Quantized paths decode inside the kernel instead of
expanding weights to F32. This is a collaboration relationship, not an architectural hierarchy
from platform backend down to kernel.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-key-decisions&quot;&gt;4. Key Decisions&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Decision&lt;&#x2F;th&gt;&lt;th&gt;Rationale&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Keep &lt;code&gt;model_spec&lt;&#x2F;code&gt; data-only instead of introducing a behavioral thin model layer&lt;&#x2F;td&gt;&lt;td&gt;Runtime orchestration and weight assembly share one specification without depending on each other&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Use associated &lt;code&gt;Tensor&lt;&#x2F;code&gt;, &lt;code&gt;Weight&lt;&#x2F;code&gt;, and &lt;code&gt;Cache&lt;&#x2F;code&gt; types instead of one universal object&lt;&#x2F;td&gt;&lt;td&gt;CPU values, Metal buffers, ROCm dual host&#x2F;device state, and wgpu resources are fundamentally different&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Split capability traits by domain&lt;&#x2F;td&gt;&lt;td&gt;Model dependency surfaces stay readable and new capabilities can enter incrementally&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Define reference semantics before optimized kernels&lt;&#x2F;td&gt;&lt;td&gt;Cross-backend behavior remains testable while optimization proceeds independently&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Keep the single model flow in runtime and platform composition next to it&lt;&#x2F;td&gt;&lt;td&gt;Avoid N-model × M-platform copies of the same execution algorithm&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Drive entry points through YAML configuration&lt;&#x2F;td&gt;&lt;td&gt;Keep service and tool entry points converged; validation and profiling remain separate concerns&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;5-benefits&quot;&gt;5. Benefits&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Combination cost grows linearly.&lt;&#x2F;strong&gt; A new model built from existing operations requires no
backend changes. A new operation on a new backend requires a kernel and a capability
implementation, not another full runtime.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Correctness has an anchor.&lt;&#x2F;strong&gt; Reference behavior and tests allow a backend to start with a
slower correct path and optimize later.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Platforms evolve independently.&lt;&#x2F;strong&gt; Metal replay, ROCm stage pipelines and CPU&#x2F;GPU DSA
cooperation, CUDA paths, Vulkan, and experimental NPU work can progress without changing
model semantics.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reviews have a clear target.&lt;&#x2F;strong&gt; A change to mathematical meaning, device resources, or a
local kernel has a mostly unique home.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;6-costs-and-trade-offs&quot;&gt;6. Costs and Trade-offs&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Hot semantics still need multiple implementations.&lt;&#x2F;strong&gt; Rust references, MSL, HIP, CUDA,
WGSL, and AscendC do not optimize themselves.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Generic instantiation increases compile time and binary size.&lt;&#x2F;strong&gt; Capability bounds also
become longer as models add domains.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Capability growth requires discipline.&lt;&#x2F;strong&gt; Every new architecture concept creates pressure
to add shared trait methods even when the capability is not yet general.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Platform-specific behavior must be documented.&lt;&#x2F;strong&gt; Fallbacks, shape constraints, replay,
CPU&#x2F;GPU cooperation, and multi-GPU paths differ by backend.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Peer backend and kernel tracks require careful ownership.&lt;&#x2F;strong&gt; Dispatch and resource
lifetime belong to the backend; local math belongs to the kernel. Moving either concern to
the other track makes the boundary harder to audit.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;7-series-guide&quot;&gt;7. Series Guide&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;02 · Model Layer: formats, config, weight assembly, and quantization&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;en&#x2F;blog&#x2F;algorithm-layer&#x2F;&quot;&gt;03 · Algorithm Layer: attention, MoE, KV cache, prefill, and decode orchestration&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;04 · Backend: device abstraction, resources, submission, and platform behavior&lt;&#x2F;li&gt;
&lt;li&gt;05 · Kernels: primitive and fused kernels, precision selection, and hardware mapping&lt;&#x2F;li&gt;
&lt;li&gt;06 · Service Layer: fences, protocols, input&#x2F;output, and the embedded library&lt;&#x2F;li&gt;
&lt;li&gt;07 · Multi-node: scheduling, admission, cross-node state, and stage pipelines&lt;&#x2F;li&gt;
&lt;li&gt;08 · Speculative Decoding: transactional verification, MTP, DSpark, and fences&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why Did One Sentence Consume Hundreds of Thousands of Tokens?</title>
        <published>2026-08-30T00:00:00+00:00</published>
        <updated>2026-08-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/http-api-prefix-cache-tool-fence/"/>
        <id>https://zhuai.tech/en/blog/http-api-prefix-cache-tool-fence/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/http-api-prefix-cache-tool-fence/">&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;http-token-cache&#x2F;hero-v2.png&quot; alt=&quot;A short message expands into a huge context, enters the inference core and VRAM, while evicted cache state is backed up to SSD and restored on demand&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Behind one short sentence is a huge token stream containing system instructions, tool definitions, project context, and history. Hot state stays in VRAM; evicted state is backed up to SSD and restored when needed.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You typed one sentence. Why did the usage report show tens of thousands—or even hundreds of thousands—of input tokens?&lt;&#x2F;p&gt;
&lt;p&gt;Because agents such as Claude Code and Codex do not send only the text visible in the input box. Before calling the model, the client assembles a complete working context: system and developer instructions, tool descriptions, JSON Schemas, workspace rules, conversation history, previous tool calls and results, plus file excerpts or summaries needed to continue the task. Your sentence may be only 20 tokens, but it is the final sliver of a very large prompt.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, every request must tell the model three things: &lt;strong&gt;who it is, what it can do, and what has already happened&lt;&#x2F;strong&gt;. This is not training and it does not permanently write knowledge into the model. It is context supplied for this inference request, and much of it normally has to be present again in the next request.&lt;&#x2F;p&gt;
&lt;p&gt;That is exactly why prefix caching matters. A client may resend tens of thousands of identical tokens, but a server that retained the model state for that prefix does not need to recompute it from the beginning.&lt;&#x2F;p&gt;
&lt;p&gt;At first glance, an LLM service looks like an ordinary JSON-over-HTTP application: send messages in, stream tokens out. Once it must support several client protocols, long conversations, persistent cache state, and reliable tool calls, the problem becomes much deeper than adding a few routes.&lt;&#x2F;p&gt;
&lt;p&gt;In zLLM we implemented three common interfaces—OpenAI Chat Completions, OpenAI Responses, and Anthropic Messages—plus a two-level prefix cache with VRAM as the active tier and SSD as its backup, and token-level generation fences for DeepSeek DSML tool calls. They appear to be separate features, but all enforce the same invariant: &lt;strong&gt;protocol state, the tokens seen by the model, and the execution state stored on the device must agree exactly.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-tens-of-thousands-of-tokens-behind-one-sentence&quot;&gt;The tens of thousands of tokens behind one sentence&lt;&#x2F;h2&gt;
&lt;p&gt;A typical Claude Code or Codex request contains roughly these parts:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;http-token-cache&#x2F;request-context-flow.en.svg&quot; alt=&quot;One user message is combined with system instructions, tool definitions, workspace knowledge, history, and tool results; hot prefix state resides in VRAM and is backed up to SSD&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Context component&lt;&#x2F;th&gt;&lt;th&gt;What it contains&lt;&#x2F;th&gt;&lt;th&gt;Why it may reappear every turn&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;System and developer instructions&lt;&#x2F;td&gt;&lt;td&gt;Identity, safety boundaries, output rules, coding conventions, workflow&lt;&#x2F;td&gt;&lt;td&gt;A model does not remember these rules across independent HTTP requests&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Tool definitions&lt;&#x2F;td&gt;&lt;td&gt;Names, descriptions, and parameter Schemas for shell, files, search, browser, and other tools&lt;&#x2F;td&gt;&lt;td&gt;The model must know which actions exist and how to invoke them legally&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Workspace knowledge&lt;&#x2F;td&gt;&lt;td&gt;Current directory, repository rules, AGENTS.md, environment and permissions&lt;&#x2F;td&gt;&lt;td&gt;The agent must act in the correct project under the correct constraints&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Conversation history&lt;&#x2F;td&gt;&lt;td&gt;User requests, prior reasoning, completed work, intermediate conclusions&lt;&#x2F;td&gt;&lt;td&gt;Keeps a multi-turn task coherent&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Tool calls and results&lt;&#x2F;td&gt;&lt;td&gt;Commands, file contents, errors, logs, and returned data&lt;&#x2F;td&gt;&lt;td&gt;Later decisions depend on these observations&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Current input&lt;&#x2F;td&gt;&lt;td&gt;The sentence you just typed&lt;&#x2F;td&gt;&lt;td&gt;Usually only the tail of the full prompt&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This is what people mean when they say an agent first “injects tens of K of knowledge” into the model. Here, K means a thousand tokens. Tokens are not characters: a tokenizer splits prose, punctuation, paths, source code, and JSON differently. Tool Schemas are particularly expensive because every field name, description, enum, nested object, and protocol example counts as input. With dozens of enabled tools, their declarations alone can occupy a substantial part of the context window.&lt;&#x2F;p&gt;
&lt;p&gt;The UI usually shows only the user&#x27;s messages and the final answer, making this cost easy to mistake for secret model output. In practice, usage has at least three relevant categories:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;input_tokens&lt;&#x2F;code&gt;: the complete context supplied this turn, not just the newest user message;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cached_tokens&lt;&#x2F;code&gt;: the prefix within that input whose execution state the server reused;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;output_tokens&lt;&#x2F;code&gt;: newly generated reasoning, text, or tool calls.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Billing rules differ by provider, but computationally, cached tokens are still part of the input. They really are present in the prompt; the server simply avoids repeating the same prefill work. This produces a result that looks contradictory but is not: &lt;strong&gt;a request can contain 100,000 input tokens while only the final few hundred tokens require new computation.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Without a cache hit, all 100,000 tokens must pass through every layer of the model again, increasing time to first token. With a hit, the server restores the previous state and runs append prefill only for the new user message and assistant prefix.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;three-major-apis-the-differences-go-beyond-field-names&quot;&gt;Three major APIs: the differences go beyond field names&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM exposes three primary endpoints:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;API&lt;&#x2F;th&gt;&lt;th&gt;Typical clients&lt;&#x2F;th&gt;&lt;th&gt;Main input&lt;&#x2F;th&gt;&lt;th&gt;Streaming output&lt;&#x2F;th&gt;&lt;th&gt;Continuation model&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST &#x2F;v1&#x2F;chat&#x2F;completions&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;OpenAI SDKs and general chat clients&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;messages&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Chat chunk SSE&lt;&#x2F;td&gt;&lt;td&gt;Client resends history and may supply a cache identifier&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST&#x2F;GET &#x2F;v1&#x2F;responses&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Codex, ZCode, newer agent clients&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;input&lt;&#x2F;code&gt; items&lt;&#x2F;td&gt;&lt;td&gt;Responses SSE or WebSocket events&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;previous_response_id&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;POST &#x2F;v1&#x2F;messages&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Claude Code and Anthropic SDKs&lt;&#x2F;td&gt;&lt;td&gt;top-level &lt;code&gt;system&lt;&#x2F;code&gt; plus &lt;code&gt;messages&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Anthropic event SSE&lt;&#x2F;td&gt;&lt;td&gt;Client resends history&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;All three eventually become the same internal tasks. Structured messages pass through the model&#x27;s chat template and tokenizer, followed by &lt;code&gt;NewPrefill&lt;&#x2F;code&gt;, &lt;code&gt;AppendPrefill&lt;&#x2F;code&gt;, and &lt;code&gt;DecodeRound&lt;&#x2F;code&gt;. The adapter layer, however, cannot be a collection of field renames.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;chat-completions-the-most-direct-common-representation&quot;&gt;Chat Completions: the most direct common representation&lt;&#x2F;h3&gt;
&lt;p&gt;Chat Completions centers on an ordered &lt;code&gt;messages&lt;&#x2F;code&gt; array. Tools use the OpenAI function-tool shape:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;deepseek-v4&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;messages&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [{&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;role&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Check the weather in Shanghai&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;}],
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tools&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [{
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;get_weather&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;parameters&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;object&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;properties&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;city&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;}},
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;required&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;city&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }],
&lt;&#x2F;span&gt;&lt;span&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;stream&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A non-streaming response collects text and &lt;code&gt;tool_calls&lt;&#x2F;code&gt; into one assistant message. A streaming response must keep each tool call&#x27;s &lt;code&gt;index&lt;&#x2F;code&gt;, ID, function name, and argument deltas stable. Tool-call IDs cannot be derived from the function name alone: the same function can appear in multiple requests or be called more than once in a response. zLLM hashes a request-level scope, call index, and function name together.&lt;&#x2F;p&gt;
&lt;p&gt;Chat Completions is a useful internal canonical form because roles, tools, and sampling controls are explicit. Responses and Anthropic requests are carefully converted into this representation before they reach the scheduler and runtime.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;responses-an-event-protocol-not-chat-with-different-json&quot;&gt;Responses: an event protocol, not Chat with different JSON&lt;&#x2F;h3&gt;
&lt;p&gt;The Responses API accepts a stream of &lt;code&gt;input&lt;&#x2F;code&gt; items rather than just a message array. Items may include messages, &lt;code&gt;function_call&lt;&#x2F;code&gt;, &lt;code&gt;function_call_output&lt;&#x2F;code&gt;, images, and the &lt;code&gt;additional_tools&lt;&#x2F;code&gt; prefix used by Codex Responses Lite. Those tools may be wrapped in namespaces, so conversion must expand executable functions while skipping hosted tool types that the local runtime cannot execute.&lt;&#x2F;p&gt;
&lt;p&gt;Output is not a growing &lt;code&gt;delta.content&lt;&#x2F;code&gt; string either. Text, reasoning, and function calls are separate output items with their own created, delta, and completed events. Reasoning must be exposed as a distinct &lt;code&gt;output[type=reasoning]&lt;&#x2F;code&gt; item; folding it into answer text prevents clients such as Codex from rendering and restoring state correctly.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM supports both HTTP POST with SSE and a WebSocket upgrade on &lt;code&gt;GET &#x2F;v1&#x2F;responses&lt;&#x2F;code&gt;. The latter serially reuses one connection for multiple &lt;code&gt;response.create&lt;&#x2F;code&gt; requests, which suits long-running agent sessions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;previous_response_id&lt;&#x2F;code&gt; needs special attention. zLLM persists the protocol messages and cache key of the previous response, so it can reconstruct a conversation chain after restart. But that record &lt;strong&gt;does not own model KV state&lt;&#x2F;strong&gt;. Restoring protocol history only means the complete input can be rebuilt; whether VRAM or SSD state is available remains a separate runtime decision.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;anthropic-messages-content-blocks-and-event-order-are-semantic&quot;&gt;Anthropic Messages: content blocks and event order are semantic&lt;&#x2F;h3&gt;
&lt;p&gt;Anthropic puts &lt;code&gt;system&lt;&#x2F;code&gt; at the top level and represents content as blocks. Tool definitions use &lt;code&gt;name&lt;&#x2F;code&gt;, &lt;code&gt;description&lt;&#x2F;code&gt;, and &lt;code&gt;input_schema&lt;&#x2F;code&gt;; invocations and results are &lt;code&gt;tool_use&lt;&#x2F;code&gt; and &lt;code&gt;tool_result&lt;&#x2F;code&gt; blocks. The adapter must convert these structures in both directions instead of flattening them into strings.&lt;&#x2F;p&gt;
&lt;p&gt;Streaming event order is also strict: &lt;code&gt;message_start&lt;&#x2F;code&gt;, then &lt;code&gt;content_block_start&lt;&#x2F;code&gt;, the corresponding deltas, a block stop, and finally &lt;code&gt;message_delta&lt;&#x2F;code&gt; and &lt;code&gt;message_stop&lt;&#x2F;code&gt;. Text and multiple tool calls may occupy different block indexes. Renaming Chat SSE fields produces incorrectly ordered or unclosed blocks.&lt;&#x2F;p&gt;
&lt;p&gt;Claude CLI also calls &lt;code&gt;&#x2F;v1&#x2F;messages&#x2F;count_tokens&lt;&#x2F;code&gt; to decide when to compact the context. Even an estimated count must include system content, messages, and tools. Counting only user prose delays compaction far beyond the intended threshold.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-a-cache-hit-actually-means&quot;&gt;What a cache hit actually means&lt;&#x2F;h2&gt;
&lt;p&gt;In an inference service, a cache hit does not mean “we found a similar question,” and it does not mean “return the old answer.” Its precise meaning is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The beginning of the new request&#x27;s token sequence is exactly equal, token for token, to a saved session. The runtime can restore every layer&#x27;s KV and auxiliary DSA&#x2F;MTP state at the end of that prefix and continue from there.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Suppose the saved terminal state represents:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;[system][tools][user-1][assistant-1]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The next request is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;[system][tools][user-1][assistant-1][user-2][assistant-prefix]
&lt;&#x2F;span&gt;&lt;span&gt;|&amp;lt;----------- cached_tokens -----------&amp;gt;|&amp;lt;-- append prefill --&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The first &lt;code&gt;cached_tokens&lt;&#x2F;code&gt; do not run through the full Transformer again. Only the appended suffix receives prefill before decoding begins. A hit reduces repeated prefill and time to first token; it does not eliminate decoding for the new answer.&lt;&#x2F;p&gt;
&lt;p&gt;Several consequences follow:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Tokens must match; visually similar text is insufficient.&lt;&#x2F;strong&gt; Whitespace, JSON serialization, tool-schema order, thinking controls, templates, and special tokens can all fork the prefix.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;A cache ID is a lookup hint, not proof of correctness.&lt;&#x2F;strong&gt; The runtime still verifies the token prefix and namespace, falling back to a longest-prefix search or cold prefill if they disagree.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The cached length must be shorter than the new prompt.&lt;&#x2F;strong&gt; A complete terminal state without appended tokens is not a valid append boundary.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;A hit does not imply residency.&lt;&#x2F;strong&gt; The matching state may be in VRAM or backed up on SSD. Both avoid recomputing the prefix, but restore latency differs greatly.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Terminal-cache identity includes not only user-visible messages, but also model and template-related request state, assistant output, and structured tool calls. Otherwise, two requests that look identical on screen could share incompatible execution state.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-vram-needs-an-ssd-backup-tier&quot;&gt;Why VRAM needs an SSD backup tier&lt;&#x2F;h2&gt;
&lt;p&gt;Long-context KV is expensive. Agent conversations with tens or hundreds of thousands of tokens benefit enormously from retaining historical branches, but VRAM cannot grow without bound. A pure LRU that simply discards old sessions makes a recently evicted long conversation restart from zero.&lt;&#x2F;p&gt;
&lt;p&gt;The actual hierarchy is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Request arrives
&lt;&#x2F;span&gt;&lt;span&gt;  ├─ exact cache ID resident in VRAM ─────────┐
&lt;&#x2F;span&gt;&lt;span&gt;  ├─ longest matching prefix in VRAM ─────────┤
&lt;&#x2F;span&gt;&lt;span&gt;  ├─ exact cache ID backed up on SSD ─────────┤→ restore to VRAM → append prefill → decode
&lt;&#x2F;span&gt;&lt;span&gt;  ├─ longest matching prefix on SSD ──────────┤
&lt;&#x2F;span&gt;&lt;span&gt;  └─ no match → new prefill in VRAM ──────────┘
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;VRAM: active primary cache, smallest capacity, lowest latency
&lt;&#x2F;span&gt;&lt;span&gt;  │
&lt;&#x2F;span&gt;&lt;span&gt;  ├── eviction backup ──&amp;gt; SSD
&lt;&#x2F;span&gt;&lt;span&gt;  └── on-demand restore &amp;lt;── SSD
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;SSD: larger backup store; it never serves as a parallel inference destination
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;level-one-resident-terminal-state-in-vram&quot;&gt;Level one: resident terminal state in VRAM&lt;&#x2F;h3&gt;
&lt;p&gt;After a successful request, the engine retains the terminal session: token sequence, namespace, parent and round relationship, and model-private execution state. The common cache layer owns entries, prefix graphs, and eviction; each model runtime defines its own KV, compressed rings, DSpark target cache, and other state.&lt;&#x2F;p&gt;
&lt;p&gt;Admission must use a real residency budget, not a limit such as “keep N sessions.” Sessions with similar token counts can differ by several times in allocated bytes due to fixed and stepped costs: recent&#x2F;compressed rings, batch scratch, speculative blocks, and target caches. zLLM accounts using actual &lt;code&gt;allocated_bytes&lt;&#x2F;code&gt; and reserves room for KV growth during decode.&lt;&#x2F;p&gt;
&lt;p&gt;The cache is not a flat LRU list. Agent conversations branch from shared history, so the memory tier maintains a shared prefix block graph. Parents may be reused by several descendants, while only dead branches are released. A prefix-round limit prevents every intermediate terminal state from remaining resident forever.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-two-ssd-backup-and-restore&quot;&gt;Level two: SSD backup and restore&lt;&#x2F;h3&gt;
&lt;p&gt;When VRAM evicts a terminal state worth retaining, the runtime downloads and encodes the model state into a versioned SSD snapshot. The index records cache ID, complete token sequence, namespace, round, head status, resident byte count, and blob generation; large KV data is stored in chunks.&lt;&#x2F;p&gt;
&lt;p&gt;Writes need a commit boundary. The manifest switches to a new generation only after its blob is complete, so a crash cannot leave an index pointing to a half-written cache. Reads treat disk snapshots as untrusted input: version, lengths, counts, and trailing bytes are validated before allocation.&lt;&#x2F;p&gt;
&lt;p&gt;On an SSD hit, the engine first obtains a reusable device session, uploads each stage cache, restores DSpark and other auxiliary state, and only then continues computation in VRAM. SSD is therefore a backup capacity tier, not another compute path.&lt;&#x2F;p&gt;
&lt;p&gt;SSD also supports longest-prefix lookup. Matching remains strict: &lt;code&gt;tokens.starts_with(snapshot.tokens)&lt;&#x2F;code&gt;, isolated by namespace. To diagnose almost-hits, zLLM logs snapshots sharing at least 1,024 tokens and more than 90% of the old prefix. Those reports often reveal a template, tool echo, or serialization fork immediately.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-hardest-cache-problems-we-encountered&quot;&gt;The hardest cache problems we encountered&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;eviction-order-and-admission-deadlock&quot;&gt;Eviction order and admission deadlock&lt;&#x2F;h3&gt;
&lt;p&gt;An early implementation tried to reserve memory for a new request before evicting old cache entries. A request could be rejected even though reclaimable state existed. The correct order is to identify disposable resident entries, finish any required backup and release, and only then perform admission—while still reserving capacity for decode growth.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;asynchronous-restore-still-needs-explicit-dependencies&quot;&gt;Asynchronous restore still needs explicit dependencies&lt;&#x2F;h3&gt;
&lt;p&gt;SSD reads, CPU decoding, and multi-GPU host-to-device uploads can run concurrently, but their dependencies cannot disappear. We encountered restore jobs writing cache buffers after a compute stream had begun reading them, as well as cache-open commands whose resources were reused before draining. The fix was not a blanket global synchronization. Each stage publishes a restore-completion event; independent I&#x2F;O and uploads overlap, while the first consumer waits on the exact dependency it needs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reusable-pools-can-still-leak-capacity&quot;&gt;Reusable pools can still leak capacity&lt;&#x2F;h3&gt;
&lt;p&gt;If every SSD restore creates a new session, old device buffers may accumulate indefinitely in a “reusable” pool without actually being reused. Restore must take and overwrite a pooled session first. Failure and cancellation paths must reset and return sessions just as carefully as the success path.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tool-echoes-are-a-frequent-cache-chain-breakpoint&quot;&gt;Tool echoes are a frequent cache-chain breakpoint&lt;&#x2F;h3&gt;
&lt;p&gt;A tool call travels through four forms: native model text, structured API call, client execution, and re-encoded next-turn history. If forward parsing and history rendering are not inverse operations, the next turn forks at the tool position. DSML continuation failures we saw came from differences in tags, string-versus-JSON parameter encoding, and case normalization.&lt;&#x2F;p&gt;
&lt;p&gt;Each &lt;code&gt;ToolDialect&lt;&#x2F;code&gt; therefore owns four related operations: instruction injection, tool-call history rendering, tool-result rendering, and model-output parsing. They must come from the same protocol implementation rather than being scattered between HTTP adapters and backends.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tool-call-fences-why-parsing-after-generation-is-not-enough&quot;&gt;Tool-call fences: why parsing after generation is not enough&lt;&#x2F;h2&gt;
&lt;p&gt;Prompting a model to “return valid JSON” is not a guarantee. It may invent a tool, repeat a parameter, omit a required field, emit a closing tag inside JSON, or fall into a loop after long reasoning. Parsing the final output can only report failure after the decode work has already been wasted.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM therefore implements tool support in three layers:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Protocol prompting and history rendering&lt;&#x2F;strong&gt; encode a common function schema into GLM XML, ChatML JSON, or DeepSeek DSML.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Incremental parsing&lt;&#x2F;strong&gt; separates visible text from structured tool regions without leaking half a tag to the client.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Token-level generation fences&lt;&#x2F;strong&gt; force or exclude candidates before sampling so structured regions can follow only valid paths.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The third layer is the actual fence. For a named DeepSeek DSML tool, the state machine is:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;fixed tool_calls&#x2F;invoke prefix
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;choose one unseen parameter
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;generate its value under JSON Schema
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;close parameter; are all required fields present?
&lt;&#x2F;span&gt;&lt;span&gt;        ├─ no: another parameter is required
&lt;&#x2F;span&gt;&lt;span&gt;        └─ yes: invoke&#x2F;tool_calls may close
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The fence forces fixed tags, the tool name, and parameter names. &lt;code&gt;JsonSchemaFence&lt;&#x2F;code&gt; determines legal value tokens. A parameter can appear only once, required fields must be complete before closing, markup-opening tokens are excluded inside strings, and terminal tokens remain illegal while the structure is unfinished.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;tool_choice=auto&lt;&#x2F;code&gt; is harder. The model must remain free while writing ordinary text; the fence activates only after detecting the DSML trigger. Several tool candidates may remain possible, so their state machines advance in parallel and incompatible branches are discarded. The allowed next-token set is the union of surviving branches. Forcing a tool from the beginning would incorrectly turn &lt;code&gt;auto&lt;&#x2F;code&gt; into &lt;code&gt;required&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The fence sits between the output head and sampling. Ordinary decode, every batch row, MTP or DSpark draft generation, and speculative verification must all use the same request-level fence state. Missing one path creates the classic failure mode: tools are valid without speculative decoding and intermittently malformed when it is enabled.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tool-fence-bugs-that-taught-us-the-most&quot;&gt;Tool-fence bugs that taught us the most&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;tokenizer-boundaries-are-not-string-boundaries&quot;&gt;Tokenizer boundaries are not string boundaries&lt;&#x2F;h3&gt;
&lt;p&gt;A closing tag may span tokens or share a special &lt;code&gt;&amp;lt;&#x2F;&lt;&#x2F;code&gt; token with other text. A fence cannot assume one character or one tag per token. At initialization, all literals are encoded with the active model tokenizer and closing boundaries are verified. An incompatible tokenizer causes fence construction to fail early instead of entering an unreachable generation state.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;auto-branches-require-a-union-not-the-wrong-intersection&quot;&gt;Auto branches require a union, not the wrong intersection&lt;&#x2F;h3&gt;
&lt;p&gt;Different candidate tools may permit different next tokens. A token can be excluded only when every surviving branch rejects it. Naively merging excluded sets kills valid branches too early. After each generated token, branches incompatible with that token must also be removed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;draft-tokens-need-a-cloned-state-advanced-one-token-at-a-time&quot;&gt;Draft tokens need a cloned state advanced one token at a time&lt;&#x2F;h3&gt;
&lt;p&gt;Speculative decoding proposes several tokens at once. The current fence cannot validate the whole batch as one unit, and drafts must not mutate committed state. zLLM clones the guard, computes a fence for each position, advances the probe token by token, and advances the real state only for the accepted prefix. Positions already forced by the grammar can bypass unnecessary logit work.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;parser-patches-are-not-a-substitute-for-generation-constraints&quot;&gt;Parser patches are not a substitute for generation constraints&lt;&#x2F;h3&gt;
&lt;p&gt;We added compatibility for case differences, escaped DSML tags, and stray closing tags. Parser tolerance helps with existing model behavior, but predictable structural errors should still be prevented before sampling. Otherwise a non-streaming result may appear repaired after the fact while a streaming client has already received the malformed fragment.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;syntax-fences-and-loop-protection-must-compose&quot;&gt;Syntax fences and loop protection must compose&lt;&#x2F;h3&gt;
&lt;p&gt;Tool grammar prevents invalid structure; it does not stop a model from repeating its reasoning indefinitely. zLLM&#x27;s &lt;code&gt;GenerationGuard&lt;&#x2F;code&gt; detects repeated tokens, repeated patterns, and evidence-backed semantic restarts. It excludes the known looping successor before the next sample; if a speculative batch already contains a loop, it truncates accepted rows and may force the reasoning-end token. Tool grammar and loop protection combine into one &lt;code&gt;TokenFence&lt;&#x2F;code&gt;, but remain separate state machines so model protocol logic does not contaminate the general generation layer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-complete-path-of-one-request&quot;&gt;The complete path of one request&lt;&#x2F;h2&gt;
&lt;p&gt;Putting the APIs, cache hierarchy, and tool fence together, a real request follows this path:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;Chat &#x2F; Responses &#x2F; Anthropic request
&lt;&#x2F;span&gt;&lt;span&gt;        ↓  validation and normalization
&lt;&#x2F;span&gt;&lt;span&gt;common messages + function tools
&lt;&#x2F;span&gt;&lt;span&gt;        ↓  model dialect, template, tokenizer
&lt;&#x2F;span&gt;&lt;span&gt;complete prompt tokens + cache identity
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;exact&#x2F;longest prefix in VRAM → exact&#x2F;longest backup on SSD → cold start
&lt;&#x2F;span&gt;&lt;span&gt;        ↓                                │
&lt;&#x2F;span&gt;&lt;span&gt;        └──────── restore to VRAM ───────┘
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;NewPrefill or AppendPrefill
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;output head → request token fence → sampling&#x2F;speculative verification
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;incremental native tool-stream parser
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;Chat chunks &#x2F; Responses events &#x2F; Anthropic content blocks
&lt;&#x2F;span&gt;&lt;span&gt;        ↓
&lt;&#x2F;span&gt;&lt;span&gt;successful terminal state remains resident in VRAM;
&lt;&#x2F;span&gt;&lt;span&gt;evicted state is backed up to SSD
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The HTTP layer does not own KV. The cache layer does not understand model syntax. The backend does not know tool names. The model runtime does not emit a particular external SSE format. Each layer owns only the semantics it needs, but passes verifiable state across its boundary rather than a vague “session ID.”&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-final-test&quot;&gt;The final test&lt;&#x2F;h2&gt;
&lt;p&gt;A prefix cache is not correct because a log line says &lt;code&gt;hit=true&lt;&#x2F;code&gt;. Restored tokens, positions, per-layer KV, and auxiliary state must match a full prefill. Performance is measured by real time to first token, not only by the number of skipped tokens.&lt;&#x2F;p&gt;
&lt;p&gt;A tool implementation is not correct merely because a final string passes a JSON parser once. Streaming event order, history replay, next-turn cache continuation, batch decode, and speculative paths must all obey the same constraints.&lt;&#x2F;p&gt;
&lt;p&gt;The lesson is simple: &lt;strong&gt;API compatibility does not end when the JSON looks similar. A cache hit does not end when the ID matches. Structured generation does not end when malformed output can be repaired afterward.&lt;&#x2F;strong&gt; Reliable inference ultimately comes down to the exact tokens seen by the model and the exact state held by the device.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>MacBook Air M5: Run a Multimodal LLM with Zero Runtime Dependencies</title>
        <published>2026-08-29T00:00:00+00:00</published>
        <updated>2026-08-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/macbook-air-m5-native-multimodal/"/>
        <id>https://zhuai.tech/en/blog/macbook-air-m5-native-multimodal/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/macbook-air-m5-native-multimodal/">&lt;blockquote&gt;
&lt;p&gt;zLLM requires no Python, PyTorch, Conda, Docker, or resident model server. End users receive one native executable and model weights; Rust developers can put the same engine directly inside their own process.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;On a 10-core MacBook Air M5 with 24 GB of unified memory, the current release build of &lt;code&gt;zllm-metal&lt;&#x2F;code&gt; is 8.1 MB. Add the Q4_K_M Gemma 4 E4B model and its vision projector, and this small program can run multi-turn chat and image understanding directly through Metal.&lt;&#x2F;p&gt;
&lt;p&gt;“Zero dependencies” here means &lt;strong&gt;zero external framework dependencies at deployment and runtime&lt;&#x2F;strong&gt;. There is no Python environment, PyTorch dynamic library, or separately managed C++ inference runtime. Model weights remain required data, and source builds naturally compile zLLM&#x27;s Rust crates and macOS system bindings through Cargo.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;two-interfaces-one-inference-path&quot;&gt;Two interfaces, one inference path&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM offers both a library and a command-line interface. They share the same model Runtime, Metal Backend, Kernels, weight loading, KV cache, and generation state machine.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Interface&lt;&#x2F;th&gt;&lt;th&gt;Best for&lt;&#x2F;th&gt;&lt;th&gt;Starts a service?&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;zllm::embedded::Engine&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Rust desktop apps, tools, and services&lt;&#x2F;td&gt;&lt;td&gt;No; it runs in-process&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;zllm-metal&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Immediate local chat and image understanding&lt;&#x2F;td&gt;&lt;td&gt;No; it is one native CLI&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Library mode is not an HTTP client wrapper. &lt;code&gt;Engine&lt;&#x2F;code&gt; owns the model and KV cache directly. Requests use familiar OpenAI Chat JSON semantics, while generated tokens arrive through a Rust callback.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;embed-zllm-in-a-rust-program&quot;&gt;Embed zLLM in a Rust program&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The zLLM source is now public on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;zllm-lab&#x2F;zllm&quot;&gt;GitHub&lt;&#x2F;a&gt;.&lt;&#x2F;strong&gt; Use the Git dependency directly, or clone it and switch to a local &lt;code&gt;path&lt;&#x2F;code&gt; during development.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Add zLLM to &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt; with the source path available on your machine:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[dependencies]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;zllm &lt;&#x2F;span&gt;&lt;span&gt;= { &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;zllm-lab&#x2F;zllm&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;serde_json &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Load the engine once and call &lt;code&gt;generate&lt;&#x2F;code&gt; repeatedly:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;std::io::{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, Write};
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;serde_json::json;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;use &lt;&#x2F;span&gt;&lt;span&gt;zllm::embedded::Engine;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span&gt;() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; engine = Engine::from_config(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;gemma4-metal.yaml&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)?;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; cancellation = engine.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cancellation&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result = engine.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;generate&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;amp;json!({
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;gemma4&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;messages&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [{
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;role&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Explain in one sentence why an inference engine belongs in-process&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;            }],
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;max_completion_tokens&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;128
&lt;&#x2F;span&gt;&lt;span&gt;        }),
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;amp;cancellation,
&lt;&#x2F;span&gt;&lt;span&gt;        |&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_token&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;| {
&lt;&#x2F;span&gt;&lt;span&gt;            print!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{text}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;            io::stdout().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;flush&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;is_ok&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;    )?;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\n&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;finish=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; tokens=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        result.finish_reason, result.completion_tokens);
&lt;&#x2F;span&gt;&lt;span&gt;    Ok(())
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Return &lt;code&gt;false&lt;&#x2F;code&gt; from the callback or call &lt;code&gt;cancellation.cancel()&lt;&#x2F;code&gt; from another thread to stop generation promptly. &lt;code&gt;GenerationResult&lt;&#x2F;code&gt; also contains token counts, the finish reason, and a &lt;code&gt;cache_id&lt;&#x2F;code&gt; for subsequent turns. Application code never manages Metal command buffers or platform KV objects.&lt;&#x2F;p&gt;
&lt;p&gt;Image input uses the same interface with ordered multimodal content parts:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; cancellation = engine.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cancellation&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result = engine.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;generate&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;amp;json!({
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;gemma4&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;messages&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [{
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;role&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;user&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;                {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;Describe this image&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;},
&lt;&#x2F;span&gt;&lt;span&gt;                {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;image_url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;image_url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: {
&lt;&#x2F;span&gt;&lt;span&gt;                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;absolute&#x2F;path&#x2F;to&#x2F;photo.png&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;                }}
&lt;&#x2F;span&gt;&lt;span&gt;            ]
&lt;&#x2F;span&gt;&lt;span&gt;        }],
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;max_completion_tokens&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;256
&lt;&#x2F;span&gt;&lt;span&gt;    }),
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;amp;cancellation,
&lt;&#x2F;span&gt;&lt;span&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_token&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;| {
&lt;&#x2F;span&gt;&lt;span&gt;        print!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{text}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;    },
&lt;&#x2F;span&gt;&lt;span&gt;)?;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Local paths, HTTP(S) URLs, and &lt;code&gt;data:&lt;&#x2F;code&gt; URLs share the same image materialization and vision-encoding path. Gemma 4 E4B&#x27;s &lt;code&gt;mmproj&lt;&#x2F;code&gt; loads lazily on the first image request, so text-only startup does not pay the vision-tower cost.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;get-gemma-4-e4b&quot;&gt;Get Gemma 4 E4B&lt;&#x2F;h2&gt;
&lt;p&gt;This guide uses the instruction-tuned Gemma 4 E4B release:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;huggingface.co&#x2F;unsloth&#x2F;gemma-4-E4B-it-GGUF&quot;&gt;Hugging Face: unsloth&#x2F;gemma-4-E4B-it-GGUF&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;modelscope.cn&#x2F;models&#x2F;unsloth&#x2F;gemma-4-E4B-it-GGUF&quot;&gt;ModelScope: unsloth&#x2F;gemma-4-E4B-it-GGUF&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Download these two files into the same directory:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;models&#x2F;gemma-4-E4B-it-GGUF&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;├── gemma-4-E4B-it-Q4_K_M.gguf   # main model, about 4.98 GB
&lt;&#x2F;span&gt;&lt;span&gt;└── mmproj-F16.gguf               # vision encoder&#x2F;projector, about 990 MB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With the Hugging Face CLI:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;hf&lt;&#x2F;span&gt;&lt;span&gt; download unsloth&#x2F;gemma-4-E4B-it-GGUF \
&lt;&#x2F;span&gt;&lt;span&gt;  gemma-4-E4B-it-Q4_K_M.gguf mmproj-F16.gguf \
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;  --local-dir&lt;&#x2F;span&gt;&lt;span&gt; .&#x2F;models&#x2F;gemma-4-E4B-it-GGUF
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The ModelScope page above exposes the same filenames for networks where it is faster. Q4_K_M is a balanced choice for this 24 GB MacBook Air; retaining the roughly 1 GB F16 vision projector favors image-understanding quality.&lt;&#x2F;p&gt;
&lt;p&gt;The minimal &lt;code&gt;gemma4-metal.yaml&lt;&#x2F;code&gt; configuration is:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;version&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;standalone
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;listen&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;127.0.0.1:8000
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;public_base_url&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:8000
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;directory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;artifacts
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;node&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cache_directory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;cache&#x2F;gemma4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;persist_kv_cache&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;max_concurrency&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;model&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;architecture&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;gemma4
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;weights_directory&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;.&#x2F;models&#x2F;gemma-4-E4B-it-GGUF&#x2F;gemma-4-E4B-it-Q4_K_M.gguf
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;lm_head_quantization&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;native
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;max_sequence_length&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;49152
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;execution&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;prefill_chunk_size&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2048
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;backend&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;kind&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;metal
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;device&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;default
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;an-8-mb-metal-command-line-program&quot;&gt;An 8 MB Metal command-line program&lt;&#x2F;h2&gt;
&lt;p&gt;For immediate use, no configuration file or server is needed. Clone the public repository and build the native CLI:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span&gt; clone https:&#x2F;&#x2F;github.com&#x2F;zllm-lab&#x2F;zllm.git
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cd&lt;&#x2F;span&gt;&lt;span&gt; zllm
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span&gt; build&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt; --release --bin&lt;&#x2F;span&gt;&lt;span&gt; zllm-metal
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then pass the model directly to the release executable:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;.&#x2F;target&#x2F;release&#x2F;zllm-metal &lt;&#x2F;span&gt;&lt;span&gt;\
&lt;&#x2F;span&gt;&lt;span&gt;  .&#x2F;models&#x2F;gemma-4-E4B-it-GGUF&#x2F;gemma-4-E4B-it-Q4_K_M.gguf
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;zllm-metal&lt;&#x2F;code&gt; automatically:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;detects Gemma 4 from GGUF metadata;&lt;&#x2F;li&gt;
&lt;li&gt;discovers a sibling &lt;code&gt;mmproj-*.gguf&lt;&#x2F;code&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;enables MTP speculative decoding when a matching sibling &lt;code&gt;mtp-*.gguf&lt;&#x2F;code&gt; exists;&lt;&#x2F;li&gt;
&lt;li&gt;derives a KV budget from unified memory, weight size, and a safety margin;&lt;&#x2F;li&gt;
&lt;li&gt;loads the Metal Runtime, enters multi-turn chat, and reuses the terminal KV cache.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Type normally to chat:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt; Introduce yourself in one sentence and say that you are running locally on this Mac.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For a local image, put the question and path in the same message. Bare, quoted, backtick-wrapped, and space-containing paths are supported:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt; Describe this image in two sentences `&#x2F;Users&#x2F;me&#x2F;Pictures&#x2F;demo.png`
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Or copy an image and use:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt; &#x2F;paste Describe the image on the clipboard
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Useful commands are:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#2b303b;color:#c0c5ce;&quot;&gt;&lt;code&gt;&lt;span&gt;&#x2F;stats    Show context and KV memory
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;reset    Clear the conversation
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;compact  Summarize older history
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;paste    Read a PNG image from the macOS clipboard
&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;exit     Exit
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;demo-on-real-hardware&quot;&gt;Demo on real hardware&lt;&#x2F;h2&gt;
&lt;p&gt;The interaction video below uses commands, startup figures, token counts, and response text from a real run on the same MacBook Air M5. The 8.1 MB release executable selected a 49,152-token context; repeated warm-file-cache starts took 0.9–1.6 seconds. Three cold-start text runs averaged 40.47 token&#x2F;s over &lt;code&gt;tg50&lt;&#x2F;code&gt;, while the measured 323-token image request took 6.469 seconds for prefill and 7.312 seconds to first token. Results vary with model revision, context length, sampling, file cache state, and thermals.&lt;&#x2F;p&gt;
&lt;video class=&quot;post-video&quot; controls playsinline preload=&quot;metadata&quot; poster=&quot;&#x2F;videos&#x2F;gemma4-e4b-terminal-poster.png&quot;&gt;
  &lt;source src=&quot;&#x2F;videos&#x2F;gemma4-e4b-terminal-demo.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;
  Your browser does not support embedded video; download the demo file instead.
&lt;&#x2F;video&gt;
&lt;blockquote&gt;
&lt;p&gt;The 23-second video validates the complete loading, text-chat, and vision-input path. The terminal output is line-wrapped and paced for the frame; it is not a model-quality evaluation.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-important-boundary&quot;&gt;The important boundary&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM is not replacing a Python environment with another heavyweight runtime. It makes model execution part of the application: one Rust object, one model, and one native backend. The application owns its threads, cancellation, cache, and lifetime instead of delegating its core capability to another process.&lt;&#x2F;p&gt;
&lt;p&gt;That turns “add local AI to an existing Rust program” from a deployment project into an ordinary library call—and gives an executable measured in megabytes complete multimodal inference on a Mac.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why We Are Rewriting an Inference Engine in Rust</title>
        <published>2026-08-29T00:00:00+00:00</published>
        <updated>2026-08-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://zhuai.tech/en/blog/welcome/"/>
        <id>https://zhuai.tech/en/blog/welcome/</id>
        
        <content type="html" xml:base="https://zhuai.tech/en/blog/welcome/">&lt;blockquote&gt;
&lt;p&gt;zLLM is not a Rust wrapper around an existing framework. It is a native inference engine designed around model execution, data lifecycles, and hardware resources, with Rust at its core and only the third-party dependencies that are truly necessary.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;why-rust&#x2F;resource-flow.en.svg&quot; alt=&quot;The complete zLLM inference task and resource flow&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This article answers three questions: why zLLM needs to own its engine boundaries, why Rust is a natural language for expressing those boundaries, and how that choice appears in real code and complete inference paths.&lt;&#x2F;p&gt;
&lt;p&gt;Building another LLM inference engine today invites two obvious questions: why not use a mature engine, and why choose Rust?&lt;&#x2F;p&gt;
&lt;p&gt;If the only goal is to get a model running quickly, an existing framework is usually the economical choice. zLLM is solving a broader problem. The same engine must work across Apple UMA, CPU, CUDA, ROCm, Vulkan, NPUs, local SSDs, massive MoE weights, long-context KV caches, and eventually layer-partitioned multi-node execution. These resources differ in location, synchronization, bandwidth, lifetime, and failure boundaries. The hard problem is not wrapping another operator API; it is making the resource relationships of the complete inference process explicit enough to verify, optimize, and evolve.&lt;&#x2F;p&gt;
&lt;p&gt;That is why zLLM starts from a clean design. Not because existing projects are inadequate, and not for the sake of being self-built, but because the boundaries we need differ from the historical boundaries of many existing systems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-rewriting-from-scratch-means&quot;&gt;What “rewriting from scratch” means&lt;&#x2F;h2&gt;
&lt;p&gt;Starting over does not mean rejecting standards or rebuilding every piece of infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM continues to read Safetensors, GGUF&#x2F;GGML, compressed-tensors, and ModelOpt weight formats. It uses Metal, CUDA, ROCm, Vulkan, and other platform capabilities, along with proven general-purpose Rust libraries. We do not reinvent file formats, network protocols, or operating-system interfaces.&lt;&#x2F;p&gt;
&lt;p&gt;What we redesign is the inference engine itself:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;how model specifications are represented;&lt;&#x2F;li&gt;
&lt;li&gt;how model algorithms are written once;&lt;&#x2F;li&gt;
&lt;li&gt;which capabilities a backend must provide;&lt;&#x2F;li&gt;
&lt;li&gt;how kernels stay parameterized instead of binding themselves to a model;&lt;&#x2F;li&gt;
&lt;li&gt;who owns weights, KV cache, activations, experts, and scratch memory;&lt;&#x2F;li&gt;
&lt;li&gt;how complete prefill, append-prefill, and decode paths are scheduled;&lt;&#x2F;li&gt;
&lt;li&gt;how single-node, embedded, and layer-partitioned multi-node execution share one set of semantics;&lt;&#x2F;li&gt;
&lt;li&gt;how CPU reference and cross-backend consistency anchor correctness.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In short, zLLM &lt;strong&gt;reuses open standards and platform capabilities while owning model execution and resource semantics&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Reused&lt;&#x2F;th&gt;&lt;th&gt;Owned by zLLM&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Safetensors, GGUF&#x2F;GGML, compressed-tensors, ModelOpt&lt;&#x2F;td&gt;&lt;td&gt;Model specifications, algorithm orchestration, and weight assembly&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Metal, CUDA, ROCm, Vulkan, and NPU interfaces&lt;&#x2F;td&gt;&lt;td&gt;Backend capabilities, placement, and synchronization&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Mature serialization, networking, and async Rust libraries&lt;&#x2F;td&gt;&lt;td&gt;KV, expert, activation, and scratch lifecycles&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Platform-native GPU kernel languages&lt;&#x2F;td&gt;&lt;td&gt;Complete prefill, append-prefill, and decode rounds&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This is also the difference between redesigning and merely rewriting. A rewrite can reproduce an old structure in a new language. A redesign returns to data dependencies, resource locations, and real workloads, then asks whether every module deserves to exist.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-minimal-engine-not-a-minimal-feature-set&quot;&gt;A minimal engine, not a minimal feature set&lt;&#x2F;h2&gt;
&lt;p&gt;For zLLM, “minimal” does not mean a demo with limited capability. It means minimizing the number of concepts in the system.&lt;&#x2F;p&gt;
&lt;p&gt;Our long-term rule is &lt;strong&gt;Simple &#x2F; Short &#x2F; Straight&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simple:&lt;&#x2F;strong&gt; solve a responsibility in one clear module rather than splitting it into layers of abstraction;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Short:&lt;&#x2F;strong&gt; keep code, scopes, and object lifetimes as short as possible;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Straight:&lt;&#x2F;strong&gt; define things near their use, so control flow and dependencies can be followed directly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Directories, files, traits, wrappers, managers, registries, and contexts are not free. Every additional entity adds a name to remember, another forwarding layer to understand, and another lifetime to trace. An entity is justified only when it owns distinct data, behavior, lifetime, or dependency boundaries.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM therefore avoids generic flows without real implementations, speculative abstract factories, and facade shells that only forward calls. There is one model execution flow. Platform differences belong to capability traits and concrete backends. Kernels express operators and do not know model names.&lt;&#x2F;p&gt;
&lt;p&gt;The goal is not to minimize capability, but to minimize the mental load required to understand the system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-fewest-necessary-third-party-dependencies&quot;&gt;The fewest necessary third-party dependencies&lt;&#x2F;h2&gt;
&lt;p&gt;Minimal dependencies do not mean zero dependencies. There is no reason to reimplement mature password hashing, serialization, async runtimes, HTTP, or standard-format parsing for ideological purity.&lt;&#x2F;p&gt;
&lt;p&gt;Our principle is simple: a dependency may provide general-purpose capability, but it must not own the core semantics of our inference engine.&lt;&#x2F;p&gt;
&lt;p&gt;Model execution, resource residency, KV lifecycles, weight assembly, scheduling policy, and cross-backend boundaries must remain visible in zLLM itself. A third-party library should not invisibly decide where a tensor lives, when it moves, when it synchronizes, when it is reclaimed, or how a model executes.&lt;&#x2F;p&gt;
&lt;p&gt;This has three direct benefits.&lt;&#x2F;p&gt;
&lt;p&gt;First, the dependency graph is easier to audit. Updating a network library should not change model numerics. Adding a weight format should not invade the runtime. Replacing a platform binding should not require rewriting model algorithms.&lt;&#x2F;p&gt;
&lt;p&gt;Second, performance costs stay visible. Inference systems are especially vulnerable to a convenient abstraction hiding an implicit copy, a device synchronization, or temporary dequantization. Owning the critical path lets us trace the origin, destination, and lifetime of every large allocation.&lt;&#x2F;p&gt;
&lt;p&gt;Third, long-term evolution is not constrained by an upstream framework. zLLM can reuse standards and general libraries without handing its architecture to a large tensor runtime or Python extension ecosystem.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-rust-fits-the-job&quot;&gt;Why Rust fits the job&lt;&#x2F;h2&gt;
&lt;p&gt;Rust matters for more than “no GC” or “faster than Python.” For an inference engine, its strongest advantage is the ability to encode resource relationships in program structure and reject many illegal states at compile time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-ownership-and-lifetimes-match-inference-resources&quot;&gt;1. Ownership and lifetimes match inference resources&lt;&#x2F;h3&gt;
&lt;p&gt;An inference engine manages expensive resources: memory-mapped weights, device buffers, KV pages, command buffers, asynchronous I&#x2F;O, distributed sessions, and temporary scratch space. Every one of them raises the same questions: who owns it, who may borrow it, when may it be released, and will it remain valid until an asynchronous task finishes?&lt;&#x2F;p&gt;
&lt;p&gt;Rust ownership, borrowing, and lifetimes are not incidental restrictions; they are direct language for these problems. A prefetch task cannot safely reference a buffer that has been released. A cache cannot be reclaimed while an execution queue still uses it. Shared state must declare its synchronization boundary. In C or C++, these rules often rely on convention and review. In garbage-collected languages, delayed reclamation and external resource lifetimes can bypass the collector. Rust turns a meaningful portion of these failures into compilation errors.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-predictable-execution-without-a-gc&quot;&gt;2. Predictable execution without a GC&lt;&#x2F;h3&gt;
&lt;p&gt;Decode is a continuous, fine-grained loop sensitive to tail latency. Unpredictable pauses, implicit allocation, and delayed destruction make performance analysis harder.&lt;&#x2F;p&gt;
&lt;p&gt;Rust has no garbage collector, and value lifetimes normally follow lexical scope. Combined with preallocation and scratch reuse, this makes allocation, release, and synchronization on hot paths more predictable. It does not guarantee performance automatically, but it provides the foundation for building a predictable system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-zero-cost-abstractions-express-backend-capabilities&quot;&gt;3. Zero-cost abstractions express backend capabilities&lt;&#x2F;h3&gt;
&lt;p&gt;Backend differences are real. A CPU tensor may be ordinary memory, a Metal tensor a device-resource handle, and ROCm may have a completely different placement and submission model. Forcing all of them into a single dynamic object tends to produce a lowest-common-denominator API, indirect calls, and runtime checks across hot paths.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM uses traits and associated types to express capabilities. A model runtime declares what it needs; a concrete backend supplies those capabilities at compile time. Generics cost compilation time and binary size, but in return provide static composition, a readable capability set, and less dynamic dispatch in performance-critical paths.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-enums-and-pattern-matching-make-format-states-explicit&quot;&gt;4. Enums and pattern matching make format states explicit&lt;&#x2F;h3&gt;
&lt;p&gt;Weights may be F32, F16, or BF16, or they may remain in FP8, FP4, W4A16, or GGUF block encoding until a kernel consumes them. Explicit enums require every backend to process or reject each legal state deliberately instead of guessing across strings, pointers, and implicit conventions.&lt;&#x2F;p&gt;
&lt;p&gt;This is particularly important for quantization. Quantization is not a loading-time detail that always expands to floating point. It is a data path orthogonal to model specifications and platform kernels. The more explicit the state, the harder it is for accidental expansion or incorrect dispatch to enter the system.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-rust-spans-the-systems-layer-and-the-service-layer&quot;&gt;5. Rust spans the systems layer and the service layer&lt;&#x2F;h3&gt;
&lt;p&gt;Many inference stacks are naturally split: C&#x2F;C++ or GPU languages at the bottom, Python, Go, or another service framework on top. Each side can be reasonable alone, but together they create duplicated data models, cross-language FFI, separate error systems, and lifetimes that are hard to unify.&lt;&#x2F;p&gt;
&lt;p&gt;Rust can handle weight parsing, CPU reference implementations, backend bindings, runtimes, schedulers, HTTP&#x2F;SSE services, and embedded library APIs. GPU kernels still use platform-native languages—Metal Shading Language, HIP&#x2F;CUDA, WGSL, or AscendC—but the engine around them shares one type system, error model, and resource semantics.&lt;&#x2F;p&gt;
&lt;p&gt;That is what zLLM means by “pure Rust.” It does not deny the existence of native platform kernels. It means there is no Python control plane and no delegation of core execution to another large tensor runtime: Rust directly owns the inference engine.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;6-one-toolchain-keeps-cross-platform-engineering-consistent&quot;&gt;6. One toolchain keeps cross-platform engineering consistent&lt;&#x2F;h3&gt;
&lt;p&gt;Cargo build, features, tests, and dependency management let CPU, Metal, CUDA, ROCm, and Vulkan paths maintain clear boundaries in one project. Platform capabilities are enabled by feature and target. CPU unit tests require no real weights, while GPU paths use the CPU oracle as their numerical anchor.&lt;&#x2F;p&gt;
&lt;p&gt;Rust cannot replace real-device testing or catch an ABI mismatch inside runtime-compiled MSL or HIP at compile time. It can, however, confine platform-specific unsafe boundaries to a relatively small region while the rest of the system retains static checking.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-zllm-s-architecture-reflects-these-choices&quot;&gt;How zLLM’s architecture reflects these choices&lt;&#x2F;h2&gt;
&lt;p&gt;zLLM divides the system into regions with distinct responsibilities and one-way dependencies:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;why-rust&#x2F;architecture.en.svg&quot; alt=&quot;The layered zLLM architecture&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The directory names are less important than the direction of dependency:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;model specifications do not depend on platforms;&lt;&#x2F;li&gt;
&lt;li&gt;model algorithms are written once rather than copied into every backend;&lt;&#x2F;li&gt;
&lt;li&gt;backends and kernels do not know concrete model names;&lt;&#x2F;li&gt;
&lt;li&gt;domains such as attention, MoE, and KV cache keep device-independent semantics and reference implementations;&lt;&#x2F;li&gt;
&lt;li&gt;the weight layer owns standard formats, naming, shape validation, and lifetimes—not model algorithms;&lt;&#x2F;li&gt;
&lt;li&gt;CPU is the correctness oracle, while GPU kernels are accelerated implementations verified against it.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This avoids the most common form of combinatorial explosion. Adding a model does not mean copying its execution flow for every platform. Adding a backend does not mean relearning every model. A new model mainly adds specifications, orchestration, and weight adaptation. A new backend mainly implements existing capabilities. Domain interfaces expand only when a genuinely new operator semantic appears.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;start-from-data-flow-not-semantic-labels&quot;&gt;Start from data flow, not semantic labels&lt;&#x2F;h2&gt;
&lt;p&gt;A clean-sheet design lets zLLM organize the system around real data dependencies instead of paper sections or model terminology.&lt;&#x2F;p&gt;
&lt;p&gt;If A produces the input consumed by B, they form a serial pipeline and should stay close while sharing intermediate state. If A and B independently read the same hidden state, they should remain separate so a backend can schedule them concurrently. Weight structures, kernel fusion, and resource lifetimes follow this same rule.&lt;&#x2F;p&gt;
&lt;p&gt;Production inference recognizes only three complete tasks: prefill for a new session, append-prefill for an existing session, and a complete decode round. Stopping at one layer, running one kernel, or comparing a partial output can be useful diagnostics, but cannot replace an end-to-end workload. Optimization must ultimately return to TTFT, decode latency, throughput, peak memory, SSD stalls, and device utilization.&lt;&#x2F;p&gt;
&lt;p&gt;This keeps the system from being distracted by attractive but irrelevant local numbers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rust-does-not-automatically-deliver-correctness-or-performance&quot;&gt;Rust does not automatically deliver correctness or performance&lt;&#x2F;h2&gt;
&lt;p&gt;Choosing Rust does not mean the language replaces engineering discipline.&lt;&#x2F;p&gt;
&lt;p&gt;Rust cannot prove a GPU kernel numerically correct, prevent a bad algorithm, or guarantee that an elegant abstraction has no performance cost. Multiple backends mean one kernel semantic may need optimized Rust reference, MSL, HIP, CUDA, WGSL, or AscendC implementations. Generics increase compilation cost. Capability traits can still grow without discipline.&lt;&#x2F;p&gt;
&lt;p&gt;zLLM therefore combines the language with explicit rules:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;every new operator starts with reference semantics and unit tests;&lt;&#x2F;li&gt;
&lt;li&gt;CPU and device backends are checked for consistency;&lt;&#x2F;li&gt;
&lt;li&gt;kernel dimensions are parameterized rather than embedding model constants;&lt;&#x2F;li&gt;
&lt;li&gt;isolated kernel benchmarks never substitute for complete-path metrics;&lt;&#x2F;li&gt;
&lt;li&gt;complexity is not added for performance without measurement;&lt;&#x2F;li&gt;
&lt;li&gt;source presence, successful compilation, oracle validation, and real-device end-to-end validation remain distinct states;&lt;&#x2F;li&gt;
&lt;li&gt;every change is the smallest one required for the current task.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Rust provides solid ground. Engine quality still depends on clear boundaries, reproducible validation, and sustained restraint toward complexity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-starting-over-is-worth-it&quot;&gt;Why starting over is worth it&lt;&#x2F;h2&gt;
&lt;p&gt;Rewriting an engine is expensive. Weight formats must be understood directly, kernels optimized platform by platform, services and schedulers refined in-house, and no upstream framework absorbs the mistakes. In the short term, this path is unquestionably slower than integrating a mature runtime.&lt;&#x2F;p&gt;
&lt;p&gt;What it buys is long-term ownership of every critical decision.&lt;&#x2F;p&gt;
&lt;p&gt;When a model changes, we know whether to modify its specification, orchestration, or domain capabilities. When a platform changes, we know whether the backend or kernel should change. When performance regresses, we can follow weights, activations, KV, experts, scratch, and synchronization points one by one. When the system expands across machines, it sends layer-boundary activations rather than mixing model-internal state with remote expert RPC on the hot path.&lt;&#x2F;p&gt;
&lt;p&gt;That is the purpose of zLLM: not to become the largest general AI framework with the most abstractions, but to remain a small, direct, native inference engine that truly owns hardware resources and model execution.&lt;&#x2F;p&gt;
&lt;p&gt;We chose Rust because it matches that goal: safe without surrendering low-level control; abstract without mandatory runtime cost; cross-platform without erasing platform differences; capable of writing the code nearest to a kernel and continuing all the way to services and distributed scheduling.&lt;&#x2F;p&gt;
&lt;p&gt;We chose a clean-sheet design because only then can Simple, Short, and Straight shape the entire engine from its first line instead of becoming local patches on an inherited system.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
