<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>TinyComputers.io (Posts about text diffusion)</title><link>https://tinycomputers.io/</link><description></description><atom:link href="https://tinycomputers.io/categories/text-diffusion.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 A.C. Jokela 
&lt;!-- div style="width: 100%" --&gt;
&lt;a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"&gt;&lt;img alt="" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/80x15.png" /&gt; Creative Commons Attribution-ShareAlike&lt;/a&gt;&amp;nbsp;|&amp;nbsp;
&lt;!-- /div --&gt;
</copyright><lastBuildDate>Thu, 11 Jun 2026 00:43:19 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Running DiffusionGemma on AMD Strix Halo and Decade-Old Tesla P40s</title><link>https://tinycomputers.io/posts/running-diffusiongemma-on-strix-halo-and-tesla-p40s.html?utm_source=feed&amp;utm_medium=rss&amp;utm_campaign=rss</link><dc:creator>A.C. Jokela</dc:creator><description>&lt;div class="audio-widget"&gt;
&lt;div class="audio-widget-header"&gt;
&lt;span class="audio-widget-icon"&gt;🎧&lt;/span&gt;
&lt;span class="audio-widget-label"&gt;Listen to this article&lt;/span&gt;
&lt;/div&gt;
&lt;audio controls preload="metadata"&gt;
&lt;source src="https://tinycomputers.io/running-diffusiongemma-on-strix-halo-and-tesla-p40s_tts.mp3" type="audio/mpeg"&gt;
&lt;/source&gt;&lt;/audio&gt;
&lt;div class="audio-widget-footer"&gt;25 min · AI-generated narration&lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Google released &lt;a href="https://baud.rs/KxTPsM"&gt;DiffusionGemma&lt;/a&gt; this week, and it's the most interesting thing to come out of the Gemma program in a while — not because it's bigger or smarter than what came before, but because it generates text in a fundamentally different way. Instead of predicting one token at a time, left to right, the way every mainstream LLM has worked since GPT-2, DiffusionGemma starts with a canvas of 256 masked tokens and iteratively denoises the whole block at once. Google's headline claim is up to 4x faster generation: over 1,000 tokens per second on an H100, 700+ on an RTX 5090.&lt;/p&gt;
&lt;p&gt;I do not own an H100. I own an &lt;a href="https://tinycomputers.io/posts/running-deepseek-v4-flash-on-amd-strix-halo.html"&gt;AMD Strix Halo APU&lt;/a&gt; and a rack-mount server with &lt;a href="https://tinycomputers.io/posts/repurposing-enterprise-gpus-the-tesla-p40-home-lab-story.html"&gt;four NVIDIA Tesla P40s&lt;/a&gt; from 2016. Neither appears anywhere in Google's launch material, and for good reason — the official deployment paths assume hardware features that one of these machines lacks entirely and the other only half-has. Getting DiffusionGemma running on both took an unmerged llama.cpp pull request, a community quantization, and the usual amount of stubbornness.&lt;/p&gt;
&lt;p&gt;It runs on both. And the benchmark result surprised me: the integrated GPU in a mini PC beat four discrete NVIDIA server cards — 96GB of combined VRAM, roughly 48 TFLOPS of aggregate FP32 — by a factor of more than two. This post covers the setup on each machine, the head-to-head numbers, and why diffusion inference inverts some of the assumptions I've built up over years of running autoregressive models on this hardware.&lt;/p&gt;
&lt;h3&gt;What Block Diffusion Actually Does&lt;/h3&gt;
&lt;p&gt;DiffusionGemma is built on the Gemma 4 26B A4B architecture: a Mixture-of-Experts model with 25.2 billion total parameters, of which only 3.8 billion are active for any given token. The MoE part is familiar. The generation process is not.&lt;/p&gt;
&lt;p&gt;An autoregressive model produces text serially. Each new token requires a full forward pass conditioned on everything before it, so a 256-token response means 256 sequential passes through the network. The KV cache makes each pass cheap, but the serial dependency is structural: token N cannot begin until token N-1 is finished. Generation speed is dominated by how fast you can stream weights through the GPU's memory bus, over and over.&lt;/p&gt;
&lt;p&gt;DiffusionGemma instead works on a 256-token block it calls a canvas. Every position starts as a mask token. Each denoising step runs one forward pass over the &lt;em&gt;entire&lt;/em&gt; canvas and proposes tokens for every position simultaneously. Positions where the model is confident get committed; uncertain ones stay masked for the next round. The sampler in the released implementation is called entropy-bound: rather than unmasking a fixed number of tokens per step, it commits every position whose predicted distribution has entropy below a threshold, which means easy spans of text resolve in a handful of steps while tricky ones get more iterations. For sequences longer than one canvas, the model chains blocks autoregressively — each new 256-token canvas conditions on the completed text before it. Google calls this block-autoregressive multi-canvas sampling, which is a lot of words for "diffusion inside the block, autoregression between blocks."&lt;/p&gt;
&lt;p&gt;The performance implication is the whole point. If a canvas resolves in 20 denoising steps, you've produced 256 tokens with 20 forward passes instead of 256. The trade is that each pass is much heavier: you're computing attention and expert routing for 2,300-odd positions of context-plus-canvas every step, not one new position. Diffusion converts text generation from a memory-bandwidth-bound serial problem into a compute-bound parallel one.&lt;/p&gt;
&lt;p&gt;Keep that sentence in mind. It decides the entire benchmark.&lt;/p&gt;
&lt;h3&gt;The Official Paths Don't Fit&lt;/h3&gt;
&lt;p&gt;The weights are on Hugging Face under Apache 2.0 as &lt;code&gt;google/diffusiongemma-26B-A4B-it&lt;/code&gt; — about 50GB in BF16. Google's developer guide shows a vLLM serving command and notes support in Transformers, SGLang, and MLX, with quantized deployment fitting in 18GB of VRAM.&lt;/p&gt;
&lt;p&gt;None of that helps either of my machines.&lt;/p&gt;
&lt;p&gt;The P40 problem is the same one I keep running into with this hardware. vLLM requires compute capability 7.0 or higher; the P40's Pascal GP102 die is 6.1, so vLLM won't even initialize. The Transformers path technically exists, but the weights are BF16 — a format Pascal has no hardware support for — and the fallback, FP16, runs on the P40 at 1/64th the rate of FP32 because Pascal's half-precision units were an afterthought on this die. I've &lt;a href="https://tinycomputers.io/posts/running-ltx-video-on-four-tesla-p40s.html"&gt;written before&lt;/a&gt; about the contortions required to run BF16-native models on these cards, and a 25-billion-parameter model in FP32 would need 100GB — more than the 96GB the four cards have between them.&lt;/p&gt;
&lt;p&gt;The Strix Halo could plausibly run the Transformers path, since ROCm's PyTorch handles BF16 on RDNA 3.5 fine. But a 50GB BF16 model plus activations through HuggingFace Transformers on an iGPU is the slow, painful version of this experiment. What both machines actually want is what they always want: llama.cpp and a good GGUF.&lt;/p&gt;
&lt;p&gt;The launch blog said llama.cpp support was "coming soon." Soon turned out to already be in flight: pull request &lt;a href="https://baud.rs/PDycIw"&gt;#24423&lt;/a&gt; on the llama.cpp repository implements the DiffusionGemma architecture and a dedicated &lt;code&gt;llama-diffusion-cli&lt;/code&gt; binary, and the Unsloth team had already published GGUF quantizations built against it — the kind of community velocity that has become the norm within days of any open-weights release. The PR is unmerged as I write this, which means building it yourself, but that's hardly a hardship.&lt;/p&gt;
&lt;p&gt;The Unsloth repository offers the usual ladder:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Quantization&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Notes&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;50.5 GB&lt;/td&gt;
&lt;td&gt;Full precision reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q8_0&lt;/td&gt;
&lt;td&gt;26.9 GB&lt;/td&gt;
&lt;td&gt;Near-lossless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q6_K&lt;/td&gt;
&lt;td&gt;22.7 GB&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q5_K_M&lt;/td&gt;
&lt;td&gt;19.1 GB&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Q4_K_M&lt;/td&gt;
&lt;td&gt;16.8 GB&lt;/td&gt;
&lt;td&gt;Fits a single 24GB card&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I went with Q8_0 on both machines. The P40 box has 96GB of VRAM to spread it across, the Strix Halo has 121GB of unified memory, and for a model whose output quality is already documented as a step below standard Gemma 4, I didn't want quantization noise muddying the comparison.&lt;/p&gt;
&lt;h3&gt;Machine One: Four Tesla P40s&lt;/h3&gt;
&lt;p&gt;The P40 server is the familiar workhorse: four GP102GL cards with 24GB of GDDR5X each, Ubuntu 24.04, the NVIDIA 580-series driver, and a 64-core host. It already had a llama.cpp checkout from December, with a working CUDA build configured for &lt;code&gt;CMAKE_CUDA_ARCHITECTURES=61&lt;/code&gt;. Rather than disturb a known-good build, I pulled the PR into a git worktree alongside it:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/llama.cpp
git&lt;span class="w"&gt; &lt;/span&gt;fetch&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;pull/24423/head:diffusiongemma
git&lt;span class="w"&gt; &lt;/span&gt;worktree&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;~/llama.cpp-diffusiongemma&lt;span class="w"&gt; &lt;/span&gt;diffusiongemma

&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/llama.cpp-diffusiongemma
cmake&lt;span class="w"&gt; &lt;/span&gt;-B&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-DGGML_CUDA&lt;span class="o"&gt;=&lt;/span&gt;ON&lt;span class="w"&gt; &lt;/span&gt;-DCMAKE_CUDA_ARCHITECTURES&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;61&lt;/span&gt;
cmake&lt;span class="w"&gt; &lt;/span&gt;--build&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--target&lt;span class="w"&gt; &lt;/span&gt;llama-diffusion-cli
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The worktree trick is worth adopting if you haven't: one clone, multiple checked-out branches in separate directories, no second &lt;code&gt;.git&lt;/code&gt; download, and the original build directory stays untouched. The build target matters too — DiffusionGemma doesn't run under the standard &lt;code&gt;llama-cli&lt;/code&gt; or &lt;code&gt;llama-server&lt;/code&gt;; the diffusion sampling loop lives in its own &lt;code&gt;llama-diffusion-cli&lt;/code&gt; binary.&lt;/p&gt;
&lt;p&gt;The CUDA 12.0 toolkit that ships in Ubuntu 24.04's repositories compiled the sm_61 kernels without complaint. While it built, the Q8_0 GGUF downloaded from Hugging Face. First run:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;./build/bin/llama-diffusion-cli&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;~/models/diffusiongemma-26B-A4B-it-Q8_0.gguf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;-ngl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;256&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Explain why text diffusion models can generate text faster than autoregressive models."&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Two log lines worth noting before the results. First:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;sched_reserve&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CUDA0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;but&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Attention&lt;/span&gt;
&lt;span class="n"&gt;tensor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CPU&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usually&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;due&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;support&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sched_reserve&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Flash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Attention&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The PR doesn't yet have Flash Attention kernels wired up for this architecture on this hardware, so attention falls back to the unfused path. That's a real performance tax on a workload that's almost entirely attention-over-2,300-positions, and it suggests headroom once the PR matures.&lt;/p&gt;
&lt;p&gt;Second:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;diffusion_eb&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GPUs&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;diffusion&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;force&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The diffusion sampler maintains an optional KV cache over the already-committed prompt prefix, so each denoising step only recomputes attention for the active canvas. With the model split across four GPUs, the PR disables this by default. File that away; it becomes a benchmark variable later.&lt;/p&gt;
&lt;p&gt;And then it worked. Nineteen entropy-bound denoising steps, 42 seconds, and a coherent 256-token answer — produced by a model that was, frankly, fascinating to watch. With &lt;code&gt;--diffusion-visual&lt;/code&gt; you can see the canvas refine in place: scattered high-confidence words appear first, connective tissue fills in around them, and the text snaps into focus over successive steps like a developing photograph. It is the most legible window into "what is the model doing" I've encountered since attention map visualizations stopped being useful.&lt;/p&gt;
&lt;h3&gt;Machine Two: Strix Halo&lt;/h3&gt;
&lt;p&gt;The Strix Halo machine is the same one that ran &lt;a href="https://tinycomputers.io/posts/running-deepseek-v4-flash-on-amd-strix-halo.html"&gt;DeepSeek V4 Flash&lt;/a&gt;: a Ryzen AI MAX+ 395 with the integrated Radeon 8060S (gfx1151, 40 RDNA 3.5 compute units), 128GB of LPDDR5X unified memory, and &lt;a href="https://tinycomputers.io/posts/upgrading-rocm-7.0-to-7.2-on-amd-strix-halo-gfx1151.html"&gt;ROCm 7.2&lt;/a&gt;. The setup mirrors the P40 box almost line for line, with the CUDA flags swapped for HIP:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/llama.cpp
git&lt;span class="w"&gt; &lt;/span&gt;fetch&lt;span class="w"&gt; &lt;/span&gt;--no-tags&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;pull/24423/head:diffusiongemma
git&lt;span class="w"&gt; &lt;/span&gt;worktree&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;~/llama.cpp-diffusiongemma&lt;span class="w"&gt; &lt;/span&gt;diffusiongemma

&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~/llama.cpp-diffusiongemma
cmake&lt;span class="w"&gt; &lt;/span&gt;-B&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-DGGML_HIP&lt;span class="o"&gt;=&lt;/span&gt;ON&lt;span class="w"&gt; &lt;/span&gt;-DAMDGPU_TARGETS&lt;span class="o"&gt;=&lt;/span&gt;gfx1151&lt;span class="w"&gt; &lt;/span&gt;-DCMAKE_BUILD_TYPE&lt;span class="o"&gt;=&lt;/span&gt;Release
cmake&lt;span class="w"&gt; &lt;/span&gt;--build&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--target&lt;span class="w"&gt; &lt;/span&gt;llama-diffusion-cli
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Instead of downloading the 26.9GB GGUF a second time, I rsynced it across the LAN from the P40 server. Both machines sit on the same switch, and Hugging Face's CDN doesn't get faster than a local wire.&lt;/p&gt;
&lt;p&gt;One memorable wrinkle: this box's disk was 97% full when I started, with 65GB free — enough for the model, but barely. The subsequent archaeology turned up 329GB of forgotten Ollama models and 118GB of GGUFs cached by root from September experiments, and the cleanup freed almost 600GB. The home lab equivalent of finding grocery money in last winter's coat.&lt;/p&gt;
&lt;p&gt;The HIP build compiled the PR without a single source change — the same commit, &lt;code&gt;c84e85af6&lt;/code&gt;, that built for CUDA sm_61 also built for gfx1151. Whatever else you want to say about the ggml project, its backend abstraction has earned its keep. The same Flash Attention fallback warning appeared, so both machines run the same unfused attention path, which keeps the comparison honest. One difference: as a single-GPU configuration, the Strix Halo got the diffusion KV cache enabled by default.&lt;/p&gt;
&lt;h3&gt;The Benchmark&lt;/h3&gt;
&lt;p&gt;Same model file, same PR commit, same prompt, same flags, two generation lengths. The only differences are the silicon and the KV cache default. The &lt;code&gt;llama-diffusion-cli&lt;/code&gt; output reports total wall time and per-step time directly:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Strix Halo (Radeon 8060S)&lt;/th&gt;
&lt;th&gt;4x Tesla P40&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;256 tokens (1 canvas)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;17.4s&lt;/strong&gt; — 17 steps, 1,025 ms/step&lt;/td&gt;
&lt;td&gt;42.5s — 19 steps, 2,235 ms/step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;512 tokens (2 canvases)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;37.1s&lt;/strong&gt; — 36 steps, 1,031 ms/step&lt;/td&gt;
&lt;td&gt;92.1s — 38 steps, 2,423 ms/step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effective throughput&lt;/td&gt;
&lt;td&gt;~14 tokens/sec&lt;/td&gt;
&lt;td&gt;~5.6 tokens/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The integrated GPU in a mini PC that idles at a few dozen watts is 2.2x faster than four server cards drawing 250 watts apiece. Per denoising step, it's 1.03 seconds versus 2.2–2.4 seconds, and that per-step time holds almost perfectly flat as the generation grows from one canvas to two — 1,025ms to 1,031ms on the AMD side — so longer outputs scale linearly with block count on both machines.&lt;/p&gt;
&lt;p&gt;My first suspect for the gap was the KV cache asymmetry, since the P40 box had it disabled. Easy to test: force it on with &lt;code&gt;--diffusion-kv-cache on&lt;/code&gt; and rerun. Result: 2,179 ms/step versus 2,235 — a 2.5% improvement. Not the answer. The gap is the hardware, and it's worth understanding why, because the explanation is the inverse of every previous benchmark I've run on these two machines.&lt;/p&gt;
&lt;h3&gt;Why the iGPU Wins&lt;/h3&gt;
&lt;p&gt;For autoregressive inference, the P40s' saving grace has always been memory bandwidth. Each card moves about 346 GB/s from GDDR5X, and token-by-token generation is essentially a memory streaming benchmark — which is why these $200 relics have stayed &lt;a href="https://tinycomputers.io/posts/the-economics-of-owning-your-own-inference.html"&gt;economically relevant&lt;/a&gt; for chat workloads years after their compute became obsolete. The Strix Halo's LPDDR5X manages roughly 256 GB/s shared between CPU and GPU, so for ordinary LLM generation the P40s usually hold their own or win.&lt;/p&gt;
&lt;p&gt;Diffusion flips the workload. Every denoising step is one enormous batched forward pass: 2,300+ positions of attention, MoE routing, and expert FFNs computed simultaneously. The weights are read once per step and amortized across all 256 canvas positions, so memory bandwidth stops being the bottleneck. What matters is raw arithmetic throughput on big matrix multiplies — exactly the regime where modern architectures embarrass Pascal.&lt;/p&gt;
&lt;p&gt;Three specific factors stack up against the P40s:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pascal's arithmetic is stuck in 2016.&lt;/strong&gt; No tensor cores, useless FP16, no DP4A path that helps here. Every matmul in the unfused attention and expert layers runs through plain FP32 CUDA cores at GP102's native rate. RDNA 3.5 brings WMMA instructions and dual-issue FP32 — per-clock, per-unit, it simply does more math, and on a compute-bound workload that's the whole game.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Four GPUs synchronize every step.&lt;/strong&gt; llama.cpp splits the model by layers, so each denoising step's forward pass marches through GPU 0, then 1, then 2, then 3, handing activations across PCIe 3.0 at every boundary — for a batch of 2,300 positions, a meaningfully larger transfer than single-token inference ever produces. In autoregressive mode this pipeline overhead hides behind memory streaming; at 48 steps per 512-token generation, it's pure tax. The unified-memory APU pays nothing. It doesn't even cross a PCIe bus to read the weights.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Neither machine has Flash Attention here, but the penalty isn't symmetric.&lt;/strong&gt; Unfused attention materializes large intermediate matrices and burns bandwidth and compute on a canvas-sized sequence every step. The architecture with more arithmetic headroom absorbs that better.&lt;/p&gt;
&lt;p&gt;The result is a benchmark where one of the oldest tricks in the home lab playbook — gang cheap cards together until the VRAM adds up — actively hurts, and the boring little APU that just holds everything in one pool of memory wins by a wide margin. The same dynamic decided the &lt;a href="https://tinycomputers.io/posts/running-deepseek-v4-flash-on-amd-strix-halo.html"&gt;DeepSeek V4 experiment&lt;/a&gt;, but for a different reason: there it was instruction set support; here the P40s run the model &lt;em&gt;correctly&lt;/em&gt; and still lose on the shape of the computation. Ten-year-old hardware doesn't fail all at once. It fails one workload category at a time.&lt;/p&gt;
&lt;h3&gt;Watching It Think&lt;/h3&gt;
&lt;p&gt;A few qualitative observations that the timing table doesn't capture.&lt;/p&gt;
&lt;p&gt;The entropy-bound sampler's step count genuinely varies with content. Across runs I saw single canvases resolve in anywhere from 15 to 19 steps against a configured maximum of 48 — the sampler's confidence and entropy thresholds (&lt;code&gt;t=[0.400, 0.800]&lt;/code&gt;, &lt;code&gt;entropy_bound=0.1&lt;/code&gt;, &lt;code&gt;confidence=0.005&lt;/code&gt; in the defaults) decide when each position commits, so boilerplate prose converges fast while denser passages take more iterations. The practical effect is that generation time depends on how &lt;em&gt;hard&lt;/em&gt; the text is, not just how long. That's a strange property to develop intuitions for after years of fixed per-token costs.&lt;/p&gt;
&lt;p&gt;The instruction-tuned model also produces an explicit planning trace — drafting bullet points, weighing alternatives, revising phrasing — before its final answer, in the now-familiar reasoning-model style. Watching a &lt;em&gt;diffusion&lt;/em&gt; model do this is doubly strange, because the plan and the answer crystallize as blocks rather than as a stream, paragraph-scale thoughts emerging whole.&lt;/p&gt;
&lt;p&gt;And the quality caveat is real, so I'll repeat Google's own framing: DiffusionGemma trades output quality for speed relative to standard Gemma 4. It's an experimental release aimed at speed-critical applications — real-time editing, latency-sensitive drafting — not a production daily driver. On my hardware, which can't reach the speeds that make the trade compelling, it's best understood as a working preview of a genuinely different inference paradigm. That's worth 27GB of disk to me.&lt;/p&gt;
&lt;h3&gt;The Recipe, Condensed&lt;/h3&gt;
&lt;p&gt;For either machine, the full setup is four commands and a download. CUDA flavor:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/ggml-org/llama.cpp
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;llama.cpp
git&lt;span class="w"&gt; &lt;/span&gt;fetch&lt;span class="w"&gt; &lt;/span&gt;origin&lt;span class="w"&gt; &lt;/span&gt;pull/24423/head:diffusiongemma&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;diffusiongemma
cmake&lt;span class="w"&gt; &lt;/span&gt;-B&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-DGGML_CUDA&lt;span class="o"&gt;=&lt;/span&gt;ON&lt;span class="w"&gt; &lt;/span&gt;-DCMAKE_CUDA_ARCHITECTURES&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;61&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# 61 = Pascal/P40&lt;/span&gt;
cmake&lt;span class="w"&gt; &lt;/span&gt;--build&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-j&lt;span class="w"&gt; &lt;/span&gt;--target&lt;span class="w"&gt; &lt;/span&gt;llama-diffusion-cli
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;ROCm flavor, swap the configure line:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;cmake&lt;span class="w"&gt; &lt;/span&gt;-B&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;-DGGML_HIP&lt;span class="o"&gt;=&lt;/span&gt;ON&lt;span class="w"&gt; &lt;/span&gt;-DAMDGPU_TARGETS&lt;span class="o"&gt;=&lt;/span&gt;gfx1151&lt;span class="w"&gt; &lt;/span&gt;-DCMAKE_BUILD_TYPE&lt;span class="o"&gt;=&lt;/span&gt;Release
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then grab a GGUF from &lt;code&gt;unsloth/diffusiongemma-26B-A4B-it-GGUF&lt;/code&gt; on Hugging Face — Q8_0 if you have 27GB of memory to spend, Q4_K_M if you're fitting a single 24GB card — and run:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;./build/bin/llama-diffusion-cli&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;diffusiongemma-26B-A4B-it-Q8_0.gguf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;-ngl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-cnv&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2048&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--diffusion-visual
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;--diffusion-visual&lt;/code&gt; flag is optional and you should absolutely use it anyway. Once the PR merges into mainline llama.cpp, the fetch-and-checkout step disappears and this becomes as routine as running any other GGUF.&lt;/p&gt;
&lt;p&gt;The deeper takeaway from this experiment isn't about DiffusionGemma specifically. It's that inference hardware assumptions are workload assumptions in disguise. The P40s survive in my rack because autoregressive generation is kind to old silicon with decent memory bandwidth. The first mainstream model family to change &lt;em&gt;how&lt;/em&gt; tokens get generated — not just how many parameters produce them — quietly rewrote that bargain. If text diffusion graduates from experiment to standard practice, the hardware hierarchy of the home lab gets reshuffled, and the winners will be whatever has the most matmul per dollar, not the most gigabytes per second. I suspect the P40s will still find work. They always do. But I've stopped assuming I know which jobs they'll be good at.&lt;/p&gt;</description><category>amd</category><category>cuda</category><category>diffusiongemma</category><category>gemma</category><category>gfx1151</category><category>gguf</category><category>home lab</category><category>inference</category><category>llama.cpp</category><category>llm</category><category>machine learning</category><category>moe</category><category>open-source</category><category>pascal</category><category>quantization</category><category>rocm</category><category>strix halo</category><category>tesla p40</category><category>text diffusion</category><guid>https://tinycomputers.io/posts/running-diffusiongemma-on-strix-halo-and-tesla-p40s.html</guid><pubDate>Wed, 10 Jun 2026 23:30:00 GMT</pubDate></item></channel></rss>