<?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: Helgard</title>
    <description>The latest articles on DEV Community by Helgard (@helgard_orlm).</description>
    <link>https://dev.to/helgard_orlm</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%2F3980095%2F54f659c3-a39f-45b2-84f8-3d10deac3dd1.png</url>
      <title>DEV Community: Helgard</title>
      <link>https://dev.to/helgard_orlm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/helgard_orlm"/>
    <language>en</language>
    <item>
      <title>Running NVIDIA's new 120B Nemotron-3.5-Super on an 8 GB GPU — skipping the zeros, and finding the real bottleneck</title>
      <dc:creator>Helgard</dc:creator>
      <pubDate>Sat, 17 Oct 2026 12:42:22 +0000</pubDate>
      <link>https://dev.to/helgard_orlm/running-nvidias-new-120b-nemotron-35-super-on-an-8-gb-gpu-skipping-the-zeros-and-finding-the-44o4</link>
      <guid>https://dev.to/helgard_orlm/running-nvidias-new-120b-nemotron-35-super-on-an-8-gb-gpu-skipping-the-zeros-and-finding-the-44o4</guid>
      <description>&lt;p&gt;NVIDIA released &lt;strong&gt;Nemotron-3.5-Super&lt;/strong&gt; on October 15: a 120B mixture-of-experts model, 75 GB even in their 4-bit NVFP4 format. The usual answer for an 8 GB card is "not on your hardware". Two days later it runs on my home PC — &lt;strong&gt;one RTX 5060 with 8 GB&lt;/strong&gt; — as an ordinary chat model in Open WebUI at &lt;strong&gt;~8 tokens/s&lt;/strong&gt;, it reads images, and its answers match the Hugging Face reference implementation.&lt;/p&gt;

&lt;p&gt;Code: &lt;strong&gt;&lt;a href="https://github.com/helgard-orlm/nemotron-super-8gb" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/nemotron-super-8gb&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who did what, up front:&lt;/strong&gt; I picked the model, set the goal and made the calls (including the idea to exploit the zeros after ReLU², below). The engine, kernels, server and measurements were written by &lt;strong&gt;Claude&lt;/strong&gt; (Anthropic) in my sessions. "We" below means the two of us.&lt;/p&gt;

&lt;p&gt;This is the third model in a series on the same box — after &lt;a href="https://dev.to/helgard_orlm/running-a-133-gb-moe-model-on-an-8-gb-gpu-at-11-tokenss-by-streaming-experts-from-nvme-207b"&gt;Qwen3.8-Flash-Next, 133 GB, 11 tok/s&lt;/a&gt; and &lt;a href="https://dev.to/helgard_orlm/running-the-510-gb-deepseek-v41-flash-on-an-8-gb-gpu-and-three-bugs-that-never-raise-an-error-5647"&gt;DeepSeek-V4.1-Flash, 510 GB, 2.4 tok/s&lt;/a&gt;. Same idea, different model, and two new tricks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The machine
&lt;/h2&gt;

&lt;p&gt;RTX 5060 8 GB (Blackwell, PCIe 5.0 ×8), Core Ultra 5 225F, 32 GB DDR5, a Gen5 NVMe drive used only for model files. Nothing exotic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only number that matters: bytes per token
&lt;/h2&gt;

&lt;p&gt;In a mixture-of-experts model most of the weights are experts, and each token uses only a few of them. So the question isn't "does 75 GB fit in 8 GB" — it's &lt;strong&gt;how many expert bytes does one token need&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nemotron-3.5-Super: 88 layers (40 Mamba-2, 40 MoE, 8 attention), 512 experts per MoE layer, &lt;strong&gt;top-22&lt;/strong&gt;. That sounds expensive, but NVIDIA made the experts tiny: they work in a 1024-wide "latent" space instead of the full 4096. One expert is &lt;code&gt;up 2688×1024&lt;/code&gt; + &lt;code&gt;down 1024×2688&lt;/code&gt;, ReLU², no gate — &lt;strong&gt;3.10 MB in NVFP4&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;22 experts × 40 layers × 3.10 MB ≈ 2.7 GB per token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Between Qwen (1.3 GB/token) and DeepSeek (4.2 GB/token). Everything that isn't a routed expert — Mamba, attention, shared experts, router, latent projections, output head — is &lt;strong&gt;6.6 GiB on the GPU&lt;/strong&gt;, kept in the checkpoint's own FP8/NVFP4 formats and multiplied by our own Triton kernels. The 59 GiB of routed experts stay on the NVMe drive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engine, step by step
&lt;/h2&gt;

&lt;p&gt;No existing runtime streams experts for this architecture, so it's a PyTorch + Triton engine with the &lt;code&gt;transformers&lt;/code&gt; &lt;code&gt;NemotronH&lt;/code&gt; code as the skeleton (Mamba mixers, attention, router). We replaced the experts, the dense layers and the per-token MoE step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. One block per expert on disk.&lt;/strong&gt; Each expert's four tensors (&lt;code&gt;up&lt;/code&gt;, its scales, &lt;code&gt;down&lt;/code&gt;, its scales) are rewritten into one 3,096,576-byte block — exactly 756 pages of 4 KiB. One expert = one &lt;code&gt;pread&lt;/code&gt; with &lt;code&gt;O_DIRECT&lt;/code&gt; into pinned RAM. Repacking 59 GiB takes about 2.5 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A RAM cache in front of the disk.&lt;/strong&gt; LRU over pinned memory, 32 reader threads. Routing is concentrated: with &lt;strong&gt;24% of the experts in RAM, 81–84% of lookups hit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Prefetch into the gap.&lt;/strong&gt; Run the &lt;em&gt;next&lt;/em&gt; MoE layer's router on the &lt;em&gt;current&lt;/em&gt; input: it guesses the next layer's experts ~72% right. The missing guesses are read while the GPU is busy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Skip the zeros (new).&lt;/strong&gt; After ReLU², &lt;strong&gt;92.5% of an expert's neurons are exactly zero&lt;/strong&gt; for a given token. A zero neuron doesn't need its column of &lt;code&gt;down&lt;/code&gt; — so why move it across PCIe? The catch: NVFP4 shares one scale per 16 consecutive values, so you can't just pick neurons out of the original layout. We re-laid &lt;code&gt;down&lt;/code&gt; on disk once (a pure byte permutation, same file size): each neuron's 4-bit values are contiguous, scales grouped per 16 neurons. Now only &lt;code&gt;up&lt;/code&gt; (1.55 MB) is copied to the GPU; the &lt;code&gt;down&lt;/code&gt; kernel reads directly from pinned RAM over PCIe, and only the 16-neuron groups that contain a live neuron. &lt;strong&gt;+12%&lt;/strong&gt;, 6.6 → 7.3–7.6 tok/s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Chat server.&lt;/strong&gt; OpenAI-compatible &lt;code&gt;/v1/chat/completions&lt;/code&gt; with streaming, a &lt;code&gt;-think&lt;/code&gt; model id for reasoning mode, and dialog snapshots (~170 MB: 8 attention KV layers + 40 Mamba states) so the next message only processes its new tail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the time actually went — and where I was wrong
&lt;/h2&gt;

&lt;p&gt;At 7.3 tok/s a token takes ~137 ms. The plan was CUDA graphs: thousands of small kernel launches per token, surely Python overhead is the problem. Before writing graphs, we profiled with Nsight Systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPU busy ~92 ms, &lt;strong&gt;idle ~54 ms&lt;/strong&gt; per token;&lt;/li&gt;
&lt;li&gt;of the idle time, gaps between launches (what a CUDA graph would fix) were only &lt;strong&gt;~13 ms&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we tried two "obvious" fixes for the remaining waits: reordering work so the shared expert runs before the router result comes back, and copying the &lt;em&gt;predicted&lt;/em&gt; experts early. Both were bit-for-bit correct. Neither helped — the speculative copies made it &lt;strong&gt;7% slower&lt;/strong&gt;, because wrong guesses queued on the bus ahead of the right ones.&lt;/p&gt;

&lt;p&gt;So we timed what the &lt;strong&gt;CPU&lt;/strong&gt; was waiting for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;wait per token&lt;/th&gt;
&lt;th&gt;ms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NVMe reads (experts missing from the RAM cache)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50–55&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python cache bookkeeping&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;waiting for the router's choice&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The disk was the biggest wait. An earlier measurement had said "disk ~3 ms" — but that run had GPU copies switched off, which hid the disk behind nothing. The first README said that; it's corrected now. (The bad number was pointed out by a second agent reviewing the README, not by me.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix was boring: a bigger RAM cache.&lt;/strong&gt; Before touching the live server we recorded the routing of 5 conversations and replayed an LRU cache offline. The simulation matched live miss counts within ±0.4 reads per token, so we could pick a size on paper:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;RAM cache&lt;/th&gt;
&lt;th&gt;disk reads / token&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;14 GB&lt;/td&gt;
&lt;td&gt;156&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18 GB&lt;/td&gt;
&lt;td&gt;117&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;20 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;102&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24 GB&lt;/td&gt;
&lt;td&gt;78&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;20 GB live: 136.9 → 121.5 ms per token, &lt;strong&gt;7.3 → 8.2 tokens/s&lt;/strong&gt;. That leaves ~7 GB of RAM for the rest of a 32 GB system, which is why it stops there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;decode, tokens/s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;first run, no prefetch&lt;/td&gt;
&lt;td&gt;6.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ prefetch&lt;/td&gt;
&lt;td&gt;6.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ skip ReLU² zeros&lt;/td&gt;
&lt;td&gt;7.3–7.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;+ 20 GB RAM cache (current)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.2&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A 32–40 token prompt: ~5 s. Load time: 7–27 s.&lt;/p&gt;

&lt;h2&gt;
  
  
  It reads images too
&lt;/h2&gt;

&lt;p&gt;Nemotron-3.5-Super-VL ships a vision tower (C-RADIO, 1.3 GB). The engine's 6.6 GiB plus the tower don't fit on the card at once, so the tower's weights live in pinned RAM and are streamed to the GPU &lt;strong&gt;layer by layer&lt;/strong&gt; while it runs (0.4–1.6 s per image, vs 10–31 s on the CPU). In Open WebUI you just attach a photo.&lt;/p&gt;

&lt;p&gt;A test that's easy to fake and hard to pass: an invoice of 60 lines in a 15-px font, asked for the code, quantity and price of lines 21–30. At normal resolution (1047 image tokens) it got 7–9 of 10 per field — swapped letters, one made-up line. At NVIDIA's maximum resolution (2204 tokens, a separate &lt;code&gt;-doc&lt;/code&gt; model id): &lt;strong&gt;10/10 on all three fields&lt;/strong&gt;, 116 s total. The vision code is in the repo too (&lt;code&gt;vision/&lt;/code&gt;, tests with the invoice and its ground truth in &lt;code&gt;tests/vision/&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  How we know it's right
&lt;/h2&gt;

&lt;p&gt;Speed is meaningless if the model says different things. The reference is &lt;code&gt;transformers&lt;/code&gt; &lt;code&gt;NemotronHForCausalLM&lt;/code&gt; in fp32 on the CPU, with weights read from the &lt;strong&gt;original&lt;/strong&gt; checkpoint — not our repack — and unpacked with transformers' own FP4 code:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;text&lt;/th&gt;
&lt;th&gt;PPL reference&lt;/th&gt;
&lt;th&gt;PPL engine&lt;/th&gt;
&lt;th&gt;engine's greedy answer = reference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;English paragraph&lt;/td&gt;
&lt;td&gt;5.233&lt;/td&gt;
&lt;td&gt;5.280&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chat answer, TCP vs UDP&lt;/td&gt;
&lt;td&gt;6.592&lt;/td&gt;
&lt;td&gt;6.714&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;63/64&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chat answer, Russian&lt;/td&gt;
&lt;td&gt;5.873&lt;/td&gt;
&lt;td&gt;5.854&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;64/64&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mean KL ≤ 0.003. The one differing token is a real tie: the reference itself scores &lt;code&gt;-layer&lt;/code&gt; and &lt;code&gt;layer&lt;/code&gt; 0.11 apart, less than one BF16 step.&lt;/p&gt;

&lt;p&gt;Plus checks that &lt;em&gt;must&lt;/em&gt; fail: swap the two 4-bit halves of each byte and PPL jumps to 18,784 — so the unpacking order is tested, not assumed. Repack and re-layout were compared byte-for-byte with the original on 200 experts each.&lt;/p&gt;

&lt;p&gt;One bug of ours was caught by running the &lt;strong&gt;same input three times&lt;/strong&gt;: perplexity drifted between 5.19 and 5.29. Cause: &lt;code&gt;index_add_&lt;/code&gt; with repeated indices uses atomics in random order. Fixed with a per-(token, expert) buffer summed in a fixed order. A second source of run-to-run difference wasn't a bug: &lt;code&gt;mamba-ssm&lt;/code&gt; Triton kernels pick configs by timing, so separate processes rounded differently; &lt;code&gt;TRITON_CACHE_AUTOTUNING=1&lt;/code&gt; makes them bit-identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Putting the prompt's experts into the RAM cache — prompts got slower, decode didn't gain.&lt;/li&gt;
&lt;li&gt;Prefetching 6 guesses instead of 4 — more wasted reads.&lt;/li&gt;
&lt;li&gt;Skipping zeros per single neuron — re-reading scales per neuron was slower than copying the whole block. Per 16-neuron group worked.&lt;/li&gt;
&lt;li&gt;Speculative early copies (above) — correct, slower.&lt;/li&gt;
&lt;li&gt;Keeping recently used experts on the GPU — 40% of a layer's experts repeat on the next token, but holding them needs ~2.7 GB of VRAM that an 8 GB card doesn't have next to the 6.6 GB dense part.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The repo has the engine, the Triton kernels, the server, the repack/verify tools and every measurement script behind the numbers above. Honest caveat: &lt;strong&gt;it hasn't been run end-to-end from a fresh clone yet&lt;/strong&gt; — the README lists the exact steps used on my machine. You need a Blackwell GPU (the kernels use the NVFP4 hardware path), ~70 GB free on a fast NVMe drive, and 32 GB of RAM.&lt;/p&gt;

&lt;p&gt;The point is the same as in the earlier posts: for a mixture-of-experts model, "does it fit in VRAM" is the wrong question. Count &lt;strong&gt;bytes per token&lt;/strong&gt; — and then measure where the time goes before you fix it, because the obvious culprit (here: Python launch overhead) can be the wrong one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Engine, kernels, server, measurements and this write-up: Claude (Anthropic). Model choice, goal, the ReLU² idea and the decisions: me. All numbers are from logs on the machine described above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>cuda</category>
      <category>python</category>
    </item>
    <item>
      <title>I taught a local vision model to find the same AliExpress item cheaper — variant and shipping included</title>
      <dc:creator>Helgard</dc:creator>
      <pubDate>Wed, 14 Oct 2026 21:01:27 +0000</pubDate>
      <link>https://dev.to/helgard_orlm/i-taught-a-local-vision-model-to-find-the-same-aliexpress-item-cheaper-variant-and-shipping-5d1n</link>
      <guid>https://dev.to/helgard_orlm/i-taught-a-local-vision-model-to-find-the-same-aliexpress-item-cheaper-variant-and-shipping-5d1n</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: the price you see on AliExpress is rarely the price you pay
&lt;/h2&gt;

&lt;p&gt;You're searching for a phone charger on AliExpress and find it for $1.09. You click on it and realize $1.09 is a short cable price for the same listing. The actual price of the charger is $2.55. And you pay for shipping at the checkout.&lt;/p&gt;

&lt;p&gt;It happens quite often. On 14 October 2026 I checked out 3 "chargers" from search results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What the listing says&lt;/th&gt;
&lt;th&gt;Price in the search results&lt;/th&gt;
&lt;th&gt;What that price actually buys&lt;/th&gt;
&lt;th&gt;Real price of the charger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"40W Fast Charger Type C Dual Port PD"&lt;/td&gt;
&lt;td&gt;$1.09&lt;/td&gt;
&lt;td&gt;a 25 cm USB-C cable&lt;/td&gt;
&lt;td&gt;$2.55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"120W Fast Charger 4 Port USB Type C"&lt;/td&gt;
&lt;td&gt;$2.28&lt;/td&gt;
&lt;td&gt;a 25 cm cable&lt;/td&gt;
&lt;td&gt;$4.09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Original Samsung 25W Charger PD"&lt;/td&gt;
&lt;td&gt;$1.15&lt;/td&gt;
&lt;td&gt;a cable&lt;/td&gt;
&lt;td&gt;$3.16&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The usual tricks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On search it shows the cheapest in the list. However, often the product is different - cable, adapter, instead of EU - US plug etc.&lt;/li&gt;
&lt;li&gt;the price doesn't include shipping;&lt;/li&gt;
&lt;li&gt;some prices are 'welcome deals' which are only available when you first buy something;&lt;/li&gt;
&lt;li&gt;"sort by price" is not really sorting at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you want to get it cheaper, you got to open 20 tabs, click on each thing from every listing, calculate the item + shipping costs yourself. That's what I do every week, buying electronics parts from AliExpress.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;pricehunt&lt;/strong&gt; is a free Chrome extension that automates all that for you.&lt;/p&gt;

&lt;p&gt;Go to the item on AliExpress that you want to buy. Select your specific options (power, color, plug, size etc.) Then click on &lt;strong&gt;Find the same thing cheaper&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This extension then diligently searches AliExpress like someone would, by picture, by the seller's title, clicking on the listing one by one and checking all the options. And you finish with a list of offers of the &lt;strong&gt;same thing, same option you chose, shipping included&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2hbxccwhqo2hi5v6tq3u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2hbxccwhqo2hi5v6tq3u.jpg" alt="The panel after a search: the same Essager 33W charger, black, EU plug, found for $8.42 instead of $12.43" width="800" height="362"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Genuine search. Found item at $12.43. 2 minutes later the extension finds the same charger (same model, black, EU plug, free shipping) at $8.42. Manually I could find only $8.89 as a best option.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each result plainly says what it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact match, Similar or Different and you can't fake a look-alike&lt;/li&gt;
&lt;li&gt;the price is split into item + shipping;&lt;/li&gt;
&lt;li&gt;like "$8.28 is the US plug price, not the one you selected", "$1.09 is for new buyer only" or "to get this discount, you must have 3 different items in your cart".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is being powered by a local AI model on your own computer (not the cloud). It compares pictures of the product and determines if they are actually the same product. Your account on AliExpress, cart, searches are all local on your computer. Free search.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which AI model?&lt;/strong&gt; I've done the creation and testing using &lt;strong&gt;MiniCPM-V 4.7&lt;/strong&gt;, an open free picture understanding model. It is the best one to use and I've done all the results in this post on it. But it doesn't mean you have to use that. You can hook up any &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; image understanding model. Or any model that runs through some OpenAI-compatible server. Or any other model. The other ones that I've tested were just slightly less careful (numbers below).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who it's for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;anyone that ordered a 'charger' and got a cable;&lt;/li&gt;
&lt;li&gt;repair shops and hobbyists that have to order the same parts every month;&lt;/li&gt;
&lt;li&gt;small resellers from AliExpress buying and selling locally. Every dollar they save buying is a profit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's like a coffee to you to save $4 on one charger. But it's the difference between a small side business succeeding or not if you save $4 on every one of 30 parts that you have to reorder each month.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/f5O_n8uFkPY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi3thig4soj2jcpfzqt38.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi3thig4soj2jcpfzqt38.png" alt="Before the search: the option buttons, the reference photo and the model setting" width="800" height="343"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Before search - your choice (with buttons, 33W, black, EU plug) - this is a picture extension will look for. You can choose a different picture if you prefer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What's happening in the side panel: how many listings it's looked at so far, which ones it's opened, the current lowest price. If you get a captcha on AliExpress, it will switch that tab to the foreground and wait for you to solve it. No cheating.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/helgard-orlm" rel="noopener noreferrer"&gt;
        helgard-orlm
      &lt;/a&gt; / &lt;a href="https://github.com/helgard-orlm/pricehunt" rel="noopener noreferrer"&gt;
        pricehunt
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Find the real cheapest AliExpress offer: the variant you want + shipping, checked by a local open-weight vision model
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;pricehunt — the same thing, cheaper&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A browser extension that finds the &lt;strong&gt;real&lt;/strong&gt; cheapest offer for an AliExpress product: &lt;strong&gt;the exact variant you want plus shipping to your country&lt;/strong&gt;. It's checked by an open-weight vision model running on &lt;strong&gt;your own machine&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Open a product page, pick the variant with buttons (power, colour, plug…), press &lt;strong&gt;Find the same thing cheaper&lt;/strong&gt;, and watch the agent work in the side panel.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Built for the DEV Hacktoberfest 2026 Open-Source AI Challenge, week 2 ("Side Hustle"). Started 2026-10-14.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Marketplace search shows the lowest price of &lt;em&gt;any&lt;/em&gt; variant, and very often that's not the thing in the title:&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Listing title&lt;/th&gt;
&lt;th&gt;Price shown in search&lt;/th&gt;
&lt;th&gt;What that price buys&lt;/th&gt;
&lt;th&gt;Real price of the charger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"40W Fast Charger Type C Dual Port PD"&lt;/td&gt;
&lt;td&gt;$1.09&lt;/td&gt;
&lt;td&gt;a 0.25 m USB-C cable&lt;/td&gt;
&lt;td&gt;$2.55&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"120W Fast Charger 4 Port USB Type C"&lt;/td&gt;
&lt;td&gt;$2.28&lt;/td&gt;
&lt;td&gt;a 0.25 m cable&lt;/td&gt;
&lt;td&gt;$4.09&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/helgard-orlm/pricehunt" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;How to install:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the zip from Releases. Unzip it.&lt;/li&gt;
&lt;li&gt;Go to chrome://extensions. Enable &lt;strong&gt;Developer mode&lt;/strong&gt;. Choose &lt;strong&gt;Load unpacked&lt;/strong&gt; and select the folder.&lt;/li&gt;
&lt;li&gt;Select your AI model in the extension's settings - either your local &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; on your computer or any OpenAI-compatible server.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;The way I manually locate parts. I walked the extension through this process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Search by the photo of the one option you chose.&lt;/strong&gt; When you go to the list of items you'll usually see a collage of all the options first. And a picture showing only the option you want will have a lot more hits.&lt;/li&gt;
&lt;li&gt;Look only in the listings that are of the same item, starting with the cheapest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search again by the seller's exact title.&lt;/strong&gt; A lot of things don't actually have names, but people will steal the titles from each other. So if you find one good listing you can find the others.&lt;/li&gt;
&lt;li&gt;Sort the results yourself. Do it for free shipping AND for paid. Don't rely on the sorting on the site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read every option inside the listing&lt;/strong&gt; and pick only the one you want. 33W is 33W and make sure you have a plug for your country.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeat from the new cheapest offer&lt;/strong&gt; until a few rounds in a row you fail to find anything cheaper.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;A trick that has nothing to do with AI.&lt;/strong&gt; Sellers don't take photos of their own goods - they all use the same few photos from the factory. So the extension will remember which photos your item started with, and when it finds them on other items it'll know, even if the AI model's not confident because the item is at a different angle. (Only tested it on one family of chargers so far so let's call it a promising idea not a proven fact.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI model.&lt;/strong&gt; Ran MiniCPM-V (v4.7) on my home PC, with a garden-variety 8 GB gfx card. It's an extension that asks it basic, y/n questions about it: "Is it the same model? same body? same ports? same power print?" Turns out it's very sensitive to phrasing. For example, asking "Is it the same &lt;em&gt;type&lt;/em&gt; of product?" allowed 7 out of 11 different chargers through. Asking that strict question above only allowed 1 out of 11 chargers through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I measured&lt;/strong&gt; (all with MiniCPM-V 4.7):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Telling a charger from a cable, a charger+cable set or a car charger, by the option photo (49 photos)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;49 of 49 correct&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Me vs the extension: a 100 W COB LED chip&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;a tie at $3.39&lt;/strong&gt;: it found exactly the offer already in my cart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Me vs the extension: an Essager 33 W charger, starting from $12.43&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;$8.42&lt;/strong&gt;; my own best by hand was $8.89&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What I haven't tested yet:&lt;/strong&gt; how good it is at spotting truly deceptive listings, where the listing title says "charger" but the image is of a cable. Those 49 images above were all available, with honest titles. So getting two wins against a human doesn't really set any sort of benchmark. It's two wins.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  For the technically curious
  &lt;ul&gt;
&lt;li&gt;Using llama.cpp with mixture of experts weights stored in system RAM (--cpu-moe) so only 4G on the gpu.&lt;/li&gt;
&lt;li&gt;Token probabilities for "Yes" are the match percentages (logprobs). If a server does not provide logprobs then it'll default back to just a Yes/No.&lt;/li&gt;
&lt;li&gt;We take 256bit diffhash of reference product photos (we remove brand banners first). But we only consider this diffhash matched if model also confirms that it's same type of product.&lt;/li&gt;
&lt;li&gt;Real listings testing revealed some bugs: 35/59 discounted cards, the price parser used the crossed-out price. The extension added shipping when AliExpress displays free shipping. And the photo CDN server responds to multiple requests with 403 pages that are cached by browser for 1 year.&lt;/li&gt;
&lt;li&gt;By getting a developer log, it is possible to send the full search trace to some small receiver program on my computer. Most of the bugs that were fixed for version 1.5 and 1.6 were the result of analyzing these traces.
&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Because the marketplace won't build this for you.&lt;/strong&gt; The "best price" stuff on AliExpress works for AliExpress. For a tool whose sole purpose is to give you a lower price you have to be able to trust it. You want the tool to run on your computer so you can verify and swap out the model.&lt;/p&gt;

&lt;p&gt;Three things were enabled by open models.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy.&lt;/strong&gt; This extension uses your login account to browse. No cookies, cart, history etc are ever uploaded off your computer. I'd only use something like this for local model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost.&lt;/strong&gt; 1 search = dozens of photo comparisons. If it's a paid cloud AI it would cost money for every search. Here it's just a little bit of electricity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choice.&lt;/strong&gt; Just add an open model and try it yourself. So Gemma 4 12B has 45/49 on photo test. And Gemma 4 31B has 47/49 on photo test. Both fell into cable only pitfall which MiniCPM-V detected. So all the numbers in this thread have the model in which they were measured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Co-written with Claude (Anthropic) in Claude Code. Claude wrote most of the code and the first draft of this post. I brought the idea, the search method and the key decisions, and tested everything against my own purchases.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover: AI-generated illustration. All screenshots are real.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Running the 510 GB DeepSeek-V4.1-Flash on an 8 GB GPU — and three bugs that never raise an error</title>
      <dc:creator>Helgard</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:25:53 +0000</pubDate>
      <link>https://dev.to/helgard_orlm/running-the-510-gb-deepseek-v41-flash-on-an-8-gb-gpu-and-three-bugs-that-never-raise-an-error-5647</link>
      <guid>https://dev.to/helgard_orlm/running-the-510-gb-deepseek-v41-flash-on-an-8-gb-gpu-and-three-bugs-that-never-raise-an-error-5647</guid>
      <description>&lt;p&gt;My home machine for local models is modest: an &lt;strong&gt;RTX 5060 with 8 GB&lt;/strong&gt;, a Core Ultra 5 225F, 31 GiB of RAM and a Gen5 NVMe drive used only for model files. DeepSeek-V4.1-Flash is 510 GB on disk. It now runs on that box as a normal chat model in Open WebUI: &lt;strong&gt;~1.6 tokens/s reading experts from disk only, ~2.4 tokens/s with a 16 GB RAM cache.&lt;/strong&gt; That is slow, and this post is honest about why — but it works, and every speed-up keeps the output token-for-token identical to a plain version that does all the math with DeepSeek's own code.&lt;/p&gt;

&lt;p&gt;Code: &lt;strong&gt;&lt;a href="https://github.com/helgard-orlm/deepseek-v41-flash-8gb" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/deepseek-v41-flash-8gb&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who did what, up front:&lt;/strong&gt; I set the goal, chose the model, proposed some of the ideas (predicting the next layer's experts among them) and made the calls. The engine, the server and the bug hunts were done by &lt;strong&gt;Claude&lt;/strong&gt; (Anthropic) in my sessions; the overlap scheme and the one-token short path came from &lt;strong&gt;Codex&lt;/strong&gt; (OpenAI) reviewing the engine. "We" below means the three of us.&lt;/p&gt;

&lt;p&gt;This is the second model we run this way. The first, a 133 GB Qwen at 11 tokens/s, is described in &lt;a href="https://dev.to/helgard_orlm/running-a-133-gb-moe-model-on-an-8-gb-gpu-at-11-tokenss-by-streaming-experts-from-nvme-207b"&gt;the previous post&lt;/a&gt;. The comparison between the two is the most useful part of this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bytes per token decide everything
&lt;/h2&gt;

&lt;p&gt;For a mixture-of-experts model, the size on disk barely matters. What matters is how many expert bytes one token pulls in.&lt;/p&gt;

&lt;p&gt;DeepSeek-V4.1-Flash: 40 layers, 384 experts per layer, 6 chosen per token, each expert 17.93 MiB in FP4 with scales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6 experts × 40 layers × 17.93 MiB ≈ 4.2 GiB per token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Qwen model needs 1.27 GiB per token. Same machine, same disk, same method — 3.3× more bytes, and that alone explains most of the speed gap.&lt;/p&gt;

&lt;p&gt;Where the 475 GiB go:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;part&lt;/th&gt;
&lt;th&gt;size&lt;/th&gt;
&lt;th&gt;where it lives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;attention (FP8), shared experts, norms, router&lt;/td&gt;
&lt;td&gt;~6.7 GiB&lt;/td&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;routed experts (15,360 of them)&lt;/td&gt;
&lt;td&gt;269 GiB&lt;/td&gt;
&lt;td&gt;NVMe → RAM cache → GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engram tables (a per-token memory, FP8)&lt;/td&gt;
&lt;td&gt;189 GiB&lt;/td&gt;
&lt;td&gt;NVMe, ~48 rows per token, 5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;embeddings + output head&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;RAM, head computed on the CPU in FP32 like the reference&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The approach: don't rewrite the model, only move its storage
&lt;/h2&gt;

&lt;p&gt;DeepSeek publishes its reference inference code (&lt;code&gt;model.py&lt;/code&gt; + &lt;code&gt;kernel.py&lt;/code&gt;, MIT). We use it &lt;strong&gt;as is&lt;/strong&gt; for all the arithmetic and replace only where the weights come from. That has a nice consequence: there is no conversion step. In the original safetensors files each expert's three matrices already sit next to each other (17.7 MB) and its scales are one more block (1.1 MB), so an expert is two &lt;code&gt;pread&lt;/code&gt; calls with &lt;code&gt;O_DIRECT&lt;/code&gt; straight from the Hugging Face files. A read benchmark showed this already uses 94–97% of what the drive can do, so re-laying-out the data on disk had nothing to give.&lt;/p&gt;

&lt;p&gt;We first measured the whole thing on a rented RTX 5090 with VRAM artificially capped at 7.3 GiB, before buying the Gen5 drive. Once the numbers looked worth it, the model moved home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three bugs on RTX 50xx that never raise an error
&lt;/h2&gt;

&lt;p&gt;This is the part I'd want to read before trying anything similar on a Blackwell consumer card.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. TileLang 0.1.8 computes garbage on sm_120, silently.&lt;/strong&gt; The reference pins this version. On our card the FP4 matrix multiply had cosine 0.0006 to a plain torch computation (i.e. unrelated numbers), the FP8 one returned NaN, and the model happily wrote "athaatha Stone". The reference self-test passed — it checks shapes, not values. TileLang 0.1.9: cosine 0.9996.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A race in the reference &lt;code&gt;act_quant&lt;/code&gt; kernel.&lt;/strong&gt; With DeepSeek's scale format (&lt;code&gt;ue8m0&lt;/code&gt;, always on in this model) the kernel is built with &lt;code&gt;num_stages = 0&lt;/code&gt;. On the RTX 5060, for more than 64 rows, part of the FP8 output comes out NaN — and differently every time: the same input five times gave 367, then up to 2065 NaN values. Generating one token at a time is clean, so only prompts were hit. It wasn't the compiler (CUDA 12.8 did the same). &lt;code&gt;num_stages = 2&lt;/code&gt; gives zero NaN and is bit-exact with a torch implementation at 1, 17, 300 and 1024 rows. That one line is the only change the setup script makes to DeepSeek's code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The sparse attention kernel wants 141 KB of shared memory&lt;/strong&gt;; consumer Blackwell has ~99 KB. Heads are independent, so we call the same kernel in groups of 16 heads. Small trap inside: &lt;code&gt;.contiguous()&lt;/code&gt; on a head slice of a 1-token tensor doesn't change its strides, and the kernel checks strides — &lt;code&gt;clone(memory_format=torch.contiguous_format)&lt;/code&gt; does.&lt;/p&gt;

&lt;p&gt;The common lesson: on new hardware, "it ran without errors" says nothing. Compare every kernel with a dumb torch version on real weights before trusting a single generated word.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making it faster: v1 to v4
&lt;/h2&gt;

&lt;p&gt;The rule for every step: the 136 generated tokens of a fixed 4-question check must stay &lt;strong&gt;identical&lt;/strong&gt; to the plain version (v1). Anything that changed a token was rejected or fixed. To be precise about what that proves: the speed-ups changed nothing. It does not prove v1 equals DeepSeek's untouched program run side by side — that program can't load the model on 8 GB, so we never ran it. What backs v1 instead: it calls DeepSeek's own model and kernel code for every computation, the individual kernels were compared with plain torch on real weights (above), the expanded &lt;code&gt;wo_a&lt;/code&gt; matches DeepSeek's &lt;code&gt;convert.py&lt;/code&gt; bit for bit, and the answers are right (Canberra, a correct TCP/UDP explanation, working merge code).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v1 — plain streaming: 0.89 tokens/s&lt;/strong&gt; from disk, 1.17 with a 14 GB RAM cache. Read the layer's 6 experts, copy them to the GPU, wait, compute. Disk, bus and compute simply added up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v2 — overlap (Codex's scheme): 1.28.&lt;/strong&gt; A separate copy thread with CUDA events instead of a global synchronize. The surprise: issuing all 6 expert reads at once was &lt;em&gt;slower&lt;/em&gt;. They share the drive, so they all finish together at the end of the layer and computing can't start any earlier. What worked was a deep queue but experts &lt;strong&gt;one after another&lt;/strong&gt;: at most 2 in flight, each read in 4 parallel pieces. Waiting for the disk went from 0.65 to 0.31 s per token. (The first version of this had a race of its own — a ring buffer overwrote an expert that hadn't reached the GPU yet, and token 29 changed. The token check caught it.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v3 — a short path for one token: +5.7%.&lt;/strong&gt; The reference finds each expert's tokens with &lt;code&gt;torch.where(indices == e)&lt;/code&gt; — a CPU↔GPU sync, 240 times per token, and each one delays the next copy. For a single token the indices are now moved to the CPU once per layer. Same arithmetic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v4 — prefetch two experts of the next layer into the gap: 1.64 from disk (+21%), 2.3 with RAM.&lt;/strong&gt; Apply layer i+1's router to layer i's input (rescaled by the two layers' norm weights) and you get the right top-6 71% of the time. Once layer i's real reads are in flight, read the top 2 guesses that aren't already in RAM. 94% of them get used. Three or four guesses are worse: they start competing with the reads that are actually needed.&lt;/p&gt;

&lt;p&gt;One thing we got wrong along the way: I tried to reduce "bytes read in vain" by counting guesses that were already in RAM against the budget. Fewer wasted bytes, lower speed. Reads that land in the gap, while the disk would otherwise idle, are free; the only thing that matters is how many experts arrive on time.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;disk only&lt;/th&gt;
&lt;th&gt;with RAM cache&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;v1 plain&lt;/td&gt;
&lt;td&gt;0.89&lt;/td&gt;
&lt;td&gt;1.17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v2 overlap&lt;/td&gt;
&lt;td&gt;1.28&lt;/td&gt;
&lt;td&gt;1.42–1.67&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v3 short path&lt;/td&gt;
&lt;td&gt;1.35&lt;/td&gt;
&lt;td&gt;1.95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v4 prefetch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.64&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;2.28–2.34&lt;/strong&gt; (14 GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;live chat, RAM 16 GB&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2.43&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A 311-token prompt takes 18 s (27 s in v1). Peak VRAM 7.06 GiB. Follow-up messages don't re-read the conversation: the server snapshots the full model state (30 MiB — KV, the sliding window, compression tails, indexer cache, Engram history) after each answer and processes only the new tail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we measured and dropped
&lt;/h2&gt;

&lt;p&gt;Some of these were my ideas, some Claude's; all were measured on the real weights rather than argued about.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Store deltas between experts or layers, they'll compress better.&lt;/strong&gt; The FP4 codes have 3.895 bits of entropy; the difference to the same expert in the next layer, or to a neighbour, has 3.997 — worse than the original. Even after the best neuron permutation and per-neuron scaling, the remaining difference is 0.999–1.000 of the original, exactly what two random matrices give.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skip "silent" neurons and don't read their part of the down projection.&lt;/strong&gt; To keep each expert within 5% error you still need 61–75% of the neurons: ~12% fewer bytes, and the read would have to wait for the first half of the computation. This model's SwiGLU neurons simply don't go quiet the way ReLU² ones do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smarter cache policies.&lt;/strong&gt; A simulation over recorded routes: LRU beat LFU with ageing, per-layer LRU and SLRU. The theoretical optimum is ~15 points higher, but nothing simple gets near it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More reader threads, splitting reads into smaller pieces&lt;/strong&gt; — the drive was already near its limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why it stops here
&lt;/h2&gt;

&lt;p&gt;From disk, a token takes about 0.6 s, and just reading its 4.2 GiB at the 8.2 GiB/s the drive delivers on these reads is ~0.5 s of that. There's very little software left between us and that number. The only ways to go faster are fewer bytes (more RAM for the cache — the whole expert set is 269 GiB, so a cache is always partial) or a second drive. With Qwen the same method gives 11 tokens/s because each token needs a third of the bytes. If you're choosing a MoE model to stream on a home PC, look at &lt;strong&gt;bytes per token&lt;/strong&gt; before parameter count.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;You need a fast NVMe drive with ~520 GB free and a Blackwell GPU (the fixes above are for sm_120; other cards may need fewer of them). &lt;code&gt;setup.sh&lt;/code&gt; downloads the exact model revision we used if it isn't there, checks that &lt;code&gt;kernel.py&lt;/code&gt; is the expected file, and builds a copy of DeepSeek's code with the one-line fix. Before publishing, we cloned the repository fresh on the same machine, ran the setup against the downloaded model and started the server from the clone: it loaded with every parameter accounted for and answered correctly. The engine and server files are byte-identical to what runs at home. The full 510 GB download from scratch was not repeated for this test.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Engine, server, bug hunts and this write-up: Claude (Anthropic). Overlap scheme and the one-token short path: Codex (OpenAI). Goal, model choice, the expert-prediction idea and decisions: me. All numbers are from logs on the machines described above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>cuda</category>
      <category>python</category>
    </item>
    <item>
      <title>Running a 133 GB MoE model on an 8 GB GPU at 11 tokens/s by streaming experts from NVMe</title>
      <dc:creator>Helgard</dc:creator>
      <pubDate>Sat, 03 Oct 2026 11:46:52 +0000</pubDate>
      <link>https://dev.to/helgard_orlm/running-a-133-gb-moe-model-on-an-8-gb-gpu-at-11-tokenss-by-streaming-experts-from-nvme-207b</link>
      <guid>https://dev.to/helgard_orlm/running-a-133-gb-moe-model-on-an-8-gb-gpu-at-11-tokenss-by-streaming-experts-from-nvme-207b</guid>
      <description>&lt;p&gt;I run local models on one home machine: an &lt;strong&gt;RTX 5060 with 8 GB&lt;/strong&gt;, a Core Ultra 5 225F, 31 GiB of RAM and a Gen5 NVMe drive used only for model files. When NVIDIA published &lt;code&gt;Qwen3.8-Flash-Next&lt;/code&gt; in NVFP4 (133 GB on disk), the obvious answer was "it doesn't fit". It does now: it runs as a normal chat model in Open WebUI at &lt;strong&gt;9–12 tokens/s&lt;/strong&gt;, and its output matches the Hugging Face reference implementation.&lt;/p&gt;

&lt;p&gt;Code: &lt;strong&gt;&lt;a href="https://github.com/helgard-orlm/qwen-flash-next-8gb" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/qwen-flash-next-8gb&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who did what, up front:&lt;/strong&gt; I set the goal, chose the model and made the calls along the way. The engine, kernels and server were written by &lt;strong&gt;Claude&lt;/strong&gt; (Anthropic) in my sessions; the CUDA-graph speed-up was written by &lt;strong&gt;Codex&lt;/strong&gt; (OpenAI) in a parallel session, and Claude then found and fixed a crash in it. "We" below means the three of us.&lt;/p&gt;

&lt;p&gt;This post is about the method, the measurements, and what didn't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this model, of all models
&lt;/h2&gt;

&lt;p&gt;Most of the weights of a mixture-of-experts model are experts, and each token uses only a few of them. So the question is not "how big is the model" but &lt;strong&gt;how many expert bytes one token needs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Qwen3.8-Flash-Next: 48 layers, 512 experts per layer, top-10 routing, and each expert is tiny — three 2560×640 matrices, 2.70 MiB in NVFP4 with scales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10 experts × 48 layers × 2.70 MiB ≈ 1.27 GiB per token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For comparison, DeepSeek-V4.1-Flash, which we run on the same box with the same trick, needs about 4.2 GiB per token. Same disk, a third of the bytes. (How DeepSeek went — 2.4 tokens/s and three silent bugs on RTX 50xx — is in &lt;a href="https://dev.to/helgard_orlm/running-the-510-gb-deepseek-v41-flash-on-an-8-gb-gpu-and-three-bugs-that-never-raise-an-error-5647"&gt;a separate post&lt;/a&gt;, code at &lt;a href="https://github.com/helgard-orlm/deepseek-v41-flash-8gb" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/deepseek-v41-flash-8gb&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Everything else (embeddings, attention, the 36 Gated DeltaNet layers, router, shared expert, lm_head) is about 7.2 GB in BF16. Storing the big DeltaNet matrices in FP8 with one scale per row brings the resident part to &lt;strong&gt;6.13 GiB of VRAM&lt;/strong&gt;. The 63 GiB of experts live on the NVMe drive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engine
&lt;/h2&gt;

&lt;p&gt;There was no runtime for this architecture that could stream experts, so it's a from-scratch PyTorch + Triton engine. The math was ported from &lt;code&gt;transformers&lt;/code&gt; 5.18 (&lt;code&gt;qwen4_exp&lt;/code&gt;): DeltaNet, full attention with a block indexer that kicks in beyond ~2048 tokens, four hyper-connection streams, and an n-gram embedding table (PLE, used in layer 1) with 51 billion parameters.&lt;/p&gt;

&lt;p&gt;The pieces, in the order they mattered:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Repack experts for the disk.&lt;/strong&gt; In the original files an expert's six tensors are scattered. We rewrote them into one 2,764,800-byte block per expert (675 pages of 4 KiB): &lt;code&gt;gate | up | down | gate_scale | up_scale | down_scale&lt;/code&gt;. One expert = one &lt;code&gt;pread&lt;/code&gt; with &lt;code&gt;O_DIRECT&lt;/code&gt; into page-aligned pinned memory = one copy to the GPU.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A RAM cache in front of the disk.&lt;/strong&gt; LRU over pinned memory (16 GB holds about a quarter of all experts), a pool of reader threads, and two banks of GPU slots so the next layer can load while this one computes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Prefetch into the gap.&lt;/strong&gt; After a layer's required reads are issued, guess the next layer's experts and read two of them while the GPU is busy (91% of guesses are used). The first version started the prefetch &lt;em&gt;at the same time&lt;/em&gt; as the required reads — they shared the disk and everything got slower. It has to go into the gap, not next to the real work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Own kernels.&lt;/strong&gt; An NVFP4 SwiGLU expert kernel for a single token using Blackwell's hardware &lt;code&gt;cvt.rn.f16x2.e2m1x2&lt;/code&gt; instruction (115 µs per layer, 3.6e-7 from a torch reference), and an FP8 row-scaled matrix-vector kernel. The generic unpack path for the FP8 DeltaNet matrices had been costing ~106 ms per token on its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Don't read 66 GB per prompt chunk.&lt;/strong&gt; The first prompt path processed 512 tokens at a time, and each chunk needed nearly every expert, so the whole expert set was read again for every chunk. Running the whole prompt through each layer at once, with experts read in batches of 32, took a 2451-token prompt from &lt;strong&gt;71.6 s to 23.3 s&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Read only the PLE rows you need.&lt;/strong&gt; The n-gram table is a 53.7 GB file; each token needs a handful of rows. Random reads from NVMe: 0.3–0.5 ms per token. (On the HDD it would be a disk seek per row — a second AI acting as reviewer caught that the table was still on the HDD in the first run.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the time actually goes
&lt;/h2&gt;

&lt;p&gt;With every expert already in RAM, a token took 80–88 ms. Splitting it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;39 ms&lt;/strong&gt; — Python issuing GPU work (the GPU was waiting on Python, not the other way round);&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;47 ms&lt;/strong&gt; — moving 1.33 GB of experts over PCIe (measured 28.6 GB/s, PCIe 5.0 ×8);&lt;/li&gt;
&lt;li&gt;and the two ran &lt;strong&gt;one after the other&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are 52 CPU↔GPU syncs per token (mostly reading the router's chosen experts). We expected them to be the problem; measured, they are cheap. They only hurt because the expert copy for layer L can't start until layer L's router has answered. That pointed to three changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speculative copies.&lt;/strong&gt; Run layer L+1's router on layer L's MoE input. It picks the right top-10 65% of the time — enough to start most copies one layer early. (Prefetching top-16 to raise the hit rate moved more bytes over the bus than it saved.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CUDA graphs for the 36 one-token DeltaNet layers&lt;/strong&gt; (Codex). One shared memory pool: 36 private pools ran out of memory on an 8 GB card. Graphs are dropped before any prompt ≥1024 tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin the decode thread to a P-core.&lt;/strong&gt; On a hybrid CPU the unpinned thread wandered between P and E cores: 93–108 ms per token, jumping around. Pinned to core 0: a flat 81.6 ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;configuration&lt;/th&gt;
&lt;th&gt;tokens/s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;first version, experts from disk, no cache&lt;/td&gt;
&lt;td&gt;2.68&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;disk only + prefetch&lt;/td&gt;
&lt;td&gt;4.72–4.75&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM cache 16 GB + prefetch&lt;/td&gt;
&lt;td&gt;8.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;live chat, 1000-token answer, before the last three changes&lt;/td&gt;
&lt;td&gt;9.71&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;live chat, 1000-token answer, after&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11.65&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prompt processing: 24 tokens in 4.5 s, 2451 in 23.3 s, 7182 in 98 s. Follow-up messages are fast because the server snapshots the whole recurrent state (DeltaNet matrices + KV + indexer keys) in RAM and only processes the new tail.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we know it's right
&lt;/h2&gt;

&lt;p&gt;Speed without correctness is a random number generator, so every change was checked against the &lt;code&gt;transformers&lt;/code&gt; reference fed with the &lt;strong&gt;same&lt;/strong&gt; quantized weights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;next-token argmax: &lt;strong&gt;32/32&lt;/strong&gt;, mean |Δlogit| 0.077;&lt;/li&gt;
&lt;li&gt;a 2451-token prompt, long enough for the attention indexer to start selecting blocks: &lt;strong&gt;11/11&lt;/strong&gt;, and the needle-in-a-haystack fact comes back right;&lt;/li&gt;
&lt;li&gt;perplexity EN 2.21 / RU 2.22 — plus control runs that &lt;em&gt;must&lt;/em&gt; break: with nibbles swapped it's 2016 / 33,269, with experts removed 588 / 5,351. That checks the NVFP4 unpacking independently of the reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A fused DeltaNet kernel&lt;/strong&gt; (one kernel for the recurrence, 4.3 → 0.43 ms per token across 36 layers). 5 of 256 tokens came out different from the reference. Rejected — 4 ms isn't worth a model that says different things.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 8192-token context didn't actually work&lt;/strong&gt;, in the old version either: a 7182-token prompt ran out of VRAM in attention. Fixed by sizing prompt pieces so that &lt;code&gt;piece × (position + piece) ≤ 3072²&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A crash only after long prompts:&lt;/strong&gt; CUDA graph capture in the default &lt;code&gt;global&lt;/code&gt; mode was invalidated by expert-reader threads calling &lt;code&gt;event.synchronize()&lt;/code&gt;. &lt;code&gt;capture_error_mode="thread_local"&lt;/code&gt; fixed it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The repository has the engine, the server (OpenAI-compatible, streaming, a thinking-mode model id), the repack and reference-check tools, and a &lt;code&gt;setup.sh&lt;/code&gt; that prepares a working directory from a Hugging Face snapshot. Before publishing, we ran it the way a stranger would: a fresh &lt;code&gt;git clone&lt;/code&gt; of the repo on the same box, &lt;code&gt;setup.sh&lt;/code&gt; into an empty directory (15 min 48 s, mostly reading ~120 GB from an HDD; spot check of 204 repacked experts against the originals: 0 differences), then the server from the clone. The repacked experts and the extracted non-expert weights came out &lt;strong&gt;byte-identical&lt;/strong&gt; to the production directory, and three greedy test prompts gave identical answers from both.&lt;/p&gt;

&lt;p&gt;You need a Blackwell GPU (the NVFP4 kernel uses the sm_120a &lt;code&gt;e2m1&lt;/code&gt; instruction), a fast NVMe drive with ~130 GB free, and patience for a first-time setup that reads most of the 133 GB.&lt;/p&gt;

&lt;p&gt;The bigger point: on a mixture-of-experts model, "does it fit in VRAM" is the wrong question. The right one is &lt;strong&gt;bytes per token&lt;/strong&gt;, and a home PC with a fast drive can serve a lot more of them than it looks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Engine, kernels, server and this write-up: Claude (Anthropic). CUDA graphs and the speculation/pinning A/B: Codex (OpenAI). Goal, model choice and decisions: me. All numbers are from logs on the machine described above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>cuda</category>
      <category>python</category>
    </item>
    <item>
      <title>An AI hardware advisor that walks every path — and gets its math checked by the rules in Sanity</title>
      <dc:creator>Helgard</dc:creator>
      <pubDate>Sat, 03 Oct 2026 05:49:32 +0000</pubDate>
      <link>https://dev.to/helgard_orlm/an-ai-hardware-advisor-that-walks-every-path-and-gets-its-math-checked-by-the-rules-in-sanity-21db</link>
      <guid>https://dev.to/helgard_orlm/an-ai-hardware-advisor-that-walks-every-path-and-gets-its-math-checked-by-the-rules-in-sanity-21db</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path One: Ship an Agent That Queries Real Content&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;An agent that answers "what computer do I need to run &lt;em&gt;this&lt;/em&gt; AI model?" — including "don't buy, rent" and&lt;br&gt;
"you already have enough". It reads a &lt;strong&gt;Sanity dataset of criteria&lt;/strong&gt;, not a catalog: laws (formulas for weights&lt;br&gt;
memory, KV cache, speed ceiling, own-vs-rent breakeven), machine-checkable rules, 11 &lt;strong&gt;solution paths&lt;/strong&gt;&lt;br&gt;
(keep what you have · upgrade · used · one GPU · multi-GPU · unified memory · MoE experts in system RAM ·&lt;br&gt;
CPU-only small model · smaller model · cloud API · rented GPU), run reports and reference hardware.&lt;br&gt;
New models appear every week, so anything not in the base is looked up on the web &lt;strong&gt;by the base's own checklist&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sanity project: &lt;code&gt;onwa0wvs&lt;/code&gt;, public dataset &lt;code&gt;v2&lt;/code&gt; (200 documents), Studio: &lt;a href="https://hw-for-ai-lab-v2.sanity.studio" rel="noopener noreferrer"&gt;https://hw-for-ai-lab-v2.sanity.studio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent connection: &lt;strong&gt;Sanity Context MCP&lt;/strong&gt; on the full dataset with &lt;strong&gt;embeddings enabled&lt;/strong&gt;
(&lt;code&gt;/context/mcp/onwa0wvs/v2&lt;/code&gt;); all numbers below are measured through this connection&lt;/li&gt;
&lt;li&gt;Second entry: a &lt;strong&gt;Knowledge Base&lt;/strong&gt; (&lt;code&gt;kbW7wsbtJQkl&lt;/code&gt;, built from the core 138 documents of the same dataset, refreshed weekly)
behind its own Context MCP endpoint in KB mode — created and connected, &lt;strong&gt;not measured separately&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demo — try it with your own agent:&lt;/strong&gt; &lt;a href="https://hw-advisor.helgardorlm.tech" rel="noopener noreferrer"&gt;https://hw-advisor.helgardorlm.tech&lt;/a&gt; (MCP &lt;code&gt;https://hw-advisor.helgardorlm.tech/mcp&lt;/code&gt;, no login) · &lt;strong&gt;Replay of all 27 measured runs:&lt;/strong&gt; &lt;a href="https://helgard-orlm.github.io/sanity-ai-hardware-advisor/" rel="noopener noreferrer"&gt;https://helgard-orlm.github.io/sanity-ai-hardware-advisor/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/helgard-orlm/sanity-ai-hardware-advisor" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/sanity-ai-hardware-advisor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live — bring your own agent (no login, no keys):&lt;/strong&gt; &lt;a href="https://hw-advisor.helgardorlm.tech" rel="noopener noreferrer"&gt;https://hw-advisor.helgardorlm.tech&lt;/a&gt; · MCP endpoint &lt;code&gt;https://hw-advisor.helgardorlm.tech/mcp&lt;/code&gt;&lt;br&gt;
Replay of all 27 measured runs: &lt;a href="https://helgard-orlm.github.io/sanity-ai-hardware-advisor/" rel="noopener noreferrer"&gt;https://helgard-orlm.github.io/sanity-ai-hardware-advisor/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/helgard-orlm/sanity-ai-hardware-advisor" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/sanity-ai-hardware-advisor&lt;/a&gt; (the public proxy with &lt;code&gt;check_answer&lt;/code&gt; is in &lt;a href="https://github.com/helgard-orlm/sanity-ai-hardware-advisor/tree/main/public_mcp" rel="noopener noreferrer"&gt;&lt;code&gt;public_mcp/&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Project ID &lt;strong&gt;&lt;code&gt;onwa0wvs&lt;/code&gt;&lt;/strong&gt;, public dataset &lt;strong&gt;&lt;code&gt;v2&lt;/code&gt;&lt;/strong&gt; — 200 documents, 15 types (law, rule, solutionPath, aiModel, gpu, cpu, runReport, offer, cloudOffer, situationTemplate, …)&lt;/li&gt;
&lt;li&gt;Query it directly: &lt;a href="https://onwa0wvs.api.sanity.io/v2025-01-01/data/query/v2?query=*%5B_type==%22law%22%5D" rel="noopener noreferrer"&gt;https://onwa0wvs.api.sanity.io/v2025-01-01/data/query/v2?query=*[_type=="law"]&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Studio: &lt;a href="https://hw-for-ai-lab-v2.sanity.studio" rel="noopener noreferrer"&gt;https://hw-for-ai-lab-v2.sanity.studio&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it with your own agent (no keys, no login)
&lt;/h2&gt;

&lt;p&gt;The demo is a public MCP endpoint, not a hosted chatbot: &lt;strong&gt;you bring the agent, the base brings the knowledge and the checking.&lt;/strong&gt;&lt;br&gt;
Add &lt;code&gt;https://hw-advisor.helgardorlm.tech/mcp&lt;/code&gt; to ChatGPT (developer mode), Claude, Claude Code, Codex, Cursor or VS Code.&lt;br&gt;
There are one-click buttons for Cursor and VS Code on the page. Or paste one line into any agent that can run commands:&lt;br&gt;
&lt;em&gt;"Read &lt;a href="https://hw-advisor.helgardorlm.tech/setup.md" rel="noopener noreferrer"&gt;https://hw-advisor.helgardorlm.tech/setup.md&lt;/a&gt; and follow it."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Behind it is a ~230-line stdlib proxy. It passes the four &lt;strong&gt;Sanity Context MCP&lt;/strong&gt; tools through unchanged and keeps the token&lt;br&gt;
server-side. It puts today's date and the advisor's work order in front of &lt;code&gt;initial_context&lt;/code&gt;. It adds one tool, &lt;strong&gt;&lt;code&gt;check_answer&lt;/code&gt;&lt;/strong&gt;,&lt;br&gt;
which is the same validator the app uses: it recomputes every calculation with the &lt;code&gt;law.formula&lt;/code&gt; stored in Sanity.&lt;br&gt;
In the first outside test (Codex, its own subscription), &lt;code&gt;check_answer&lt;/code&gt; rejected the first draft with 4 errors. The second draft passed:&lt;br&gt;
5 calculations recomputed, 11/11 solution paths walked. A test from ChatGPT (developer mode) also went through to a passing check. Agents that can only browse get the same tools as plain HTTPS links&lt;br&gt;
(&lt;code&gt;/api/context&lt;/code&gt;, &lt;code&gt;/api/query?q=…&lt;/code&gt;, &lt;code&gt;/api/check&lt;/code&gt;), listed in &lt;code&gt;/llms.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Known limit:&lt;/strong&gt; the check covers numbers, completeness, sources and identity. It does not cover whether a verdict makes sense. In one test an agent said &lt;em&gt;yes&lt;/em&gt; to "MoE experts in RAM" for a dense model, and the check passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;p&gt;Sanity Context MCP tools the agent calls: &lt;code&gt;initial_context&lt;/code&gt;, &lt;code&gt;groq_query&lt;/code&gt;, &lt;code&gt;schema_explorer&lt;/code&gt;, &lt;code&gt;array_field_reader&lt;/code&gt; (plus our &lt;code&gt;check_answer&lt;/code&gt;). What it does with the content it retrieves — the structure is used, not just searched:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Paths are data.&lt;/strong&gt; The agent must give a yes/no/maybe with a number for &lt;em&gt;every&lt;/em&gt; &lt;code&gt;solutionPath&lt;/code&gt; document.
Before this, the same model answered a 321B-MoE question with "4×H200" and never mentioned the
one-GPU + large-RAM path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules are the search checklist.&lt;/strong&gt; For any model/card/CPU that is not in the base, the app requires the
fields that the base's &lt;code&gt;rule&lt;/code&gt; documents test for that type (e.g. &lt;code&gt;cpu.instructionSets&lt;/code&gt;, &lt;code&gt;aiModel.variants.fileGb&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laws are recomputed by code.&lt;/strong&gt; The agent writes every calculation with its inputs into a hidden check block;
the app evaluates the &lt;code&gt;law.formula&lt;/code&gt; from Sanity and sends the answer back if the number is wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The person's situation is stored&lt;/strong&gt;, not remembered: country, budget, existing PC survive a change of mind.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same code checks the &lt;strong&gt;base itself&lt;/strong&gt;: every &lt;code&gt;law&lt;/code&gt; variable and &lt;code&gt;rule&lt;/code&gt; path must be a real schema field&lt;br&gt;
(coverage check), and laws get property tests. That caught the filler model writing an own-vs-rent formula that&lt;br&gt;
&lt;em&gt;added&lt;/em&gt; electricity instead of subtracting it and billed rent for 24 h/day — &lt;strong&gt;12.3 vs 110 months&lt;/strong&gt; on the same inputs.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real trace (GLM-5.3-Flash, not in the base) — &lt;a href="https://helgard-orlm.github.io/sanity-ai-hardware-advisor/?sc=A2_not_in_base&amp;amp;run=1" rel="noopener noreferrer"&gt;open it in the replay&lt;/a&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;GROQ over the base → no &lt;code&gt;aiModel&lt;/code&gt; document → 8 web searches &lt;strong&gt;by the base's own checklist&lt;/strong&gt;: the model card and
&lt;code&gt;config.json&lt;/code&gt; (layers, KV heads, head dim), file sizes, and the GPU fields the base's rules test (slots, bandwidth, compute capability).&lt;/li&gt;
&lt;li&gt;Laws from Sanity: weights &lt;code&gt;320 × 4 / 8 = 160 GB&lt;/code&gt; (NVFP4), KV for 8k context 22.5 GiB (flagged as rough for hybrid attention).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validator round 1 sent the answer back:&lt;/strong&gt; the speed ceiling was written as &lt;strong&gt;19.91&lt;/strong&gt; tok/s — code evaluated the
&lt;code&gt;law.formula&lt;/code&gt; from Sanity with the agent's own inputs and got &lt;strong&gt;199.1&lt;/strong&gt; (a 10× slip); plus three entities missing
fields that the base's rules check (e.g. &lt;code&gt;variants.fileGb&lt;/code&gt;, &lt;code&gt;slots&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Round 2: no errors. All 11 solution paths get a verdict — multi-GPU = yes, MoE-in-RAM / unified memory = maybe, cloud = no
(the person ruled it out) — and the build is 3 × RTX PRO 6000 (288 GB) with the numbers above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Other catches by the same recomputation in the measured run: KV cache 0.949 → 6.75 GiB (A4), monthly electricity cost 0.675 → 2.025 (A5).&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured, honestly
&lt;/h2&gt;

&lt;p&gt;Blind grader (a different model, sees only the dialogue and a frozen checklist), 9 scenarios × 3 runs,&lt;br&gt;
78 checklist items, same base state for all arms:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;version&lt;/th&gt;
&lt;th&gt;score&lt;/th&gt;
&lt;th&gt;median time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;v2: base + instructions&lt;/td&gt;
&lt;td&gt;47/78&lt;/td&gt;
&lt;td&gt;62 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v3: + solution paths, validator&lt;/td&gt;
&lt;td&gt;53/78&lt;/td&gt;
&lt;td&gt;104 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;v3.1 via &lt;strong&gt;Sanity Context MCP&lt;/strong&gt;: + rules-as-checklist, recomputed laws, dated prices&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;59/78&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;181 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What this does &lt;strong&gt;not&lt;/strong&gt; show: that structured retrieval beats plain text for reading. The same 200 documents&lt;br&gt;
pasted as text notes scored the same as v3 (53/78) — at this size the model reads either. Where structure&lt;br&gt;
mattered in our runs is &lt;strong&gt;checking&lt;/strong&gt;: fewer first answers rejected by the validator with structured queries&lt;br&gt;
(8/27 vs 14/27), and the gains of v3.1 are exactly in the scenarios where code recomputes Sanity's laws&lt;br&gt;
(KV for 8 users 7/9 → 9/9, own vs rent 5/12 → 7/12, "don't buy" 4/6 → 6/6, v2 → v3.1). The price: answers take ~3× longer.&lt;br&gt;
An earlier full run of v3.1 scored 62/78 with two bugs in my own checker (nested fields, European number format) that sent&lt;br&gt;
correct answers back for rework; after fixing them the clean run scored 59/78 — with N=3 per scenario, 59 vs 62 is noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it was built
&lt;/h2&gt;

&lt;p&gt;Architect method: a &lt;strong&gt;risk catalog&lt;/strong&gt; (16 classes of agent failure seen in earlier sessions) → acceptance scenarios&lt;br&gt;
written &lt;em&gt;before&lt;/em&gt; changes → build → blind grading. The base was filled by a model (GPT Luna) from an empty schema and&lt;br&gt;
two prompts; I never wrote data by hand. An independent judge model watched the session and corrected the method&lt;br&gt;
several times (frozen checklist, snapshot baseline, text control).&lt;/p&gt;

&lt;h2&gt;
  
  
  Known limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Answers are slow (minutes) because the validator can send them back up to twice.&lt;/li&gt;
&lt;li&gt;Ratings N=3 per scenario; differences of 1–2 points are noise.&lt;/li&gt;
&lt;li&gt;"unknown after search" is allowed and can be abused for vague entities.&lt;/li&gt;
&lt;li&gt;In the measured run 12 of 27 answers came out in Russian to English questions (an account language setting leaked in);
the app now checks the answer's language in code and sends it back (3/3 English on a re-test).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>sanity</category>
      <category>ai</category>
    </item>
    <item>
      <title>We create a way to unload Qwen2.5 KV cache to RAM.</title>
      <dc:creator>Helgard</dc:creator>
      <pubDate>Thu, 11 Jun 2026 20:14:41 +0000</pubDate>
      <link>https://dev.to/helgard_orlm/we-create-a-way-to-unload-qwen25-kv-cache-to-ram-57fo</link>
      <guid>https://dev.to/helgard_orlm/we-create-a-way-to-unload-qwen25-kv-cache-to-ram-57fo</guid>
      <description>&lt;p&gt;Two separate problems with local LLMs, one mechanism fixes both:&lt;/p&gt;

&lt;p&gt;Long context doesn't fit. The KV-cache grows linearly with every token; on 8 GB it dies around 110k.&lt;br&gt;
Memory doesn't survive a restart. Kill the process and the cache is gone — next session re-reads everything from scratch (that's all RAG is: re-reading text, every query).&lt;br&gt;
What the KV-cache actually is: as the model reads, each layer stores a key/value vector per token — this is the model's working state, the thing it attends over to pick the next token. It's not text, it's computed internal state. Normally it lives entirely in VRAM and only exists while the process is alive.&lt;/p&gt;

&lt;p&gt;The mechanism:&lt;/p&gt;

&lt;p&gt;Keep only the last N tokens' KV in VRAM (the hot window).&lt;br&gt;
Stream everything older to CPU RAM (8-bit), then disk. It's not deleted, just moved somewhere 10× cheaper.&lt;br&gt;
Keep a tiny index in VRAM: one vector per sentence. On a query, that index finds the few relevant chunks and copies only those KV vectors back into VRAM for the attention step.&lt;br&gt;
So VRAM stays flat — it holds the hot window plus a small retrieval budget, regardless of whether total context is 50k or 800k. The cap moves from VRAM to your RAM stick.&lt;/p&gt;

&lt;p&gt;The part that makes it memory and not just offload: you write that cold KV to disk and reload it in a different process days later. The model resumes from its own stored state — a few ms of memcpy, no re-reading the source. Two things make the positions line up across the gap: keys are stored already rotary-rotated at their absolute position, and the query is injected at its true position, so relative distances stay exact even between tokens 700k apart.&lt;/p&gt;

&lt;p&gt;Result (RTX 5060 8 GB, 4-bit): a planted fact is the top prediction from 120k–500k tokens, top-2 at 800k, VRAM flat ~6 GB — where a normal full cache OOMs at 65k. The live store behind my assistant is 2.5M tokens of past sessions in 467 MB, answered in ~1s on CPU. Miss → LOW CONFIDENCE, not a hallucination.&lt;/p&gt;

&lt;p&gt;Why Qwen2.5-7B-1M and not anything else — two independent requirements, most models fail one:&lt;/p&gt;

&lt;p&gt;Architecture must keep a growing KV. Mamba/SSM and linear-attention models (Granite-4, RWKV, Qwen3.5's hybrid layers) compress history into one fixed-size state — there's no per-token KV to stream out or pull back. The whole method is inapplicable to them.&lt;br&gt;
The model must actually read at that range. Effective context is ~50–70% of the advertised number (RULER). MiniCPM-1B (128k nominal) goes to garbage by ~120k even with a perfect full cache in VRAM — that's the model's limit, not the cache's. No infrastructure fixes it.&lt;br&gt;
Qwen2.5-7B-1M passes both and fits 8 GB in 4-bit. Substrate alone OOMs at 65k; the model alone can't persist across restarts. The combination is the whole point.&lt;/p&gt;

&lt;p&gt;Limits: confidence drops with distance (p 0.98 → 0.21 at 800k); many near-identical facts confuse the cheap index (need full attention there); on messy logs generation can override the retrieved KV with its own prior, so I quote stored text verbatim instead. Research PoC — the core (spilling KV to CPU) has precedent (ArkVale, RetrievalAttention), credited in the README.&lt;/p&gt;

&lt;p&gt;Code (MIT): &lt;a href="https://github.com/helgard-orlm/living-kv-cache" rel="noopener noreferrer"&gt;https://github.com/helgard-orlm/living-kv-cache&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>performance</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
