<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jahn</title>
    <description>The latest articles on DEV Community by Jahn (@conatusai).</description>
    <link>https://dev.to/conatusai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4094531%2Fa215ccbd-8e58-4149-9bf3-e97f47195d43.png</url>
      <title>DEV Community: Jahn</title>
      <link>https://dev.to/conatusai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/conatusai"/>
    <language>en</language>
    <item>
      <title>The sm_120 shared-memory cliff: why FP8 KV cache crashes vLLM on workstation Blackwell</title>
      <dc:creator>Jahn</dc:creator>
      <pubDate>Tue, 25 Aug 2026 21:02:33 +0000</pubDate>
      <link>https://dev.to/conatusai/the-sm120-shared-memory-cliff-why-fp8-kv-cache-crashes-vllm-on-workstation-blackwell-1dji</link>
      <guid>https://dev.to/conatusai/the-sm120-shared-memory-cliff-why-fp8-kv-cache-crashes-vllm-on-workstation-blackwell-1dji</guid>
      <description>&lt;p&gt;If you run vLLM with &lt;code&gt;--kv-cache-dtype fp8&lt;/code&gt; on a DeepSeek-family (MLA) model and your GPU is a GB10, an RTX PRO 6000, or any workstation or consumer Blackwell card, there is a decent chance the engine dies on startup with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;triton.runtime.errors.OutOfResources: out of resource: shared memory,
Required: 102400, Hardware limit: 101376. Reducing block sizes or `num_stages` may help.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We hit this, measured exactly where the 1,024 missing bytes come from, and posted the measurements on the upstream issue (vllm-project/vllm#53748). This post is the diagnostic method, because it applies to any Triton kernel on this hardware class, not just this one bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hardware fact underneath
&lt;/h2&gt;

&lt;p&gt;Datacenter Blackwell parts advertise a larger per-block shared-memory budget than the workstation and consumer silicon. On an RTX PRO 6000 Blackwell (sm_120), &lt;code&gt;torch.cuda.get_device_properties&lt;/code&gt; reports:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;property&lt;/th&gt;
&lt;th&gt;bytes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shared_memory_per_block_optin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;101,376&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shared_memory_per_multiprocessor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;102,400&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;GB10 (DGX Spark) reports the same 101,376-byte per-block opt-in limit. So a kernel tuned to fit 102,400 bytes fits the SM but not the block, and the whole workstation Blackwell class fails together. If your kernel config was validated on H100 or B200 numbers, this is the cliff you fall off when someone runs it on a desk machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five-minute probe
&lt;/h2&gt;

&lt;p&gt;Do not debug this inside the serving engine. Launch the suspect kernel directly with the exact tile configuration the engine would pin, sweep &lt;code&gt;num_stages&lt;/code&gt;, and read the compiled kernel's shared-memory size. For vLLM's MLA decode kernel (&lt;code&gt;_fwd_grouped_kernel_stage1&lt;/code&gt;) with the DeepSeek tile shape (&lt;code&gt;BLOCK_DMODEL=512, BLOCK_DPE=64, BLOCK_DV=512, BLOCK_N=32, BLOCK_H=16, num_warps=4&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;num_stages&lt;/th&gt;
&lt;th&gt;KV bf16&lt;/th&gt;
&lt;th&gt;KV fp8e4m3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;59,392 (launches)&lt;/td&gt;
&lt;td&gt;83,968 (launches)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;63,488 (launches)&lt;/td&gt;
&lt;td&gt;102,400 (OutOfResources)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;63,616 (launches)&lt;/td&gt;
&lt;td&gt;102,656 (OutOfResources)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;63,616 (launches)&lt;/td&gt;
&lt;td&gt;102,656 (OutOfResources)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table says everything. With bf16 KV, an extra pipeline stage costs about 4 KB and even four stages sit far under the limit. With fp8 KV, a stage costs about 18 KB, and stage two lands on 102,400 bytes: exactly the full per-SM budget, and 1,024 bytes over the per-block cap. The crash is specific to the fp8 KV path; MLA with bf16 KV is fine at these tile sizes.&lt;/p&gt;

&lt;p&gt;The probe is ordinary Triton: import the kernel function, build dummy tensors for the tile shape, launch once per (dtype, num_stages) combination, and catch &lt;code&gt;OutOfResources&lt;/code&gt;. The compiled size is on &lt;code&gt;kernel.metadata&lt;/code&gt; after a successful launch. No server, no model download, under a minute per configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducing end to end
&lt;/h2&gt;

&lt;p&gt;DeepSeek-V2-Lite-Chat is the smallest convenient MLA model with the same &lt;code&gt;Lk=576&lt;/code&gt; head layout (512 latent plus 64 rope):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vllm serve deepseek-ai/DeepSeek-V2-Lite-Chat &lt;span class="nt"&gt;--trust-remote-code&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enforce-eager&lt;/span&gt; &lt;span class="nt"&gt;--kv-cache-dtype&lt;/span&gt; fp8 &lt;span class="nt"&gt;--max-model-len&lt;/span&gt; 4096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On sm_120 the backend selector reports &lt;code&gt;TRITON_MLA&lt;/code&gt; as the only MLA decode option, so there is no fallback path: the engine dies during the startup dummy run (vLLM 0.27.1, torch 2.13.0+cu130, triton 3.7.1, CUDA 13.1).&lt;/p&gt;

&lt;h2&gt;
  
  
  What a durable fix looks like
&lt;/h2&gt;

&lt;p&gt;The upstream patch direction pins &lt;code&gt;num_stages=1&lt;/code&gt; for MLA kernels on non-HIP devices, which makes the server start and generate correctly (we validated greedy output on sm_120 after applying it). But the sweep shows why a blanket &lt;code&gt;is_mla&lt;/code&gt; condition costs something: bf16 KV at two stages fits comfortably (63,488 &amp;lt; 101,376), and the second stage exists for throughput. The robust rule is to compute the tile's actual shared-memory requirement and compare it against &lt;code&gt;shared_memory_per_block_optin&lt;/code&gt; for the device you are actually on. That handles GB10, RTX PRO 6000, and whatever ships next, without a special case per chip.&lt;/p&gt;

&lt;p&gt;The general lesson for anyone shipping Triton kernels: your shared-memory budget is a per-block, per-device property, not a compile-time constant, and the workstation cards your users actually own report smaller numbers than the datacenter parts your CI runs on. Probe first; it takes five minutes.&lt;/p&gt;

&lt;p&gt;This came out of inference engineering work on Blackwell workstations. The benchmark that surrounds it, with raw data and reproduction scripts, is here: &lt;a href="https://conatus.jahn.ai/ai-engineering/sample-report" rel="noopener noreferrer"&gt;https://conatus.jahn.ai/ai-engineering/sample-report&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cuda</category>
      <category>llm</category>
      <category>gpu</category>
      <category>performance</category>
    </item>
    <item>
      <title>Did FP8 make the model dumber? A per-prompt regression check for quantized serving</title>
      <dc:creator>Jahn</dc:creator>
      <pubDate>Tue, 25 Aug 2026 18:12:24 +0000</pubDate>
      <link>https://dev.to/conatusai/did-fp8-make-the-model-dumber-a-per-prompt-regression-check-for-quantized-serving-595f</link>
      <guid>https://dev.to/conatusai/did-fp8-make-the-model-dumber-a-per-prompt-regression-check-for-quantized-serving-595f</guid>
      <description>&lt;p&gt;FP8 gave us a clean 1.5x on Qwen3-8B serving throughput on an RTX PRO 6000 Blackwell (1,725 to 2,597 tok/s at concurrency 32, vLLM). The uncomfortable question is always the same: did the model get dumber. This post is the exact check we ran before recommending the switch, with numbers, so you can run the same one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "run an eval suite" is usually the wrong first answer
&lt;/h2&gt;

&lt;p&gt;Standard benchmarks (MMLU and friends) are noisy instruments for quantization deltas at 8B scale. Score movement inside the error bars tells you nothing about whether YOUR prompts changed behavior. What you actually want to know is narrower: on the workload you serve, does the FP8 checkpoint produce materially different outputs than BF16, and are any of the differences wrong.&lt;/p&gt;

&lt;p&gt;That is answerable directly, cheaply, and per prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  The method
&lt;/h2&gt;

&lt;p&gt;Both configurations run the same fixed workload: 20 prompts covering reasoning, code, summarization, translation, extraction, classification, math, and instruction following. Greedy decoding, temperature 0, 256-token cap, streamed. Greedy matters: it removes sampling noise, so any output difference is attributable to the numerics.&lt;/p&gt;

&lt;p&gt;Then a three-stage comparison:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Byte equality.&lt;/strong&gt; &lt;code&gt;outputs_bf16[i] == outputs_fp8[i]&lt;/code&gt;. Anything identical is settled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Similarity triage.&lt;/strong&gt; For non-identical pairs, &lt;code&gt;difflib.SequenceMatcher.ratio()&lt;/code&gt; sorts near-identical wording drift from real divergence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Side-by-side review under a written rubric.&lt;/strong&gt; Every non-identical pair gets read. The rubric asks one question: is there a factual or numerical claim that one precision gets right and the other gets wrong. Wording changes, reordering, and equally-defensible readings are recorded but not counted as regressions.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core loop is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;difflib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;bf16&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vllm_bf16_conc1.texts.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;fp8&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vllm_fp8_conc1.texts.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bf16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp8&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;identical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;difflib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SequenceMatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ratio&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;similarity &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# non-identical pairs go to side-by-side review
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it found on Qwen3-8B FP8
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;7 of 20 outputs byte-identical.&lt;/li&gt;
&lt;li&gt;9 differed only in wording or formatting; equivalent content on review.&lt;/li&gt;
&lt;li&gt;3 minor regressions: a repeated word in a poem, one list item drifting off topic, one questionable tool suggestion.&lt;/li&gt;
&lt;li&gt;1 extraction prompt was ambiguous and produced two defensible readings.&lt;/li&gt;
&lt;li&gt;Zero cases where FP8 gave a wrong factual or numerical answer that BF16 answered correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last line is the acceptance bar. Minor stylistic wobble is expected from a numerics change; a flipped fact is a blocker. This profile passed, so the 1.5x was free for this workload.&lt;/p&gt;

&lt;p&gt;Two honest caveats. First, this validates a prompt profile, not the model in general: different domains, longer contexts, or sampled decoding need their own pass. Second, greedy-decoding equality is a strict signal but not a complete one; if you serve with sampling, run the review stage on sampled pairs too and expect more (benign) divergence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wrinkle that almost blocked the whole thing
&lt;/h2&gt;

&lt;p&gt;Getting FP8 to run at all on workstation-class Blackwell (sm_120) required routing around a kernel assertion in the default FP8 path. If you are on the same silicon and vLLM refuses to load the FP8 checkpoint, that is a known class of problem rather than something wrong with your setup.&lt;/p&gt;

&lt;p&gt;The full report this check belongs to, with the raw CSVs, environment manifest, and the one-command reproduction script, is here: &lt;a href="https://conatus.jahn.ai/ai-engineering/sample-report" rel="noopener noreferrer"&gt;https://conatus.jahn.ai/ai-engineering/sample-report&lt;/a&gt;&lt;/p&gt;

</description>
      <category>llm</category>
      <category>machinelearning</category>
      <category>performance</category>
      <category>python</category>
    </item>
    <item>
      <title>Qwen3-8B on workstation Blackwell: vLLM vs SGLang vs llama.cpp, plus an FP8 pass</title>
      <dc:creator>Jahn</dc:creator>
      <pubDate>Tue, 25 Aug 2026 16:56:08 +0000</pubDate>
      <link>https://dev.to/conatusai/qwen3-8b-on-workstation-blackwell-vllm-vs-sglang-vs-llamacpp-plus-an-fp8-pass-325c</link>
      <guid>https://dev.to/conatusai/qwen3-8b-on-workstation-blackwell-vllm-vs-sglang-vs-llamacpp-plus-an-fp8-pass-325c</guid>
      <description>&lt;p&gt;Benchmarks of the same model on the same GPU across three serving stacks, then an FP8 pass on the winner. All numbers measured on our own hardware last week. Raw CSVs, the environment manifest and a one-command reproduction script exist for every figure; the script was re-run end to end after the report was written and all figures reproduced within 6 percent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GPU: RTX PRO 6000 Blackwell, 96 GB, sm_120 (workstation Blackwell, not the datacenter parts)&lt;/li&gt;
&lt;li&gt;Model: Qwen3-8B&lt;/li&gt;
&lt;li&gt;Stacks: vLLM 0.27.1, SGLang 0.5.9, llama.cpp (CUDA build)&lt;/li&gt;
&lt;li&gt;Method: identical prompt set and sampling settings across engines, greedy decoding, output token counts matched across engines before any timing was compared. Concurrency 32 for the aggregate numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Matching token counts before comparing throughput matters more than it sounds. Engines disagree about stop conditions and detokenization, and a 10 percent difference in generated tokens quietly becomes a 10 percent throughput lie.&lt;/p&gt;

&lt;h2&gt;
  
  
  BF16, concurrency 32
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;stack&lt;/th&gt;
&lt;th&gt;aggregate tok/s&lt;/th&gt;
&lt;th&gt;TTFT p50&lt;/th&gt;
&lt;th&gt;e2e p99&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vLLM 0.27.1&lt;/td&gt;
&lt;td&gt;1,725&lt;/td&gt;
&lt;td&gt;39 ms&lt;/td&gt;
&lt;td&gt;3.4 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SGLang 0.5.9&lt;/td&gt;
&lt;td&gt;1,327&lt;/td&gt;
&lt;td&gt;42 ms&lt;/td&gt;
&lt;td&gt;5.0 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;llama.cpp (CUDA)&lt;/td&gt;
&lt;td&gt;428&lt;/td&gt;
&lt;td&gt;316 ms&lt;/td&gt;
&lt;td&gt;16.3 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  FP8 on the winner (vLLM, same settings)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;precision&lt;/th&gt;
&lt;th&gt;single-stream tok/s&lt;/th&gt;
&lt;th&gt;batch tok/s&lt;/th&gt;
&lt;th&gt;latency p50&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BF16&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;td&gt;1,725&lt;/td&gt;
&lt;td&gt;0.74 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FP8 (official checkpoint)&lt;/td&gt;
&lt;td&gt;130&lt;/td&gt;
&lt;td&gt;2,597&lt;/td&gt;
&lt;td&gt;0.49 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What the numbers say
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stack choice barely matters for one user.&lt;/strong&gt; Single-stream decode sits in the 83 to 96 tok/s band on every stack. If your service has one concurrent user, pick whatever is easiest to operate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack choice is a 4x decision under load.&lt;/strong&gt; At concurrency 32 the spread is 428 to 1,725 tok/s for the same model on the same silicon. Time to first token spreads 4x as well (39 ms vs 316 ms p50), which dominates perceived snappiness for chat workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FP8 was a clean 1.5x.&lt;/strong&gt; 1,725 to 2,597 tok/s aggregate, 86 to 130 single-stream, p50 latency down a third. We ran a fixed 20-prompt factual check before and after: zero regressions. For an 8B model on a 96 GB card the VRAM saving is irrelevant, the win is throughput.&lt;/p&gt;

&lt;h2&gt;
  
  
  The sm_120 wrinkle
&lt;/h2&gt;

&lt;p&gt;Workstation Blackwell is not datacenter Blackwell, and default paths tuned for the latter sometimes assert on the former. Getting FP8 to run here required routing around a kernel assertion in the default FP8 path (DeepGEMM asserting on sm_120, resolved by falling back to the CUTLASS path). Related example of the same hardware-class gap: &lt;a href="https://github.com/vllm-project/vllm/issues/53748" rel="noopener noreferrer"&gt;vllm-project/vllm#53748&lt;/a&gt;, where the Triton MLA decode kernel overflows the 101,376-byte per-block shared-memory limit that GB10 and workstation Blackwell share, but datacenter parts do not.&lt;/p&gt;

&lt;p&gt;If you are serving on RTX PRO 6000, GB10 or consumer Blackwell, budget time for this class of issue. The performance is there, the default configs often are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Greedy decoding for determinism; throughput measured after warmup&lt;/li&gt;
&lt;li&gt;Aggregate numbers are steady-state over the full prompt set, not peak&lt;/li&gt;
&lt;li&gt;Each engine got the same max batch and context settings where the knob exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full methodology and the case study writeup: &lt;a href="https://conatus.jahn.ai/ai-engineering" rel="noopener noreferrer"&gt;conatus.jahn.ai/ai-engineering&lt;/a&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>cuda</category>
      <category>performance</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
