<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/feeds/rss-style.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Dr. TMA Pai Endowment Chair - ITIS</title>
        <link>https://blog.ecitis.org/</link>
        <description>Official website for Dr. TMA Pai Endowment Chair - ITIS</description>
        <lastBuildDate>Fri, 25 Sep 2026 08:32:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Astro</generator>
        <language>en-US</language>
        <image>
            <title>Dr. TMA Pai Endowment Chair - ITIS</title>
            <url>https://blog.ecitis.org/og/og-logo.png</url>
            <link>https://blog.ecitis.org/</link>
        </image>
        <copyright>Copyright © 2026 Dr. TMA Pai Endowment Chair - ITIS</copyright>
        <atom:link href="https://blog.ecitis.org/rss.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Optimizing This Blog: Fonts, SVG Artwork, and Navigation]]></title>
            <link>https://blog.ecitis.org/how-we-made-this-blog-faster/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/how-we-made-this-blog-faster/</guid>
            <pubDate>Thu, 24 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explain the font, SVG, stylesheet, and navigation changes that reduce the work of loading and rendering this Astro site.]]></description>
            <content:encoded><![CDATA[<p>This blog is a static Astro site with long technical articles, diagrams, mathematical notation, and generated artwork. Its performance costs sit mainly in the resources needed to display that content: fonts, document markup, styles, and the work of laying out a page after navigation.</p>
<p>The September 24 update reduced the font loaded on every page, moved repeated directory artwork into a shared SVG, and changed how styles and offscreen content are handled. It also made prefetching follow the structure of the index, where featured articles and directory rows have different browsing patterns.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-implementation" id="user-content-fnref-implementation">1</a></sup></p>
<p>The changes address different parts of the path from a request to readable content. Their file-size effects can be measured directly, while their mechanisms explain where the browser can avoid or reuse work.</p>
<h2 id="the-documents-critical-path">The document’s critical path</h2>
<p>A static page still needs to be parsed and rendered. The browser receives HTML, discovers its styles and fonts, constructs the document, computes layout, and paints. During navigation, some resources can be reused, but the new article introduces its own markup and layout.</p>
<p>This creates several independent costs. Large HTML takes work to parse. A font discovered late can delay final typography. Repeated artwork expands the document even when it compresses well on the network. A stylesheet for a feature absent from the page adds processing without contributing to that page’s appearance.</p>
<p>The <a href="https://github.com/DrTMAPaiEndowmentChair-ITIS/blog/commit/8e5049f">implementation commit</a> records the exact changes.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-implementation" id="user-content-fnref-implementation-2">1</a></sup> The payload measurements below refer to its 70-post publication build. Subsequent article revisions change document sizes, while the underlying resource choices remain inspectable in the source.</p>
<h2 id="the-inter-subset">The Inter subset</h2>
<p>The site previously preloaded a 345,588-byte Inter WOFF2 file. The Latin subset used for browser text is 100,176 bytes, a reduction of 245,412 bytes, or about 71 percent, for that font resource.</p>
<p>Subsetting works because the browser text uses only part of the original font’s glyph coverage. The <code>unicode-range</code> declaration identifies the characters served by the face, and <code>font-display</code> determines how text appears while the font is loading. Preloading makes the font discoverable before the browser would otherwise find it through the rendered text.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-fonts" id="user-content-fnref-fonts">2</a></sup></p>
<p>The original Inter file remains available to social-card generation, which has a different coverage requirement. Browser text and build-time image rendering therefore use separate resources suited to their inputs.</p>
<p>This saving applies most directly to a first visit that needs the font. A warm visit may reuse an existing response from the browser cache. Page latency also includes HTML, styles, layout, and other resources, so the percentage reduction in the font file describes one component of the load.</p>
<h2 id="font-selection-and-layout">Font selection and layout</h2>
<p>The update also serves selected mono faces locally instead of importing a broader font package. Body text, headings, and code have different font requirements; loading the faces used by those elements makes the dependency explicit.</p>
<p>A preload gives a resource priority. The useful set is the faces needed early in the page, since preloading every available weight makes them compete for network capacity. The selected heading weight and body face are more likely to contribute to the first viewport than a face used only deep in an article.</p>
<p>Coverage affects layout as well as download size. Accented names, punctuation, code symbols, and non-Latin characters can invoke fallback fonts when a subset does not contain them. Different metrics can change line breaks. That is why the subset and the page’s actual character coverage are considered together.</p>
<h2 id="shared-svg-artwork">Shared SVG artwork</h2>
<p>The directory originally embedded each row’s generated SVG paths directly in the homepage. The new implementation stores those definitions in <code>/thumbnails.svg</code> and gives each row a small <code>&lt;use&gt;</code> reference. Featured cards and article banners remain inline.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-implementation" id="user-content-fnref-implementation-3">1</a></sup></p>
<p>On the same 70-post corpus, the publication build’s homepage was approximately 177 KB. Expanding the row references back into inline artwork would make it approximately 309 KB, a difference of about 132 KB in the document. The shared sprite was about 143 KB uncompressed and 13 KB with gzip.</p>
<p>The distinction between document size and transfer size is important here. Gzip compresses repeated markup effectively, so moving paths into a separate file does not remove 132 KB from the compressed first-visit transfer. The cold transfer totals are similar. The main document contains less markup to parse, and the artwork becomes a separately reusable resource.</p>
<p>The cache boundary now follows the content boundary. A change to article text does not inherently change the SVG definitions, and a page using a row reference does not have to repeat the paths in its HTML. The actual reuse still follows the response’s cache policy and validation behavior.</p>
<h2 id="rendering-the-sprite-in-context">Rendering the sprite in context</h2>
<p>Each row retains its dimensions and surrounding theme styles. The external symbol supplies the drawing. This preserves the role of the artwork while changing where its markup lives.</p>
<p>The important rendering cases are the light and dark themes, a fresh index load, and returning to the index through client navigation. A valid <code>&lt;use&gt;</code> element establishes the reference, while the visible paths establish that the resource loaded and inherited the expected appearance.</p>
<p>Article banners stay inline because they represent one article at a time. Downloading the entire directory sprite for a single banner would introduce unrelated artwork into that request. Keeping the two representations separate follows their reuse patterns.</p>
<h2 id="math-styles-as-an-article-dependency">Math styles as an article dependency</h2>
<p>The Markdown pipeline records whether an article contains math. The article layout uses that result to include a separate KaTeX stylesheet. Pages without math avoid the dependency; pages with equations load the rules and font resources needed to display them.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-implementation" id="user-content-fnref-implementation-4">1</a></sup></p>
<p>This is a build-time decision. The browser receives the appropriate stylesheet link with the article rather than discovering the requirement through a later script.</p>
<p>Currency notation makes the classification slightly more involved. Markdown math delimiters also use dollar signs, so money prose can be parsed as inline math. The accompanying currency pass recognizes common price patterns before the page’s math flag is determined. Actual equations retain the math path.</p>
<p>Client navigation exercises the dependency in both directions. A math article needs its stylesheet when reached from a non-math page, and a fresh context establishes that the page is not relying on a stylesheet retained from an earlier visit. These cases connect the build-time classification to the browser’s resource lifecycle.</p>
<h2 id="offscreen-rendering">Offscreen rendering</h2>
<p>Long articles contain blocks with substantial layout and paint work: code listings, figures, tables, equations, and diagrams. The stylesheet applies <code>content-visibility: auto</code> to selected blocks and supplies an estimated intrinsic size.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-visibility" id="user-content-fnref-visibility">3</a></sup></p>
<p>Supporting browsers can skip some rendering work for those blocks while they are offscreen. The content remains in the document, and its network bytes are still part of the page. The saving is in when rendering work is performed.</p>
<p>The intrinsic-size estimate gives the browser a provisional layout before it renders the block. As the actual content becomes relevant, its true dimensions replace that estimate. Tall code blocks, wide tables, and anchor navigation make this behavior visible, because their geometry affects the position of surrounding content.</p>
<p>The table of contents also moved from a global per-scroll update loop to intersection-observer-driven updates. The top gradient uses a CSS scroll-driven animation where supported. These changes reduce the amount of JavaScript tied directly to scrolling while preserving the navigation and visual behavior.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-implementation" id="user-content-fnref-implementation-5">1</a></sup></p>
<h2 id="prefetching-follows-browsing-intent">Prefetching follows browsing intent</h2>
<p>Astro supports prefetch strategies based on hover, focus, viewport entry, and other triggers.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-prefetch" id="user-content-fnref-prefetch">4</a></sup> The index uses viewport-based prefetching for featured links and hover or focus for directory rows.</p>
<p>The difference follows the page’s structure. Featured articles are a small set of likely destinations. The directory is a large collection that readers may scan without opening most of it. Warming every row as it enters the viewport can transfer many articles that are never visited.</p>
<p>Hover and focus provide a more selective signal for the directory. They allow the browser to begin fetching a chosen article before activation. A touch interaction has less advance notice, so its request sequence can differ from the pointer path.</p>
<p>Prefetching and prerendering perform different amounts of work. Prefetching obtains resources in advance; prerendering can prepare a document for a later navigation.<sup><a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fn-speculation" id="user-content-fnref-speculation">5</a></sup> The configuration enables Astro’s supported client-prerender path, with actual behavior determined by browser support and policy.</p>
<p>The useful tradeoff is between work before the click and time after it. Recording prefetched bytes and subsequent visits establishes how much of the background work contributes to navigation. A lower click-to-content time and a smaller total browsing transfer are related but distinct objectives.</p>
<h2 id="reproducing-the-payload-measurements">Reproducing the payload measurements</h2>
<p>The current build can be inspected with:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">bun</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> --frozen-lockfile</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">bun</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> build</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">bun</span><span style="color:var(--astro-code-token-string)"> -e</span><span style="color:var(--astro-code-token-string-expression)"> &#39;import { statSync } from &quot;node:fs&quot;; for (const p of [&quot;dist/index.html&quot;, &quot;dist/thumbnails.svg&quot;, &quot;public/fonts/Inter.woff2&quot;, &quot;public/fonts/Inter-latin.woff2&quot;]) console.log(p, statSync(p).size)&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">gzip</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string)"> dist/index.html</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-function)"> wc</span><span style="color:var(--astro-code-token-string)"> -c</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">gzip</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string)"> dist/thumbnails.svg</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-function)"> wc</span><span style="color:var(--astro-code-token-string)"> -c</span></span></code></pre>
<p>The file sizes separate raw document storage from compressed transfer payload. The same corpus and build settings isolate the effect of an implementation change. Comparing an older 61-post index with a newer 70-post index would combine engineering changes with content growth.</p>
<p>Browser verification adds the execution context: a fresh homepage, a warm article switch, a math article, and a long mobile page. The publication checks covered article rendering, artwork, math-style loading, and mobile overflow. These establish that the changed resource paths still deliver the intended content.</p>
<p>Field timing answers the remaining performance question. Click-to-usable-content distributions, separated by device and cache state, show how the resource changes affect readers. The established result is the reduced font resource and lighter homepage markup, together with more selective loading and rendering. Each addresses a specific cost in delivering the same technical material.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-implementation">
<p>Site source, <a href="https://github.com/DrTMAPaiEndowmentChair-ITIS/blog/commit/8e5049f" rel="noopener noreferrer">commit 8e5049f</a>, September 24, 2026. Fonts, thumbnail sprite, conditional math styles, rendering, and navigation changes. <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-implementation" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-implementation-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-implementation-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-implementation-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-implementation-5" class="data-footnote-backref">↩<sup>5</sup></a></p>
</li>
<li id="user-content-fn-fonts">
<p>Ilya Grigorik, <a href="https://web.dev/articles/optimize-webfont-loading" rel="noopener noreferrer">Optimize WebFont loading and rendering</a>, web.dev. Subsetting, preloading, and font-display behavior. <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-fonts" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-visibility">
<p>MDN, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/content-visibility" rel="noopener noreferrer">content-visibility</a>. Rendering containment and offscreen behavior. <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-visibility" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-prefetch">
<p>Astro, <a href="https://docs.astro.build/en/guides/prefetch/" rel="noopener noreferrer">Prefetch</a>. Supported prefetch strategies and configuration. <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-prefetch" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-speculation">
<p>MDN, <a href="https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Speculative_loading" rel="noopener noreferrer">Speculative loading</a>. Distinctions between prefetching and prerendering. <a href="https://blog.ecitis.org/how-we-made-this-blog-faster/#user-content-fnref-speculation" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Performance</category>
            <category>Web engineering</category>
            <category>This site</category>
            <enclosure length="74096" type="image/png" url="https://blog.ecitis.org/open-graph/how-we-made-this-blog-faster.png"/>
        </item>
        <item>
            <title><![CDATA[The AI Frontier: Capability, Efficiency, and Access]]></title>
            <link>https://blog.ecitis.org/crossing-the-ai-frontier/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/crossing-the-ai-frontier/</guid>
            <pubDate>Wed, 23 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Examine how capability, efficiency, reliability, and access shape the AI frontier and the different views of its development.]]></description>
            <content:encoded><![CDATA[<p>The AI frontier describes the best capabilities available under a set of constraints. Those constraints can be cost, latency, hardware, access, or the reliability required to complete a task. An improvement moves the frontier when it makes a previously unavailable combination possible: a more capable model at the same cost, a useful model on smaller hardware, or an agent that can complete a longer piece of work.</p>
<p>This gives the term several related meanings. Benchmark progress measures capability on defined tasks. Efficiency progress changes the resources needed to obtain a result. Deployment progress makes the capability available in a working system. The distinction explains why different accounts of the frontier emphasize different kinds of progress.</p>
<h2 id="the-quality-cost-frontier">The quality-cost frontier</h2>
<p>In a quality-cost comparison, a system is dominated when another is at least as good on both dimensions and strictly better on one. The remaining systems form a Pareto frontier. Several systems can belong to it because a gain in quality may require more time or compute.</p>
<p>Consider an illustrative document-extraction workload:</p>



































<div class="table-wrap"><table><thead><tr><th>System</th><th>Correct documents</th><th>Cost per document</th><th>p95 latency</th></tr></thead><tbody><tr><td>A</td><td>94%</td><td>1 unit</td><td>1 second</td></tr><tr><td>B</td><td>97%</td><td>3 units</td><td>2 seconds</td></tr><tr><td>C</td><td>95%</td><td>4 units</td><td>3 seconds</td></tr><tr><td>D</td><td>92%</td><td>0.2 units</td><td>0.4 seconds</td></tr></tbody></table></div>
<p>B dominates C under these measurements. A, B, and D remain useful at different operating points. A new system matching B’s quality at A’s cost would expand the set of choices even without introducing a new category of task.</p>
<p>The definition of correctness determines the comparison. Requiring every field in a document to be correct is stricter than averaging field-level accuracy. Abstentions and human review also change the cost of a completed result. A frontier is therefore attached to a task and measurement procedure, rather than to a model name alone.</p>
<p>Adding privacy, memory, or hardware requirements introduces more dimensions. A local model can occupy a useful point even if a hosted system has higher maximum quality, because local execution satisfies a constraint that the hosted option does not.</p>
<h2 id="capability-and-usable-reliability">Capability and usable reliability</h2>
<p>A capability result establishes that a system can perform a task under specified conditions. Reliability describes how consistently it succeeds across the conditions that matter to the application. Deployment combines that behavior with tools, data access, recovery, and an operating budget.</p>
<p>A coding benchmark may provide a prepared repository, a precise issue, and an executable test suite. A deployed coding agent may also need to identify the repository, interpret an incomplete request, preserve unrelated changes, and verify the resulting application. The model contributes to all of this, but the surrounding execution system determines which capabilities become usable.</p>
<p>HELM evaluates models across multiple scenarios and dimensions, including properties beyond accuracy.<sup><a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fn-helm" id="user-content-fnref-helm">1</a></sup> This supports a broader account of improvement: calibration matters when confidence determines an action, robustness matters when inputs vary, and efficiency matters when the application must serve many requests.</p>
<p>A reproducible comparison identifies the model revision, prompt, tools, retry policy, time budget, and evaluator. Improvements in any of those can move the system-level frontier. Recording them separates a model improvement from an improvement in the way the model is used.</p>
<h2 id="task-duration-as-a-capability-measure">Task duration as a capability measure</h2>
<p>Longer tasks introduce state, dependencies, and opportunities for recovery. METR’s work measures agent performance against task duration as experienced by human professionals, at a specified success probability.<sup><a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fn-horizon" id="user-content-fnref-horizon">2</a></sup> The human duration provides a measure of task difficulty; it is distinct from the time the AI spends executing it.</p>
<p>This makes a capability claim more specific. A 50-percent-success horizon and an 80-percent-success horizon describe different levels of reliability on the same task distribution. Their relationship is useful for understanding how performance changes as work becomes longer.</p>
<p>A simple model illustrates the compounding effect. If a workflow required 50 independent steps and each succeeded with probability 0.99, the probability that every step succeeded would be about 0.605. At 0.999 per step it would be about 0.951.</p>
<p>Real workflows have correlated errors, unequal step difficulty, and recovery, so the calculation is a simplified example. Its significance is that local competence and completed-task reliability are different quantities. An agent that detects and repairs a failed tool call can improve the second without changing the first.</p>
<p>Long-task evaluation therefore includes completed artifacts, interventions, recovery behavior, and the state retained between steps. These measurements connect progress in the model to the amount of work the system can reliably carry forward.</p>
<h2 id="amodei-and-the-rate-of-capability-growth">Amodei and the rate of capability growth</h2>
<p>Dario Amodei’s September 2026 essay treats the frontier as a rate-of-progress problem. His concern is that increasingly capable AI can accelerate subsequent AI development faster than alignment and operational safeguards improve. He proposes embedded external evaluators, coordination among companies in democratic countries, and broader international coordination.<sup><a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fn-amodei" id="user-content-fnref-amodei">3</a></sup></p>
<p>The embedded-evaluator proposal gives the argument an operational component. Ongoing access to development and evaluation processes would allow outside teams to examine adherence to commitments and report findings. Coordination would then have a source of evidence beyond each company’s own account.</p>
<p>This position connects capability advancement to the time available for evaluation, alignment, and institutional response. The relevant boundary is the pace at which systems gain consequential abilities relative to the mechanisms used to understand and manage them.</p>
<h2 id="openai-and-coordination-around-deployment">OpenAI and coordination around deployment</h2>
<p>Sam Altman and Jakub Pachocki’s June 2026 plan also addresses coordination, including a proposed international body that could respond when safety and social resilience fall behind frontier development. OpenAI’s Frontier Governance Framework describes risk assessment, outside input, and incident response.<sup><a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fn-openai-plan" id="user-content-fnref-openai-plan">4</a></sup></p>
<p>The emphasis is on how decisions about powerful systems are made and which institutions participate. Assessments and response procedures connect a capability result to a deployment decision. A model’s measured ability becomes one input to a broader process involving safeguards and the conditions of use.</p>
<p>These proposals overlap with Amodei’s concern about the relationship between capability and preparedness, while differing in their mechanisms. The comparison turns on evaluator access, decision authority, and the information available to external participants. The essays and frameworks are primary sources for the positions their authors advocate.</p>
<h2 id="huang-and-the-infrastructure-frontier">Huang and the infrastructure frontier</h2>
<p>Jensen Huang’s five-layer account of AI places models within a larger system of energy, chips, infrastructure, and applications.<sup><a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fn-nvidia" id="user-content-fnref-nvidia">5</a></sup> In this view, the frontier moves through the capacity to train, serve, and apply increasingly capable systems.</p>
<p>NVIDIA’s <a href="https://blogs.nvidia.com/blog/ai-future-open-and-proprietary/">account of open and proprietary AI</a> presents both as parts of that development. Open models broaden the set of organizations that can adapt and deploy AI, while proprietary systems continue to contribute capabilities and products.</p>
<p>The infrastructure view explains a different constraint on progress. A capability that requires unavailable compute or uneconomic serving costs has limited reach. Better accelerators, networking, runtimes, and model efficiency can make the same capability useful at a much larger scale.</p>
<p>This is closely related to the quality-cost frontier. Reducing the resources required for an accepted result expands the feasible set of applications, even when the maximum benchmark score is unchanged.</p>
<h2 id="delangue-and-access-to-the-frontier">Delangue and access to the frontier</h2>
<p>Clément Delangue’s <a href="https://huggingface.co/blog/clem/opensourceairobotics">account of open-source AI robotics</a> describes openness across models, datasets, and hardware. The practical effect is that more participants can inspect, modify, and build on the components of a system.</p>
<p>Hugging Face’s summer 2026 open-model report adds evidence about how that ecosystem is used. Its observations distinguish attention around new releases from download activity concentrated in established models.<sup><a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fn-open-models" id="user-content-fnref-open-models">6</a></sup> A model can attract interest as a frontier release while smaller, older models continue to support much of the deployed work.</p>
<p>Access therefore has several layers. Weights allow local execution and adaptation. A suitable license permits the intended use. Runtime support and hardware make execution practical. Data and methodological detail help other teams understand and reproduce the result.</p>
<p>These layers determine how far a capability spreads. An open model that fits existing machines can change who is able to build an application, while a larger model may primarily change what the best-resourced teams can attempt.</p>
<h2 id="from-a-general-capability-to-a-local-application">From a general capability to a local application</h2>
<p>A university maintenance assistant illustrates how the different frontiers meet. The model must recognize urgent failures, understand the languages used in requests, and route uncertain cases to staff. The application also needs data access, a review process, and enough serving capacity to respond within its deadline.</p>
<p>A held-out set of ordinary and difficult cases measures the capability relevant to that setting. Fixing the review budget and response-time target makes the comparison operational. Missed urgent cases and harmless routing errors have different consequences, so their rates remain separate in the result.</p>
<p>A shadow deployment can measure predictions before they affect real queues. If the system reduces review work while preserving the required handling of urgent cases, it has moved an operational boundary. Better inference efficiency may make that result affordable; local deployment may make its data handling acceptable; recovery may make it reliable across longer workflows.</p>
<p>This is how frontier progress becomes usable. Capability, efficiency, access, and operating reliability each remove a different constraint. Describing which constraint changed makes the result understandable to the next team trying to build with it.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-helm">
<p>Liang et al., <a href="https://arxiv.org/abs/2211.09110" rel="noopener noreferrer">Holistic Evaluation of Language Models</a>, 2022. Multi-scenario, multi-metric evaluation. <a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fnref-helm" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-horizon">
<p>METR, <a href="https://metr.org/blog/2025-03-19-measuring-ai-ability-to-complete-long-tasks/" rel="noopener noreferrer">Measuring AI Ability to Complete Long Software Tasks</a>, March 19, 2025. Human task duration, success-probability thresholds, and methodological limitations. <a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fnref-horizon" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-amodei">
<p>Dario Amodei, <a href="https://darioamodei.com/post/we-must-pace-the-frontier" rel="noopener noreferrer">We Must Pace the Frontier</a>, September 2026. The author’s policy argument. <a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fnref-amodei" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-openai-plan">
<p>Sam Altman and Jakub Pachocki, <a href="https://openai.com/index/built-to-benefit-everyone-our-plan/" rel="noopener noreferrer">Built to benefit everyone: our plan</a>, and OpenAI, <a href="https://openai.com/index/openai-frontier-governance-framework/" rel="noopener noreferrer">Frontier Governance Framework</a>. Coordination proposals and governance commitments. <a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fnref-openai-plan" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-nvidia">
<p>Jensen Huang, <a href="https://blogs.nvidia.com/blog/ai-5-layer-cake/" rel="noopener noreferrer">AI Is a 5-Layer Cake</a>, NVIDIA. Infrastructure-centered account of AI development. <a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fnref-nvidia" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-open-models">
<p>Hugging Face, <a href="https://huggingface.co/blog/state-of-open-models-summer-2026" rel="noopener noreferrer">State of Open Models: Summer 2026 Observations</a>. Open-model ecosystem evidence. <a href="https://blog.ecitis.org/crossing-the-ai-frontier/#user-content-fnref-open-models" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>AI frontier</category>
            <category>Open models</category>
            <category>AI policy</category>
            <enclosure length="71028" type="image/png" url="https://blog.ecitis.org/open-graph/crossing-the-ai-frontier.png"/>
        </item>
        <item>
            <title><![CDATA[Persistent Agent Architectures: State, Recovery, and Execution]]></title>
            <link>https://blog.ecitis.org/persistent-agent-harnesses/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/persistent-agent-harnesses/</guid>
            <pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explain the durable state, checkpoints, leases, and action recovery that let agents continue work across process restarts.]]></description>
            <content:encoded><![CDATA[<p>A persistent agent separates the lifetime of a task from the lifetime of the process running it. The task can wait for a build, pause for input, survive a worker restart, and continue with the same objective and working state. Persistence comes from the software around the model: durable records, recoverable execution, and a defined path from an unfinished action to its next step.</p>
<p>Anthropic’s managed-agent architecture separates the session log, the harness that runs the model/tool loop, and the sandbox that executes work.<sup><a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fn-managed" id="user-content-fnref-managed">1</a></sup> Meta’s Muse architecture makes another implementation of the same broad distinction, separating the agent runtime from durable application state, credentials, and permission enforcement.<sup><a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fn-muse-design" id="user-content-fnref-muse-design">2</a></sup></p>
<p>These boundaries allow a long-running task to outlive any individual model call or worker. They also make failures local: a lost process becomes something the system can recover from using the records it already holds.</p>
<h2 id="the-task-state-machine">The task state machine</h2>
<p>A task moves through a small set of states:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>queued -&gt; running -&gt; waiting_on_tool -&gt; running -&gt; done</span></span>
<span class="line"><span>                   -&gt; needs_user</span></span>
<span class="line"><span>                   -&gt; failed -&gt; retry or stop</span></span></code></pre>
<p>Each state identifies what can happen next. A running task belongs to a worker. A waiting task records the operation or event that can wake it. A task needing user input retains the question and the point at which work paused. Completion records the resulting artifact and the checks that establish it.</p>
<p>The task record contains the objective, owner, allowed operations, deadline, remaining budget, and current status. These fields remain authoritative even when the model’s context is compacted. A summary can help the next model call understand the task, while the durable record determines what it is permitted and expected to do.</p>
<p>An event log records transitions such as <code>tool_requested</code>, <code>tool_completed</code>, <code>checkpoint_saved</code>, and <code>user_approved</code>. Sequence numbers establish their order. The current state can be reconstructed from those events or stored alongside them for efficient lookup.</p>
<h2 id="session-state-and-working-files">Session state and working files</h2>
<p>Three forms of state have different roles:</p>

























<div class="table-wrap"><table><thead><tr><th>State</th><th>Purpose</th><th>Example</th></tr></thead><tbody><tr><td>Task state</td><td>Ownership, objective, and progress</td><td>Waiting for deployment 123</td></tr><tr><td>Session history</td><td>Observations, decisions, and tool results</td><td>Build output and the action that produced it</td></tr><tr><td>Artifact state</td><td>The work being created or changed</td><td>A repository revision or saved report</td></tr></tbody></table></div>
<p>The distinction makes recovery precise. A worker can reconstruct its context from the session history, identify the current artifact revision, and resume according to the task state. A container’s filesystem alone does not contain all three guarantees.</p>
<p>A small implementation can represent them with a few tables:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>tasks(id, objective, status, owner, lease_until, version, deadline, budget)</span></span>
<span class="line"><span>events(task_id, sequence, kind, payload, created_at)</span></span>
<span class="line"><span>actions(id, task_id, request_key, status, external_id, result)</span></span>
<span class="line"><span>checkpoints(task_id, last_sequence, summary, artifact_revision)</span></span></code></pre>
<p>Unique event sequences and action request keys give each record a stable identity. A transaction can update the task state and append its corresponding event together, avoiding a status change without an explanation in the log.</p>
<h2 id="checkpoints-and-context-reconstruction">Checkpoints and context reconstruction</h2>
<p>A checkpoint records the last processed event, a compact account of the work, and the artifact revision it describes. Recovery loads that checkpoint and the subsequent events. The worker does not need to replay every model call to discover the next unfinished operation.</p>
<p>Raw events remain useful after compaction. A summary may omit the details of a failed command or the reason an alternative was rejected. Keeping the original result allows the agent or a reviewer to retrieve it when it becomes relevant again. Anthropic’s managed-agent design explicitly separates durable session history from the model’s working context.<sup><a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fn-managed" id="user-content-fnref-managed-2">1</a></sup></p>
<p>Long-term facts have a different lifecycle from task events. A project decision, user preference, or file pointer benefits from a source and update time. Retrieval selects the relevant facts for the current task, while the event log retains the complete execution record.</p>
<p>Model-level context management complements this storage. Meta describes context compaction and subagent coordination in <a href="https://ai.meta.com/blog/introducing-muse-spark-meta-model-api/">Muse Spark 1.1</a>. The model can use context more effectively, while the surrounding system preserves information across restarts and decides which state remains current.</p>
<h2 id="recovery-across-an-external-action">Recovery across an external action</h2>
<p>The most difficult restart boundary occurs after an external action succeeds but before the worker records the result. Consider an agent submitting a report. The remote service accepts it, then the worker crashes before saving the returned identifier. Locally, the action still appears unfinished.</p>
<p>Recording the request before sending it establishes intent, but the outcome is uncertain until it can be reconciled. Recovery follows one of three paths:</p>
<ol>
<li>Retry with the same idempotency key under the external service’s documented contract.</li>
<li>Query the service using a stable identifier or request metadata.</li>
<li>Retain an uncertain state for resolution when neither operation is available.</li>
</ol>
<p>Stripe’s idempotency API is a concrete example of the first mechanism.<sup><a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fn-idempotency" id="user-content-fnref-idempotency">3</a></sup> The key identifies the operation across attempts. Its retention period, parameter matching, and handling of errors determine which retries the guarantee covers.</p>
<p>A local events table and a remote API usually do not share a transaction. The action record bridges them by retaining the information needed to discover what happened. This is what prevents an ordinary process restart from becoming a duplicate message, report, or purchase.</p>
<h2 id="worker-leases-and-fencing">Worker leases and fencing</h2>
<p>A lease assigns a task to a worker for a limited period. The worker renews it while active; after expiry, another worker can take over. This provides recovery when a process disappears without releasing its task.</p>
<p>Expiry does not guarantee that the original process has stopped. A paused worker can resume after a new owner has started. A monotonically increasing ownership version resolves that case: each state-changing operation includes the version, and the store rejects an older owner’s writes.</p>
<p>This fencing check also matters at the tool boundary. Rejecting a stale database write after an external action has already happened cannot undo the action. The executor needs enough ownership information to reject stale work before performing it.</p>
<p>Artifact revisions provide a related check for collaborative work. A checkpoint can name a commit or content hash. On resume, a difference between that revision and the current workspace identifies changes made by another worker or a person. Recovery can then reconcile the new state rather than restoring an obsolete copy.</p>
<h2 id="the-restart-sequence">The restart sequence</h2>
<p>With these records in place, resuming a task has a defined sequence:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>claim task and obtain the current ownership version</span></span>
<span class="line"><span>load objective, permissions, checkpoint, and later events</span></span>
<span class="line"><span>reconcile pending actions with their external systems</span></span>
<span class="line"><span>compare artifact revision with the current workspace</span></span>
<span class="line"><span>construct context from the current task and relevant evidence</span></span>
<span class="line"><span>continue within the remaining budget</span></span></code></pre>
<p>The sequence resolves uncertainty before asking the model for its next action. It also reduces unnecessary work: a completed deployment or tool call can be observed and recorded rather than launched again.</p>
<p>Waiting becomes inexpensive under this design. A task waiting for a build does not need a continuously running model loop. A completion event can wake a replaceable worker, which resumes from the durable state.</p>
<h2 id="permission-as-application-state">Permission as application state</h2>
<p>An approval has a scope: the action, destination, and artifact it authorizes. Binding it to the action arguments or an artifact revision makes it usable during recovery. The worker can determine whether the operation now being attempted is the one that was approved.</p>
<p>Authorization is enforced by the executor. A tool may technically support deletion while the current task permits only reading. The task’s permission record restricts the operation independently of what the model proposes.</p>
<p>Meta’s published Muse design places permission enforcement and credential handling outside its agent runtime.<sup><a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fn-muse-design" id="user-content-fnref-muse-design-2">2</a></sup> That separation lets the application check external actions without making the generated code responsible for protecting the credentials it could otherwise access.</p>
<p>Provenance serves a similar role for information. A document or web page is task data, even when its contents resemble instructions. Retaining that distinction in context and checking actions against the task record limits how external text can redefine the work.</p>
<h2 id="completion-and-evaluation">Completion and evaluation</h2>
<p>Completion can be expressed as a predicate over recorded evidence. A code task may require a saved diff and passing checks. A research task may require a report with source-backed claims. A deployment task may require a successful build and a verified live artifact.</p>
<p>Anthropic’s long-running development work treats evaluation as part of the agent system.<sup><a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fn-long-running" id="user-content-fnref-long-running">4</a></sup> Separating the evaluator from the worker’s self-assessment gives the completion record meaning beyond the model saying it has finished.</p>
<p>Recovery tests exercise the same design at its boundaries: before a tool call, during execution, after remote completion, and after checkpoint persistence. Duplicate wake events, expired leases, and revoked permissions test whether the task remains coherent under changes in ownership and authority.</p>
<p>Budgets bound the lifetime of unsuccessful work. Elapsed time, model spending, tool invocations, and repeated failures can each have a limit. A transient transport error can be retried; an invalid request or revoked permission needs a different transition.</p>
<p>The resulting system has continuity that can be inspected. Its last checkpoint, pending action, wake condition, and remaining budget describe what is happening even while no model call is running. That is the operational basis for an agent that can resume work reliably over hours or days.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-managed">
<p>Anthropic, <a href="https://www.anthropic.com/engineering/managed-agents" rel="noopener noreferrer">Scaling Managed Agents: Decoupling the brain from the hands</a>, April 8, 2026. Durable sessions, replaceable execution, and credential separation. <a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fnref-managed" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fnref-managed-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-muse-design">
<p>Tarek Sheasha, Meta, <a href="https://research.meta.ai/blog/security-and-safety-for-ai-agents-our-approach-with-muse" rel="noopener noreferrer">How We Built Safety Into Muse</a>, September 8, 2026. Runtime isolation, durable state, credentials, and permission enforcement. <a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fnref-muse-design" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fnref-muse-design-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-idempotency">
<p>Stripe, <a href="https://docs.stripe.com/api/idempotent_requests" rel="noopener noreferrer">Idempotent requests</a>. A concrete external API retry contract. <a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fnref-idempotency" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-long-running">
<p>Anthropic, <a href="https://www.anthropic.com/engineering/harness-design-long-running-apps" rel="noopener noreferrer">Harness design for long-running application development</a>, March 24, 2026. Agent development and evaluation design. <a href="https://blog.ecitis.org/persistent-agent-harnesses/#user-content-fnref-long-running" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Agents</category>
            <category>Harness design</category>
            <category>Persistence</category>
            <enclosure length="62533" type="image/png" url="https://blog.ecitis.org/open-graph/persistent-agent-harnesses.png"/>
        </item>
        <item>
            <title><![CDATA[Recent ML Advances: Attention Kernels, Agent Training, and Inference]]></title>
            <link>https://blog.ecitis.org/recent-ml-improvements-2026/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/recent-ml-improvements-2026/</guid>
            <pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Connect recent attention kernels, agent training environments, local multimodal models, and inference-time reasoning to their engineering benefits.]]></description>
            <content:encoded><![CDATA[<p>Recent progress in machine learning is changing several parts of the engineering stack at once. Attention kernels reduce the cost of executing an existing model. Training environments make it possible to improve agents against task outcomes. Open multimodal models bring more workloads onto local hardware. Inference-time reasoning allocates additional computation to the problems that benefit from it.</p>
<p>These developments act at different levels, which is what makes them complementary. A model can become cheaper to run without changing its weights, more capable through training, or more reliable through an execution policy that adds tools and verification. Understanding the mechanism identifies where each improvement belongs in an application.</p>
<h2 id="four-places-where-computation-changes">Four places where computation changes</h2>



































<div class="table-wrap"><table><thead><tr><th>Development</th><th>Level of change</th><th>Main benefit</th><th>Governing constraint</th></tr></thead><tbody><tr><td>Attention kernels</td><td>Execution of tensor operations</td><td>Lower runtime and memory traffic</td><td>Hardware and tensor shapes</td></tr><tr><td>Outcome-based agent training</td><td>Learned behavior</td><td>Better task completion</td><td>Environment and reward quality</td></tr><tr><td>Local multimodal models</td><td>Deployment options</td><td>More tasks on smaller hardware</td><td>Model fit and input processing</td></tr><tr><td>Inference-time allocation</td><td>Work performed per request</td><td>More capability on difficult tasks</td><td>Latency, compute, and selection</td></tr></tbody></table></div>
<p>The baseline differs in each row. A kernel comparison holds weights and inputs fixed. A training comparison holds the evaluation task and resource budget fixed. A deployment comparison asks whether another model satisfies the application’s requirements. An inference-policy comparison includes all attempts and checks used to produce the answer.</p>
<p>This separation also explains why gains cannot generally be multiplied together. Two changes may reduce the same part of the execution, and an improvement at one layer can expose a bottleneck at another.</p>
<h2 id="attention-as-a-memory-system-problem">Attention as a memory-system problem</h2>
<p>Conventional attention forms a score matrix, normalizes it, and multiplies the resulting probabilities by the value matrix. Materializing those intermediates creates substantial off-chip memory traffic. The original FlashAttention algorithm reorganizes exact attention around tiling and online softmax, reducing transfers between high-bandwidth memory and on-chip storage.<sup><a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fn-flash-original" id="user-content-fnref-flash-original">1</a></sup></p>
<p>The scale is visible from the intermediate alone. A 32,768-by-32,768 matrix contains about 1.07 billion entries. At two bytes per entry, one such matrix occupies 2 GiB. This calculation describes the straightforward materialized representation for one head, before other tensors are included.</p>
<p>A tiled algorithm works on smaller blocks and retains the running statistics needed to produce the same attention operation. Avoiding the full intermediate reduces both storage and memory traffic. Numerical differences can still arise from floating-point ordering, as with other fused implementations.</p>
<p>This is an execution improvement that can benefit existing weights. Its value depends on how much attention contributes to the workload, which differs between training, prefill, and decode.</p>
<h2 id="flashattention-4-and-asymmetric-hardware-scaling">FlashAttention-4 and asymmetric hardware scaling</h2>
<p>FlashAttention-4 targets Blackwell’s change in the balance between matrix throughput and supporting resources. Tensor-core compute grows faster than some of the bandwidth and non-matrix operations needed to feed it. The work combines algorithm changes with a revised kernel pipeline to keep those resources coordinated.<sup><a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fn-flash-four" id="user-content-fnref-flash-four">2</a></sup></p>
<p>The paper reports B200 results against specified cuDNN and Triton baselines. The useful engineering lesson is the dependency between algorithm and hardware: increasing matrix throughput changes which operations must be reduced, overlapped, or reorganized.</p>
<p>The end-to-end gain follows the fraction of time affected. If attention occupies 30 percent of a workload and a new kernel makes it twice as fast, normalized total time becomes:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>0.70 + 0.30 / 2 = 0.85</span></span></code></pre>
<p>That is an overall speedup of about 1.18x. If attention occupies 5 percent, the same local improvement gives about 1.026x overall. The calculation connects a kernel result to the application profile.</p>
<p>The <a href="https://blog.ecitis.org/modern-attention-kernels/">attention-kernel guide</a> develops the underlying tiling and memory argument. The recent work extends it to a different balance of hardware resources.</p>
<h2 id="training-agents-against-executable-outcomes">Training agents against executable outcomes</h2>
<p>An agent produces a trajectory of observations and actions. When the environment can evaluate the result, training can reward completed work: a patch passes checks, a tool sequence reaches a target state, or an output satisfies a verifier.</p>
<p>OpenEnv addresses the infrastructure needed to run these experiments through consistent environment interfaces.<sup><a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fn-openenv" id="user-content-fnref-openenv">3</a></sup> Observations, actions, resets, and outcomes become repeatable parts of a rollout. That makes it easier to collect experience across tasks and compare changes in the policy.</p>
<p>The environment defines the learning signal. In a coding task with reward 1 for passing tests and 0 otherwise, the evaluator must distinguish implementing the feature from changing the tests or hard-coding the fixture. Hidden cases and protected evaluation code preserve that distinction.</p>
<p>Infrastructure reliability matters for the same reason. A dependency service failing during a run creates an unsuccessful outcome even when the proposed code is correct. Recording tool failures, invalid actions, and timeouts separately helps distinguish environment problems from the behavior being trained.</p>
<h2 id="generalization-depends-on-the-task-distribution">Generalization depends on the task distribution</h2>
<p>A training improvement is useful when it carries to new work. Near-duplicate tasks can make a random split unusually easy, so repository, template, or time-based splits can expose different kinds of generalization.</p>
<p>The tool environment also defines the distribution. A policy trained with unrestricted access can behave differently when deployed with a scoped filesystem or fewer tools. Matching the relevant execution conditions connects the training result to the intended agent.</p>
<p>Task success, number of attempts, intervention rate, and resource use describe different aspects of that transfer. A policy may improve completion while using more tool calls, or become more efficient while preserving its success rate. Both changes can be valuable under different operating budgets.</p>
<p>The <a href="https://blog.ecitis.org/persistent-agent-harnesses/">persistent-agent architecture</a> supplies the corresponding deployment machinery: durable state, recoverable actions, and explicit completion conditions. Better learned behavior and better execution support contribute to the same completed task.</p>
<h2 id="open-multimodal-models-on-local-hardware">Open multimodal models on local hardware</h2>
<p>The Gemma 4 ecosystem announcement describes multimodal model variants and integration across runtimes including MLX and llama.cpp.<sup><a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fn-gemma" id="user-content-fnref-gemma">4</a></sup> The practical change is a broader set of tasks that can be implemented with locally available weights and execution.</p>
<p>Multimodal deployment includes more than the text decoder. A document assistant may resize an image, encode visual content, retrieve relevant context, and generate a response. Each stage contributes to latency and memory use. A text-only token rate measures only part of that path.</p>
<p>A useful local workload therefore begins with the original input. For a scanned document, this includes rotation, small print, tables, handwriting, and language variation. An answer linked to its supporting page or region connects extraction quality to the evidence in the document.</p>
<p>The model variant and runtime determine which inputs and operations are supported. Quantization changes storage and execution. The complete configuration includes those choices alongside the model’s license and the hardware available to run it.</p>
<h2 id="capacity-creates-new-application-choices">Capacity creates new application choices</h2>
<p>A compact model that meets a task’s quality requirement can shift execution from a hosted service to a local device. This changes latency, availability, and data movement as well as inference cost.</p>
<p>The memory budget includes weights, modality encoders, temporary tensors, and the attention cache. Long inputs can move the limiting factor from weight storage to activations or cached state. The <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/">MLX hardware comparison</a> separates capacity, bandwidth, and compute for that reason.</p>
<p>Local execution also changes where document contents travel. The inference step can remain on the device while model downloads, telemetry, or a separate OCR service use the network. Describing those stages independently gives an accurate account of the application’s data path.</p>
<p>The resulting choice is task-specific. A smaller local model may provide the required extraction quality at low latency, while difficult synthesis remains on a larger system. The application can combine those paths rather than assigning every request to the same model.</p>
<h2 id="inference-time-reasoning-and-parallel-attempts">Inference-time reasoning and parallel attempts</h2>
<p>Training determines the model’s behavior, while an inference policy determines how much work it performs for a particular request. Additional reasoning, tools, independent attempts, and verification can improve difficult-task success without applying the same cost to every request.</p>
<p>Meta’s Muse Spark report discusses both efficient reasoning and parallel work on difficult tasks.<sup><a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fn-muse" id="user-content-fnref-muse">5</a></sup> These address two different parts of the budget: the work within an attempt and the number of attempts available for selection.</p>
<p>Under a simplified independence assumption, if one attempt succeeds with probability p, the chance that at least one of k attempts succeeds is:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>1 - (1 - p)^k</span></span></code></pre>
<p>With p = 0.4 and k = 4, that is about 87 percent. This describes the opportunity available to a selector. The final result also depends on whether the correct candidate can be recognized, and correlated errors reduce the benefit of additional attempts.</p>
<p>Executable code tests can provide a strong selection signal for some tasks. For factual synthesis, retrieving evidence may contribute more than another answer generated from the same information. The most useful additional computation addresses the source of uncertainty.</p>
<h2 id="allocation-across-requests">Allocation across requests</h2>
<p>A serving policy can use a short path for routine tasks and reserve verification or additional attempts for a difficult subset. Its performance includes the quality of the routing decision: sending a hard task to the short path can erase the saving through failure or rework.</p>
<p>Total cost per accepted result captures generation, selection, tools, and retries. Wall-clock time distinguishes serial reasoning from parallel work. These measurements explain whether extra computation produces a useful result within the application’s budget.</p>
<p>The recent developments expand the available design space at each layer. Kernels lower the execution cost, environments improve what agents learn, local models change where tasks can run, and inference policies decide where additional work is valuable. Their shared effect is finer control over the relationship between capability and resources.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-flash-original">
<p>Dao et al., <a href="https://arxiv.org/abs/2205.14135" rel="noopener noreferrer">FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness</a>, NeurIPS 2022. <a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fnref-flash-original" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-flash-four">
<p>Zadouri et al., <a href="https://arxiv.org/abs/2603.05451" rel="noopener noreferrer">FlashAttention-4: Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling</a>, March 2026. Hardware and baseline-specific results. <a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fnref-flash-four" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-openenv">
<p>Hugging Face and collaborators, <a href="https://huggingface.co/blog/openenv-agentic-rl" rel="noopener noreferrer">The Open Source Community is backing OpenEnv for Agentic RL</a>. Environment infrastructure for agentic reinforcement learning. <a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fnref-openenv" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-gemma">
<p>Hugging Face, <a href="https://huggingface.co/blog/gemma4" rel="noopener noreferrer">Welcome Gemma 4: Frontier multimodal intelligence on device</a>, April 2, 2026. Model variants and runtime integration. <a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fnref-gemma" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-muse">
<p>Meta, <a href="https://ai.meta.com/blog/introducing-muse-spark-msl/" rel="noopener noreferrer">Introducing Muse Spark: Scaling Towards Personal Superintelligence</a>, 2026. Vendor account of model and inference-time design. <a href="https://blog.ecitis.org/recent-ml-improvements-2026/#user-content-fnref-muse" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Machine learning</category>
            <category>Agents</category>
            <category>Inference</category>
            <enclosure length="72356" type="image/png" url="https://blog.ecitis.org/open-graph/recent-ml-improvements-2026.png"/>
        </item>
        <item>
            <title><![CDATA[MLX on M5 and M6: GPU Acceleration and Unified Memory]]></title>
            <link>https://blog.ecitis.org/mlx-m5-m6-neural-architecture/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/mlx-m5-m6-neural-architecture/</guid>
            <pubDate>Sun, 20 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Connect Apple silicon GPU accelerators, memory bandwidth, and capacity to MLX prefill and decode performance.]]></description>
            <content:encoded><![CDATA[<p>Apple silicon exposes several routes for machine-learning computation: CPU operations, GPU kernels, matrix accelerators within the GPU, and the separate Neural Engine. MLX uses the CPU and GPU over a shared memory pool. On M5, its Metal path also uses the Neural Accelerators introduced inside the GPU cores.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-m5-study" id="user-content-fnref-m5-study">1</a></sup></p>
<p>This architecture explains the different gains seen in prompt processing and token generation. Prefill provides large matrix operations that benefit from more compute. Small-batch decode repeatedly reads weights and attention state, making memory bandwidth a major limit. Memory capacity determines which models and context lengths can be held in the first place.</p>
<p>The M3-to-M6 progression changes all three quantities, but by different amounts. Comparing them separately gives a useful account of local model performance.</p>
<h2 id="gpu-accelerators-and-the-neural-engine">GPU accelerators and the Neural Engine</h2>
<p>The Neural Engine is a dedicated processor block. The Neural Accelerators introduced in M5 are inside the GPU cores and are reached through the GPU programming path. Apple describes MLX using Metal 4 Tensor Operations and Metal Performance Primitives to use this matrix hardware.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-m5-study" id="user-content-fnref-m5-study-2">1</a></sup></p>
<p>An MLX operation dispatched to the GPU therefore remains GPU execution even when those accelerators perform its matrix work. Core ML and other system frameworks provide a separate deployment path whose hardware placement depends on supported operations and runtime decisions.</p>
<p>The distinction also applies to specifications. More Neural Engine compute does not directly imply the same increase in MLX generation speed. The relevant quantities for an MLX workload are the GPU operations it executes, their efficiency on the selected hardware, and the memory traffic supplying them.</p>
<h2 id="hardware-progression">Hardware progression</h2>



































<div class="table-wrap"><table><thead><tr><th>Chip family</th><th>AI hardware</th><th>Memory configuration</th></tr></thead><tbody><tr><td>M3</td><td>16-core Neural Engine; GPU before the per-core Neural Accelerator design</td><td>Unified memory shared across the system</td></tr><tr><td>M5</td><td>Neural Accelerator in each GPU core; faster 16-core Neural Engine</td><td>Base-chip bandwidth of 153 GB/s</td></tr><tr><td>M5 Pro/Max</td><td>Accelerator design scaled to 20/40 GPU cores</td><td>Up to 307/614 GB/s and 64/128 GB</td></tr><tr><td>M5 Ultra</td><td>Up to 80 GPU cores with Neural Accelerators; 32-core Neural Engine</td><td>Up to 512 GB and 1.2 TB/s</td></tr><tr><td>M6</td><td>12 GPU cores with Neural Accelerators; dual 16-core Neural Engines</td><td>Up to 32 GB and 170 GB/s</td></tr></tbody></table></div>
<p>The specifications come from Apple’s <a href="https://www.apple.com/newsroom/2023/10/apple-unveils-m3-m3-pro-and-m3-max-the-most-advanced-chips-for-a-personal-computer/">M3 announcement</a>, <a href="https://www.apple.com/newsroom/2025/10/apple-unleashes-m5-the-next-big-leap-in-ai-performance-for-apple-silicon/">M5 announcement</a>, <a href="https://www.apple.com/newsroom/2026/03/apple-debuts-m5-pro-and-m5-max-to-supercharge-the-most-demanding-pro-workflows/">M5 Pro/Max announcement</a>, and M6/M5 Ultra announcement.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-m6-spec" id="user-content-fnref-m6-spec">2</a></sup> The M6 entry describes the announced base chip in Mac mini.</p>
<p>M5 adds matrix throughput within each GPU core. M6 extends that GPU design to twelve cores and increases base-chip memory bandwidth. M5 Ultra occupies a different capacity class, with a much larger memory pool and higher bandwidth for models that exceed the base chips’ working sets.</p>
<p>These are separate ways to improve a workload. More capacity admits larger models or more live sequences. More bandwidth supplies weights and cache state faster. More matrix throughput accelerates operations with enough arithmetic to use it.</p>
<h2 id="unified-memory-and-the-working-set">Unified memory and the working set</h2>
<p>MLX arrays can be used by CPU and GPU operations without explicit transfers between separate host and device allocations.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-unified" id="user-content-fnref-unified">3</a></sup> This makes mixed execution straightforward: a GPU operation can produce an array that a CPU operation subsequently consumes in the same memory system.</p>
<p>The shared pool still has finite capacity and bandwidth. Model weights, attention caches, temporary arrays, runtime allocations, and other applications compete for it. A model’s checkpoint size is therefore only one part of its execution footprint.</p>
<p>For a dense model with 8 billion parameters, raw two-byte weights occupy about 16 GB. Four-bit weights have an idealized storage cost of 4 GB. Quantization metadata and tensors retained at higher precision add to that lower bound. The KV cache grows separately with context length and the number of live sequences.</p>
<p>Mixture-of-experts models make the distinction between storage and computation especially visible. Active parameter count describes the experts used for a token. Total parameter count describes the collection of weights that may need to remain resident. A model with 30 billion total parameters and a few billion active parameters has a larger storage requirement than its active count suggests.</p>
<p>Unified memory makes these larger working sets accessible to the GPU, but fitting them remains an explicit allocation problem. Swapping changes the data path and can dominate the benefit of faster arithmetic.</p>
<h2 id="prefill-uses-matrix-throughput">Prefill uses matrix throughput</h2>
<p>Prefill processes the input sequence and constructs the attention state used during generation. Many token positions participate in the layer operations, producing matrix workloads with substantial parallelism and reuse.</p>
<p>Apple’s M5 study compares selected Qwen and GPT-OSS models on similarly configured M4 and M5 MacBook Pros. It uses 4,096-token prompts and reports TTFT separately from generation speed.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-m5-study" id="user-content-fnref-m5-study-3">1</a></sup> The GPU matrix hardware has a larger effect on prompt processing than on the subsequent token stream.</p>
<p>This phase matters most when a request contains a long document and produces a short answer, or when the application starts a new context frequently. Prefix caching changes the balance by reusing previously computed state. A warm-prefix request can have a much shorter prefill even on unchanged hardware.</p>
<p>A hardware comparison therefore has two meaningful prefill cases: a new context that requires the full computation, and a repeated prefix that allows reuse. They describe different application behavior.</p>
<h2 id="decode-and-memory-bandwidth">Decode and memory bandwidth</h2>
<p>At batch size one, dense-model decoding often spends much of each step supplying weights from memory. A simplified lower bound is the weight bytes read divided by sustained bandwidth.</p>
<p>For an illustrative 4 GB of weight reads per generated token:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>at 120 GB/s: 4 / 120 seconds ≈ 33 ms per token</span></span>
<span class="line"><span>at 150 GB/s: 4 / 150 seconds ≈ 27 ms per token</span></span></code></pre>
<p>Those bounds correspond to about 30 and 37.5 tokens/s before attention traffic, dequantization, synchronization, and other work. They describe a bandwidth model, not measured rates for a particular Mac.</p>
<p>Batching changes the arithmetic because a weight read can serve several token positions. Sparse expert activation changes which weights are used. Cache residency and implementation details change the achieved traffic. These effects explain why aggregate throughput and the speed of one interactive stream are different measurements.</p>
<p>Apple’s reported M5 generation improvements range from 19 to 27 percent across its tested models, with the memory-bandwidth increase from 120 to 153 GB/s identified as the main contribution.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-m5-study" id="user-content-fnref-m5-study-4">1</a></sup> M6’s 170 GB/s is roughly 11 percent above base M5. A bandwidth-bound workload can benefit from that increase, with its actual gain determined by the rest of the execution path.</p>
<h2 id="lazy-execution-and-timing">Lazy execution and timing</h2>
<p>MLX builds computation lazily. Constructing an array expression records work that may execute only when the result is needed. Evaluation boundaries therefore matter when timing an operation.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-lazy" id="user-content-fnref-lazy">4</a></sup></p>
<p>An array benchmark can measure graph construction, execution, or both. Materializing the result establishes that the computation has completed. Without that boundary, a short elapsed time may describe only the creation of an expression.</p>
<p>Language-model measurements add their own phases: loading the model, first-run initialization, processing the prompt, and generating output. A cold-start experiment includes the first two. A warm serving experiment begins with the model already available. Keeping those intervals separate explains where a change in storage, runtime, or chip affects the result.</p>
<h2 id="mlx-lm-and-reproducible-execution">MLX LM and reproducible execution</h2>
<p>MLX LM supplies model-loading, generation, and quantization workflows on top of MLX.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-mlx-lm" id="user-content-fnref-mlx-lm">5</a></sup> Its <a href="https://github.com/ml-explore/mlx-lm/releases">release history</a> also records changes to batching, caching, model support, and generation behavior. The software version is part of a performance configuration alongside the hardware.</p>
<p>An isolated environment and package record can be created with:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> venv</span><span style="color:var(--astro-code-token-string)"> .venv</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> --python</span><span style="color:var(--astro-code-token-string)"> .venv/bin/python</span><span style="color:var(--astro-code-token-string)"> mlx-lm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> --no-project</span><span style="color:var(--astro-code-token-string)"> --python</span><span style="color:var(--astro-code-token-string)"> .venv/bin/python</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string-expression)"> &#39;import mlx.core; import mlx_lm&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> freeze</span><span style="color:var(--astro-code-token-string)"> --python</span><span style="color:var(--astro-code-token-string)"> .venv/bin/python</span><span style="color:var(--astro-code-token-keyword)"> &gt;</span><span style="color:var(--astro-code-token-string)"> requirements-benchmark.txt</span></span></code></pre>
<p>The model repository and immutable revision identify the weights separately from the Python environment. Quantization, tokenizer, sampling settings, and prompt content complete the configuration. Apple’s M5 research note also specifies macOS 26.2 or later for the accelerator path it describes.<sup><a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fn-m5-study" id="user-content-fnref-m5-study-5">1</a></sup></p>
<p>This record makes comparisons interpretable. A cache-policy change can lower TTFT on the same machine, while a different quantization can change both memory use and output quality.</p>
<h2 id="workload-level-comparison">Workload-level comparison</h2>
<p>A compact experiment matrix covers the main execution regimes:</p>



































<div class="table-wrap"><table><thead><tr><th>Experiment</th><th>Request shape</th><th>Dominant information</th></tr></thead><tbody><tr><td>Cold launch</td><td>Unloaded model, short prompt</td><td>Loading and initialization</td></tr><tr><td>Short chat</td><td>Warm model, short prompt, sustained output</td><td>Interactive decode</td></tr><tr><td>Document question</td><td>Long prompt, short answer</td><td>Prefill and context memory</td></tr><tr><td>Repeated prefix</td><td>Shared context, changing suffix</td><td>Cache reuse</td></tr><tr><td>Concurrent requests</td><td>Several live sequences</td><td>Batching and contention</td></tr></tbody></table></div>
<p>TTFT, completion time, output count, and peak memory describe each run. Quality establishes whether the configurations perform the same useful task. Power mode, battery state, competing GPU work, and sustained thermal behavior explain variation between repeated runs.</p>
<p>The practical ordering follows the architecture. Capacity determines whether the workload fits. Prefill and decode measurements determine which resource limits it. The runtime determines how effectively the available hardware is used. This is why a larger previous-generation system can be the better fit for one model while a newer base chip is faster on another.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-m5-study">
<p>Apple Machine Learning Research, <a href="https://machinelearning.apple.com/research/exploring-llms-mlx-m5" rel="noopener noreferrer">Exploring LLMs with MLX and the Neural Accelerators in the M5 GPU</a>. Hardware, models, prompt lengths, and measured results. <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-m5-study" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-m5-study-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-m5-study-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-m5-study-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-m5-study-5" class="data-footnote-backref">↩<sup>5</sup></a></p>
</li>
<li id="user-content-fn-m6-spec">
<p>Apple, <a href="https://www.apple.com/newsroom/2026/08/apple-introduces-m6-and-m5-ultra-for-a-big-leap-in-performance-and-ai-compute/" rel="noopener noreferrer">Apple introduces M6 and M5 Ultra</a>, August 25, 2026. Hardware specifications. <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-m6-spec" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-unified">
<p>MLX documentation, <a href="https://ml-explore.github.io/mlx/build/html/usage/unified_memory.html" rel="noopener noreferrer">Unified Memory</a>. CPU/GPU array sharing. <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-unified" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-lazy">
<p>MLX documentation, <a href="https://ml-explore.github.io/mlx/build/html/usage/lazy_evaluation.html" rel="noopener noreferrer">Lazy Evaluation</a>. Evaluation behavior and timing implications. <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-lazy" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mlx-lm">
<p>Apple ML Research, <a href="https://github.com/ml-explore/mlx-lm" rel="noopener noreferrer">MLX LM repository</a>. Installation, supported workflows, generation, and quantization. <a href="https://blog.ecitis.org/mlx-m5-m6-neural-architecture/#user-content-fnref-mlx-lm" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>MLX</category>
            <category>Apple silicon</category>
            <category>On-device AI</category>
            <enclosure length="60165" type="image/png" url="https://blog.ecitis.org/open-graph/mlx-m5-m6-neural-architecture.png"/>
        </item>
        <item>
            <title><![CDATA[Jev and System One Models: Typed Decisions for Software]]></title>
            <link>https://blog.ecitis.org/jev-system-one-decisions/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/jev-system-one-decisions/</guid>
            <pubDate>Sat, 19 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explain how typed decisions, calibrated probabilities, and application policies compose into a System One workflow.]]></description>
            <content:encoded><![CDATA[<p>Many uses of language models inside software end in a small decision: selecting a queue, assigning a score, deciding whether to call a tool, or estimating whether a case needs review. The output space is known in advance. What the application needs from the model is a judgment over that space.</p>
<p>Jev, released by TypeSafe in early access on September 15, 2026, makes this the model interface. Application state and typed questions go in; values and probabilities come back. TypeSafe calls this a System One model, borrowing the distinction between fast judgment and extended deliberation to describe the kind of work it is optimized for.<sup><a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fn-launch" id="user-content-fnref-launch">1</a></sup></p>
<p>This changes how a model fits into a program. A decision can be composed with thresholds, rules, and downstream functions directly, while generation remains available for the parts of the workflow that need language.</p>
<h2 id="state-questions-and-return-types">State, questions, and return types</h2>
<p>A request contains a description of the current state and one or more questions about it. The question defines the answer space. TypeSafe exposes three question types:<sup><a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fn-interface" id="user-content-fnref-interface">2</a></sup></p>

























<div class="table-wrap"><table><thead><tr><th>Type</th><th>Operation</th><th>Returned information</th></tr></thead><tbody><tr><td>Choice</td><td>Select among supplied options</td><td>Choice, probabilities, and confidence</td></tr><tr><td>Score</td><td>Evaluate against a rubric</td><td>Score, probabilities, and confidence</td></tr><tr><td>Noul</td><td>Estimate whether a statement is true</td><td>A value between zero and one</td></tr></tbody></table></div>
<p>The distinction matters to the caller. A queue selection is categorical. A score depends on the scale and rubric. A probability describes a specified event. These values support different operations even when each can be represented by a number.</p>
<p>A support workflow might be described as follows. This is a conceptual request, with the exact API representation defined in the documentation.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>State:</span></span>
<span class="line"><span>    Customer reports a failed payment and a duplicate charge.</span></span>
<span class="line"><span>    Account history and the relevant support policy are attached.</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Questions:</span></span>
<span class="line"><span>    route: choose [billing, fraud, technical]</span></span>
<span class="line"><span>    urgent: probability that the ticket requires a response within one hour</span></span>
<span class="line"><span>    refund_risk: score against the supplied refund-risk rubric</span></span></code></pre>
<p>The application can send the ticket to billing, raise its priority, and request a human review independently. A generative model can then draft the response using those decisions and the same underlying evidence. Routing and writing have separate outputs and can be improved separately.</p>
<h2 id="parallel-decisions-within-a-request">Parallel decisions within a request</h2>
<p>TypeSafe describes the questions in a request as evaluated in parallel against the same state. Its launch reports end-to-end service latency of 70 to 500 ms and a training method called Reinforcement Learning for Calibrated Decisions.<sup><a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fn-launch" id="user-content-fnref-launch-2">1</a></sup> The intended workload is a collection of bounded judgments that would otherwise be expressed through generated text.</p>
<p>This is useful for decisions on a frequently executed path. A tool router runs before the selected tool. A moderation decision runs before content is accepted. A retrieval filter runs before a larger model reads the remaining candidates. Latency at these boundaries is paid repeatedly, so reducing the work between state and decision can improve the whole interaction.</p>
<p>Parallel evaluation also makes decomposition useful. A support policy may depend on urgency, customer status, and the likelihood of a duplicate charge. Each can be a separate question, with application code combining the results. A policy change can then alter the combination without changing the definition of every individual judgment.</p>
<p>The joint probability still requires care. Knowing the probability of “urgent” and the probability of “billing” does not determine the probability of “urgent and billing.” Multiplying the two assumes independence. Where the joint event drives the action, it needs an explicit definition and evaluation.</p>
<h2 id="type-safety-accuracy-and-calibration">Type safety, accuracy, and calibration</h2>
<p>Three properties determine whether a returned decision is useful.</p>
<p><strong>Type safety</strong> means the value belongs to the declared output space. A Choice answer selects from the available options. A numeric answer satisfies its representation constraints. The application receives a value it can process.</p>
<p><strong>Accuracy</strong> concerns the judgment itself. A valid queue name may still be the wrong destination for the ticket.</p>
<p><strong>Calibration</strong> concerns the relationship between probabilities and observed outcomes. Among cases assigned probability 0.8, the event should occur about 80 percent of the time over the relevant population. Calibration research treats this separately from classification accuracy.<sup><a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fn-calibration" id="user-content-fnref-calibration">3</a></sup></p>
<p>The definitions of the questions establish what can be measured. “Urgent” could mean a safety incident, an unhappy customer, or a response due within an hour. A labeling guide turns that ambiguity into a repeatable target. Cases with missing information can have their own outcome rather than being forced into a confident label.</p>
<p>Score has a similar dependency on its rubric. A risk score of 0.8 is not necessarily an 80 percent probability of a future loss. The application needs to preserve the meaning of the returned quantity when it uses it in a formula or threshold.</p>
<h2 id="from-probability-to-action">From probability to action</h2>
<p>The threshold for taking an action follows from the relative costs of its errors. Consider a simplified moderation policy. Let p be a calibrated probability that a submission violates a rule. Assign a cost of 100 units to allowing a violation and 2 units to blocking a valid submission.</p>
<p>The expected losses are:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>allow: 100 × p</span></span>
<span class="line"><span>block:   2 × (1 - p)</span></span></code></pre>
<p>Blocking has lower expected loss when p exceeds 2/102, approximately 0.0196. The low threshold follows from the large cost assigned to a missed violation. The numbers are illustrative; the calculation shows how a policy threshold can be derived from consequences.</p>
<p>Human review introduces a third action. If perfect review costs 0.5 units, it is cheaper than allowing once p exceeds 0.005, and cheaper than blocking while p remains below 0.75. Under those assumptions, the middle range goes to review. Reviewer error, delay, and limited capacity extend the model with additional costs.</p>
<p>This separation gives the application a useful control point. The model estimates the event; the policy decides what to do about it. A change in review capacity or error cost can move the threshold without changing the question sent to the model.</p>
<p>Deterministic conditions fit naturally before or after that step. Missing required fields can route directly to review. A known duplicate can follow a fixed rule. The decision model handles the cases where the state requires judgment.</p>
<h2 id="measuring-calibration">Measuring calibration</h2>
<p>For binary outcomes, the Brier score averages the squared difference between each predicted probability and its observed zero-or-one label. A reliability plot groups predictions and compares the average probability with the observed event frequency.<sup><a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fn-calibration" id="user-content-fnref-calibration-2">3</a></sup></p>
<p>For example, suppose 100 held-out tickets receive probabilities between 0.75 and 0.85, averaging 0.80. If 58 are urgent under the labeling guide, the predictions are overconfident in that group. If 82 are urgent, the observed frequency is closer to the prediction, with uncertainty from the finite sample.</p>
<p>Breaking the result down by language, ticket length, and customer type identifies where the estimate transfers well. A model may be calibrated on common English requests while systematically overestimating urgency for another segment. Time-based holdouts are useful when policies and incoming traffic change, because near-duplicate conversations can otherwise appear on both sides of a random split.</p>
<p>Base rates provide another reference point. If only 1 percent of submissions violate a rule, a model that always predicts 0.01 may have a low aggregate error while offering little ranking value. Precision, recall, review volume, and expected cost at the chosen threshold describe what the application gains from the model.</p>
<h2 id="the-application-path">The application path</h2>
<p>The model call is one stage in a larger request. Input preparation, network transport, policy execution, and any fallback all contribute to completion time. An application-level outline is:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>load policy and question definitions</span></span>
<span class="line"><span>validate the required state</span></span>
<span class="line"><span>request typed decisions within the remaining deadline</span></span>
<span class="line"><span></span></span>
<span class="line"><span>if the request completes:</span></span>
<span class="line"><span>    combine decisions with deterministic rules</span></span>
<span class="line"><span>    choose the action under the current policy</span></span>
<span class="line"><span>else:</span></span>
<span class="line"><span>    use the fallback queue</span></span>
<span class="line"><span></span></span>
<span class="line"><span>record the model, policy, probabilities, and action</span></span></code></pre>
<p>The record makes the result reproducible. A later change in queue assignment can be traced to the model, question definition, threshold, or input. Retaining only the final label loses that distinction.</p>
<p>Deadlines connect this design to the rest of the system. A synchronous call inside a 300 ms interaction has a different budget from an asynchronous queue processor. Retries and review also contribute to total cost per completed workflow, even when the initial decision call is inexpensive.</p>
<h2 id="where-the-interface-is-useful">Where the interface is useful</h2>
<p>A bounded decision model fits applications with repeated judgments and explicit outcomes: classification, routing, ranking, scoring, and branching. A text model fits explanation, writing, code, and synthesis. Rules remain efficient for conditions that can be specified exactly.</p>
<p>Comparisons become clearer at that workflow boundary. Decision quality, calibration, schema failures, latency, timeout rate, and review cost can be measured on the same cases. TypeSafe’s published workflow evaluation uses reference-model probabilities for parts of its comparison; independently labeled application outcomes answer a separate question about correctness.<sup><a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fn-launch" id="user-content-fnref-launch-3">1</a></sup></p>
<p>The architectural value is composability. A probability can feed a threshold, a Choice can select a function, and a score can order a queue. Giving these judgments explicit types and measurable outcomes makes them ordinary components of software, with the same scope for testing and improvement as the code around them.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-launch">
<p>Diogo Almeida, <a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener noreferrer">Introducing System One Models &amp; Jev</a>, TypeSafe, September 15, 2026. Release, design claims, latency range, and evaluation caveats. <a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fnref-launch" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fnref-launch-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fnref-launch-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-interface">
<p>TypeSafe, <a href="https://docs.typesafe.ai/introduction" rel="noopener noreferrer">Developer introduction</a>. Request types and supported model interfaces. <a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fnref-interface" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-calibration">
<p>Guo et al., <a href="https://arxiv.org/abs/1706.04599" rel="noopener noreferrer">On Calibration of Modern Neural Networks</a>, ICML 2017. Calibration concepts and evaluation. <a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fnref-calibration" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/jev-system-one-decisions/#user-content-fnref-calibration-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Jev</category>
            <category>System One</category>
            <category>Decision models</category>
            <enclosure length="65376" type="image/png" url="https://blog.ecitis.org/open-graph/jev-system-one-decisions.png"/>
        </item>
        <item>
            <title><![CDATA[LLM-Assisted Performance Optimization: Profiling and Iteration]]></title>
            <link>https://blog.ecitis.org/llm-performance-hill-climbing/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/llm-performance-hill-climbing/</guid>
            <pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explain how coding models use profiles, benchmarks, and behavioral checks to iterate on software performance.]]></description>
            <content:encoded><![CDATA[<p>Performance optimization has a repeatable inner loop: identify the work on a slow path, change its cost, and measure the result while preserving behavior. Coding models can participate throughout that loop. They can follow execution paths, inspect profiles, construct benchmarks, and implement candidate changes, which makes a well-defined performance objective a useful engineering task for an agent.</p>
<p>The leverage comes from feedback. A trace identifies where time goes; a correctness check constrains the implementation; a benchmark determines whether the candidate improved the path. Each result gives the next iteration more information than the original description of the problem.</p>
<p>Anthropic’s account of its claude.ai performance work describes this approach across user journeys and local benchmarks.<sup><a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fn-claude" id="user-content-fnref-claude">1</a></sup> The method applies more broadly because the essential components are ordinary profiling, experimentation, and software review.</p>
<h2 id="the-optimization-objective">The optimization objective</h2>
<p>A useful objective begins and ends with observable application behavior. Opening a project may begin with a click and end when its records are rendered and search accepts input. Sending a message may end when the interface displays the submitted message and is ready for the next action.</p>
<p>These boundaries determine which work is part of the problem. A spinner marks progress, but it does not establish that the requested content is usable. Receiving a response establishes network completion, while parsing and rendering may still be on the critical path.</p>
<p>A concrete task definition can be expressed as:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Journey: open a project containing 10,000 records.</span></span>
<span class="line"><span>Start: the user activates its link.</span></span>
<span class="line"><span>End: visible records are rendered and search accepts input.</span></span>
<span class="line"><span>Behavior: preserve ordering, selection, navigation, and result counts.</span></span>
<span class="line"><span>Objective: reduce p95 duration within a defined memory budget.</span></span>
<span class="line"><span>Evidence: profiles, raw timing samples, checks, and the implementation diff.</span></span></code></pre>
<p>The fixture and behavior define the work being optimized. Keeping them stable allows a sequence of candidate implementations to be compared against the same task. A correction to the benchmark itself is a separate change, with its own explanation.</p>
<h2 id="the-profile-connects-a-symptom-to-a-mechanism">The profile connects a symptom to a mechanism</h2>
<p>Elapsed time contains both execution and waiting. CPU sampling identifies active functions. A timeline reveals sequential requests, scheduling gaps, and blocked work. Resource measurements show whether the execution is limited by CPU, memory, storage, or another service.</p>
<p>Brendan Gregg’s USE method organizes resource investigation around utilization, saturation, and errors.<sup><a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fn-use" id="user-content-fnref-use">2</a></sup> The distinction is useful here: an idle CPU waiting on storage presents a different optimization problem from a saturated CPU repeatedly parsing the same input.</p>
<p>Suppose a 500 ms operation spends 300 ms parsing, 150 ms waiting for data, and 50 ms rendering. Making parsing twice as fast reduces completion time to 350 ms, a 1.43x speedup. Eliminating all rendering work would save at most 50 ms. The phase breakdown provides an upper bound on the value of each candidate.</p>
<p>A measured mechanism then gives the agent a tractable task. Building an index once can replace repeated scans. Reusing a parsed representation can remove repeated decoding. Overlapping independent requests can reduce serial wait. Each proposal identifies both the work removed and the condition under which that saving should appear.</p>
<h2 id="the-experiment-loop">The experiment loop</h2>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>reproduce the path</span></span>
<span class="line"><span>    -&gt; profile its execution</span></span>
<span class="line"><span>    -&gt; propose a change tied to the profile</span></span>
<span class="line"><span>    -&gt; run behavioral checks</span></span>
<span class="line"><span>    -&gt; repeat the benchmark</span></span>
<span class="line"><span>    -&gt; inspect the result and implementation</span></span>
<span class="line"><span>    -&gt; retain or revert the candidate</span></span></code></pre>
<p>Small changes make this loop informative. When one candidate changes parsing, caching, and scheduling together, a gain or regression is harder to attribute. A sequence of focused changes produces an explanation for each improvement and makes later regressions easier to locate.</p>
<p>A useful experiment record includes failed candidates as well as successful ones:</p>





























<div class="table-wrap"><table><thead><tr><th>Candidate</th><th>Mechanism</th><th>Behavioral condition</th><th>Performance question</th></tr></thead><tbody><tr><td>Cached index</td><td>Avoid repeated scans</td><td>Result IDs and order remain correct</td><td>Does lookup dominate enough to matter?</td></tr><tr><td>Deferred parser</td><td>Move work off the opening path</td><td>Content is ready before interaction</td><td>Does the next action inherit the delay?</td></tr><tr><td>Larger batch</td><td>Amortize fixed overhead</td><td>Cancellation and errors remain correct</td><td>How does the tail change?</td></tr></tbody></table></div>
<p>The record preserves information about the workload. A rejected candidate may become useful at another scale, while its failure explains why it was unsuitable for the current path.</p>
<h2 id="measuring-a-browser-journey">Measuring a browser journey</h2>
<p>Browser User Timing marks provide application-defined start and end events.<sup><a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fn-timing" id="user-content-fnref-timing">3</a></sup> They make it possible to measure a particular interaction rather than treating all page activity as one load metric.</p>
<p>The end event depends on the result. Data arrival, DOM update, layout, and paint are distinct stages. An input can appear before its event handling is ready, and a framework can finish a render before the browser displays the frame. The timing boundary should correspond to the application behavior being studied.</p>
<p>Browser traces complement the interval with an explanation. Long tasks identify stretches where input handling competes with other main-thread work. Layout and style recalculation show how a small data update can trigger a large amount of rendering work. Network timing identifies resources that are discovered late or requested serially.</p>
<p>The same principle applies outside a browser. A CLI’s start-to-exit duration includes process and module initialization. A database request includes query execution and result transfer. A model request includes queueing and output generation. The boundary follows the task.</p>
<h2 id="stable-proxies-for-a-fast-inner-loop">Stable proxies for a fast inner loop</h2>
<p>Wall-clock measurements include variation from scheduling, caches, and shared resources. Counts of instructions, allocations, queries, or rendering operations can provide more stable feedback for a narrow path.</p>
<p>Anthropic’s article describes using local metrics and checking that improvements in those metrics translated into elapsed-time gains.<sup><a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fn-claude" id="user-content-fnref-claude-2">1</a></sup> The connection is what makes a proxy useful. Fewer instructions may still leave a memory bottleneck unchanged. Fewer queries may transfer more data. Fewer DOM mutations may require a larger replacement operation.</p>
<p>A proxy therefore has two roles. It gives the agent a low-noise measure while exploring, and it can become a regression check after its relationship to the user journey is established. Wall-clock and field measurements remain the connection back to the application.</p>
<h2 id="experimental-variation">Experimental variation</h2>
<p>Warmup, caching, thermal behavior, and background load affect the comparison. Alternating baseline and candidate runs reduces the chance that a changing environment consistently favors one version. A steady-state benchmark warms the relevant JIT and caches; a cold-start benchmark measures those costs explicitly.</p>
<p>Individual samples preserve the shape of the result. A median describes typical behavior, while the tail exposes occasional expensive paths. A p95 estimated from twenty runs depends on roughly the slowest one or two observations, so its stability is different from a percentile estimated from thousands of requests.</p>
<p>Input variation is equally important. A parser improvement can depend on text encoding. A lookup improvement can depend on collection size. A cache can perform well until updates invalidate it. A second fixture with different size or structure shows whether the mechanism extends beyond the case used during optimization.</p>
<p>For UI changes, behavior includes keyboard navigation, selection, scroll position, and the next interaction. Deferring work can improve opening time while moving the delay to the first keystroke. Measuring the adjacent action reveals that transfer.</p>
<h2 id="different-workloads-expose-different-levers">Different workloads expose different levers</h2>



































<div class="table-wrap"><table><thead><tr><th>Workload</th><th>Useful objective</th><th>Common sources of cost</th></tr></thead><tbody><tr><td>Web application</td><td>Time to usable input, navigation latency</td><td>Initialization, rendering, resource discovery</td></tr><tr><td>CLI</td><td>Cold start and task completion</td><td>Module loading, subprocesses, repeated parsing</td></tr><tr><td>Database service</td><td>Tail latency at a fixed arrival rate</td><td>Query plans, round trips, serialization</td></tr><tr><td>Model server</td><td>Goodput under latency limits</td><td>Batching, KV storage, prefill scheduling</td></tr><tr><td>Data pipeline</td><td>Completion time and records per second</td><td>I/O, allocation, compression, skew</td></tr></tbody></table></div>
<p>The objective leaves room for the implementation to change. A request to reduce opening time can lead to an algorithmic improvement, a cache, or a better schedule. Prescribing memoization in advance narrows the search before the profile has identified the expensive work.</p>
<p>Correctness checks define the allowable search space. Exact CLI output, query results, model-quality criteria, and browser interactions each establish what the faster implementation must continue to do.</p>
<h2 id="load-and-tail-latency">Load and tail latency</h2>
<p>An isolated request measures service cost under little contention. A deployed service also has queues. Reducing service time can improve both the request itself and the waiting time of subsequent requests. Shared caches, larger batches, and additional concurrency can change that relationship.</p>
<p><em>The Tail at Scale</em> describes why occasional delays become significant in large services.<sup><a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fn-tail" id="user-content-fnref-tail">4</a></sup> A request that depends on many components can inherit the slowest of them, making tail behavior a system-level property.</p>
<p>Arrival-rate experiments expose this effect. Error rates and rejected requests remain part of the result, since dropping slow work can otherwise look like a latency improvement. For interactive software, slower devices reveal contention that may be almost invisible on a development machine.</p>
<h2 id="from-a-local-result-to-a-maintained-improvement">From a local result to a maintained improvement</h2>
<p>A release connects the experiment to field behavior. Comparing the same journey by build, device class, and cache state shows where the gain appears. A mismatch between local and field results identifies a difference in workload or critical path that the next experiment can address.</p>
<p>Once a metric is stable and correlated with the application, it can protect the improvement in CI. The threshold represents an observed property of the implementation, with enough tolerance for the measurement method. The profile and experiment record explain the reason for the threshold.</p>
<p>The agent’s contribution is the speed of the loop: more candidate explanations can be investigated, implemented, and measured. The quality of the outcome comes from the objective and the feedback. Together they turn a vague report of slowness into a set of specific, maintainable changes.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-claude">
<p>Raymond Wang, Sam Attard, and Issac G., <a href="https://claude.dev/blog/how-we-made-claude-ai-faster/" rel="noopener noreferrer">How we made claude.ai 3x faster in two weeks</a>, September 23, 2026. Added in the September 25 revision. <a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fnref-claude" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fnref-claude-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-use">
<p>Brendan Gregg, <a href="https://www.brendangregg.com/usemethod.html" rel="noopener noreferrer">The USE Method</a>. Resource utilization, saturation, and errors. <a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fnref-use" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-timing">
<p>MDN, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark" rel="noopener noreferrer">Performance.mark()</a>. Application-defined timing markers. <a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fnref-timing" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-tail">
<p>Jeffrey Dean and Luiz André Barroso, <a href="https://research.google/pubs/the-tail-at-scale/" rel="noopener noreferrer">The Tail at Scale</a>, Communications of the ACM, 2013. Tail latency in large services. <a href="https://blog.ecitis.org/llm-performance-hill-climbing/#user-content-fnref-tail" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Performance</category>
            <category>Coding agents</category>
            <category>Benchmarking</category>
            <enclosure length="73827" type="image/png" url="https://blog.ecitis.org/open-graph/llm-performance-hill-climbing.png"/>
        </item>
        <item>
            <title><![CDATA[Optimizing LLM Inference: Prefill, Decode, and Goodput]]></title>
            <link>https://blog.ecitis.org/inference-performance-climbs/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/inference-performance-climbs/</guid>
            <pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Connect prefill, decode, cache management, and scheduling to the latency and goodput of an LLM service.]]></description>
            <content:encoded><![CDATA[<p>Inference performance is a resource-allocation problem across prefill, decode, cache storage, and scheduling. Each phase responds to different changes. A long prompt benefits from faster prefill or reuse of its prefix; a long response benefits from lower decode cost; a busy service benefits from keeping useful work on the accelerator without pushing requests past their deadlines.</p>
<p>Separating these phases turns optimization into a sequence of measurable decisions. The workload determines the objective, the profile identifies the limiting resource, and a configuration sweep finds the operating point. The result is a latency and capacity curve for the actual service.</p>
<h2 id="latency-accounting">Latency accounting</h2>
<p>A generated response has an initial wait followed by a sequence of token arrivals. For N output tokens:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>end-to-end latency = TTFT + sum of the remaining inter-token gaps</span></span>
<span class="line"><span>mean TPOT = (completion time - first-token time) / (N - 1)</span></span></code></pre>
<p>Time to first token, or TTFT, includes queueing, prompt processing, and transport when measured at the client. Time per output token, or TPOT, describes the generation phase. A one-token response has no subsequent token gap, so TPOT is undefined under this formulation.</p>
<p>The arithmetic identifies where a change pays off. A request with 400 ms TTFT and 199 gaps averaging 25 ms takes 5.375 seconds. Halving TTFT saves 200 ms. Reducing each gap by 5 ms saves 995 ms. A one-token classification request has the opposite allocation: nearly all its latency is in the path to the first token.</p>
<p>For agents, this accounting extends across calls. Ten sequential model steps accumulate ten queue waits and ten prefills, as well as tool execution. Per-call improvements matter in proportion to their contribution to the trajectory.</p>
<h2 id="workload-shape-selects-the-optimization">Workload shape selects the optimization</h2>








































<div class="table-wrap"><table><thead><tr><th>Inference shape</th><th>Main resource pressure</th><th>Useful mechanisms</th></tr></thead><tbody><tr><td>Long prompt, short answer</td><td>Prefill compute and scheduling</td><td>Prefix caching, chunked prefill</td></tr><tr><td>Short prompt, long answer</td><td>Weight and cache reads during decode</td><td>Quantization, batching, speculation</td></tr><tr><td>Many short requests</td><td>Scheduler capacity and queueing</td><td>Continuous batching, admission control</td></tr><tr><td>Long context</td><td>KV capacity and attention traffic</td><td>Cache layout, attention kernels, context selection</td></tr><tr><td>Many adapters</td><td>Adapter residency and transfers</td><td>Adapter batching and loading policy</td></tr><tr><td>Model split across GPUs</td><td>Collectives and shard placement</td><td>Parallelism plan, communication overlap</td></tr></tbody></table></div>
<p>The same model can occupy several rows depending on the application. A document assistant and an autocomplete service may share weights while needing different replica configurations. Prompt and output length distributions, arrival rates, and shared-prefix structure define the workload more precisely than the model name.</p>
<p>Quantization reduces the bytes used for weights and can reduce memory traffic. Its effect on completed work also depends on output quality and response length. A faster token stream that requires more retries may consume more total resources per successful request.</p>
<h2 id="prefix-caching-removes-repeated-prefill">Prefix caching removes repeated prefill</h2>
<p>An attention cache records state for the preceding tokens. When another request begins with the same token prefix, that state can be reused. Automatic prefix caching therefore saves computation on shared instructions, documents, or earlier conversation turns.<sup><a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fn-prefix" id="user-content-fnref-prefix">1</a></sup></p>
<p>The position of changing content matters. A request-specific timestamp near the beginning breaks the shared prefix before the reusable document. Placing stable instructions and common context first exposes a larger reusable region where the application semantics allow it.</p>
<p>The saving applies to repeated prefill. Every newly generated token still requires decode work. A service dominated by long outputs therefore sees a different gain from a service repeatedly asking short questions about the same long document.</p>
<p>Cache residency connects reuse to capacity. Frequently reused blocks are valuable only while they remain available. A workload with many unrelated long prefixes can evict them, so hit rate depends on the traffic mix and memory budget as well as prompt construction.</p>
<h2 id="paged-storage-increases-useful-cache-capacity">Paged storage increases useful cache capacity</h2>
<p>The KV cache grows as a sequence generates tokens and shrinks when the request completes. Reserving contiguous space for the maximum possible length wastes memory and makes sharing difficult. PagedAttention addresses this with logical blocks mapped to physical cache storage.<sup><a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fn-paged" id="user-content-fnref-paged">2</a></sup></p>
<p>The block table lets a sequence grow without a single large contiguous allocation. Shared prefixes can refer to the same physical blocks, while later tokens occupy separate blocks. This increases the number of live sequences that fit in a fixed memory budget.</p>
<p>For a conventional full-attention decoder, approximate cache storage is:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>KV bytes = 2 × layers × cached tokens</span></span>
<span class="line"><span>             × KV heads × head dimension × bytes per element</span></span></code></pre>
<p>The factor of two accounts for keys and values. Grouped-query attention uses the number of KV heads in this expression, rather than the larger number of query heads. Sliding-window and compressed-cache architectures change the storage model.</p>
<p>For 32 layers, 8 KV heads, head dimension 128, and two-byte elements, each cached token occupies 128 KiB. An 8,192-token sequence needs 1 GiB, and sixteen such sequences need about 16 GiB before weights, temporary buffers, and allocation overhead. Weight quantization and cache quantization are separate choices.</p>
<p>This is why cache management can improve throughput without changing the speed of an individual matrix multiplication. More of the memory holds active work, so more sequences can share the cost of reading weights.</p>
<h2 id="chunked-prefill-controls-interference">Chunked prefill controls interference</h2>
<p>A long prefill and a one-token decode step have different execution shapes. Running the entire prefill at once can delay requests that are already streaming. Chunking divides the prompt into smaller scheduling units so the engine can interleave the two kinds of work.</p>
<p>SARATHI develops this combination of chunked prefills and decode work.<sup><a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fn-sarathi" id="user-content-fnref-sarathi">3</a></sup> The chunk size controls the tradeoff. Larger chunks give prompt processing more work per scheduling step. Smaller chunks give ongoing decodes more frequent opportunities to run.</p>
<p>The scheduler’s token budget links TTFT, TPOT, and throughput. A configuration that finishes one large prompt sooner may lengthen the token gaps of every other request. vLLM’s tuning documentation describes the corresponding scheduling and memory controls.<sup><a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fn-tuning" id="user-content-fnref-tuning">4</a></sup> Their useful values depend on the distribution of prompt lengths and the service objective.</p>
<h2 id="speculation-changes-the-amount-of-work-per-target-call">Speculation changes the amount of work per target call</h2>
<p>Speculative decoding uses a cheaper model to propose several tokens, then has the target model verify them together. Exact speculative sampling includes a correction procedure that preserves the target distribution within numerical limits.<sup><a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fn-speculation" id="user-content-fnref-speculation">5</a></sup></p>
<p>Its gain comes from completing several accepted tokens during a verification pass whose cost is lower than producing those tokens through separate target calls. The draft model adds work, so acceptance rate and draft latency jointly determine the result.</p>
<p>A small draft with moderate acceptance can outperform a larger, more accurate draft if it is substantially cheaper. Longer proposals can increase accepted tokens per pass while also increasing wasted work after rejection. The useful measurements are draft time, verification time, accepted tokens, and end-to-end generation time.</p>
<p>Speculation is especially attractive when the target has spare parallel compute during small-batch decoding. At high utilization, the additional work can interact differently with batching. It belongs in a workload-specific sweep alongside the other serving parameters.</p>
<h2 id="goodput-identifies-the-operating-point">Goodput identifies the operating point</h2>
<p>Throughput counts completed work. Goodput counts work that meets the service’s latency and quality requirements. As offered load increases, throughput can continue rising after deadline attainment has begun to fall. The <a href="https://blog.ecitis.org/inference-slos/">inference SLO guide</a> develops this distinction in more detail.</p>
<p>Consider an illustrative comparison at a fixed arrival rate with a 700 ms TTFT limit:</p>





























<div class="table-wrap"><table><thead><tr><th>Configuration</th><th>Successful requests/s</th><th>p95 TTFT</th><th>Requests meeting all limits/s</th></tr></thead><tbody><tr><td>A</td><td>24</td><td>620 ms</td><td>23</td></tr><tr><td>B</td><td>30</td><td>1,100 ms</td><td>18</td></tr><tr><td>C</td><td>26</td><td>650 ms</td><td>25</td></tr></tbody></table></div>
<p>B has the highest throughput. C delivers the most requests within the stated limits. The objective therefore selects C, subject to the same quality and cost requirements applied to the other configurations.</p>
<p>Client-side timestamps also expose buffering and streaming stalls. Mean TPOT can be acceptable while a response contains a long pause, so the distribution of token gaps complements request-level latency.</p>
<h2 id="capacity-and-arrival-rate-experiments">Capacity and arrival-rate experiments</h2>
<p>A closed-loop generator maintains a fixed number of outstanding requests, replacing each as it completes. It measures throughput and latency at that concurrency. An open-loop generator schedules arrivals independently of completions, revealing how a queue grows when demand continues during a slowdown.</p>
<p>Both experiments are useful. A concurrency sweep locates capacity and saturation; an arrival-rate sweep establishes deadline attainment under production-shaped demand. Cold caches, repeated prefixes, bursts, and mixed prompt lengths exercise different parts of the system.</p>
<p>The request record contains arrival time, first-token time, completion time, token counts, cache status, and errors. Failed and rejected requests remain part of the offered workload. This keeps apparent latency gains from being confused with dropping difficult work.</p>
<h2 id="communication-in-a-distributed-replica">Communication in a distributed replica</h2>
<p>Splitting a model across GPUs reduces local matrix work while adding transfers. Tensor parallelism introduces collectives around partitioned layers; expert parallelism introduces token dispatch and combination. The exposed part of that communication determines the gain from another device.</p>
<p>A larger parallel group that saves 20 ms of compute and adds 30 ms of communication increases latency. Placement, transfer size, and overlap therefore belong in the same experiment as batch size and cache allocation. The <a href="https://blog.ecitis.org/parallel-kittens-ai-networking/">ParallelKittens article</a> examines those boundaries at the kernel level.</p>
<p>An inference configuration is a balance among these resources. Phase-level measurements explain why a change works, while goodput identifies where that change becomes useful capacity for the application.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-prefix">
<p>vLLM, <a href="https://docs.vllm.ai/en/latest/features/automatic_prefix_caching/" rel="noopener noreferrer">Automatic Prefix Caching</a>. Reuse conditions and limitations. <a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fnref-prefix" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-paged">
<p>Kwon et al., <a href="https://arxiv.org/abs/2309.06180" rel="noopener noreferrer">Efficient Memory Management for Large Language Model Serving with PagedAttention</a>, SOSP 2023. KV-cache allocation and serving experiments. <a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fnref-paged" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-sarathi">
<p>Agrawal et al., <a href="https://arxiv.org/abs/2308.16369" rel="noopener noreferrer">SARATHI: Efficient LLM Inference by Piggybacking Decodes with Chunked Prefills</a>, 2023. <a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fnref-sarathi" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-tuning">
<p>vLLM, <a href="https://docs.vllm.ai/en/latest/configuration/optimization/" rel="noopener noreferrer">Optimization and Tuning</a>. Scheduling and memory settings are version-dependent; record the version used. <a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fnref-tuning" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-speculation">
<p>Chen et al., <a href="https://arxiv.org/abs/2302.01318" rel="noopener noreferrer">Accelerating Large Language Model Decoding with Speculative Sampling</a>, 2023. Distribution-preserving verification and measured decoding results. <a href="https://blog.ecitis.org/inference-performance-climbs/#user-content-fnref-speculation" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Inference</category>
            <category>Performance</category>
            <category>Benchmarking</category>
            <enclosure length="53046" type="image/png" url="https://blog.ecitis.org/open-graph/inference-performance-climbs.png"/>
        </item>
        <item>
            <title><![CDATA[ParallelKittens: Communication and Compute in Multi-GPU Kernels]]></title>
            <link>https://blog.ecitis.org/parallel-kittens-ai-networking/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/parallel-kittens-ai-networking/</guid>
            <pubDate>Wed, 16 Sep 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Trace how partitioning, collective operations, and kernel scheduling determine exposed communication in multi-GPU workloads.]]></description>
            <content:encoded><![CDATA[<p>Multi-GPU execution adds a communication schedule to the model’s computation graph. A matrix multiplication can finish on one device while its consumer waits for a partial result from another. The time spent at that boundary depends on message size, topology, synchronization, and how much independent work is available during the transfer.</p>
<p>ParallelKittens approaches this as a kernel-design problem. It extends ThunderKittens with reusable primitives for combining computation and communication, so data movement can be scheduled alongside the operations that produce and consume it.<sup><a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fn-pk" id="user-content-fnref-pk">1</a></sup> The useful target is exposed communication: the portion of transfer time that remains on the critical path after overlap.</p>
<h2 id="communication-follows-the-partition">Communication follows the partition</h2>
<p>The parallelism strategy determines what moves between devices.</p>
<p><strong>Tensor parallelism</strong> partitions operations within a layer. Devices compute partial results and use collectives to combine or redistribute them. These boundaries can occur repeatedly during every generated token.</p>
<p><strong>Expert parallelism</strong> places different experts on different devices. Tokens travel to the selected experts, then their outputs return for combination. The traffic depends on routing decisions as well as batch size.</p>
<p><strong>Pipeline parallelism</strong> assigns consecutive stages to different devices. Activations move between stages, and the schedule determines whether a stage has useful work while it waits for the next microbatch.</p>
<p><strong>Distributed attention</strong> partitions sequence or attention state. The kernel exchanges the shards needed to form the output, with communication shaped by the chosen attention algorithm.</p>
<p>These patterns create different opportunities. A regular reduction has a different schedule from a variable-sized exchange of expert tokens. The relevant network workload is the sequence of these operations, including their dependencies and sizes.</p>
<h2 id="collective-operations">Collective operations</h2>
<p>An all-reduce combines values across participants and returns the reduced result to each. An all-gather assembles distributed shards into a complete result on every participant. Reduce-scatter combines reduction with partitioned output. NCCL specifies these semantics independently of the algorithms used to execute them.<sup><a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fn-collectives" id="user-content-fnref-collectives">2</a></sup></p>
<p>The distinction is useful when reading a model trace:</p>



































<div class="table-wrap"><table><thead><tr><th>Boundary</th><th>Data that moves</th><th>Main scheduling dependency</th></tr></thead><tbody><tr><td>Tensor-parallel layer</td><td>Partial activations or reductions</td><td>The next operation needs the combined result</td></tr><tr><td>Expert dispatch</td><td>Tokens and routing metadata</td><td>Each expert needs its assigned inputs</td></tr><tr><td>Expert combine</td><td>Weighted expert outputs</td><td>The following layer needs the combined activation</td></tr><tr><td>Pipeline stage</td><td>Activations</td><td>The next stage waits for a microbatch</td></tr><tr><td>Distributed attention</td><td>Attention-related shards</td><td>A tile waits for the required remote state</td></tr></tbody></table></div>
<p>The runtime selects a particular implementation for each boundary. That selection establishes the actual number of stages, temporary buffers, and synchronization points. The same high-level model can therefore produce different communication behavior under different serving engines.</p>
<h2 id="latency-and-bandwidth-regimes">Latency and bandwidth regimes</h2>
<p>A first-order transfer model separates startup latency from transmission time:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>transfer time ≈ startup latency + message bytes / effective bandwidth</span></span></code></pre>
<p>Effective bandwidth is the rate achieved on the path being used. Collective algorithms add stages, and topology changes which links carry the traffic, but the model already explains why small and large transfers favor different optimizations.</p>
<p>For an illustrative 4 microsecond startup and 100 GB/s effective bandwidth, transmitting 4 KiB takes about 0.041 microseconds. Nearly all the elapsed time is startup. A 64 MiB transfer takes about 671 microseconds for transmission, so bandwidth dominates instead.</p>
<p>Doubling bandwidth barely changes the small-message case. Reducing launch overhead or combining messages can improve it substantially. Large transfers benefit more directly from higher sustained bandwidth and avoiding redundant copies.</p>
<p>Message fusion introduces a scheduling tradeoff. Combining several small transfers amortizes startup, but the combined transfer waits until its inputs are ready. If one result is available much earlier than the others, fusion can delay its consumer. The useful grouping follows readiness and dependencies as well as byte count.</p>
<h2 id="the-physical-path">The physical path</h2>
<p>Within a server, communication may use PCIe or a dedicated GPU interconnect such as NVLink. Across servers, the path includes network adapters and a fabric such as InfiniBand or Ethernet with RDMA. Placement determines how often the execution crosses each boundary.</p>
<p>A frequently executed collective benefits from keeping its participants on a fast local path. A larger model may require crossing a slower boundary to fit, in which case the partition plan determines how much traffic pays that cost. Capacity and communication are coupled decisions.</p>
<p>RDMA can remove host-side copies from the transfer path. It does not remove queueing in the fabric, oversubscribed links, or uneven traffic among destinations. Those effects appear in the tail of distributed execution, especially when every participant waits for the slowest result.</p>
<h2 id="compute-communication-overlap">Compute-communication overlap</h2>
<p>For a serial stage, elapsed time is compute time plus communication time. With independent work and sufficient resources, the two can overlap:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>serial stage:        T = compute + communication</span></span>
<span class="line"><span>ideal overlap:       T = max(compute, communication)</span></span>
<span class="line"><span>exposed communication = time remaining on the critical path</span></span></code></pre>
<p>A stage with 6 ms of compute and 4 ms of communication takes 10 ms serially and 6 ms under ideal overlap, a 1.67x stage speedup. The bound assumes the transfer does not slow the computation through shared resource contention.</p>
<p>Its application-level effect depends on the fraction of total time in that stage. If it occupies 20 percent of request time, the idealized normalized total becomes approximately 0.8 + 0.2/1.67 = 0.92. That is about a 1.09x request speedup.</p>
<p>Tiling creates finer dependencies. A consumer may begin once the first tile arrives rather than waiting for the entire tensor. The producer, transfer, and consumer can then form a pipeline. Tile size controls reuse, synchronization frequency, and the amount of work exposed for overlap.</p>
<p>The pipeline also needs explicit ownership. A buffer cannot be overwritten while another device is still reading it, and a consumer must observe the producer’s completed writes before using the tile. Synchronization is part of the algorithm, with both a correctness cost and a scheduling cost.</p>
<h2 id="the-parallelkittens-template">The ParallelKittens template</h2>
<p>ParallelKittens organizes kernels around loader, storer, consumer, and communicator roles. Loaders and storers move tiles, consumers perform computation, and communicators handle inter-GPU work. Its primitives and template encode recurring scheduling patterns across multi-GPU kernels.<sup><a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fn-pk" id="user-content-fnref-pk-2">1</a></sup></p>
<p>This makes transfer mechanism and resource assignment visible design choices. Copy-engine transfers and thread-level peer operations have different overheads. Dedicated communication resources can make progress independently, while using those resources also changes what remains available to computation.</p>
<p>The paper evaluates Hopper and Blackwell workloads and reports maxima of 2.33x for data- and tensor-parallel workloads, 4.08x for sequence-parallel workloads, and 1.22x for expert-parallel workloads.<sup><a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fn-pk" id="user-content-fnref-pk-3">1</a></sup> These results describe the tested kernels and baselines. Their significance is that a reusable design can capture gains across several communication patterns rather than requiring an unrelated implementation for every operator.</p>
<h2 id="reading-a-distributed-trace">Reading a distributed trace</h2>
<p>Nsight Systems separates CPU overhead, launch latency, and GPU execution time.<sup><a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fn-trace" id="user-content-fnref-trace">3</a></sup> Across multiple devices, those timelines explain whether a gap comes from delayed submission, a dependency, or data movement.</p>
<p>A collective that appears slow on most ranks can be waiting for one late participant. If seven GPUs arrive at the boundary early and the eighth finishes its preceding work late, the apparent collective duration includes that skew. The cause may be expert imbalance, input preparation, or a competing workload on the late device.</p>
<p>A trace therefore connects three intervals: production of the input, execution of the transfer, and the point where the consumer resumes. Communication that overlaps successfully may occupy a large interval without extending the critical path. A short transfer immediately before a dependent operation may matter more.</p>
<p>The shape sweep includes small and large messages, prefill and decode batches, and representative sequence lengths. Packing, unpacking, barriers, and launch overhead belong in the measured operation because they are part of moving a usable tensor to its consumer.</p>
<h2 id="kernel-and-system-validation">Kernel and system validation</h2>
<p>A reference implementation establishes the expected output, with numerical tolerances appropriate to the operation. Partial tiles, changing rank counts, irregular expert assignments, and repeated execution exercise the boundaries most likely to expose ordering errors.</p>
<p>The performance comparison has two levels. A kernel benchmark measures whether the transfer and computation schedule improved. A serving benchmark measures whether that improvement reduces request latency, increases capacity, or lowers cost at the same output quality.</p>
<p>Custom fused kernels are most useful when the operation repeats often, accounts for a substantial share of runtime, and has a manageable shape space. A mature collective implementation remains a valuable baseline and fallback as model layouts and hardware change.</p>
<p>The connection between networking and kernel design is direct: the partition decides which data moves, the topology decides its path, and the schedule decides how much of the movement is visible to the next operation. Improving any of those can turn idle accelerator time into useful computation.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-pk">
<p>Sul, Arora, Spector, and Ré, <a href="https://arxiv.org/abs/2511.13940" rel="noopener noreferrer">ParallelKittens: Systematic and Practical Simplification of Multi-GPU AI Kernels</a>, 2025. The reported 2.33x, 4.08x, and 1.22x maxima above come from this paper’s tested workloads. <a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fnref-pk" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fnref-pk-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fnref-pk-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-collectives">
<p>NVIDIA, <a href="https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/collectives.html" rel="noopener noreferrer">NCCL collective operations</a>. Definitions of all-reduce, all-gather, reduce-scatter, and related operations. <a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fnref-collectives" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-trace">
<p>NVIDIA, <a href="https://developer.nvidia.com/blog/understanding-the-visualization-of-overhead-and-latency-in-nsight-systems/" rel="noopener noreferrer">Understanding the Visualization of Overhead and Latency in Nsight Systems</a>. Interpreting launch and execution timelines. <a href="https://blog.ecitis.org/parallel-kittens-ai-networking/#user-content-fnref-trace" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>Multi-GPU</category>
            <category>Networking</category>
            <category>ParallelKittens</category>
            <enclosure length="58802" type="image/png" url="https://blog.ecitis.org/open-graph/parallel-kittens-ai-networking.png"/>
        </item>
        <item>
            <title><![CDATA[The Unit Distance Problem: What a Model Actually Proved]]></title>
            <link>https://blog.ecitis.org/erdos-unit-distance-problem/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/erdos-unit-distance-problem/</guid>
            <pubDate>Fri, 07 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A reasoning model disproved a 1946 Erdős conjecture in May 2026. Separating the mathematical content from the claims made about it.]]></description>
            <content:encoded><![CDATA[<p>Place <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> points in the plane. Count the pairs that are exactly one unit apart. Call the largest possible count <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<p>Paul Erdős asked how fast <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> grows in 1946.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> The question needs no background to state and no notation beyond a ruler. It has been open ever since.</p>
<p>On 20 May 2026 OpenAI published a proof, produced by an internal reasoning model, that Erdős’s own answer to the question was wrong.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> The result is real and the mathematics is now checked several ways over, including by a nine-person panel of specialists within a day of the announcement.</p>
<h2 id="the-problem-is-easy-to-state-and-hard-for-a-specific-reason">The problem is easy to state and hard for a specific reason</h2>
<p>Distance is a rigid constraint. Fix two points at distance 1; the set of points at distance 1 from both is at most two points. So unit distances cannot pile up locally the way, say, collinearity can. Any configuration with many unit distances has to distribute the coincidences globally, across the whole point set, and that is where the difficulty lives.</p>
<p>Erdős’s own construction is the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msqrt><mi>n</mi></msqrt><mo>×</mo><msqrt><mi>n</mi></msqrt></mrow></semantics></math> integer grid, rescaled so that many integer pairs <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>a</mi><mo separator="true">,</mo><mi>b</mi><mo stretchy="false">)</mo></mrow></semantics></math> share the same value of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup></mrow></semantics></math>. The number of such representations grows, but only barely, and the resulting count is</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>u</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><msup><mi>n</mi><mrow><mn>1</mn><mo>+</mo><mi>c</mi><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi></mrow></msup></mrow></semantics></math>
<p>for a constant <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup> The exponent exceeds 1 but tends to 1 as <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> grows. In eighty years nobody improved on it.</p>
<figure><figcaption><strong>The square grid, the configuration everyone assumed was optimal</strong> — Each segment joins a pair of points at distance exactly 1. Diagonals are longer, so they do not count.</figcaption><p><a href="https://blog.ecitis.org/erdos-unit-distance-problem/">View this figure on the site &#8594;</a></p></figure>
<h2 id="two-bounds-moving-toward-each-other-slowly">Two bounds moving toward each other, slowly</h2>
<p>There are two separate questions, and conflating them is the most common error in coverage of this result.</p>
<p>The <strong>upper bound</strong> question asks what a proof forbids. Erdős gave <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>3</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math>. Spencer, Szemerédi and Trotter brought it to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> in 1984, and that has stood for forty-two years.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>The <strong>lower bound</strong> question asks what a construction achieves. That is the grid bound above.</p>
<p>Erdős conjectured that the truth was at the bottom: that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mo>=</mo><msup><mi>n</mi><mrow><mn>1</mn><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></msup></mrow></semantics></math>, that the grid was essentially optimal, and that the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> upper bound was the artefact. Nearly all subsequent effort went into pushing the upper bound down toward the grid, because that is where the answer was assumed to be.</p>
<p>The May 2026 result moved the lower bound. It exhibits an infinite family of point sets with</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>u</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><msup><mi>n</mi><mrow><mn>1</mn><mo>+</mo><mi>δ</mi></mrow></msup></mrow></semantics></math>
<p>for an absolute constant <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math> and infinitely many <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup> Since <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow></semantics></math> is fixed rather than shrinking with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>, this contradicts <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></msup></mrow></semantics></math> directly. The conjecture is false. The upper bound was not improved, and the gap between the two bounds is now wider than it was before, in the sense that the plausible location of the answer has expanded rather than contracted.</p>
<figure><figcaption><strong>Eighty years of exponents, and which side finally moved</strong> — Exponent β in u(n) ≈ nβ. Upper bounds come down from proofs; lower bounds come up from constructions. The shaded band is the region nobody could eliminate.</figcaption><p><a href="https://blog.ecitis.org/erdos-unit-distance-problem/">View this figure on the site &#8594;</a></p></figure>
<p>Reporting that described this as “solving” the unit distance problem inverted what happened. The problem is more open than it was on 19 May.</p>
<h2 id="what-the-model-produced">What the model produced</h2>
<p>The construction does not come from discrete geometry. It comes from algebraic number theory.</p>
<p>The route, as reconstructed by the mathematicians who read it, is to build number fields of large degree with small discriminant and many primes of small norm, and to extract a point configuration from the resulting lattice. The tool for producing such fields is the Golod–Shafarevich criterion, which guarantees that certain class field towers are infinite. The nine-author companion note attributes the essential ideas, in retrospect, to Golod–Shafarevich, to Hajir–Maire–Ramakrishna, and to Ellenberg–Venkatesh.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-4" id="user-content-fnref-4">4</a></sup></p>
<p>This is the interesting part of the story. Golod–Shafarevich towers were developed in the 1960s to answer a question about class field towers that has nothing to do with points in a plane. Using them to build unit-distance configurations requires knowing both bodies of work well enough to see that one supplies what the other needs, and then being willing to grind through a long calculation whose payoff is uncertain.</p>
<p>Jacob Tsimerman, one of the verifiers, has said publicly that he had considered a strategy of this shape and abandoned it, because such techniques consume a great deal of time and frequently do not work.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> That is a precise description of where the model had an advantage: not insight, but breadth of recall plus indifference to sunk cost.</p>
<p>Scott Aaronson, who reported the account of the run from Lijie Chen, described it as essentially one-shot: the problem was handed over, the model thought, and it emitted a several-page argument that held up under expert analysis.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-6" id="user-content-fnref-6">6</a></sup> Separately, OpenAI has indicated the model solved the problem roughly half the time at maximum token budget, which means the single successful run was drawn from a distribution that also contains failures.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-5" id="user-content-fnref-5-2">5</a></sup></p>
<h2 id="how-it-was-checked">How it was checked</h2>
<p>The verification chain is unusually well documented, and worth walking through because it is the part that generalises.</p>
<figure><figcaption><strong>From model output to machine-checked artefact</strong> — The result was announced and refereed on the same day, then sharpened and formalised over the following six weeks.</figcaption><p><a href="https://blog.ecitis.org/erdos-unit-distance-problem/">View this figure on the site &#8594;</a></p></figure>
<p>Four things happened, in order.</p>
<p>The nine-author note by Alon, Bloom, Gowers, Litt, Sawin, Shankar, Tsimerman, Wang and Matchett Wood appeared the same day, presenting “a short, digested, human-verified version” of the argument along with reflections on it.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-4" id="user-content-fnref-4-2">4</a></sup> That is a nine-person panel of specialists assembled in under a day.</p>
<p>Will Sawin, independently and also on 20 May, posted a preprint making the exponent explicit: there are point sets with more than <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mn>1.014</mn></msup></mrow></semantics></math> unit distances, via the same Golod–Shafarevich route.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-7" id="user-content-fnref-7">7</a></sup> The frequently quoted figure of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi><mo>≈</mo><mn>0.014</mn></mrow></semantics></math> is Sawin’s, not the model’s. The model’s <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow></semantics></math> was inexplicit.</p>
<p>Explicit certificate optimisation followed. Work in June searching over prime sets pushed the achievable <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>δ</mi></mrow></semantics></math> past <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0.015</mn></mrow></semantics></math> and then above <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0.03</mn></mrow></semantics></math>, with contemporaneous MathOverflow discussion reporting better still.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-8" id="user-content-fnref-8">8</a></sup> These are engineering improvements on a fixed method, not new ideas.</p>
<p>Formalisation came last. On 26 June, Logical Intelligence reported a Lean 4 proof produced by their Aleph Prover system: roughly 33,000 lines across 252 Mathlib modules, re-checked with two independent Lean kernels.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-9" id="user-content-fnref-9">9</a></sup> It is conditional. The Golod–Shafarevich inequality and Shafarevich’s rank-relation bound are not in Mathlib and were taken as axioms, with mathematicians confirming the statements matched textbook references. The process involved a human review pass between two prover iterations. So the answer to “was it formalised” is yes, a month later, by a different group, modulo two classical inputs.</p>
<h2 id="separating-this-from-adjacent-episodes">Separating this from adjacent episodes</h2>
<p>Three distinct things have been called “AI solves an Erdős problem” in the past year, and they are not the same kind of event.</p>
<p><strong>Erdős Problem #728</strong>, resolved in January 2026, is the cleanest formal case: GPT-5.2 Pro generated an argument, Harmonic’s Aristotle formalised it in Lean, and the Lean proof was translated back into readable mathematics for arXiv.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-10" id="user-content-fnref-10">10</a></sup> The result concerns a logarithmic-gap phenomenon in factorial divisibility. It is new, fully machine-checked, and far less famous than the unit distance problem.</p>
<p><strong>The Erdős problems database episodes</strong> of late 2025 were something else again. Models were credited with resolving open problems when what they had actually done was locate an existing proof in the literature that the database’s maintainers had not indexed. That is a useful capability and not a mathematical discovery.</p>
<p><strong>The unit distance result</strong> is a third category: a new construction in a famous problem, produced informally, refereed by humans first and formalised later. It has the highest mathematical stakes and the weakest a-priori verification guarantee of the three.</p>
<figure><figcaption><strong>What was claimed against what was confirmed</strong> — Six statements that circulated in May 2026, graded against the primary sources.</figcaption><p><a href="https://blog.ecitis.org/erdos-unit-distance-problem/">View this figure on the site &#8594;</a></p></figure>
<h2 id="questions-worth-keeping-in-view">Questions worth keeping in view</h2>
<p>Several critiques were raised in May and June. Some hold up.</p>
<p><strong>Selection effects are unmeasured.</strong> OpenAI has not published failure rates, attempt counts, or compute. A model that produces one correct famous-problem proof out of some unreported number of attempts across some unreported number of problems is a different object from one that produces correct proofs reliably, and the public record does not distinguish them.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-11" id="user-content-fnref-11">11</a></sup> Aaronson makes the same point about problem selection: human effort concentrated on proving Erdős right, leaving the counterexample direction comparatively unexplored.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-6" id="user-content-fnref-6-2">6</a></sup></p>
<p><strong>Verification capacity is the bottleneck, not generation.</strong> Thomas Bloom’s formulation is the sharp one: “It could be right. It could be nonsense. Who’s going to be able to check this?”<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-11" id="user-content-fnref-11-2">11</a></sup> The unit distance proof got nine of the world’s relevant experts within a day because it was a famous problem announced by a major lab. That does not scale. A hundred such claims a month would exhaust the referee pool for exactly the problems where refereeing matters most.</p>
<p><strong>Attribution has no mechanism.</strong> Melanie Matchett Wood’s observation is that a model has read every paper, and there is no principled way for it to credit the prior work it is drawing on.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-11" id="user-content-fnref-11-3">11</a></sup> The nine-author note had to reconstruct the intellectual ancestry after the fact.</p>
<p><strong>The institutional response was immediate.</strong> The Leiden Declaration, published 2 June 2026, called for oversight of AI in mathematical research and had gathered around 1,590 signatures within three days.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-11" id="user-content-fnref-11-4">11</a></sup> A July essay by Jun-Yong Park argues the deeper risk is that human capacity to verify, interpret and challenge research-level mathematics is itself eroding, and proposes that AI systems be required to present decision-critical claims in machine-checkable form.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-12" id="user-content-fnref-12">12</a></sup> Whatever one makes of the framing, the proposal is concrete: formalisation converts the verification bottleneck from a scarce-expert problem into a compute problem.</p>
<p>The critiques that do not hold up are the ones asserting the mathematics is wrong or trivial. Gowers said that if a human had submitted this to the <em>Annals</em>, he would have recommended acceptance without hesitation.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-13" id="user-content-fnref-13">13</a></sup> Litt called it the first autonomously produced AI result he found interesting in itself.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-13" id="user-content-fnref-13-2">13</a></sup> Matchett Wood, one of the sceptics on the broader questions, called it a beautiful piece of mathematics.<sup><a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fn-11" id="user-content-fnref-11-5">11</a></sup></p>
<h2 id="what-this-does-and-does-not-predict">What this does and does not predict</h2>
<p>The honest reading is narrow and still significant.</p>
<p>The task had properties that favour a model: a crisply stated target, a verifiable output, a cross-field connection requiring recall across two literatures that few individuals hold simultaneously, and a long unpleasant computation that a human would rationally decline to start. Not every open problem has this shape. Most do not.</p>
<p>But the shape is not rare either, and it was not previously thought to be reachable. Cross-domain technique transfer is a large fraction of what mathematical progress consists of, and it is exactly the part that scales with breadth of reading rather than depth of a single specialism.</p>
<p>Three things follow for anyone building or evaluating these systems.</p>
<p>Reported successes without denominators are not evidence about capability. Publish attempt counts and failure modes, or expect the result to be discounted.</p>
<p>Formalisation is the only verification method that scales with output volume. It arrived a month late here, incomplete, from a third party. Treating it as the default deliverable rather than a follow-up changes the economics of the whole pipeline.</p>
<p>And the useful unit of analysis is not “the model solved it” but the full chain: generation, human digestion, independent refinement, formalisation. Every stage in that chain did work that the others could not have done. The construction came from a model. The explicit exponent came from Sawin. The confidence came from nine people reading carefully. The audit trail came from Lean. Reporting that collapses this into a single claim about autonomy describes something that did not occur.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Paul Erdős, “On sets of distances of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> points,” American Mathematical Monthly, 1946. <a href="https://www.renyi.hu/~p_erdos/1946-03.pdf" rel="noopener noreferrer">https://www.renyi.hu/~p_erdos/1946-03.pdf</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>OpenAI, “An OpenAI model has disproved a central conjecture in discrete geometry,” 20 May 2026. <a href="https://openai.com/index/model-disproves-discrete-geometry-conjecture/" rel="noopener noreferrer">https://openai.com/index/model-disproves-discrete-geometry-conjecture/</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Joel Spencer, Endre Szemerédi and William Trotter, “Unit distances in the Euclidean plane,” in <em>Graph Theory and Combinatorics</em>, 1984. <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Noga Alon, Thomas F. Bloom, W. T. Gowers, Daniel Litt, Will Sawin, Arul Shankar, Jacob Tsimerman, Victor Wang and Melanie Matchett Wood, “Remarks on the disproof of the unit distance conjecture,” arXiv, 20 May 2026. <a href="https://arxiv.org/abs/2605.20695" rel="noopener noreferrer">https://arxiv.org/abs/2605.20695</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-4" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-4-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-5">
<p>Timothy B. Lee, “OpenAI’s math breakthrough played to AI’s strengths,” Understanding AI, 2026. <a href="https://www.understandingai.org/p/openais-milestone-math-breakthrough" rel="noopener noreferrer">https://www.understandingai.org/p/openais-milestone-math-breakthrough</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-5" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-5-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-6">
<p>Scott Aaronson, “Dispatches from the possibly last days of human relevance,” Shtetl-Optimized, 27 May 2026. <a href="https://scottaaronson.blog/?p=9782" rel="noopener noreferrer">https://scottaaronson.blog/?p=9782</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-6" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-6-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-7">
<p>Will Sawin, “An explicit lower bound for the unit distance problem,” arXiv, 20 May 2026. <a href="https://arxiv.org/abs/2605.20579" rel="noopener noreferrer">https://arxiv.org/abs/2605.20579</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>Michael T. M. Emmerich, “Optimizing Explicit Unit-Distance Lower-Bound Certificates,” arXiv, June 2026. <a href="https://arxiv.org/abs/2606.03419" rel="noopener noreferrer">https://arxiv.org/abs/2606.03419</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p>Logical Intelligence, “Aleph Prover uses formal methods to verify the Erdős disproof in Lean 4,” 26 June 2026. <a href="https://logicalintelligence.com/blog/aleph-prover-erdos-disproof-lean-4-formal-methods" rel="noopener noreferrer">https://logicalintelligence.com/blog/aleph-prover-erdos-disproof-lean-4-formal-methods</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10">
<p>“Resolution of Erdős Problem #728: a writeup of Aristotle’s Lean proof,” arXiv, January 2026. <a href="https://arxiv.org/abs/2601.07421" rel="noopener noreferrer">https://arxiv.org/abs/2601.07421</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11">
<p>Science News, “An AI math breakthrough sparks calls for new guardrails,” 2026. <a href="https://www.sciencenews.org/article/ai-guardrails-erdos-math-problem" rel="noopener noreferrer">https://www.sciencenews.org/article/ai-guardrails-erdos-math-problem</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-11" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-11-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-11-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-11-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-11-5" class="data-footnote-backref">↩<sup>5</sup></a></p>
</li>
<li id="user-content-fn-12">
<p>Jun-Yong Park, “Automation Without Understanding,” arXiv, 7 July 2026. <a href="https://arxiv.org/abs/2607.06377" rel="noopener noreferrer">https://arxiv.org/abs/2607.06377</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p>The Conversation, “An AI solution to an 80-year-old problem has shocked mathematicians,” 2026. <a href="https://theconversation.com/an-ai-solution-to-an-80-year-old-problem-has-shocked-mathematicians-283686" rel="noopener noreferrer">https://theconversation.com/an-ai-solution-to-an-80-year-old-problem-has-shocked-mathematicians-283686</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-13" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/erdos-unit-distance-problem/#user-content-fnref-13-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Theory &amp; Mathematics</category>
            <category>Reasoning</category>
            <category>Mathematics</category>
            <category>Formal verification</category>
            <category>Evaluation</category>
            <enclosure length="61878" type="image/png" url="https://blog.ecitis.org/open-graph/erdos-unit-distance-problem.png"/>
        </item>
        <item>
            <title><![CDATA[Ramsey Numbers and Extremal Graphs: Two Combinatorics Results]]></title>
            <link>https://blog.ecitis.org/openai-ramsey-extremal-graphs/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/openai-ramsey-extremal-graphs/</guid>
            <pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A superexponential lower bound settles the growth rate of multicolour Ramsey numbers, and two bipartite constructions refute the compactness and degeneracy conjectures.]]></description>
            <content:encoded><![CDATA[<p>The last two chapters of OpenAI’s collection of ten results sit in extremal combinatorics.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> They are the most classical problems in the set: one asks how many colours you need before a triangle becomes unavoidable, the other asks how many edges a graph can carry while avoiding a fixed subgraph.</p>
<p>Both chapters answer questions that Erdős posed and, in two cases, attached prize money to. Neither answer is a small improvement to a constant. The Ramsey chapter closes a gap between an exponential lower bound and a factorial upper bound that had stood since the 1950s. The extremal chapter produces explicit graphs that falsify two conjectures about how extremal numbers should behave.</p>
<h2 id="the-multicolour-ramsey-function">The multicolour Ramsey function</h2>
<p>Write <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></semantics></math> for the least <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow></semantics></math> such that every colouring of the edges of the complete graph <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mi>N</mi></msub></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> colours contains a monochromatic triangle. For <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>=</mo><mn>2</mn></mrow></semantics></math> the answer is 6: the edges of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mn>5</mn></msub></mrow></semantics></math> split into an outer pentagon and an inner pentagram, each a five-cycle and therefore triangle-free, while no two-colouring of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mn>6</mn></msub></mrow></semantics></math> escapes.</p>
<figure><figcaption><strong>Two colours run out of room at six vertices</strong> — R2(3) = 6: five vertices can be coloured without a monochromatic triangle, six cannot.</figcaption><p><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/">View this figure on the site &#8594;</a></p></figure>
<p>The pigeonhole argument behind <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mn>6</mn></msub></mrow></semantics></math> generalises. Pick a vertex, look at its <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>−</mo><mn>1</mn></mrow></semantics></math> edges, and note that some colour appears on at least <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>N</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi>k</mi></mrow></semantics></math> of them. If that neighbourhood contains an edge of the same colour you have a triangle; otherwise you have reduced to a <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>k</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math>-colour problem on a smaller vertex set. Iterating the recurrence gives the Erdős–Szekeres-style bound, which after successive refinements stands at</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>≤</mo><mrow><mo fence="true">(</mo><mi>e</mi><mo>−</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mn>1</mn><mn>6</mn></mfrac></mstyle><mo fence="true">)</mo></mrow><mi>k</mi><mo stretchy="false">!</mo><mo>+</mo><mn>1</mn><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>k</mi><mo>≥</mo><mn>4</mn><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Factorial growth. Written in the form <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>=</mo><msup><mi>k</mi><mrow><mi>θ</mi><mi>k</mi></mrow></msup></mrow></semantics></math>, this says <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi><mo>→</mo><mn>1</mn></mrow></semantics></math>.</p>
<p>Lower bounds worked from the other side, by exhibiting triangle-free colourings. The standard tool is a product construction: a triangle-free <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>-colouring on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>N</mi><mn>1</mn></msub></mrow></semantics></math> vertices and a triangle-free <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ℓ</mi></mrow></semantics></math>-colouring on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>N</mi><mn>2</mn></msub></mrow></semantics></math> vertices combine into a triangle-free <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>k</mi><mo>+</mo><mi mathvariant="normal">ℓ</mi><mo stretchy="false">)</mo></mrow></semantics></math>-colouring on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>N</mi><mn>1</mn></msub><msub><mi>N</mi><mn>2</mn></msub></mrow></semantics></math> vertices, so</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mrow><mi>k</mi><mo>+</mo><mi mathvariant="normal">ℓ</mi></mrow></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo>≥</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>R</mi><mi mathvariant="normal">ℓ</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Tensoring a good small colouring with itself is efficient, but it is inherently exponential: the best you can extract is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>c</mi><mi>k</mi></msup></mrow></semantics></math> for whatever base <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow></semantics></math> your seed colouring supplies. Decades of work on sum-free partitions and hand-found colourings pushed that base up, ending at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>≥</mo><msup><mn>380</mn><mrow><mi>k</mi><mi mathvariant="normal">/</mi><mn>5</mn></mrow></msup><mo>−</mo><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<h2 id="the-gap-was-not-a-constant-factor">The gap was not a constant factor</h2>
<p>An exponential lower bound against a factorial upper bound is a widening gap, not a stubborn constant. Fekete’s lemma applied to the product construction shows the limit</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo>=</mo><munder><mrow><mi>lim</mi><mo>⁡</mo></mrow><mrow><mi>k</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><msup><mo stretchy="false">)</mo><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>k</mi></mrow></msup></mrow></semantics></math>
<p>exists in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>1</mn><mo separator="true">,</mo><mi mathvariant="normal">∞</mi><mo stretchy="false">]</mo></mrow></semantics></math>, and the open question was whether it is finite at all. Erdős offered $250 for the value of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> and $100 for deciding whether it is finite. Graham, Rothschild and Spencer recorded the superexponential growth question in their textbook; Conlon, Fox and Sudakov flagged the exponential-versus-factorial gap again in their survey.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p>Plotting the exponent <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow></semantics></math> makes the shape of the gap visible. The old lower bound’s exponent decays like <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>k</mi></mrow></semantics></math>, so it certified nothing at all in the limit.</p>
<figure><figcaption><strong>The old lower bound had a vanishing exponent</strong> — Write the bounds as R = k^(θk) and plot θ. An exponential bound gives θ → 0.</figcaption><p><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/">View this figure on the site &#8594;</a></p></figure>
<p>Chapter 9 proves the following.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mtext mathvariant="bold">Theorem.</mtext><mtext> </mtext></mrow><mi mathvariant="normal">∃</mi><mtext> </mtext><mi>c</mi><mo>&gt;</mo><mn>0</mn><mtext>  </mtext><mi mathvariant="normal">∀</mi><mi>k</mi><mo>≥</mo><mn>2</mn><mo>:</mo><mspace width="1em"></mspace><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><msup><mrow><mo fence="true">(</mo><mfrac><mrow><mi>c</mi><mtext> </mtext><msup><mi>k</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup></mrow><mrow><mi>log</mi><mo>⁡</mo><mi>k</mi></mrow></mfrac><mo fence="true">)</mo></mrow><mrow><mtext> ⁣</mtext><mi>k</mi></mrow></msup><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Combined with the factorial upper bound this gives</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>k</mi><mrow><mo stretchy="false">(</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>−</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>k</mi></mrow></msup><mtext>  </mtext><mo>≤</mo><mtext>  </mtext><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mtext>  </mtext><mo>≤</mo><mtext>  </mtext><msup><mi>k</mi><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>k</mi></mrow></msup><mo separator="true">,</mo><mspace width="2em"></mspace><mtext>so</mtext><mspace width="2em"></mspace><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>=</mo><msup><mi>k</mi><mrow><mi mathvariant="normal">Θ</mi><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup><mo separator="true">,</mo></mrow></semantics></math>
<p>and in particular <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mo>+</mo><mi mathvariant="normal">∞</mi></mrow></semantics></math>. The growth rate is now pinned to within a constant factor in the exponent’s exponent, which is the coarse question that had been open. The exact constant, whether <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow></semantics></math> is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn></mrow></semantics></math>, or something between, remains open.</p>
<h2 id="how-the-colouring-is-built">How the colouring is built</h2>
<p>The construction is recursive and its invariant is stronger than triangle-freeness: at stage <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>j</mi></mrow></semantics></math>, each colour class is required to be properly <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>j</mi><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math>-colourable as a graph.</p>
<p>Vertices are grouped into blocks. Each block carries a <em>palette</em> <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow></semantics></math>, a subset of the available colours, recording exactly which colours are <strong>missing</strong> from that block; the rest are active there. Inside a block sits a relabelled copy of the previous stage’s colouring, using only the active colours, so every active colour arrives with a proper vertex labelling.</p>
<p>The work is in colouring edges between two blocks. Because the palettes were chosen by a maximal packing, distinct palettes differ in many colours in each direction, so for blocks <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>≠</mo><mi>Q</mi></mrow></semantics></math> there are colours <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>a</mi><mi>s</mi></msub><mo>∈</mo><mi>Q</mi><mo>∖</mo><mi>P</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>b</mi><mi>s</mi></msub><mo>∈</mo><mi>P</mi><mo>∖</mo><mi>Q</mi></mrow></semantics></math>. A vertex’s internal labels in those colours form a word in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mi>H</mi><msup><mo stretchy="false">]</mo><mi>s</mi></msup></mrow></semantics></math>. The choice of cross-edge colour is then made by a pair of fixed maps <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo separator="true">,</mo><mi>g</mi><mo>:</mo><mo stretchy="false">[</mo><mi>H</mi><msup><mo stretchy="false">]</mo><mi>s</mi></msup><mo>→</mo><mo stretchy="false">[</mo><mi>H</mi><msup><mo stretchy="false">]</mo><mi>s</mi></msup></mrow></semantics></math> with the property that for all words <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo separator="true">,</mo><mi>y</mi></mrow></semantics></math> there is a coordinate <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow></semantics></math> with</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>x</mi><mi>d</mi></msub><mo>=</mo><mi>f</mi><mo stretchy="false">(</mo><mi>y</mi><msub><mo stretchy="false">)</mo><mi>d</mi></msub><mspace width="1em"></mspace><mtext>or</mtext><mspace width="1em"></mspace><msub><mi>y</mi><mi>d</mi></msub><mo>=</mo><mi>g</mi><mo stretchy="false">(</mo><mi>x</mi><msub><mo stretchy="false">)</mo><mi>d</mi></msub><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Such maps come from a <em>saturated matrix</em>: a random <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mo>×</mo><msup><mi>H</mi><mi>m</mi></msup></mrow></semantics></math> matrix over <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> colours in which any <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mo>+</mo><mn>1</mn></mrow></semantics></math> columns contain a row displaying every colour, which a union bound supplies for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mo>≍</mo><mi>H</mi><mi>log</mi><mo>⁡</mo><mi>H</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mo>≍</mo><msup><mi>H</mi><mn>2</mn></msup><msup><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msup><mi>H</mi></mrow></semantics></math>. This ingredient is adapted from work of Alon, Ben-Eliezer, Shangguan and Tamo on zero-error list decoding; the chapter’s contribution is applying it to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mo stretchy="false">(</mo><mn>3</mn><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<p>Two consequences do the combinatorial work. Every cross-edge colour is active at exactly one endpoint’s block and missing at the other, and the proper label at the active endpoint is determined by the opposite endpoint. Triangles spanning two blocks die because two same-coloured edges from an outside vertex force equal internal labels, so the edge between their endpoints cannot carry that colour. Triangles spanning three blocks die by a parity argument on symmetric differences: a colour would have to lie in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mi mathvariant="normal">△</mi><mi>Q</mi></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><mi mathvariant="normal">△</mi><mi>R</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mi mathvariant="normal">△</mi><mi>R</mi></mrow></semantics></math> simultaneously, which is impossible.</p>
<p>Multiplying palette-family sizes over <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> stages yields a triangle-free colouring with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mi>H</mi></msub><mo>≍</mo><msup><mi>H</mi><mn>3</mn></msup><msup><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>3</mn></msup><mi>H</mi></mrow></semantics></math> colours on at least <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>c</mi><mn>0</mn></msub><mi>H</mi><msup><mo stretchy="false">)</mo><msub><mi>k</mi><mi>H</mi></msub></msup></mrow></semantics></math> vertices. Since <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi><mo>≫</mo><msup><mi>k</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>k</mi></mrow></semantics></math>, that is the theorem.</p>
<h2 id="a-corollary-about-shannon-capacity">A corollary about Shannon capacity</h2>
<p>The Ramsey–Shannon correspondence, implicit in Erdős, McEliece and Taylor and made explicit by Alon and Orlitsky, converts the bound into a statement about information theory.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Given a triangle-free <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>-colouring of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mi>N</mi></msub></mrow></semantics></math>, let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>i</mi></msub></mrow></semantics></math> be the graph of colour <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow></semantics></math> and take the complete join <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><msub><mi>H</mi><mn>1</mn></msub><mo>∨</mo><mo>⋯</mo><mo>∨</mo><msub><mi>H</mi><mi>k</mi></msub></mrow></semantics></math>. Then <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>=</mo><mn>2</mn></mrow></semantics></math>, while the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow></semantics></math> diagonal words form an independent set in the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>-fold strong power, so the Shannon capacity satisfies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Θ</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>≥</mo><msup><mi>N</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>k</mi></mrow></msup></mrow></semantics></math>.</p>
<p>Setting <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>=</mo><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn></mrow></semantics></math> and using <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>lim</mi><mo>⁡</mo></mrow><mi>k</mi></msub><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><msup><mo stretchy="false">)</mo><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>k</mi></mrow></msup><mo>=</mo><mi mathvariant="normal">∞</mi></mrow></semantics></math> produces graphs with independence number 2 and arbitrarily large Shannon capacity. Shannon capacity therefore cannot be bounded above by any function of the independence number.</p>
<h2 id="turn-type-extremal-numbers">Turán-type extremal numbers</h2>
<p>Chapter 10 changes the question. For a family <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi></mrow></semantics></math> of graphs, the extremal number is</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo><mo>:</mo><mo>=</mo><mi>max</mi><mo>⁡</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">{</mo><mtext> </mtext><mi mathvariant="normal">∣</mi><mi>E</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mi mathvariant="normal">∣</mi><mo>:</mo><mi mathvariant="normal">∣</mi><mi>V</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mi mathvariant="normal">∣</mi><mo>=</mo><mi>n</mi><mo separator="true">,</mo><mtext> </mtext><mi>G</mi><mtext> is </mtext><mi mathvariant="script">F</mi><mtext>-free</mtext><mtext> </mtext><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">}</mo><mo separator="true">,</mo></mrow></semantics></math>
<p>where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi></mrow></semantics></math>-free means containing no member of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi></mrow></semantics></math> as a subgraph. For a single graph one writes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>H</mi><mo stretchy="false">)</mo></mrow></semantics></math>. When <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> is bipartite the answer is a polynomial <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>2</mn><mo>−</mo><mi>γ</mi></mrow></msup></mrow></semantics></math> for some exponent, and determining those exponents is the central open programme of the subject.</p>
<figure><figcaption><strong>Two extremal conjectures, two constructed counterexamples</strong> — Each row states the claim, the object that refutes it, and the gap the object opens.</figcaption><p><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/">View this figure on the site &#8594;</a></p></figure>
<h2 id="compactness-does-a-family-reduce-to-one-member">Compactness: does a family reduce to one member?</h2>
<p>Forbidding more graphs can only lower the extremal number. Erdős and Simonovits conjectured that it cannot lower it by much.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> The original formulation has easy counterexamples. The family <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><msub><mi>K</mi><mrow><mn>1</mn><mo separator="true">,</mo><mn>2</mn></mrow></msub><mo separator="true">,</mo><mn>2</mn><msub><mi>K</mi><mn>2</mn></msub><mo stretchy="false">}</mo></mrow></semantics></math> has <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn></mrow></semantics></math> while its members have extremal numbers <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">⌊</mo><mi>n</mi><mi mathvariant="normal">/</mi><mn>2</mn><mo stretchy="false">⌋</mo></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow></semantics></math>, so the corrected statement restricts to families whose members all contain a cycle:</p>
<blockquote>
<p>For every finite nonempty family <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi></mrow></semantics></math> of graphs, each containing a cycle, do there exist <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo>∈</mo><mi mathvariant="script">F</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo><mo>≤</mo><mi>C</mi><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo></mrow></semantics></math> for all large <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>?</p>
</blockquote>
<p>The chapter answers no, and does so with a family whose members are all connected and bipartite.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mtext mathvariant="bold">Theorem.</mtext><mtext> </mtext></mrow><mi mathvariant="normal">∃</mi><mtext> </mtext><mi mathvariant="script">F</mi><mtext> finite, connected, bipartite, each with a cycle:</mtext><mspace width="1em"></mspace><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>48</mn></mrow></msup><mo fence="true">)</mo></mrow><mo separator="true">,</mo><mspace width="1em"></mspace><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">Ω</mi><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mo fence="true">)</mo></mrow><mtext>  </mtext><mo stretchy="false">(</mo><mi>F</mi><mo>∈</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The construction takes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi><mo>=</mo><mo stretchy="false">{</mo><msub><mi>C</mi><mn>4</mn></msub><mo separator="true">,</mo><msub><mi>C</mi><mn>6</mn></msub><mo stretchy="false">}</mo><mo>∪</mo><mi mathvariant="script">J</mi><mo>∪</mo><mi mathvariant="script">K</mi></mrow></semantics></math>. The two extra pieces come from templates built out of subdivided complete bipartite graphs, where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>S</mi><mi>k</mi></msub></mrow></semantics></math> is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>K</mi><mrow><mn>3</mn><mo separator="true">,</mo><mi>k</mi></mrow></msub></mrow></semantics></math> with every edge replaced by a two-edge path, glued in the style of Bukh and Conlon’s rooted-power construction, then closed under <em>admissible quotients</em>: identifications of vertices that respect the proper two-colouring and stay injective on distinguished subgraphs. Closing under quotients is what makes the family finite and what makes it hard to avoid.</p>
<p>The two bounds come from opposite directions.</p>
<ul>
<li><strong>Upper.</strong> Counting short paths in any <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi></mrow></semantics></math>-free host graph forces <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>21</mn><mi mathvariant="normal">/</mi><mn>16</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>21</mn><mi mathvariant="normal">/</mi><mn>16</mn><mo>=</mo><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>48</mn></mrow></semantics></math>.</li>
<li><strong>Lower.</strong> For each individual <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo>∈</mo><mi mathvariant="script">F</mi></mrow></semantics></math>, the incidence graph of a generalized quadrangle is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow></semantics></math>-free and has <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> edges. Crucially, the field characteristic is allowed to depend on which <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow></semantics></math> is being avoided, so a single host does not have to work for the whole family.</li>
</ul>
<p>That asymmetry is the whole idea. Every member is individually easy to avoid at density <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup></mrow></semantics></math>, but no single algebraic host avoids all of them at once, and the gap is polynomial rather than a constant factor.</p>
<h2 id="degeneracy-does-local-sparsity-fix-the-exponent">Degeneracy: does local sparsity fix the exponent?</h2>
<p>A graph is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math>-degenerate if every nonempty subgraph has a vertex of degree at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math>. Erdős conjectured in 1967 that this local sparsity determines the extremal exponent for bipartite graphs:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>H</mi><mtext> bipartite and </mtext><mi>r</mi><mtext>-degenerate</mtext><mtext>  </mtext><mo>⟹</mo><mtext>  </mtext><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>H</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><msup><mi>n</mi><mrow><mn>2</mn><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mi>r</mi></mrow></msup><mo fence="true">)</mo></mrow><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The conjecture is true in several restricted settings: when one bipartition class has maximum degree at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math>, for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math>-degenerate blow-ups of trees, and for grids at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>2</mn></mrow></semantics></math>. Alon, Krivelevich and Sudakov proved the weaker general estimate <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>H</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>2</mn><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mo stretchy="false">(</mo><mn>4</mn><mi>r</mi><mo stretchy="false">)</mo></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> A companion conjecture of Erdős asserted that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> is 2-degenerate <em>if and only if</em> <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>H</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>3</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math>; Janzer disproved the reverse implication in 2021 by building 3-regular bipartite graphs with extremal number <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>+</mo><mi>η</mi></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math>, but the forward implication, the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>2</mn></mrow></semantics></math> case above, stayed open.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-6" id="user-content-fnref-6">6</a></sup></p>
<p>Chapter 10 kills it.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mrow><mtext mathvariant="bold">Theorem.</mtext><mtext> </mtext></mrow><mi mathvariant="normal">∃</mi><mtext> </mtext><mi>H</mi><mtext> connected, bipartite, 2-degenerate, and </mtext><mi>c</mi><mo separator="true">,</mo><mi>ε</mi><mo>&gt;</mo><mn>0</mn><mo>:</mo><mspace width="1em"></mspace><mi mathvariant="normal">ex</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>H</mi><mo stretchy="false">)</mo><mo>≥</mo><mi>c</mi><mtext> </mtext><msup><mi>n</mi><mrow><mn>3</mn><mi mathvariant="normal">/</mi><mn>2</mn><mo>+</mo><mi>ε</mi></mrow></msup><mtext>  for all large </mtext><mi>n</mi><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The counterexample <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> is built in layers. Start with a layer <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>V</mi><mn>0</mn></msub></mrow></semantics></math> of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mn>0</mn></msub></mrow></semantics></math> vertices; for each subsequent layer, add one new vertex for every pair of vertices in the previous layer, joined to exactly those two parents. Every new vertex has degree 2 at birth, so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> is 2-degenerate, connected and bipartite by construction, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow></semantics></math> layers as a tunable depth.</p>
<p>The lower bound comes from a random host. Take the two sides to be copies of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">{</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><msup><mo stretchy="false">}</mo><mi>m</mi></msup></mrow></semantics></math>, join opposite vertices whose Hamming distance is at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>=</mo><mo stretchy="false">⌊</mo><mi>τ</mi><mi>m</mi><mo stretchy="false">⌋</mo></mrow></semantics></math>, then independently retain each vertex with probability <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>=</mo><msup><mn>2</mn><mrow><mo>−</mo><mi>β</mi><mi>m</mi></mrow></msup></mrow></semantics></math>. A second-moment computation shows the sampled graph still has <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>3</mn><mi mathvariant="normal">/</mi><mn>2</mn><mo>+</mo><mi>ε</mi></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> edges for suitable <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>τ</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow></semantics></math>, and padding extends it to every large order.</p>
<p>Showing that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> does not embed is where the layered design pays off. Each candidate embedding is scored by an entropy potential: for a parent array <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi></mrow></semantics></math> and child array <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow></semantics></math>, the quantity</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>E</mi><mo stretchy="false">(</mo><mi>u</mi><mo separator="true">,</mo><mi>z</mi><mo stretchy="false">)</mo><mo>:</mo><mo>=</mo><mfrac><mn>1</mn><mi>m</mi></mfrac><munderover><mo>∑</mo><mrow><mi>j</mi><mo>=</mo><mn>1</mn></mrow><mi>m</mi></munderover><mi>H</mi><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>Z</mi><mi>j</mi></msub><mo>∣</mo><msub><mi>X</mi><mi>j</mi></msub><mo separator="true">,</mo><msub><mi>Y</mi><mi>j</mi></msub><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo></mrow></semantics></math>
<p>measures how much conditional entropy a child bit carries given its two parent bits at a random coordinate. Low-entropy arrays are too numerous to survive sampling: a counting argument shows that arrays with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mo stretchy="false">(</mo><mi>u</mi><mo separator="true">,</mo><mi>z</mi><mo stretchy="false">)</mo><mo>≤</mo><mi>β</mi><mo>−</mo><mi>δ</mi></mrow></semantics></math> are excluded with high probability, at every layer and on both sides. So any embedding must raise <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi></mrow></semantics></math> by a fixed amount per layer, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi></mrow></semantics></math> is bounded above by 1. After enough layers that is impossible, so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> has no copy in the host. Choosing the depth <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow></semantics></math> large enough to exhaust the potential is exactly the role of the parameter thresholds <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo><mo>&lt;</mo><mi>β</mi><mo>&lt;</mo><mi>C</mi><mo stretchy="false">(</mo><mi>τ</mi><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<h2 id="the-erds-ledger">The Erdős ledger</h2>
<p>Both of these chapters land on problems from Erdős’s list, as does OpenAI’s separately reported May 2026 result on the unit-distance problem in discrete geometry.<sup><a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fn-7" id="user-content-fnref-7">7</a></sup> That is not incidental. Erdős problems are unusually good targets for a search process: each is a self-contained combinatorial statement with a crisp yes-or-no answer, a documented history of partial results to calibrate against, and, in the counterexample direction, a certificate that is a finite object one can check.</p>
<p>What the three results have in common on the mathematical side is that the winning move was construction rather than argument. A superexponential Ramsey lower bound is a colouring. A refutation of compactness is a family of graphs. A refutation of the degeneracy conjecture is one bipartite graph plus a random host. In each case the difficulty was in finding an object with several tightly coupled properties, not in reasoning about objects already in hand.</p>
<p>The upper bounds, meanwhile, have barely moved. <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></semantics></math> is still bracketed between <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>k</mi><mrow><mo stretchy="false">(</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>−</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>k</mi></mrow></msup></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>k</mi><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>k</mi></mrow></msup></mrow></semantics></math>, and the value of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>lim</mi><mo>⁡</mo></mrow><mi>k</mi></msub><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><msup><mo stretchy="false">)</mo><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>k</mi></mrow></msup></mrow></semantics></math>, Erdős’s $250 question, is untouched. Deciding whether it is finite was the $100 question, and that one is now answered.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>OpenAI, “Ten Advances in Mathematics and Theoretical Computer Science,” 2026. <a href="https://cdn.openai.com/pdf/ten-proofs-oai.pdf" rel="noopener noreferrer">https://cdn.openai.com/pdf/ten-proofs-oai.pdf</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>David Conlon, Jacob Fox, and Benny Sudakov, “Recent developments in graph Ramsey theory,” Surveys in Combinatorics, 2015. <a href="https://arxiv.org/abs/1501.02474" rel="noopener noreferrer">https://arxiv.org/abs/1501.02474</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Noga Alon and Alon Orlitsky, “Repeated communication and Ramsey graphs,” IEEE Transactions on Information Theory, 1995. <a href="https://ieeexplore.ieee.org/document/412701" rel="noopener noreferrer">https://ieeexplore.ieee.org/document/412701</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Paul Erdős and Miklós Simonovits, “Compactness results in extremal graph theory,” Combinatorica, 1982. <a href="https://link.springer.com/article/10.1007/BF02579234" rel="noopener noreferrer">https://link.springer.com/article/10.1007/BF02579234</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Noga Alon, Michael Krivelevich, and Benny Sudakov, “Turán numbers of bipartite graphs and related Ramsey-type questions,” Combinatorics, Probability and Computing, 2003. <a href="https://www.cs.tau.ac.il/~nogaa/PDFS/turanr3.pdf" rel="noopener noreferrer">https://www.cs.tau.ac.il/~nogaa/PDFS/turanr3.pdf</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Oliver Janzer, “Disproof of a conjecture of Erdős and Simonovits on the Turán number of graphs with minimum degree 3,” International Mathematics Research Notices, 2023. <a href="https://arxiv.org/abs/2109.06110" rel="noopener noreferrer">https://arxiv.org/abs/2109.06110</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>OpenAI, “An OpenAI model has disproved a central conjecture in discrete geometry,” 2026. <a href="https://openai.com/index/model-disproves-discrete-geometry-conjecture/" rel="noopener noreferrer">https://openai.com/index/model-disproves-discrete-geometry-conjecture/</a> <a href="https://blog.ecitis.org/openai-ramsey-extremal-graphs/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Theory &amp; Mathematics</category>
            <category>Combinatorics</category>
            <category>Mathematics</category>
            <category>Formal verification</category>
            <enclosure length="70996" type="image/png" url="https://blog.ecitis.org/open-graph/openai-ramsey-extremal-graphs.png"/>
        </item>
        <item>
            <title><![CDATA[Circuits, Games, and Lattices: Three Complexity Results]]></title>
            <link>https://blog.ecitis.org/openai-circuits-games-lattices/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/openai-circuits-games-lattices/</guid>
            <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Read three theoretical computer science results together: superquadratic circuit lower bounds for the permanent, exponential parallel repetition for every entangled game, and fixed-polynomial hardness for the closest vector problem.]]></description>
            <content:encoded><![CDATA[<p>Chapters 5, 6, and 7 of OpenAI’s <em>Ten Advances in Mathematics and Theoretical Computer Science</em> form the theoretical computer science cluster of that collection.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> They concern three different models of computation: algebraic circuits, interactive proofs with entangled provers, and worst-case approximation hardness for lattice problems.</p>
<p>The three results share a shape. In each case a qualitative statement was already believed, a weaker quantitative version was already known, and the contribution is to close a specific, quantified part of the gap between them. All three land where the previous techniques had stalled.</p>
<h2 id="arithmetic-circuits-compute-polynomials-not-functions">Arithmetic circuits compute polynomials, not functions</h2>
<p>An arithmetic circuit over <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">C</mi></mrow></semantics></math> is a directed acyclic graph. Input gates carry variables or constants, internal gates are binary <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>+</mo></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo></mrow></semantics></math>, or <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>×</mo></mrow></semantics></math>, and one gate is the output. Size is the number of internal gates. A gate may feed its result to any number of later gates, so a subexpression computed once can be reused freely.</p>
<p>An arithmetic formula is the same object restricted to a tree. Fan-out is one. Two uses of the same intermediate polynomial require two separate subformulas, each paid for in full. The natural size measure is the number of leaves, and the sharper measure used here is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mrow><mi mathvariant="normal">v</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">r</mi></mrow></msub><mo stretchy="false">(</mo><mi mathvariant="normal">Φ</mi><mo stretchy="false">)</mo></mrow></semantics></math>, the number of leaves labeled by variables, counting repeated occurrences separately.</p>
<figure><figcaption><strong>Reuse is the only difference between a circuit and a formula</strong> — A circuit is a DAG and may share a subexpression. A formula is a tree and must recompute it.</figcaption><p><a href="https://blog.ecitis.org/openai-circuits-games-lattices/">View this figure on the site &#8594;</a></p></figure>
<p>The polynomial under study is the permanent of an <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>×</mo><mi>n</mi></mrow></semantics></math> matrix of algebraically independent variables:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mrow><mi mathvariant="normal">per</mi><mo>⁡</mo></mrow><mi>n</mi></msub><mo stretchy="false">(</mo><mi>X</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∑</mo><mrow><mi>σ</mi><mo>∈</mo><msub><mi>S</mi><mi>n</mi></msub></mrow></munder><munderover><mo>∏</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><msub><mi>x</mi><mrow><mi>i</mi><mo separator="true">,</mo><mi>σ</mi><mo stretchy="false">(</mo><mi>i</mi><mo stretchy="false">)</mo></mrow></msub></mrow></semantics></math>
<p>Its monomials enumerate perfect matchings in the complete bipartite graph on rows and columns. The determinant differs only by the signs <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">sgn</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>σ</mi><mo stretchy="false">)</mo></mrow></semantics></math>, and that difference is decisive: the determinant has polynomial-size arithmetic circuits, while the permanent is complete for Valiant’s class VNP.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Whether the permanent has polynomial-size circuits is the algebraic counterpart of P versus NP, and it is the central open problem of algebraic complexity theory.</p>
<p>Progress on it has been slow in a specific way. The permanent depends on all <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>=</mo><msup><mi>n</mi><mn>2</mn></msup></mrow></semantics></math> variables, and combining <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mn>2</mn></msup></mrow></semantics></math> distinct inputs with binary gates needs at least <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mn>2</mn></msup><mo>−</mo><mn>1</mn></mrow></semantics></math> of them. That elementary counting argument was, for general division-free circuits, essentially the best available. The classical formula lower bound was <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>3</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math>, due to Kalorkoti.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>Chapter 5 improves both.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>C</mi><mo stretchy="false">(</mo><msub><mrow><mi mathvariant="normal">per</mi><mo>⁡</mo></mrow><mi>n</mi></msub><mo stretchy="false">)</mo><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><mfrac><msup><mi>n</mi><mn>2</mn></msup><mn>144</mn></mfrac><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi><mo>−</mo><mn>3</mn><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>n</mi><mo>≥</mo><msup><mn>2</mn><mn>16</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math>
<p>so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi><mo stretchy="false">(</mo><msub><mrow><mi mathvariant="normal">per</mi><mo>⁡</mo></mrow><mi>n</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> and the ratio <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi><mo stretchy="false">(</mo><msub><mrow><mi mathvariant="normal">per</mi><mo>⁡</mo></mrow><mi>n</mi></msub><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><msup><mi>n</mi><mn>2</mn></msup></mrow></semantics></math> diverges. This is the first superquadratic bound for unrestricted division-free circuits computing the permanent. For formulas:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>L</mi><mrow><mi mathvariant="normal">v</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">r</mi></mrow></msub><mo stretchy="false">(</mo><mi mathvariant="normal">Φ</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><mfrac><msup><mi>n</mi><mn>4</mn></msup><mrow><mn>128</mn><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi></mrow></mfrac><mo separator="true">,</mo><mspace width="2em"></mspace><mi>G</mi><mo stretchy="false">(</mo><mi mathvariant="normal">Φ</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><mfrac><msup><mi>n</mi><mn>4</mn></msup><mrow><mn>256</mn><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi></mrow></mfrac><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>n</mi><mo>≥</mo><mn>32</mn><mo stretchy="false">)</mo></mrow></semantics></math>
<p>with the same asymptotic <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>4</mn></msup><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> holding for formulas that use division, provided every denominator is a nonzero rational function; only the constants change.</p>
<p>In terms of the input count <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi><mo>=</mo><msup><mi>n</mi><mn>2</mn></msup></mrow></semantics></math>, the circuit bound reads <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><mi>N</mi><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>N</mi><mo stretchy="false">)</mo></mrow></semantics></math> and the formula bounds read <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>N</mi><mn>2</mn></msup><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>N</mi><mo stretchy="false">)</mo></mrow></semantics></math>. Both are still polynomially far from the superpolynomial separation that permanent-versus-determinant demands.</p>
<h2 id="why-the-two-proofs-are-different-arguments">Why the two proofs are different arguments</h2>
<p>The circuit bound is geometric. Reverse-mode differentiation computes the full gradient of a size-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> circuit using at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mi>L</mi></mrow></semantics></math> multiplications, which is exactly the sharing that a formula cannot do. If the gradient map is a homogeneous degree-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>d</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math> map <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="double-struck">C</mi><mi>k</mi></msup><mo>→</mo><msup><mi mathvariant="double-struck">C</mi><mi>k</mi></msup></mrow></semantics></math> whose only zero is the origin, its generic fiber has <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>d</mi><mo>−</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mi>k</mi></msup></mrow></semantics></math> points, while Bézout’s inequality bounds the isolated solutions of the gate equations by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mn>3</mn><mi>L</mi></mrow></msup></mrow></semantics></math>. Comparing the two gives</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mtext>  </mtext><mo>≥</mo><mtext>  </mtext><mfrac><mrow><mi>k</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mi>d</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><mn>3</mn></mfrac></mrow></semantics></math>
<p>To use this one needs a specialization of the permanent whose critical locus has codimension <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Θ</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math> while its degree <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow></semantics></math> still grows with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>. Chapter 5 builds one from sums of subpermanents, controlling the critical locus through shared power sums and elementary symmetric functions of the columns.</p>
<p>The formula bound is algebraic instead. It marks a matching of matrix entries, expands the permanent in those variables, and counts algebraically independent coefficients. For the permanent, a matching of only <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> entries already contributes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math> independent coefficients, and independent coefficients must be charged to distinct occurrences of the marked variables. Summing over entry-disjoint matchings gives the quartic bound.</p>
<p>Both arguments are explicitly permanent-specific, and the paper is direct about the obstruction. For the determinant, a Schur complement identity compresses the coefficients of a marked <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>-entry block into a field generated by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>k</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math> elements, so the transcendence degree obeys <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">td</mi><mo>⁡</mo></mrow><mi>Y</mi></msub><mo stretchy="false">(</mo><mi>det</mi><mo>⁡</mo><mi>X</mi><mo stretchy="false">)</mo><mo>≤</mo><mi>n</mi><mi mathvariant="normal">∣</mi><mi>Y</mi><mi mathvariant="normal">∣</mi></mrow></semantics></math> and the method tops out at a cubic bound. On the circuit side, every homogeneous affine specialization of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>det</mi><mo>⁡</mo></mrow><mi>n</mi></msub></mrow></semantics></math> has <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">codim</mi><mo>⁡</mo><mi mathvariant="normal">Crit</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>P</mi><mo stretchy="false">)</mo><mo>≤</mo><mi>n</mi><mo>−</mo><mn>1</mn></mrow></semantics></math>, which caps the certificate at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math>, below the trivial <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mn>2</mn></msup><mo>−</mo><mn>1</mn></mrow></semantics></math> counting bound. The technique does not leak to the determinant.</p>
<h2 id="a-two-player-game-is-a-soundness-question">A two-player game is a soundness question</h2>
<p>A finite two-player one-round game is a tuple <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mo stretchy="false">(</mo><mi>X</mi><mo separator="true">,</mo><mi>Y</mi><mo separator="true">,</mo><mi>A</mi><mo separator="true">,</mo><mi>B</mi><mo separator="true">,</mo><mi>μ</mi><mo separator="true">,</mo><mi>V</mi><mo stretchy="false">)</mo></mrow></semantics></math>. A referee samples <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>∼</mo><mi>μ</mi></mrow></semantics></math>, sends <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow></semantics></math> to Alice and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow></semantics></math> to Bob, receives answers <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>∈</mo><mi>A</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo>∈</mo><mi>B</mi></mrow></semantics></math>, and accepts if <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo separator="true">,</mo><mi>a</mi><mo separator="true">,</mo><mi>b</mi><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn></mrow></semantics></math>. The players agree on a strategy in advance but cannot communicate after seeing their questions. The maximum acceptance probability is the classical value <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<p>An entangled game keeps the same questions, answers, and predicate, but lets the players share a bipartite quantum state before the questions arrive. Each measures their half according to their own question. The supremum of winning probabilities over all finite-dimensional shared states and local measurements is the entangled value <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<figure><figcaption><strong>Repeating an entangled game drives its value down exponentially</strong> — Two players, no communication, one shared quantum state. The open question was the rate of decay, not its direction.</figcaption><p><a href="https://blog.ecitis.org/openai-circuits-games-lattices/">View this figure on the site &#8594;</a></p></figure>
<p>Parallel repetition is the standard way to amplify soundness. In <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>G</mi><mrow><mo>⊗</mo><mi>n</mi></mrow></msup></mrow></semantics></math> the referee samples <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> independent question pairs and accepts only if every coordinate is won. The subtlety is that although the referee’s coordinates are independent, the players’ are not: Alice’s answer in coordinate <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>i</mi></mrow></semantics></math> may depend on her entire question tuple. Playing coordinates independently only gives the lower bound <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><msup><mi>G</mi><mrow><mo>⊗</mo><mi>n</mi></mrow></msup><mo stretchy="false">)</mo><mo>≥</mo><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>G</mi><msup><mo stretchy="false">)</mo><mi>n</mi></msup></mrow></semantics></math>; the work is in the matching upper bound.</p>
<p>Raz proved the classical case: every finite two-player game with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>&lt;</mo><mn>1</mn></mrow></semantics></math> satisfies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ω</mi><mo stretchy="false">(</mo><msup><mi>G</mi><mrow><mo>⊗</mo><mi>n</mi></mrow></msup><mo stretchy="false">)</mo><mo>≤</mo><mi>exp</mi><mo>⁡</mo><mo stretchy="false">(</mo><mo>−</mo><msub><mi>c</mi><mi>G</mi></msub><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> Holenstein later gave an information-theoretic proof based on correlated sampling and improved the dependence on the soundness gap.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-5" id="user-content-fnref-5">5</a></sup></p>
<p>The entangled analogue was noted as open by 2004 and stayed open. Exponential decay was proved for structured families, namely XOR games, unique games, projection games, and free games, and Bavarian, Vidick, and Yuen obtained exponential decay by first <em>transforming</em> a game into an anchored one.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-6" id="user-content-fnref-6">6</a></sup> That is enough for hardness amplification but is not a repetition theorem about the original game. For arbitrary games, the strongest general statement was Yuen’s, and it decayed only polynomially:<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-7" id="user-content-fnref-7">7</a></sup></p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><msup><mi>G</mi><mrow><mo>⊗</mo><mi>n</mi></mrow></msup><mo stretchy="false">)</mo><mtext>  </mtext><mo>≤</mo><mtext>  </mtext><mfrac><mrow><mi>c</mi><mtext> </mtext><msub><mi>s</mi><mi>G</mi></msub><mi>log</mi><mo>⁡</mo><mi>n</mi></mrow><mrow><msup><mi>ε</mi><mn>17</mn></msup><mtext> </mtext><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>4</mn></mrow></msup></mrow></mfrac></mrow></semantics></math>
<p>Chapter 6 replaces that with exponential decay, for every finite game, unmodified.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><msup><mi>G</mi><mrow><mo>⊗</mo><mi>n</mi></mrow></msup><mo stretchy="false">)</mo><mtext>  </mtext><mo>≤</mo><mtext>  </mtext><mi>exp</mi><mo>⁡</mo><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><mo>−</mo><msub><mi>c</mi><mrow><mi mathvariant="normal">q</mi><mi mathvariant="normal">s</mi></mrow></msub><mtext> </mtext><mfrac><msup><mi>ε</mi><mn>13</mn></msup><mrow><mi>ε</mi><mo>+</mo><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi mathvariant="normal">∣</mi><mi>A</mi><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><mi>B</mi><mi mathvariant="normal">∣</mi><mo stretchy="false">)</mo></mrow></mfrac><mtext> </mtext><mi>n</mi><mo fence="true">)</mo></mrow><mo separator="true">,</mo><mspace width="2em"></mspace><mi>ε</mi><mo>=</mo><mn>1</mn><mo>−</mo><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>
<p>for a universal constant <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mrow><mi mathvariant="normal">q</mi><mi mathvariant="normal">s</mi></mrow></msub><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>. The exponent <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>13</mn></mrow></semantics></math> is not claimed to be optimal; it inherits the loss in a standard quantum correlated-sampling lemma. The claim being made is about the <em>rate</em>, not the constant.</p>
<p>The proof keeps Yuen’s conditioning-and-rounding template. Assume a repeated strategy beats the bound; a greedy conditioning lemma selects a small core <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi><mo>⊊</mo><mo stretchy="false">[</mo><mi>n</mi><mo stretchy="false">]</mo></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>D</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><mn>1</mn><mi mathvariant="normal">/</mi><mi>ϑ</mi><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi>δ</mi></mrow></semantics></math> such that, conditioned on winning every coordinate in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>, an average remaining coordinate is won with probability at least <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mo>−</mo><mi>δ</mi></mrow></semantics></math>. The conditioned coordinate is then rounded back into a legal single-copy strategy that beats <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow></semantics></math>, a contradiction.</p>
<p>The obstacle in the quantum setting was always that step. Conditioning on winning all <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> coordinates is conditioning on an event of exponentially small probability, and the available quantum sampleability estimates degraded inversely with that probability, which is why the classical argument yielded only polynomial decay when transplanted. The new ingredient is a postselection-stable sampleability lemma with no such inverse dependence.</p>
<h2 id="lattice-hardness-at-a-fixed-polynomial-factor">Lattice hardness at a fixed polynomial factor</h2>
<p>The closest vector problem takes a full-rank integer basis <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mo>∈</mo><msup><mi mathvariant="double-struck">Z</mi><mrow><mi>n</mi><mo>×</mo><mi>n</mi></mrow></msup></mrow></semantics></math> and a target <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow></semantics></math>, and asks for the lattice point nearest to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mrow><mi mathvariant="normal">dist</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><mi>t</mi><mo separator="true">,</mo><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo>=</mo><munder><mrow><mi>min</mi><mo>⁡</mo></mrow><mrow><mi>z</mi><mo>∈</mo><msup><mi mathvariant="double-struck">Z</mi><mi>n</mi></msup></mrow></munder><mo stretchy="false">∥</mo><mi>t</mi><mo>−</mo><mi>B</mi><mi>z</mi><msub><mo stretchy="false">∥</mo><mn>2</mn></msub></mrow></semantics></math>
<p>The approximate promise version <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mrow><mi mathvariant="normal">G</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">C</mi><mi mathvariant="normal">V</mi><mi mathvariant="normal">P</mi></mrow><mi>γ</mi><mrow><mo stretchy="false">(</mo><mn>2</mn><mo stretchy="false">)</mo></mrow></msubsup></mrow></semantics></math> takes an additional radius <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math> and asks to distinguish <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">dist</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mi>t</mi><mo separator="true">,</mo><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>≤</mo><mi>r</mi></mrow></semantics></math> from <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">dist</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mi>t</mi><mo separator="true">,</mo><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>&gt;</mo><mi>γ</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mtext> </mtext><mi>r</mi></mrow></semantics></math>, with either answer permitted in between. Larger <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow></semantics></math> makes the problem easier, so hardness results get harder to prove as <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow></semantics></math> grows.</p>
<p>Chapter 7 gives a deterministic polynomial-time many-one reduction from 3SAT:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>φ</mi><mtext> satisfiable</mtext><mtext>  </mtext><mo>⟹</mo><mtext>  </mtext><msub><mrow><mi mathvariant="normal">dist</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mi>t</mi><mo separator="true">,</mo><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>≤</mo><mi>r</mi><mo separator="true">,</mo><mspace width="2em"></mspace><mi>φ</mi><mtext> unsatisfiable</mtext><mtext>  </mtext><mo>⟹</mo><mtext>  </mtext><msub><mrow><mi mathvariant="normal">dist</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mi>t</mi><mo separator="true">,</mo><mi mathvariant="script">L</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>&gt;</mo><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></msup><mtext> </mtext><mi>r</mi></mrow></semantics></math>
<p>so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mrow><mi mathvariant="normal">G</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">C</mi><mi mathvariant="normal">V</mi><mi mathvariant="normal">P</mi></mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></msup><mrow><mo stretchy="false">(</mo><mn>2</mn><mo stretchy="false">)</mo></mrow></msubsup></mrow></semantics></math> is NP-hard. The same construction yields factor <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>200</mn></mrow></msup></mrow></semantics></math> for binary nearest codeword and syndrome decoding, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mo stretchy="false">(</mo><mn>200</mn><mi>p</mi><mo stretchy="false">)</mo></mrow></msup></mrow></semantics></math> for closest vector in every fixed rational <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">ℓ</mi><mi>p</mi></msub></mrow></semantics></math> norm, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>≥</mo><mn>1</mn></mrow></semantics></math>.</p>
<p>The exponent looks small, and it is. What makes it interesting is that it is a <em>constant</em>. The previous best, from Dinur, Kindler, Raz, and Safra, was NP-hardness within <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mi>a</mi><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi></mrow></msup></mrow></semantics></math> for some absolute <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-8" id="user-content-fnref-8">8</a></sup> That factor grows faster than any fixed power of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>log</mi><mo>⁡</mo><mi>n</mi></mrow></semantics></math>, but its exponent tends to zero, so it never certifies hardness within <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mi>c</mi></msup></mrow></semantics></math> for any fixed <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>. Moving from a vanishing exponent to a fixed one is the actual content.</p>
<p>The reduction factors as</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>3SAT  -&gt;  binary nearest codeword  -&gt;  Euclidean CVP</span></span></code></pre>
<p>The second arrow is the standard dimension-preserving reduction. The first is the new work: assignments are encoded as Reed–Solomon evaluation tables over a characteristic-two field, with power-sum and shifted-moment constraints forcing low degree, and the resulting binary affine system <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi><mi>x</mi><mo>=</mo><mi>b</mi></mrow></semantics></math> is lifted to an integer lattice by coordinatewise reduction modulo two. Soundness recovers a satisfying assignment from any low-weight solution by reconstructing separable root sets over a rational function field.</p>
<p>Notably, this uses no randomization, no PCP theorem, and no Projection Games Conjecture; earlier fixed-polynomial lattice inapproximability results were conditional on the latter. The price is size: the field satisfies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi><mo>=</mo><mi mathvariant="normal">Θ</mi><mo stretchy="false">(</mo><msup><mi>N</mi><mn>200</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math> and the lattice rank <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>≤</mo><mn>40</mn><msup><mi>N</mi><mn>401</mn></msup></mrow></semantics></math>.</p>
<h2 id="how-far-this-is-from-cryptography">How far this is from cryptography</h2>
<figure><figcaption><strong>Where n1/400 sits on the CVP approximation axis</strong> — Hardness gets easier to prove as the approximation factor grows. The interesting factors for cryptography are far to the right.</figcaption><p><a href="https://blog.ecitis.org/openai-circuits-games-lattices/">View this figure on the site &#8594;</a></p></figure>
<p>It is worth being precise about what this does and does not say for post-quantum cryptography.</p>
<p>Lattice problems became cryptographically load-bearing through Ajtai’s worst-case to average-case connection, the Goldreich–Goldwasser–Halevi constructions, and Regev’s learning with errors, and that line now underpins NIST’s standardized key encapsulation and signature schemes. But those schemes rest on structured or average-case assumptions at polynomial approximation factors. They do not rest on the worst-case NP-hardness of CVP, and a hardness result at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></msup></mrow></semantics></math> does not strengthen them.</p>
<p>There is also a ceiling. Aharonov and Regev showed that for some absolute <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>,</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msubsup><mrow><mi mathvariant="normal">G</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">C</mi><mi mathvariant="normal">V</mi><mi mathvariant="normal">P</mi></mrow><mrow><mi>C</mi><msqrt><mi>n</mi></msqrt></mrow><mrow><mo stretchy="false">(</mo><mn>2</mn><mo stretchy="false">)</mo></mrow></msubsup><mo>∈</mo><mrow><mi mathvariant="normal">N</mi><mi mathvariant="normal">P</mi></mrow><mo>∩</mo><mrow><mi mathvariant="normal">c</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">N</mi><mi mathvariant="normal">P</mi></mrow></mrow></semantics></math>
<p>so NP-hardness at the square-root scale would imply <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">N</mi><mi mathvariant="normal">P</mi></mrow><mo>=</mo><mrow><mi mathvariant="normal">c</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">N</mi><mi mathvariant="normal">P</mi></mrow></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fn-9" id="user-content-fnref-9">9</a></sup> The exponent therefore cannot pass <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></semantics></math> by these means. Determining the largest <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>∈</mo><mo stretchy="false">[</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn><mo separator="true">,</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn><mo stretchy="false">)</mo></mrow></semantics></math> for which <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mrow><mi mathvariant="normal">G</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">C</mi><mi mathvariant="normal">V</mi><mi mathvariant="normal">P</mi></mrow><msup><mi>n</mi><mi>c</mi></msup><mrow><mo stretchy="false">(</mo><mn>2</mn><mo stretchy="false">)</mo></mrow></msubsup></mrow></semantics></math> is NP-hard is left open.</p>
<p>So the honest summary is a narrow one: a barrier that used to sit at a vanishing exponent now sits at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></semantics></math>, unconditionally and deterministically, and the region between there and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></semantics></math> is unexplored.</p>
<h2 id="what-the-three-have-in-common">What the three have in common</h2>
<p>Each result takes a statement everyone expected to be true and supplies a quantitative certificate for part of it.</p>





























<div class="table-wrap"><table><thead><tr><th>Chapter</th><th>Was known</th><th>Now known</th><th>Still open</th></tr></thead><tbody><tr><td>5</td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math> circuits, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>3</mn></msup><mo stretchy="false">)</mo></mrow></semantics></math> formulas</td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>4</mn></msup><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math></td><td>Superpolynomial bounds; permanent vs determinant</td></tr><tr><td>6</td><td>Polynomial decay for all entangled games</td><td>Exponential decay for all finite games</td><td>Optimal dependence on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ε</mi></mrow></semantics></math>; more than two players</td></tr><tr><td>7</td><td>NP-hardness within <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mi>a</mi><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi></mrow></msup></mrow></semantics></math></td><td>NP-hardness within <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></msup></mrow></semantics></math></td><td>The exponent between <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></semantics></math></td></tr></tbody></table></div>
<p>Each result moves a bound that had held for years, and the exact constants in each theorem, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>144</mn></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>13</mn></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>400</mn></mrow></semantics></math>, are worth reading as measurements: they say precisely how far the new technique reaches, which is what makes the next step easy to aim at.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>OpenAI, “Ten Advances in Mathematics and Theoretical Computer Science,” 2026. <a href="https://cdn.openai.com/pdf/ten-proofs-oai.pdf" rel="noopener noreferrer">https://cdn.openai.com/pdf/ten-proofs-oai.pdf</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Leslie G. Valiant, “Completeness Classes in Algebra,” STOC, 1979. <a href="https://doi.org/10.1145/800135.804419" rel="noopener noreferrer">https://doi.org/10.1145/800135.804419</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>K. Kalorkoti, “A Lower Bound for the Formula Size of Rational Functions,” SIAM Journal on Computing, 1985. <a href="https://doi.org/10.1137/0214051" rel="noopener noreferrer">https://doi.org/10.1137/0214051</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Ran Raz, “A Parallel Repetition Theorem,” SIAM Journal on Computing, 1998. <a href="https://doi.org/10.1137/S0097539795280895" rel="noopener noreferrer">https://doi.org/10.1137/S0097539795280895</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Thomas Holenstein, “Parallel Repetition: Simplification and the No-Signaling Case,” Theory of Computing, 2009. <a href="https://doi.org/10.4086/toc.2009.v005a008" rel="noopener noreferrer">https://doi.org/10.4086/toc.2009.v005a008</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Mohammad Bavarian, Thomas Vidick, and Henry Yuen, “Anchored Parallel Repetition for Nonlocal Games,” arXiv, 2015. <a href="https://arxiv.org/abs/1509.07466" rel="noopener noreferrer">https://arxiv.org/abs/1509.07466</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Henry Yuen, “A Parallel Repetition Theorem for All Entangled Games,” ICALP, 2016. <a href="https://arxiv.org/abs/1604.04340" rel="noopener noreferrer">https://arxiv.org/abs/1604.04340</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>Irit Dinur, Guy Kindler, Ran Raz, and Shmuel Safra, “Approximating CVP to Within Almost-Polynomial Factors is NP-Hard,” Combinatorica, 2003. <a href="https://doi.org/10.1007/s00493-003-0019-y" rel="noopener noreferrer">https://doi.org/10.1007/s00493-003-0019-y</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p>Dorit Aharonov and Oded Regev, “Lattice Problems in NP ∩ coNP,” Journal of the ACM, 2005. <a href="https://doi.org/10.1145/1089023.1089025" rel="noopener noreferrer">https://doi.org/10.1145/1089023.1089025</a> <a href="https://blog.ecitis.org/openai-circuits-games-lattices/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Theory &amp; Mathematics</category>
            <category>Complexity theory</category>
            <category>Cryptography</category>
            <category>Mathematics</category>
            <enclosure length="63722" type="image/png" url="https://blog.ecitis.org/open-graph/openai-circuits-games-lattices.png"/>
        </item>
        <item>
            <title><![CDATA[A Non-Sofic Group and the Fall of Connes’s Rigidity Conjecture]]></title>
            <link>https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/</guid>
            <pubDate>Tue, 04 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Two chapters of OpenAI’s ten-result collection settle long-standing questions in group theory and operator algebras: an explicit non-sofic group, and infinitely many property-(T) groups sharing one group von Neumann algebra.]]></description>
            <content:encoded><![CDATA[<p>OpenAI’s collection <em>Ten Advances in Mathematics and Theoretical Computer Science</em> reports results obtained by an internal model across ten separate areas.<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> Two of its chapters land in the same corner of mathematics, infinite discrete groups and the von Neumann algebras built from them, and both resolve questions that had been open for decades in the negative direction.</p>
<p>Chapter 3 constructs an explicit group that admits no finite permutation approximations at all. Chapter 4 constructs infinitely many pairwise nonisomorphic rigid groups that are indistinguishable to the operator algebra built from them.</p>
<p>The two are worth reading together because they attack the same instinct from opposite sides: the belief that infinite groups can be pinned down by finite or analytic shadows.</p>
<h2 id="soficity-is-approximation-by-permutations">Soficity is approximation by permutations</h2>
<p>Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> be a countable group. A <em>sofic approximation</em> of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> is a sequence of maps</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>p</mi><mi>n</mi></msub><mo>:</mo><mi>H</mi><mo>⟶</mo><mi mathvariant="normal">Sym</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>Y</mi><mi>n</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<p>into the symmetric groups of finite sets <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Y</mi><mi>n</mi></msub></mrow></semantics></math>, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><msub><mi>Y</mi><mi>n</mi></msub><mi mathvariant="normal">∣</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></semantics></math>. These maps are not required to be homomorphisms. They are required only to look like homomorphisms in a statistical sense, measured by <em>normalized Hamming distance</em>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>d</mi><mi>H</mi></msub><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>q</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mo stretchy="false">∣</mo><mo stretchy="false">{</mo><mi>z</mi><mo>∈</mo><mi>Y</mi><mo>:</mo><mi>p</mi><mi>z</mi><mo>≠</mo><mi>q</mi><mi>z</mi><mo stretchy="false">}</mo><mo stretchy="false">∣</mo></mrow><mrow><mo stretchy="false">∣</mo><mi>Y</mi><mo stretchy="false">∣</mo></mrow></mfrac><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>q</mi><mo>∈</mo><mi mathvariant="normal">Sym</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>Y</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>This is the fraction of points on which two permutations disagree. The conditions are:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>p</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>d</mi><mi>H</mi></msub><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>p</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mi>g</mi><mi>h</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mtext> </mtext><msub><mi>p</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo><msub><mi>p</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mi>h</mi><mo stretchy="false">)</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo>→</mo><mn>0</mn><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>d</mi><mi>H</mi></msub><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">(</mo><msub><mi>p</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mtext> </mtext><mn>1</mn><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">)</mo><mo>→</mo><mn>1</mn><mtext>  </mtext><mtext>  </mtext><mo stretchy="false">(</mo><mi>g</mi><mo>≠</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The second condition says multiplication holds at almost every point of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Y</mi><mi>n</mi></msub></mrow></semantics></math>. The third says every nonidentity element of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> moves almost every point: the approximation stays faithful, and does not quietly send distinct group elements to nearly the same permutation. A group is <em>sofic</em> if such a sequence exists.</p>
<figure><figcaption><strong>What a sofic approximation has to do</strong> — Two conditions on permutations of a finite set, both required in the limit.</figcaption><p><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/">View this figure on the site &#8594;</a></p></figure>
<p>Gromov introduced the property in work on symbolic dynamics,<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> and Weiss named sofic groups and asked whether a nonsofic one exists. The question hardened into the <em>soficity conjecture</em>: is every countable group sofic?<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>Nobody could produce a counterexample, and the class kept absorbing everything thrown at it. Amenable groups are sofic. Residually finite groups, those where every nonidentity element survives in some finite quotient, are sofic. So is the wider LEF class: a group is <em>locally embeddable into finite groups</em> if for every finite subset <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow></semantics></math> there is a finite group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math> and an injection <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>φ</mi><mo>:</mo><mi>F</mi><mo>→</mo><mi>B</mi></mrow></semantics></math> with</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>φ</mi><mo stretchy="false">(</mo><mi>x</mi><mi>y</mi><mo stretchy="false">)</mo><mo>=</mo><mi>φ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mi>φ</mi><mo stretchy="false">(</mo><mi>y</mi><mo stretchy="false">)</mo><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo separator="true">,</mo><mi>x</mi><mi>y</mi><mo>∈</mo><mi>F</mi><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></semantics></math>
<p>so each finite chunk of the multiplication table embeds exactly, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math> allowed to depend on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow></semantics></math>. Soficity is the approximate version of the same idea, and the class is closed under enough operations that the supply of candidate counterexamples was thin.</p>
<figure><figcaption><strong>Where the counterexample sits</strong> — Soficity absorbs every previously known source of finite approximation.</figcaption><p><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/">View this figure on the site &#8594;</a></p></figure>
<h2 id="why-anyone-cared">Why anyone cared</h2>
<p>Soficity mattered because a long list of conjectures had been proved <em>for sofic groups</em> and remained open in general. That made “every group is sofic” the load-bearing assumption in a whole shelf of theorems.</p>
<p>Gottschalk’s surjunctivity conjecture is the original motivation. A group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> is <em>surjunctive</em> if for every finite alphabet <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow></semantics></math>, every injective <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math>-equivariant continuous map <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>A</mi><mi>H</mi></msup><mo>→</mo><msup><mi>A</mi><mi>H</mi></msup></mrow></semantics></math> is surjective, a cellular-automaton analogue of the fact that an injective self-map of a finite set is onto. Gromov and Weiss proved every sofic group is surjunctive.</p>
<p>Connes’s embedding conjecture for groups asks whether every discrete group is <em>hyperlinear</em>: approximable by finite-dimensional unitary matrices in normalized Hilbert–Schmidt distance. Permutation matrices satisfy</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mo stretchy="false">∥</mo><msub><mi>P</mi><mi>σ</mi></msub><mo>−</mo><msub><mi>P</mi><mi>τ</mi></msub><msubsup><mo stretchy="false">∥</mo><mrow><mn>2</mn><mo separator="true">,</mo><mi>n</mi></mrow><mn>2</mn></msubsup><mo>=</mo><mn>2</mn><mtext> </mtext><msub><mi>d</mi><mi>H</mi></msub><mo stretchy="false">(</mo><mi>σ</mi><mo separator="true">,</mo><mi>τ</mi><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></semantics></math>
<p>so every sofic group is hyperlinear. Kaplansky’s direct-finiteness conjecture, which asks whether <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mi>b</mi><mo>=</mo><mn>1</mn></mrow></semantics></math> in a group algebra forces <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mi>a</mi><mo>=</mo><mn>1</mn></mrow></semantics></math>, together with Lück’s determinant conjecture, the algebraic eigenvalue conjecture, and the Kervaire–Laudenbach conjecture all have “true for sofic groups” theorems attached.</p>
<p>Earlier work had produced conditional routes to a nonsofic group: Bowen and Burton from flexible permutation stability of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">P</mi><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mi>d</mi></msub><mo stretchy="false">(</mo><mi mathvariant="double-struck">Z</mi><mo stretchy="false">)</mo></mrow></semantics></math>, Gohla and Thom from central extensions of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow></semantics></math>-adic lattices under a stability hypothesis. Every route needed an unproved stability assumption. Separately, the Aldous–Lyons conjecture, a broader statement about unimodular random rooted networks of which the soficity conjecture is the restriction to point masses on normal subgroups, was disproved by Bowen, Chapman, Lubotzky and Vidick using machinery descended from <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mrow><mi mathvariant="normal">M</mi><mi mathvariant="normal">I</mi><mi mathvariant="normal">P</mi></mrow><mo>∗</mo></msup><mo>=</mo><mrow><mi mathvariant="normal">R</mi><mi mathvariant="normal">E</mi></mrow></mrow></semantics></math>. That did not settle soficity, because a non-co-sofic invariant random subgroup need not be supported on a normal subgroup.</p>
<h2 id="the-counterexample">The counterexample</h2>
<p>Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub></mrow></semantics></math> be the field with two elements. The <em>binary Leavitt algebra</em> is</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>R</mi><mo>=</mo><msub><mi>L</mi><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub></msub><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mn>2</mn><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub><mo stretchy="false">⟨</mo><msub><mi>s</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>s</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>t</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi>t</mi><mn>1</mn></msub><mo>∣</mo><msub><mi>t</mi><mi>i</mi></msub><msub><mi>s</mi><mi>j</mi></msub><mo>=</mo><msub><mi>δ</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub><mo separator="true">,</mo><mtext>  </mtext><msub><mi>s</mi><mn>0</mn></msub><msub><mi>t</mi><mn>0</mn></msub><mo>+</mo><msub><mi>s</mi><mn>1</mn></msub><msub><mi>t</mi><mn>1</mn></msub><mo>=</mo><mn>1</mn><mo stretchy="false">⟩</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The relations are the algebraic shadow of the Cuntz algebra <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">O</mi><mn>2</mn></msub></mrow></semantics></math>. They say <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi><mo>≅</mo><msup><mi>R</mi><mn>2</mn></msup></mrow></semantics></math> as right <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow></semantics></math>-modules: the ring is isomorphic to two copies of itself. Note <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>t</mi><mn>0</mn></msub><msub><mi>s</mi><mn>0</mn></msub><mo>=</mo><mn>1</mn></mrow></semantics></math> while <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mn>0</mn></msub><msub><mi>t</mi><mn>0</mn></msub></mrow></semantics></math> is a proper idempotent, so the ring is one-sided in places. Write <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math> for the group of invertible elements. Since <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow></semantics></math> is finitely generated over a finite field, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math> is countable.</p>
<blockquote>
<p><strong>Theorem 1.1 (Chapter 3).</strong></p>
</blockquote>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>L</mi><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub></msub><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mn>2</mn><msup><mo stretchy="false">)</mo><mo>×</mo></msup><mtext>  is not sofic.</mtext></mrow></semantics></math>
<p>The proof works inside a finitely generated subgroup, which suffices because soficity passes to subgroups. A <em>complete prefix code</em> is a finite set of pairwise prefix-incomparable binary words whose cylinders partition all infinite binary strings; once its <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> words are ordered, it induces a unital ring isomorphism</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="normal">Θ</mi><mi>E</mi></msub><mo>:</mo><msub><mi>M</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mi>R</mi><mo stretchy="false">)</mo><mover><mo stretchy="true" minsize="3.0em">→</mo><mpadded width="+0.6em" lspace="0.3em"><mrow><mtext> </mtext><mo>∼</mo><mtext> </mtext></mrow></mpadded></mover><mi>R</mi><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Applying this to a specific nine-word code <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math> realizes the elementary matrix group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">E</mi><mi mathvariant="normal">L</mi></mrow><mn>9</mn></msub><mo stretchy="false">(</mo><mi>R</mi><mo stretchy="false">)</mo></mrow></semantics></math> inside <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math> as <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>:</mo><mo>=</mo><msub><mrow><mi mathvariant="normal">E</mi><mi mathvariant="normal">L</mi></mrow><mi>D</mi></msub><mo stretchy="false">(</mo><mi>R</mi><mo stretchy="false">)</mo></mrow></semantics></math>. By the Ershov–Jaikin-Zapirain theorem, elementary groups over a finitely generated ring have Kazhdan’s property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> once <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>≥</mo><mn>3</mn></mrow></semantics></math>, so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> is a property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> group.</p>
<p>Property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> is a spectral-gap condition: every unitary representation with almost invariant unit vectors has a genuine nonzero invariant vector. Its relevance here is geometric rather than analytic. Given permutations modeling a finite generating set, the <em>generator graph</em> joins each point of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Y</mi><mi>n</mi></msub></mrow></semantics></math> to its image under each generator. Kun’s theorem says the generator graph of a sofic approximation to a property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> group becomes a disjoint union of uniformly expanding graphs after changing <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mo stretchy="false">(</mo><mi mathvariant="normal">∣</mi><msub><mi>Y</mi><mi>n</mi></msub><mi mathvariant="normal">∣</mi><mo stretchy="false">)</mo></mrow></semantics></math> edges,<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> where a component <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi></mrow></semantics></math> is a <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow></semantics></math>-expander when</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mo stretchy="false">∣</mo><msub><mi mathvariant="normal">∂</mi><mi>C</mi></msub><mi>U</mi><mo stretchy="false">∣</mo><mtext> </mtext><mo>≥</mo><mtext> </mtext><mi>γ</mi><mi>min</mi><mo>⁡</mo><mo stretchy="false">{</mo><mo stretchy="false">∣</mo><mi>U</mi><mo stretchy="false">∣</mo><mo separator="true">,</mo><mtext> </mtext><mo stretchy="false">∣</mo><mi>C</mi><mo>∖</mo><mi>U</mi><mo stretchy="false">∣</mo><mo stretchy="false">}</mo><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>U</mi><mo>⊆</mo><mi>C</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The Kun–Thom centralizer theorem then says: if <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math> has property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi></mrow></semantics></math> is finitely generated, and a sofic approximation of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi><mo>×</mo><mi>J</mi></mrow></semantics></math> has a <em>single</em> uniformly expanding <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-generator graph on the whole of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Y</mi><mi>n</mi></msub></mrow></semantics></math>, then <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi></mrow></semantics></math> is LEF.</p>
<p>The gap between the two is the whole difficulty. Kun’s decomposition delivers many expanding components; Kun–Thom needs one. The gap is real, not technical: <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo>=</mo><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mn>3</mn></msub><mo stretchy="false">(</mo><mi mathvariant="double-struck">Z</mi><mo stretchy="false">)</mo></mrow></semantics></math> has property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> and is residually finite, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi><mo>=</mo><mrow><mi mathvariant="normal">B</mi><mi mathvariant="normal">S</mi></mrow><mo stretchy="false">(</mo><mn>2</mn><mo separator="true">,</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></semantics></math> is sofic but not residually finite and hence not LEF, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo>×</mo><mi>B</mi></mrow></semantics></math> is nonetheless sofic. Expanding <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi></mrow></semantics></math>-components alone cannot force a commuting factor to be LEF.</p>
<p>The chapter’s bridge is a group-theoretic criterion. Suppose <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi><mo>≤</mo><mi>G</mi></mrow></semantics></math> are infinite finitely generated property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> groups with</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>G</mi><mo>=</mo><mo stretchy="false">⟨</mo><mi mathvariant="normal">Γ</mi><mo separator="true">,</mo><msub><mi>t</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>t</mi><mi>m</mi></msub><mo stretchy="false">⟩</mo><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>t</mi><mi>i</mi></msub><mi mathvariant="normal">Γ</mi><msubsup><mi>t</mi><mi>i</mi><mrow><mo>−</mo><mn>1</mn></mrow></msubsup><mo>≤</mo><mi mathvariant="normal">Γ</mi><mspace width="1em"></mspace><mo stretchy="false">(</mo><mn>1</mn><mo>≤</mo><mi>i</mi><mo>≤</mo><mi>m</mi><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></semantics></math>
<p>and a finitely generated <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi><mo>≤</mo><mi>G</mi></mrow></semantics></math> satisfies</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mo stretchy="false">[</mo><mi mathvariant="normal">Γ</mi><mo separator="true">,</mo><mi>J</mi><mo stretchy="false">]</mo><mo>=</mo><mn>1</mn><mo separator="true">,</mo><mspace width="2em"></mspace><mi mathvariant="normal">Γ</mi><mo>∩</mo><mi>J</mi><mo>=</mo><mo stretchy="false">{</mo><mn>1</mn><mo stretchy="false">}</mo><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>t</mi><mn>1</mn></msub><mi>J</mi><msubsup><mi>t</mi><mn>1</mn><mrow><mo>−</mo><mn>1</mn></mrow></msubsup><mo>≤</mo><mi mathvariant="normal">Γ</mi><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Then soficity of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> implies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi></mrow></semantics></math> is LEF. The extra nesting, in which conjugation by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>t</mi><mn>1</mn></msub></mrow></semantics></math> pushes <em>both</em> commuting factors inside <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow></semantics></math>, is exactly what the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mn>3</mn></msub><mo stretchy="false">(</mo><mi mathvariant="double-struck">Z</mi><mo stretchy="false">)</mo><mo>×</mo><mrow><mi mathvariant="normal">B</mi><mi mathvariant="normal">S</mi></mrow><mo stretchy="false">(</mo><mn>2</mn><mo separator="true">,</mo><mn>3</mn><mo stretchy="false">)</mo></mrow></semantics></math> example lacks.</p>
<p>The argument applies Kun’s theorem separately to the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow></semantics></math>- and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>-generator graphs, giving two different partitions of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>Y</mi><mi>n</mi></msub></mrow></semantics></math>. A median-normalized size function</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mi>M</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo></mrow><mrow><mi>M</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo><mo>+</mo><msub><mi>m</mi><mi>A</mi></msub></mrow></mfrac><mo separator="true">,</mo><mspace width="2em"></mspace><mi>M</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">∣</mo><mi>C</mi><mo stretchy="false">(</mo><mi>z</mi><mo stretchy="false">)</mo><mo stretchy="false">∣</mo></mrow></semantics></math>
<p>on each <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>-component <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow></semantics></math> is almost nondecreasing along the permutations approximating the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>t</mi><mi>i</mi></msub></mrow></semantics></math> and almost preserved by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow></semantics></math>-generators. Because permutations conserve total increase and decrease, expansion pins <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi></mrow></semantics></math> near <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></semantics></math> outside a negligible set, which forces each transported <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow></semantics></math>-component to have roughly the same size as its target, so it fills more than half of it and the matching is injective. That injectivity lets the argument descend to a single expanding component and invoke Kun–Thom.</p>
<p>Finally the Leavitt configuration supplies the pieces. Inside <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><msub><mrow><mi mathvariant="normal">E</mi><mi mathvariant="normal">L</mi></mrow><mi>D</mi></msub><mo stretchy="false">(</mo><mi>R</mi><mo stretchy="false">)</mo></mrow></semantics></math> the chapter builds a property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> subgroup <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Γ</mi></mrow></semantics></math> acting inside the cylinder <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>0</mn><mo stretchy="false">]</mo></mrow></semantics></math>, units <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi><mo separator="true">,</mo><mi>v</mi></mrow></semantics></math>, and a subgroup <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi><mo>≅</mo><mi>V</mi></mrow></semantics></math>, Thompson’s group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math>, acting inside the disjoint cylinder <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>1000</mn><mo stretchy="false">]</mo></mrow></semantics></math>, with conjugation by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi></mrow></semantics></math> moving <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>J</mi></mrow></semantics></math> into <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">[</mo><mn>0001</mn><mo stretchy="false">]</mo><mo>⊆</mo><mo stretchy="false">[</mo><mn>0</mn><mo stretchy="false">]</mo></mrow></semantics></math>. Thompson’s group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math> is finitely presented, infinite and simple, and no such group is LEF. So <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> cannot be sofic, and neither can <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math>.</p>
<h2 id="what-the-counterexample-does-and-does-not-break">What the counterexample does and does not break</h2>
<p>The theorem removes an assumption; it does not by itself falsify the conjectures that relied on it.</p>

































<div class="table-wrap"><table><thead><tr><th>Question</th><th>Status for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math></th></tr></thead><tbody><tr><td>Soficity conjecture</td><td>Refuted outright</td></tr><tr><td>Gottschalk surjunctivity</td><td>Open for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math>; either answer is a headline</td></tr><tr><td>Hyperlinearity / Connes embedding for groups</td><td>Undetermined; soficity implies it, not conversely</td></tr><tr><td>Kaplansky direct finiteness</td><td>Open only in positive characteristic</td></tr><tr><td>Lück determinant, algebraic eigenvalue</td><td>Unsettled for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math></td></tr><tr><td>Kervaire–Laudenbach</td><td>Would follow if <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math> were hyperlinear</td></tr></tbody></table></div>
<p>The surjunctivity entry is the sharpest. Surjunctivity passes to subgroups, so if <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>R</mi><mo>×</mo></msup></mrow></semantics></math> is surjunctive, it is the first surjunctive nonsofic group; if it is not, Gottschalk’s conjecture is false. Either way the next result comes from the same object.</p>
<h2 id="group-von-neumann-algebras">Group von Neumann algebras</h2>
<p>Chapter 4 moves to operator algebras. For a countable discrete group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>, the left regular representation is</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>λ</mi><mi>G</mi></msub><mo>:</mo><mi>G</mi><mo>→</mo><mi mathvariant="script">U</mi><mo stretchy="false">(</mo><msup><mi mathvariant="normal">ℓ</mi><mn>2</mn></msup><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>λ</mi><mi>G</mi></msub><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo><msub><mi>δ</mi><mi>h</mi></msub><mo>=</mo><msub><mi>δ</mi><mrow><mi>g</mi><mi>h</mi></mrow></msub><mo separator="true">,</mo></mrow></semantics></math>
<p>and the <em>group von Neumann algebra</em> is the double commutant</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>λ</mi><mi>G</mi></msub><mo stretchy="false">(</mo><mi>G</mi><msup><mo stretchy="false">)</mo><mrow><mo>′</mo><mo>′</mo></mrow></msup><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>τ</mi><mi>G</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">⟨</mo><mi>x</mi><msub><mi>δ</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>δ</mi><mn>1</mn></msub><mo stretchy="false">⟩</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Concretely, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow></semantics></math> is the weak closure of finite complex combinations <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mo>∑</mo><mi>g</mi></msub><msub><mi>a</mi><mi>g</mi></msub><msub><mi>λ</mi><mi>G</mi></msub><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo></mrow></semantics></math>, with canonical trace <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>τ</mi><mi>G</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>a</mi><mn>1</mn></msub></mrow></semantics></math>. It is an analytic completion of the group algebra, and completion loses information: it is a coarse invariant by construction.</p>
<p>A group is <em>ICC</em> if it is infinite and every nonidentity conjugacy class is infinite. For infinite <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>, the classical criterion of Murray and von Neumann says <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow></semantics></math> is a <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">I</mi><mi mathvariant="normal">I</mi></mrow><mn>1</mn></msub></mrow></semantics></math> factor, an infinite-dimensional factor with a faithful normal tracial state, precisely when <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> is ICC.</p>
<p>How coarse is the invariant? Connes’s classification of injective factors gives the extreme case: <em>every</em> amenable ICC group has the same group factor, the hyperfinite <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">I</mi><mi mathvariant="normal">I</mi></mrow><mn>1</mn></msub></mrow></semantics></math> factor. All amenability collapses to one point.</p>
<p>Property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> is the opposite of amenability, and was expected to prevent the collapse entirely.</p>
<blockquote>
<p><strong>Conjecture (Connes).</strong> Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> be countable ICC groups with property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math>. If <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><mi>H</mi><mo stretchy="false">)</mo></mrow></semantics></math>, then <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>≅</mo><mi>H</mi></mrow></semantics></math>.</p>
</blockquote>
<p>It appears as Problem 1 in the appendix to Chapter 5 of his 1994 monograph.<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-5" id="user-content-fnref-5">5</a></sup></p>
<p>The surrounding notion is <em>W*-superrigidity</em>: a countable group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> is W*-superrigid if <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><mi>H</mi><mo stretchy="false">)</mo></mrow></semantics></math> implies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>≅</mo><mi>H</mi></mrow></semantics></math> for every countable <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math>, so the group is fully recoverable from its factor. Such groups exist; the first examples came from Ioana, Popa and Vaes,<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-7" id="user-content-fnref-7">6</a></sup> and the first with property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> from Chifan, Ioana, Osin and Sun. The question was whether property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> alone forces it.</p>
<h2 id="the-fiber-is-infinite">The fiber is infinite</h2>
<blockquote>
<p><strong>Theorem (Chapter 4).</strong> There exist finitely generated ICC property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> groups <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo></mrow></semantics></math> that are pairwise nonisomorphic and satisfy</p>
</blockquote>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><mi mathvariant="normal">Λ</mi><mo stretchy="false">)</mo><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi>n</mi><mo>≥</mo><mn>0</mn><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></semantics></math>
<p>with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub></mrow></semantics></math> containing a copy of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub></mrow></semantics></math> of index <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mn>4</mn><mi>n</mi></mrow></msup></mrow></semantics></math>, so the family is mutually commensurable.</p>
<p>The pair <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub></mrow></semantics></math> already refutes Connes’s conjecture. The infinite family does more. In his Madrid ICM address, Popa proved the group-factor functor is at most countable-to-one on ICC property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> groups,<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-6" id="user-content-fnref-6">7</a></sup> and later asked whether the fibers are in fact finite. They are not, and his countability bound is sharp.</p>
<figure><figcaption><strong>The fiber of the group-factor functor over L(Λ)</strong> — What property (T ) was expected to guarantee, and what it actually guarantees.</figcaption><p><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/">View this figure on the site &#8594;</a></p></figure>
<p>The mechanism is a deliberate leak in the invariant. For a countable abelian <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-module <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow></semantics></math>, Fourier transform gives</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>A</mi><mo>⋊</mo><mi>K</mi><mo stretchy="false">)</mo><mo>≅</mo><msup><mi>L</mi><mi mathvariant="normal">∞</mi></msup><mo stretchy="false">(</mo><mover accent="true"><mi>A</mi><mo stretchy="true">^</mo></mover><mo separator="true">,</mo><msub><mi>m</mi><mover accent="true"><mi>A</mi><mo stretchy="true">^</mo></mover></msub><mo stretchy="false">)</mo><mo>⋊</mo><mi>K</mi><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The right-hand side depends on the Haar <em>probability space</em> of the dual <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo stretchy="true">^</mo></mover></mrow></semantics></math> and on the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-action, but not on the compact group law of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo stretchy="true">^</mo></mover></mrow></semantics></math>. Two different group structures on the same measured <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-space give the same crossed product, and hence the same factor, while their discrete duals can be nonisomorphic groups.</p>
<p>The elementary model is the four-point space <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mi mathvariant="double-struck">F</mi><mn>2</mn><mn>2</mn></msubsup></mrow></semantics></math>. Coordinatewise addition makes it the Klein four-group. The binary carry</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>⊞</mo><mo stretchy="false">(</mo><msup><mi>x</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo separator="true">,</mo><msup><mi>y</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo><mo>=</mo><mo stretchy="false">(</mo><mi>x</mi><mo>+</mo><msup><mi>x</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo separator="true">,</mo><mtext> </mtext><mi>y</mi><mo>+</mo><msup><mi>y</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo>+</mo><mi>x</mi><msup><mi>x</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">)</mo></mrow></semantics></math>
<p>makes the same four points into <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">Z</mi><mi mathvariant="normal">/</mi><mn>4</mn><mi mathvariant="double-struck">Z</mi></mrow></semantics></math>. Same set, same uniform Haar measure, different group.</p>
<p>The construction globalizes this. The acting group is a torsion-free ICC property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> group</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>K</mi><mo>=</mo><mi>ker</mi><mo>⁡</mo><mo fence="true" stretchy="true" minsize="1.8em" maxsize="1.8em">(</mo><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mn>4</mn></msub><mo stretchy="false">(</mo><mi mathvariant="double-struck">Z</mi><mo stretchy="false">[</mo><mi>t</mi><mo stretchy="false">]</mo><mo stretchy="false">)</mo><mover><mo stretchy="true" minsize="3.0em">→</mo><mpadded width="+0.6em" lspace="0.3em"><mrow><mtext> </mtext><mi>g</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>↦</mo><mi>g</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><mn>3</mn><mtext> </mtext></mrow></mpadded></mover><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mn>4</mn></msub><mo stretchy="false">(</mo><msub><mi mathvariant="double-struck">F</mi><mn>3</mn></msub><mo stretchy="false">)</mo><mo fence="true" stretchy="true" minsize="1.8em" maxsize="1.8em">)</mo><mo separator="true">,</mo></mrow></semantics></math>
<p>which surjects onto <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mn>4</mn></msub><mo stretchy="false">(</mo><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub><mo stretchy="false">[</mo><mi>t</mi><mo stretchy="false">]</mo><mo stretchy="false">)</mo></mrow></semantics></math>. With <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi><mo>=</mo><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub><mo stretchy="false">[</mo><mi>t</mi><msup><mo stretchy="false">]</mo><mn>4</mn></msup></mrow></semantics></math> and the divided-square module</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>B</mi><mo>=</mo><msub><mrow><mi mathvariant="normal">span</mi><mo>⁡</mo></mrow><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub></msub><mo stretchy="false">{</mo><mi>v</mi><mo>⊗</mo><mi>v</mi><mo>:</mo><mi>v</mi><mo>∈</mo><mi>V</mi><mo stretchy="false">}</mo><mo separator="true">,</mo><mspace width="2em"></mspace><mi>D</mi><mo>=</mo><mi>V</mi><mo>⊕</mo><mi>B</mi><mo separator="true">,</mo></mrow></semantics></math>
<p>the dual <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>D</mi><mo stretchy="true">^</mo></mover></mrow></semantics></math> gives the common compact <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-space. Carrying the binary rule to a compact group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mn>0</mn></msub></mrow></semantics></math> with the same measured <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-action and setting</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo>=</mo><mi>D</mi><mo>⋊</mo><mi>K</mi><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub><mo>=</mo><mover accent="true"><msub><mi>C</mi><mn>0</mn></msub><mo stretchy="true">^</mo></mover><mo>⋊</mo><mi>K</mi><mo separator="true">,</mo></mrow></semantics></math>
<p>Fourier transform identifies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><mi mathvariant="normal">Λ</mi><mo stretchy="false">)</mo></mrow></semantics></math>, while order-four torsion distinguishes the two groups as abstract groups.</p>
<p>Verifying that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi></mrow></semantics></math> is ICC and has property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> suffices for the pair, because both properties transfer across an isomorphism of group factors: by the Murray–von Neumann criterion the ICC property is detected by factoriality, and by the Connes–Jones characterization an ICC group has property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> if and only if its group factor does.</p>
<p>Shifting the carry by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> coefficient positions in each of the four directions produces compact groups <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>n</mi></msub></mrow></semantics></math> with the same measured <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>-action, hence <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub><mo>=</mo><mover accent="true"><msub><mi>C</mi><mi>n</mi></msub><mo stretchy="true">^</mo></mover><mo>⋊</mo><mi>K</mi></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><mi mathvariant="normal">Λ</mi><mo stretchy="false">)</mo></mrow></semantics></math>. Pontryagin duality embeds <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub></mrow></semantics></math> in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub></mrow></semantics></math> with index <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mn>4</mn><mi>n</mi></mrow></msup></mrow></semantics></math>, and the finite-orbit part of the intrinsic quotient <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>E</mi><mi>n</mi></msub><mo stretchy="false">[</mo><mn>2</mn><mo stretchy="false">]</mo><mi mathvariant="normal">/</mi><mn>2</mn><msub><mi>E</mi><mi>n</mi></msub></mrow></semantics></math> has order <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mn>4</mn><mi>n</mi></mrow></msup></mrow></semantics></math>, an invariant computed from the abstract group that recovers <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>, which is what proves the family pairwise nonisomorphic.</p>
<p>Infinite fibers were already known outside the property-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> world: for a nontrivial finite abelian <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mn>0</mn></msub></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>≥</mo><mn>3</mn></mrow></semantics></math>, infinitely many pairwise nonisomorphic <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> satisfy <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>H</mi><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><msub><mi>H</mi><mn>0</mn></msub><mo>≀</mo><msub><mrow><mi mathvariant="normal">P</mi><mi mathvariant="normal">S</mi><mi mathvariant="normal">L</mi></mrow><mi>n</mi></msub><mo stretchy="false">(</mo><mi mathvariant="double-struck">Z</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow></semantics></math>, but that wreath product lacks property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math>. The new content is an infinite fiber sitting entirely inside the class where the countability bound applies.</p>
<h2 id="reading-the-two-together">Reading the two together</h2>
<p>Both results are negative, and both are negative in the same way: a property that looked like it should transmit rigidity turns out not to.</p>
<p>Property <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>T</mi><mo stretchy="false">)</mo></mrow></semantics></math> appears in both chapters, in opposite roles. In Chapter 3 it is the <em>tool</em>: it forces sofic approximations to be expanders, and expansion is what lets the argument match components and isolate a single one. In Chapter 4 it is the <em>hypothesis that fails to deliver</em>: spectral rigidity of the group does not survive the passage to the weak closure, because the crossed product only remembers a measure space and an action.</p>
<p>Neither proof needs a stability hypothesis or a computational reduction. Both are ordinary mathematics, checkable line by line, and OpenAI published Lean 4 certificates alongside the paper.<sup><a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fn-8" id="user-content-fnref-8">8</a></sup> Both close questions that had resisted the standard machinery for decades. That the arguments were produced by a model rather than a person is a fact about provenance; the theorems stand on their statements.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>OpenAI, “Ten Advances in Mathematics and Theoretical Computer Science,” 2026. <a href="https://openai.com/index/ten-advances-in-mathematics/" rel="noopener noreferrer">https://openai.com/index/ten-advances-in-mathematics/</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Misha Gromov, “Endomorphisms of symbolic algebraic varieties,” Journal of the European Mathematical Society, 1999. <a href="https://doi.org/10.1007/PL00011162" rel="noopener noreferrer">https://doi.org/10.1007/PL00011162</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Vladimir Pestov, “Hyperlinear and Sofic Groups: A Brief Guide,” Bulletin of Symbolic Logic, 2008. <a href="https://arxiv.org/abs/0804.3968" rel="noopener noreferrer">https://arxiv.org/abs/0804.3968</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Gábor Kun, “On sofic approximations of property (T) groups,” arXiv, 2019. <a href="https://arxiv.org/abs/1606.04471" rel="noopener noreferrer">https://arxiv.org/abs/1606.04471</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Alain Connes, “Noncommutative Geometry,” Academic Press, 1994. <a href="https://alainconnes.org/wp-content/uploads/book94bigpdf.pdf" rel="noopener noreferrer">https://alainconnes.org/wp-content/uploads/book94bigpdf.pdf</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Adrian Ioana, Sorin Popa, and Stefaan Vaes, “A class of superrigid group von Neumann algebras,” Annals of Mathematics, 2013. <a href="https://arxiv.org/abs/1007.1412" rel="noopener noreferrer">https://arxiv.org/abs/1007.1412</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Sorin Popa, “Deformation and rigidity for group actions and von Neumann algebras,” ICM Madrid, 2006. <a href="https://arxiv.org/abs/math/0603038" rel="noopener noreferrer">https://arxiv.org/abs/math/0603038</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>OpenAI, “ten-proofs: Lean certificates accompanying proofs in mathematics and theoretical computer science,” GitHub, 2026. <a href="https://github.com/openai/ten-proofs" rel="noopener noreferrer">https://github.com/openai/ten-proofs</a> <a href="https://blog.ecitis.org/openai-sofic-groups-connes-rigidity/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Theory &amp; Mathematics</category>
            <category>Group theory</category>
            <category>Operator algebras</category>
            <category>Mathematics</category>
            <enclosure length="70339" type="image/png" url="https://blog.ecitis.org/open-graph/openai-sofic-groups-connes-rigidity.png"/>
        </item>
        <item>
            <title><![CDATA[Three Bounds in the Geometry of Numbers]]></title>
            <link>https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/</guid>
            <pubDate>Mon, 03 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A reading of three chapters from OpenAI Ten Advances: the exact strength of the Cohn-Elkies linear program, exponential improvements to binary and spherical code bounds, and the sharp inequality in Ehrhart volume conjecture.]]></description>
            <content:encoded><![CDATA[<p>In August 2026 OpenAI published a collection of ten results in mathematics and theoretical computer science, each with a Lean certificate.<sup><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> Three of the chapters sit in the same corner of the subject: the geometry of numbers, where the objects are lattices, packings, codes, and convex bodies, and the technique of choice is a linear program over Fourier-positive test functions.</p>
<p>Chapters 1, 2, and 8 improve three bounds that had stood for a long time. Two of them dated from 1977 and 1978. The third was a conjecture of Ehrhart from 1964. This post explains what each problem asks, what was previously known, and the shape of the arguments.</p>
<h2 id="sphere-packing-in-high-dimensions">Sphere packing in high dimensions</h2>
<p>Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>d</mi></msub></mrow></semantics></math> be the largest density achievable by a packing of congruent balls in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup></mrow></semantics></math>. Lower bounds are easy and weak: random constructions give roughly <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mrow><mo>−</mo><mi>d</mi></mrow></msup></mrow></semantics></math>. Upper bounds are hard, and for fifty years the best general one came from Kabatianskii and Levenshtein, who proved <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>d</mi></msub><mo>≤</mo><msup><mn>2</mn><mrow><mo>−</mo><mo stretchy="false">(</mo><mn>0.59905576</mn><mo>…</mo><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>d</mi></mrow></msup></mrow></semantics></math>.</p>
<p>The dominant tool for upper bounds is a linear program introduced by Gorbachev and, independently, Cohn and Elkies.<sup><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Fix the Fourier convention <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>f</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mi>ξ</mi><mo stretchy="false">)</mo><mo>=</mo><mo>∫</mo><mi>f</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><msup><mi>e</mi><mrow><mo>−</mo><mn>2</mn><mi>π</mi><mi>i</mi><mi>x</mi><mo>⋅</mo><mi>ξ</mi></mrow></msup><mtext> </mtext><mi>d</mi><mi>x</mi></mrow></semantics></math> and let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>v</mi><mi>d</mi></msub></mrow></semantics></math> be the volume of the unit ball. Define the admissible class</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">A</mi><mi>d</mi></msub><mo>=</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">{</mo><mi>f</mi><mo>∈</mo><mi mathvariant="script">S</mi><mo stretchy="false">(</mo><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup><mo separator="true">;</mo><mi mathvariant="double-struck">R</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>:</mo><mtext>  </mtext><mover accent="true"><mi>f</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mo>&gt;</mo><mn>0</mn><mo separator="true">,</mo><mtext> </mtext><mover accent="true"><mi>f</mi><mo>^</mo></mover><mo>≥</mo><mn>0</mn><mtext> on </mtext><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup><mo separator="true">,</mo><mtext> </mtext><mi>f</mi><mo>≤</mo><mn>0</mn><mtext> on </mtext><mo stretchy="false">{</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mi mathvariant="normal">∣</mi><mo>≥</mo><mn>1</mn><mo stretchy="false">}</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">}</mo></mrow></semantics></math>
<p>and the resulting bound</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mrow><mi mathvariant="normal">L</mi><mi mathvariant="normal">P</mi></mrow><mi>d</mi></msub><mo>=</mo><mfrac><msub><mi>v</mi><mi>d</mi></msub><msup><mn>2</mn><mi>d</mi></msup></mfrac><munder><mrow><mi>inf</mi><mo>⁡</mo></mrow><mrow><mi>f</mi><mo>∈</mo><msub><mi mathvariant="script">A</mi><mi>d</mi></msub></mrow></munder><mfrac><mrow><mi>f</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo></mrow><mrow><mover accent="true"><mi>f</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo></mrow></mfrac><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi mathvariant="normal">Δ</mi><mi>d</mi></msub><mo>≤</mo><msub><mrow><mi mathvariant="normal">L</mi><mi mathvariant="normal">P</mi></mrow><mi>d</mi></msub><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Every admissible <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi></mrow></semantics></math> is a certificate. Poisson summation turns the two sign conditions into an inequality about any packing at all: the spatial condition kills contributions from distinct centers, and the Fourier condition makes the remaining sum nonnegative. This is the framework in which Viazovska settled dimension 8 and, with collaborators, dimension 24, by producing exactly optimal “magic” functions.</p>
<figure><figcaption><strong>The Cohn–Elkies program is squeezed from both sides</strong> — Sign conditions on an admissible pair, sketched schematically. Any such pair bounds the packing density from above; the uncertainty argument bounds every such pair from below.</figcaption><p><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/">View this figure on the site &#8594;</a></p></figure>
<p>What nobody knew was how strong the program is in general. Cohn and Zhao showed it is at least as strong as the Kabatianskii-Levenshtein bound.<sup><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Whether it is strictly stronger in the exponent was open, though Afkhami-Jeddi, Cohn, Hartman, de Laat, and Tajdini conjectured the answer while studying the modular bootstrap.</p>
<p>Chapter 1 proves that conjecture.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder><mrow><mi mathvariant="normal">lim sup</mi><mo>⁡</mo></mrow><mrow><mi>d</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><msubsup><mi mathvariant="normal">Δ</mi><mi>d</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>d</mi></mrow></msubsup><mtext> </mtext><mo>≤</mo><mtext> </mtext><munder><mrow><mi>lim</mi><mo>⁡</mo></mrow><mrow><mi>d</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><msubsup><mrow><mi mathvariant="normal">L</mi><mi mathvariant="normal">P</mi></mrow><mi>d</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>d</mi></mrow></msubsup><mo>=</mo><msqrt><mfrac><mi>e</mi><mrow><mn>2</mn><mi>π</mi></mrow></mfrac></msqrt></mrow></semantics></math>
<p>Written as an exponent, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>d</mi></msub><mo>≤</mo><msup><mn>2</mn><mrow><mo>−</mo><mo stretchy="false">(</mo><msub><mi>α</mi><mo>∗</mo></msub><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>d</mi></mrow></msup></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>α</mi><mo>∗</mo></msub><mo>=</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mn>1</mn><mn>2</mn></mfrac></mstyle><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mn>2</mn><mi>π</mi><mi mathvariant="normal">/</mi><mi>e</mi><mo stretchy="false">)</mo><mo>=</mo><mn>0.6044</mn><mo>…</mo></mrow></semantics></math>, against the classical <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0.59905576</mn><mo>…</mo></mrow></semantics></math>. Intervening work had improved only lower-order factors, so this is the first change to the exponent since 1978.</p>
<figure><figcaption><strong>The first improvement to the general packing exponent since 1978</strong> — Both bounds are exponential in the dimension, so a small exponent gap compounds. The line is exact arithmetic on the two exponents, not a fit.</figcaption><p><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/">View this figure on the site &#8594;</a></p></figure>
<p>The result is two-sided, and the second half matters as much as the first. The limit is an equality, so no Cohn-Elkies auxiliary function can ever do better. The linear program is now a closed question at the level of exponents, and further progress on sphere packing must come from somewhere else.</p>
<h2 id="sign-uncertainty-as-the-underlying-obstruction">Sign uncertainty as the underlying obstruction</h2>
<p>The lower bound on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">L</mi><mi mathvariant="normal">P</mi></mrow><mi>d</mi></msub></mrow></semantics></math> runs through an uncertainty principle. For <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mo>∈</mo><msup><mi>L</mi><mn>1</mn></msup><mo stretchy="false">(</mo><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup><mo separator="true">;</mo><mi mathvariant="double-struck">R</mi><mo stretchy="false">)</mo></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>g</mi><mo>^</mo></mover><mo>=</mo><mi>ς</mi><mi>g</mi></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ς</mi><mo>∈</mo><mo stretchy="false">{</mo><mo>−</mo><mn>1</mn><mo separator="true">,</mo><mo>+</mo><mn>1</mn><mo stretchy="false">}</mo></mrow></semantics></math>, let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo></mrow></semantics></math> be the smallest <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>R</mi></mrow></semantics></math> beyond which <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mo>≥</mo><mn>0</mn></mrow></semantics></math>, and set</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>A</mi><mi>ς</mi></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo><mo>=</mo><mi>inf</mi><mo>⁡</mo><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">{</mo><mi>r</mi><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo><mtext>  </mtext><mo>:</mo><mtext>  </mtext><mn>0</mn><mo>≠</mo><mi>g</mi><mo>∈</mo><msup><mi>L</mi><mn>1</mn></msup><mo stretchy="false">(</mo><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup><mo separator="true">;</mo><mi mathvariant="double-struck">R</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mtext> </mtext><mover accent="true"><mi>g</mi><mo>^</mo></mover><mo>=</mo><mi>ς</mi><mi>g</mi><mo separator="true">,</mo><mtext> </mtext><mi>g</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mo>=</mo><mn>0</mn><mo fence="true" stretchy="true" minsize="1.2em" maxsize="1.2em">}</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>A self-dual function that vanishes at the origin cannot become nonnegative too soon. The <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>+</mo><mn>1</mn></mrow></semantics></math> case is the Bourgain-Clozel-Kahane problem; the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>−</mo><mn>1</mn></mrow></semantics></math> case was introduced by Cohn and Gonçalves and tied to sphere packing.<sup><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> Chapter 1 settles both asymptotically.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder><mrow><mi>lim</mi><mo>⁡</mo></mrow><mrow><mi>d</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><mfrac><mrow><msub><mi>A</mi><mo>+</mo></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><msqrt><mi>d</mi></msqrt></mfrac><mo>=</mo><munder><mrow><mi>lim</mi><mo>⁡</mo></mrow><mrow><mi>d</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><mfrac><mrow><msub><mi>A</mi><mo>−</mo></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><msqrt><mi>d</mi></msqrt></mfrac><mo>=</mo><mfrac><mn>1</mn><mi>π</mi></mfrac></mrow></semantics></math>
<p>The link to packing is a two-line construction. Given admissible <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi></mrow></semantics></math>, rescale by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>a</mi><mo>=</mo><mo stretchy="false">(</mo><mover accent="true"><mi>F</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi>F</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><msup><mo stretchy="false">)</mo><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>d</mi></mrow></msup></mrow></semantics></math> so that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>F</mi><mo stretchy="false">(</mo><mi>a</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math> satisfies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>h</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mo>=</mo><mover accent="true"><mi>h</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo></mrow></semantics></math>. Then <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mo>=</mo><mover accent="true"><mi>h</mi><mo>^</mo></mover><mo>−</mo><mi>h</mi></mrow></semantics></math> is anti-self-Fourier, vanishes at the origin, and is nonnegative outside the ball of radius <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>a</mi></mrow></semantics></math>. Since <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>∫</mo><mi>g</mi><mo>=</mo><mn>0</mn></mrow></semantics></math>, half of its total mass is negative, and all of that negative mass sits inside that ball. A Mellin-transform estimate shows a radial eigenfunction with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mo stretchy="false">(</mo><mn>0</mn><mo stretchy="false">)</mo><mo>=</mo><mn>0</mn></mrow></semantics></math> has exponentially little <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mn>1</mn></msup></mrow></semantics></math> mass inside radius <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><msqrt><mi>d</mi></msqrt></mrow></semantics></math> for any <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>&lt;</mo><mn>1</mn><mi mathvariant="normal">/</mi><mi>π</mi></mrow></semantics></math>, which forces <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>a</mi><mo>≥</mo><mo stretchy="false">(</mo><mn>1</mn><mi mathvariant="normal">/</mi><mi>π</mi><mo>−</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><msqrt><mi>d</mi></msqrt></mrow></semantics></math>. The matching upper bound modifies the Mellin transform of a Gaussian to build a Fourier pair whose sign conditions begin at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mn>1</mn><mi mathvariant="normal">/</mi><mi>π</mi><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><msqrt><mi>d</mi></msqrt></mrow></semantics></math>. Stirling’s formula converts radii into densities.</p>
<p>The two constants are asymptotically equal but not equal: the paper records <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>A</mi><mo>+</mo></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo><mo>&lt;</mo><msub><mi>A</mi><mo>−</mo></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow></semantics></math> in every dimension.</p>
<h2 id="binary-and-spherical-codes">Binary and spherical codes</h2>
<p>Chapter 2 attacks the discrete analogues and arrives at the same exponent by a different road.</p>
<p>A binary code <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi><mo>⊆</mo><mo stretchy="false">{</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><msup><mo stretchy="false">}</mo><mi>n</mi></msup></mrow></semantics></math> with minimum Hamming distance <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow></semantics></math> has size at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>A</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>d</mi><mo stretchy="false">)</mo></mrow></semantics></math>; a spherical code <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi><mo>⊆</mo><msup><mi>S</mi><mrow><mi>n</mi><mo>−</mo><mn>1</mn></mrow></msup></mrow></semantics></math> with pairwise inner products at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow></semantics></math> has size at most <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>s</mi><mo stretchy="false">)</mo></mrow></semantics></math>. The asymptotic rates are</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mi>δ</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mrow><mi mathvariant="normal">lim sup</mi><mo>⁡</mo></mrow><mrow><mi>n</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><mfrac><mn>1</mn><mi>n</mi></mfrac><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><msub><mi>A</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mo stretchy="false">⌈</mo><mi>δ</mi><mi>n</mi><mo stretchy="false">⌉</mo><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>R</mi><mrow><mi mathvariant="normal">s</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">h</mi></mrow></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mrow><mi mathvariant="normal">lim sup</mi><mo>⁡</mo></mrow><mrow><mi>n</mi><mo>→</mo><mi mathvariant="normal">∞</mi></mrow></munder><mfrac><mn>1</mn><mi>n</mi></mfrac><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>A</mi><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>s</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>The standing records were the second McEliece-Rodemich-Rumsey-Welch bound <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>M</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mi>δ</mi><mo stretchy="false">)</mo></mrow></semantics></math> from 1977 and the Kabatianskii-Levenshtein bound <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>B</mi><mrow><mi mathvariant="normal">K</mi><mi mathvariant="normal">L</mi></mrow></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo></mrow></semantics></math> from 1978. Both come from Delsarte’s two-point linear program: a polynomial <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo>=</mo><msub><mo>∑</mo><mi>j</mi></msub><msub><mi>f</mi><mi>j</mi></msub><msub><mi>P</mi><mi>j</mi></msub></mrow></semantics></math> in the appropriate orthogonal basis, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mn>0</mn></msub><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>f</mi><mi>j</mi></msub><mo>≥</mo><mn>0</mn></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo>≤</mo><mn>0</mn></mrow></semantics></math> at every permitted inner product, certifies <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>C</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mi>F</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><msub><mi>f</mi><mn>0</mn></msub></mrow></semantics></math>.</p>
<p>The new idea is to change what sits at each code point. In the classical construction each retained harmonic space contributes one vector per code point. Here a <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>E</mi></msub></mrow></semantics></math>-dimensional subspace is attached to each point <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow></semantics></math> inside a common <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>-dimensional ambient space, moving with the point under the symmetry group, so that the overlap <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">tr</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>P</mi><mi>x</mi></msub><msub><mi>P</mi><mi>y</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math> remains a scalar function of distance. Writing <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi></mrow></semantics></math> for the largest eigenvalue of the associated weighted transition matrix, the projection bound reads</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">∣</mi><mi>C</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mfrac><mrow><mn>1</mn><mo>−</mo><mi>s</mi></mrow><mrow><mi mathvariant="normal">Λ</mi><mo>−</mo><mi>s</mi></mrow></mfrac><mo>⋅</mo><mfrac><mi>D</mi><msub><mi>d</mi><mi>E</mi></msub></mfrac><mspace width="2em"></mspace><mo stretchy="false">(</mo><mi mathvariant="normal">Λ</mi><mo>&gt;</mo><mi>s</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>An exponentially large projection rank <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>E</mi></msub></mrow></semantics></math> divides straight into the bound, provided <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi></mrow></semantics></math> stays clear of the threshold. The transition matrices come from representation graphs: tridiagonal in the binary case, and in the spherical case weighted graphs whose vertices are indexed by Young diagrams, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math> rows for the subspace at each code point and one more row for the ambient representation. That hierarchy level <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math> is the new parameter.</p>
<p>For binary codes the improvement is stated as a strict inequality against the fully optimized classical exponent, using an explicit variational quantity <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>κ</mi><mrow><mi mathvariant="normal">b</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">n</mi></mrow></msub></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mi>δ</mi><mo stretchy="false">)</mo><mo>≤</mo><msub><mi>κ</mi><mrow><mi mathvariant="normal">b</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">n</mi></mrow></msub><mo stretchy="false">(</mo><mi>δ</mi><mo stretchy="false">)</mo><mo>&lt;</mo><msub><mi>M</mi><mn>2</mn></msub><mo stretchy="false">(</mo><mi>δ</mi><mo stretchy="false">)</mo><mspace width="2em"></mspace><mtext>for every fixed </mtext><mn>0</mn><mo>&lt;</mo><mi>δ</mi><mo>&lt;</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>2.</mn></mrow></semantics></math>
<p>The classical bounds are boundary cases of the enlarged problem, obtained by setting the attached harmonic degrees to zero. For spherical codes the hierarchy is strictly monotone at every level:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mrow><mi mathvariant="normal">s</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">h</mi></mrow></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>≤</mo><munder><mrow><mi>inf</mi><mo>⁡</mo></mrow><mrow><mi>r</mi><mo>≥</mo><mn>0</mn></mrow></munder><msub><mover accent="true"><mi>κ</mi><mo stretchy="true">‾</mo></mover><mi>r</mi></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>&lt;</mo><msub><mover accent="true"><mi>κ</mi><mo stretchy="true">‾</mo></mover><mn>1</mn></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>&lt;</mo><msub><mover accent="true"><mi>κ</mi><mo stretchy="true">‾</mo></mover><mrow><mi mathvariant="normal">r</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">w</mi></mrow></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>&lt;</mo><msub><mover accent="true"><mi>κ</mi><mo stretchy="true">‾</mo></mover><mn>0</mn></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>B</mi><mrow><mi mathvariant="normal">K</mi><mi mathvariant="normal">L</mi></mrow></msub><mo stretchy="false">(</mo><mi>s</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Level <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>0</mn></mrow></semantics></math> is exactly the classical construction. Every subsequent level strictly improves it, for every fixed <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mo>∈</mo><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math>.</p>
<p>Because a packing bound follows from a spherical-code bound by hemisphere projection, the hierarchy also produces</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>n</mi></msub><mo>≤</mo><msup><mn>2</mn><mrow><mo>−</mo><mo stretchy="false">(</mo><msub><mi>λ</mi><mo>∗</mo></msub><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>n</mi></mrow></msup><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>λ</mi><mo>∗</mo></msub><mo>=</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mn>1</mn><mn>2</mn></mfrac></mstyle><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mfrac><mrow><mn>2</mn><mi>π</mi></mrow><mi>e</mi></mfrac><mo separator="true">,</mo></mrow></semantics></math>
<p>which is the Chapter 1 exponent, recovered as the small-angle limit <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mo>↑</mo><mn>1</mn></mrow></semantics></math>. Two independent methods, one Euclidean and Fourier-analytic, one spherical and representation-theoretic, meeting at the same constant is the strongest available evidence that the constant is the right one.</p>
<h2 id="ehrharts-volume-conjecture">Ehrhart’s volume conjecture</h2>
<p>Chapter 8 is a different kind of problem with the same flavour: a convexity bound forced by a lattice condition.</p>
<p>Ehrhart asked in 1964 how large a convex body can be if its barycenter is its only interior lattice point. The natural candidate is the dilated, shifted simplex <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>n</mi><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo><msub><mi mathvariant="normal">Δ</mi><mi>n</mi></msub><mo>−</mo><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math>, and the conjecture is that it is extremal.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>K</mi><mo>⊂</mo><msup><mi mathvariant="double-struck">R</mi><mi>n</mi></msup><mtext> compact convex, barycenter </mtext><mn>0</mn><mo separator="true">,</mo><mtext> </mtext><mi mathvariant="normal">int</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><mo>∩</mo><msup><mi mathvariant="double-struck">Z</mi><mi>n</mi></msup><mo>=</mo><mo stretchy="false">{</mo><mn>0</mn><mo stretchy="false">}</mo><mtext> </mtext><mo>⟹</mo><mtext> </mtext><mi mathvariant="normal">vol</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><mo>≤</mo><mfrac><mrow><mo stretchy="false">(</mo><mi>n</mi><mo>+</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mi>n</mi></msup></mrow><mrow><mi>n</mi><mo stretchy="false">!</mo></mrow></mfrac></mrow></semantics></math>
<figure><figcaption><strong>The extremal body is a dilated, shifted simplex</strong> — Drawn exactly for n = 2: the triangle 3Δ2 − (1, 1) has barycenter 0, no other interior lattice point, and area 9/2 = (n + 1)n/n!.</figcaption><p><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/">View this figure on the site &#8594;</a></p></figure>
<p>Ehrhart proved it in the plane and for simplices in every dimension. For general centered bodies, Milman-Pajor symmetrization with Minkowski’s theorem gives <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">vol</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><mo>≤</mo><msup><mn>4</mn><mi>n</mi></msup></mrow></semantics></math>, and a sequence of refinements shaved subexponential and then exponential factors off that, the last of them leaning on Klartag and Lehec’s resolution of Bourgain’s slicing problem.<sup><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> Since <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><mi>n</mi><mo>+</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mi>n</mi></msup><mi mathvariant="normal">/</mi><mi>n</mi><mo stretchy="false">!</mo></mrow></semantics></math> grows like <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mi>n</mi></msup></mrow></semantics></math> up to polynomial factors, all of those bounds were exponentially far from sharp.</p>
<p>The proof is complex-analytic. A theorem of Berman and Berndtsson supplies a smooth convex potential <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>φ</mi></mrow></semantics></math> whose gradient transports <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mrow><mo>−</mo><mi>φ</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></msup><mi>d</mi><mi>x</mi></mrow></semantics></math> to Lebesgue measure on <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi></mrow></semantics></math>.<sup><a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fn-6" id="user-content-fnref-6">6</a></sup> Pulled back to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mo>=</mo><mo stretchy="false">(</mo><msup><mi mathvariant="double-struck">C</mi><mo>∗</mo></msup><msup><mo stretchy="false">)</mo><mi>n</mi></msup></mrow></semantics></math> through logarithmic radii, the weighted holomorphic space <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">H</mi><mi>k</mi></msub></mrow></semantics></math> has Laurent monomial basis indexed by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="double-struck">Z</mi><mi>n</mi></msup><mo>∩</mo><mi mathvariant="normal">int</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>k</mi><mi>K</mi><mo stretchy="false">)</mo></mrow></semantics></math>, so the hypothesis that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow></semantics></math> is the only interior lattice point says precisely that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">H</mi><mn>1</mn></msub><mo>=</mo><mi mathvariant="double-struck">C</mi></mrow></semantics></math>.</p>
<p>That is the pivot. Filtering an orthonormal basis by vanishing order at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math> produces a ray of potentials <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo stretchy="false">(</mo><msub><mi>ψ</mi><mi>t</mi></msub><msub><mo stretchy="false">)</mo><mrow><mi>t</mi><mo>≥</mo><mn>0</mn></mrow></msub></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>ψ</mi><mn>0</mn></msub><mo>=</mo><mi>φ</mi></mrow></semantics></math>, and one studies</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mi mathvariant="normal">vol</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo></mrow></mfrac><msub><mo>∫</mo><mi>X</mi></msub><msup><mi>e</mi><mrow><mo>−</mo><msub><mi>ψ</mi><mi>t</mi></msub></mrow></msup><mtext> </mtext><mi>d</mi><mi>ν</mi><mo separator="true">,</mo><mspace width="2em"></mspace><mi>L</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>=</mo><mo>−</mo><mi>log</mi><mo>⁡</mo><mi>Z</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow></semantics></math>
<p>Berndtsson’s positivity theorem makes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> convex, because <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">H</mi><mn>1</mn></msub><mo>=</mo><mi mathvariant="double-struck">C</mi></mrow></semantics></math> keeps the relevant Bergman kernel equal to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mo stretchy="false">(</mo><mi mathvariant="normal">vol</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><mi>Z</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow></semantics></math>. Counting how many linear conditions vanishing to a given order imposes bounds the initial slope from below; a local Schwarz estimate on a ball of radius proportional to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>e</mi><mrow><mo>−</mo><mi>t</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow></msup></mrow></semantics></math> bounds it from above. With <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mi>K</mi></msub><mo>=</mo><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">!</mo><mi mathvariant="normal">vol</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><msup><mo stretchy="false">)</mo><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>n</mi></mrow></msup></mrow></semantics></math>,</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mi>n</mi><mrow><mi>n</mi><mo>+</mo><mn>1</mn></mrow></mfrac><mtext> </mtext><msub><mi>c</mi><mi>K</mi></msub><mtext> </mtext><mo>≤</mo><mtext> </mtext><msup><mi>L</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">(</mo><msup><mn>0</mn><mo>+</mo></msup><mo stretchy="false">)</mo><mtext> </mtext><mo>≤</mo><mtext> </mtext><mi>n</mi><mo separator="true">,</mo></mrow></semantics></math>
<p>so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mi>K</mi></msub><mo>≤</mo><mi>n</mi><mo>+</mo><mn>1</mn></mrow></semantics></math>, which is the theorem. The equality refinement, that every extremizer is a unimodular image of the centered simplex, is not settled here.</p>
<h2 id="what-the-cluster-has-in-common">What the cluster has in common</h2>
<p>All three arguments convert a combinatorial or geometric constraint into a positivity condition on an analytic object, then extract a sharp constant from the exact asymptotics of that object. In Chapter 1 the object is a Fourier eigenfunction and the constant falls out of Mellin analysis of a modified Gaussian. In Chapter 2 it is a family of projections indexed by Young diagrams and the constant is the top eigenvalue of a weighted graph. In Chapter 8 it is a Bergman kernel and the constant is a slope pinned between a dimension count and a Schwarz estimate.</p>
<p>Two of the three come with matching lower bounds, which is the more consequential structural fact. The Cohn-Elkies program cannot be pushed further, and the spherical hierarchy converges to the same wall. Anyone hoping to move the sphere-packing exponent again now knows which tools are exhausted.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>OpenAI, “Ten Advances in Mathematics and Theoretical Computer Science,” 2026. <a href="https://openai.com/index/ten-advances-in-mathematics/" rel="noopener noreferrer">https://openai.com/index/ten-advances-in-mathematics/</a> <a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Henry Cohn and Noam Elkies, “New upper bounds on sphere packings I,” Annals of Mathematics, 2003. <a href="https://arxiv.org/abs/math/0110009" rel="noopener noreferrer">https://arxiv.org/abs/math/0110009</a> <a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Henry Cohn and Yufei Zhao, “Sphere packing bounds via spherical codes,” Duke Mathematical Journal, 2014. <a href="https://arxiv.org/abs/1212.5966" rel="noopener noreferrer">https://arxiv.org/abs/1212.5966</a> <a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Henry Cohn and Felipe Gonçalves, “An optimal uncertainty principle in twelve dimensions via modular forms,” Inventiones mathematicae, 2019. <a href="https://arxiv.org/abs/1712.04438" rel="noopener noreferrer">https://arxiv.org/abs/1712.04438</a> <a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Boaz Klartag and Joseph Lehec, “Affirmative Resolution of Bourgain’s Slicing Problem using Guan’s Bound,” arXiv, 2024. <a href="https://arxiv.org/abs/2412.15044" rel="noopener noreferrer">https://arxiv.org/abs/2412.15044</a> <a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Robert Berman and Bo Berndtsson, “Real Monge-Ampère equations and Kähler-Ricci solitons on toric log Fano varieties,” Annales de la faculté des sciences de Toulouse, 2013. <a href="https://arxiv.org/abs/1207.6128" rel="noopener noreferrer">https://arxiv.org/abs/1207.6128</a> <a href="https://blog.ecitis.org/openai-sphere-packing-codes-ehrhart/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Theory &amp; Mathematics</category>
            <category>Discrete geometry</category>
            <category>Coding theory</category>
            <category>Mathematics</category>
            <enclosure length="61467" type="image/png" url="https://blog.ecitis.org/open-graph/openai-sphere-packing-codes-ehrhart.png"/>
        </item>
        <item>
            <title><![CDATA[Ten Advances: What OpenAI's Math Results Do and Do Not Show]]></title>
            <link>https://blog.ecitis.org/openai-ten-advances-overview/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/openai-ten-advances-overview/</guid>
            <pubDate>Sun, 02 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A walk through the ten mathematics and theoretical computer science results OpenAI released, the statements they actually prove, and how to read the verification claims.]]></description>
            <content:encoded><![CDATA[<p>On 1 August 2026 OpenAI published a 247-page document titled <em>Ten Advances in Mathematics and Theoretical Computer Science</em>. Its first sentence sets the frame: “We present a collection of results obtained by an internal OpenAI model, spanning mathematics and theoretical computer science.”<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> Ten chapters follow, each a self-contained paper with its own abstract, references, and acknowledgments.</p>
<p>It is an impressive document, and the most encouraging thing about it is how checkable it is. Each chapter makes a precise claim, and most ship with a Lean certificate anyone can recompile.</p>
<blockquote>
<p>A benchmark score is a measurement on a fixed problem set. A theorem is a claim about every instance, forever, checkable by anyone.</p>
</blockquote>
<figure><figcaption><strong>Ten results, six kinds of advance</strong> — Four chapters refute or settle a stated question. Six move a quantitative bound that had not moved.</figcaption><p><a href="https://blog.ecitis.org/openai-ten-advances-overview/">View this figure on the site &#8594;</a></p></figure>
<h2 id="the-framing-openai-chose">The framing OpenAI chose</h2>
<p>Three choices in the release shape how it should be read.</p>
<p>The paper credits no human authors. The mathematical arguments are attributed to the system; OpenAI states it prepared the manuscripts and takes responsibility for correctness, on the reasoning that claiming human authorship would misrepresent where the arguments came from. Press coverage identifies the system as an internal version of Astra, the company’s next major model.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p>Each argument is accompanied by a Lean 4 formalization, released alongside the paper, plus a model-written document reconstructing how each proof came together from the reasoning traces.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup> Simon Willison, who covered the release, notes the gap that remains: the prompts are not published, and neither is the count of problems on which the same spend produced nothing.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-2" id="user-content-fnref-2-3">2</a></sup></p>
<p>The acknowledgments name individual mathematicians who read individual chapters: Sorin Popa on the Connes chapter, and Henry Bradford, Michael Chapman, Alon Dogon, and Francesco Fournier-Facio on the non-sofic chapter.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup> They are thanked as readers, not listed as authors and not described as referees. That distinction matters, and I will come back to it.</p>
<h2 id="the-ten-results">The ten results</h2>
<p><strong>1. Sphere packing.</strong> Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>d</mi></msub></mrow></semantics></math> be the maximal sphere-packing density in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi mathvariant="double-struck">R</mi><mi>d</mi></msup></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">L</mi><mi mathvariant="normal">P</mi></mrow><mi>d</mi></msub></mrow></semantics></math> the bound from the Cohn–Elkies linear program. The paper proves <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mrow><mi mathvariant="normal">L</mi><mi mathvariant="normal">P</mi></mrow><mi>d</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mi>d</mi></mrow></msubsup><mo>→</mo><msqrt><mrow><mi>e</mi><mi mathvariant="normal">/</mi><mo stretchy="false">(</mo><mn>2</mn><mi>π</mi><mo stretchy="false">)</mo></mrow></msqrt></mrow></semantics></math>, giving <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Δ</mi><mi>d</mi></msub><mo>≤</mo><msup><mn>2</mn><mrow><mo>−</mo><mo stretchy="false">(</mo><msub><mi>α</mi><mo>∗</mo></msub><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>d</mi></mrow></msup></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>α</mi><mo>∗</mo></msub><mo>=</mo><mstyle scriptlevel="0" displaystyle="false"><mfrac><mn>1</mn><mn>2</mn></mfrac></mstyle><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mo stretchy="false">(</mo><mn>2</mn><mi>π</mi><mi mathvariant="normal">/</mi><mi>e</mi><mo stretchy="false">)</mo><mo>=</mo><mn>0.6044</mn><mo>…</mo></mrow></semantics></math> The previous general exponent, from Kabatianskii and Levenshtein in 1978, was <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0.59905576</mn><mo>…</mo></mrow></semantics></math> The matching lower bound is the sharper part: no Cohn–Elkies auxiliary function can do better, so the method is now exhausted.</p>
<p><strong>2. Codes.</strong> For every fixed relative distance <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mo>&lt;</mo><mi>δ</mi><mo>&lt;</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>2</mn></mrow></semantics></math>, a two-point linear-programming certificate strictly improves the optimized McEliece–Rodemich–Rumsey–Welch exponent for binary codes; for every fixed maximum inner product <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn><mo>&lt;</mo><mi>s</mi><mo>&lt;</mo><mn>1</mn></mrow></semantics></math> it strictly improves the optimized Kabatianskii–Levenshtein exponent for spherical codes. These are the first improvements to the two general high-dimensional exponents since 1977 and 1978. As <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi><mo>→</mo><mn>1</mn></mrow></semantics></math> the spherical construction recovers the packing exponent of Chapter 1.</p>
<p><strong>3. Sofic groups.</strong> A countable group is sofic if finite pieces of its multiplication table can be approximated by permutations of finite sets. Weiss asked in 2000 whether a non-sofic group exists. The paper proves that the unit group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><msub><mi mathvariant="double-struck">F</mi><mn>2</mn></msub></msub><mo stretchy="false">(</mo><mn>1</mn><mo separator="true">,</mo><mn>2</mn><msup><mo stretchy="false">)</mo><mo>×</mo></msup></mrow></semantics></math> of the binary Leavitt algebra is not sofic, using Kun’s expander decomposition for property-(T) groups and a contradiction routed through Thompson’s group <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math>. Earlier routes to a non-sofic group all required an unproved stability hypothesis; this one does not.</p>
<p><strong>4. Connes rigidity.</strong> Connes conjectured that the group von Neumann algebra <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo></mrow></semantics></math> of an ICC property-(T) group determines <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>. The paper constructs finitely generated ICC property-(T) groups <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo></mrow></semantics></math>, pairwise nonisomorphic, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub><mo stretchy="false">)</mo><mo>≅</mo><mi>L</mi><mo stretchy="false">(</mo><mi mathvariant="normal">Λ</mi><mo stretchy="false">)</mo></mrow></semantics></math> for all <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>, where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mi>n</mi></msub></mrow></semantics></math> contains a copy of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub></mrow></semantics></math> of index <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>24</mn><mi>n</mi></msup></mrow></semantics></math>. The pair <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Λ</mi><mo separator="true">,</mo><msub><mi mathvariant="normal">Γ</mi><mn>0</mn></msub></mrow></semantics></math> already refutes the conjecture; the infinite family additionally answers Popa’s finite-to-one question negatively and shows his countability bound is sharp.</p>
<p><strong>5. Permanent lower bounds.</strong> For exact symbolic computation of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi mathvariant="normal">p</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">r</mi></mrow><mi>n</mi></msub></mrow></semantics></math> over <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="double-struck">C</mi></mrow></semantics></math>: division-free arithmetic circuits with unrestricted reuse require <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> gates, and arithmetic formulas require <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>4</mn></msup><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> variable-labeled leaves even when division is allowed. The chapter also explains why both arguments use properties specific to the permanent and do not transfer to the determinant, which is the honest caveat, since the determinant has polynomial-size circuits.</p>
<p><strong>6. Quantum parallel repetition.</strong> For every finite two-player one-round game <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> with entangled value <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>G</mi><mo stretchy="false">)</mo><mo>=</mo><mn>1</mn><mo>−</mo><mi>ε</mi><mo>&lt;</mo><mn>1</mn></mrow></semantics></math> and answer alphabets <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo separator="true">,</mo><mi>B</mi></mrow></semantics></math>,</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>ω</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><msup><mi>G</mi><mrow><mo>⊗</mo><mi>n</mi></mrow></msup><mo stretchy="false">)</mo><mo>≤</mo><mi>exp</mi><mo>⁡</mo><mtext> ⁣</mtext><mrow><mo fence="true">(</mo><mo>−</mo><msub><mi>c</mi><mrow><mi>q</mi><mi>s</mi></mrow></msub><mtext> </mtext><mfrac><msup><mi>ε</mi><mn>13</mn></msup><mrow><mi>ε</mi><mo>+</mo><mi>log</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi mathvariant="normal">∣</mi><mi>A</mi><mi mathvariant="normal">∣</mi><mi mathvariant="normal">∣</mi><mi>B</mi><mi mathvariant="normal">∣</mi><mo stretchy="false">)</mo></mrow></mfrac><mtext> </mtext><mi>n</mi><mo fence="true">)</mo></mrow></mrow></semantics></math>
<p>for a universal constant <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mrow><mi>q</mi><mi>s</mi></mrow></msub><mo>&gt;</mo><mn>0</mn></mrow></semantics></math>. This is the quantum analogue of Raz’s 1995 theorem. Yuen had proved polynomial decay in general; Bavarian, Vidick, and Yuen had proved exponential decay only for anchored games, obtained by modifying the original game. The new ingredient is a postselection-stable sampleability estimate that avoids an inverse dependence on the conditioning event’s probability.</p>
<p><strong>7. Closest vector problem.</strong> A deterministic polynomial-time many-one reduction from 3SAT to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msubsup><mrow><mi mathvariant="normal">G</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">C</mi><mi mathvariant="normal">V</mi><mi mathvariant="normal">P</mi></mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></msup><mrow><mo stretchy="false">(</mo><mn>2</mn><mo stretchy="false">)</mo></mrow></msubsup></mrow></semantics></math>, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>200</mn></mrow></msup></mrow></semantics></math> for binary nearest codeword and syndrome decoding and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mo stretchy="false">(</mo><mn>200</mn><mi>p</mi><mo stretchy="false">)</mo></mrow></msup></mrow></semantics></math> for the closest vector problem in every fixed rational <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">ℓ</mi><mi>p</mi></msub></mrow></semantics></math> norm. The exponent <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>400</mn></mrow></semantics></math> is small; the structural point is that the reduction is direct and invokes neither the PCP theorem nor the Projection Games Conjecture.</p>
<p><strong>8. Ehrhart’s conjecture.</strong> If <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>K</mi><mo>⊂</mo><msup><mi mathvariant="double-struck">R</mi><mi>n</mi></msup></mrow></semantics></math> is a full-dimensional compact convex body with barycenter <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>0</mn></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">i</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">t</mi></mrow><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><mo>∩</mo><msup><mi mathvariant="double-struck">Z</mi><mi>n</mi></msup><mo>=</mo><mo stretchy="false">{</mo><mn>0</mn><mo stretchy="false">}</mo></mrow></semantics></math>, then <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">v</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">l</mi></mrow><mo stretchy="false">(</mo><mi>K</mi><mo stretchy="false">)</mo><mo>≤</mo><mo stretchy="false">(</mo><mi>n</mi><mo>+</mo><mn>1</mn><msup><mo stretchy="false">)</mo><mi>n</mi></msup><mi mathvariant="normal">/</mi><mi>n</mi><mo stretchy="false">!</mo></mrow></semantics></math>. This is the sharp bound Ehrhart conjectured in 1964, attained by a translated dilate of the standard simplex. The previous general bound was of the form <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>4</mn><mi>n</mi></msup><msup><mi>e</mi><mrow><mo>−</mo><mi>c</mi><mi>n</mi></mrow></msup></mrow></semantics></math>. The paper does not determine whether the simplex is the only equality case.</p>
<p><strong>9. Multicolor Ramsey.</strong> There is an absolute <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi><mo>&gt;</mo><mn>0</mn></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>≥</mo><mo stretchy="false">(</mo><mi>c</mi><msup><mi>k</mi><mrow><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mi mathvariant="normal">/</mi><mi>log</mi><mo>⁡</mo><mi>k</mi><msup><mo stretchy="false">)</mo><mi>k</mi></msup></mrow></semantics></math> for every <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi><mo>≥</mo><mn>2</mn></mrow></semantics></math>. The previous record was <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>380</mn><mrow><mi>k</mi><mi mathvariant="normal">/</mi><mn>5</mn></mrow></msup><mo>−</mo><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math>, merely exponential; the classical upper bound is factorial. The new bound gives <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>k</mi><mrow><mo stretchy="false">(</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>−</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>k</mi></mrow></msup><mo>≤</mo><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>≤</mo><msup><mi>k</mi><mrow><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mi>o</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mi>k</mi></mrow></msup></mrow></semantics></math>, so <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>k</mi></msub><mo stretchy="false">(</mo><mn>3</mn><mo stretchy="false">)</mo><mo>=</mo><msup><mi>k</mi><mrow><mi mathvariant="normal">Θ</mi><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></msup></mrow></semantics></math>, and as a corollary the Shannon capacity of graphs with independence number 2 is unbounded.</p>
<p><strong>10. Extremal graph theory.</strong> Two constructions refuting two conjectures. A finite family <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="script">F</mi></mrow></semantics></math> of connected bipartite graphs, each containing a cycle, with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">e</mi><mi mathvariant="normal">x</mi></mrow><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi mathvariant="script">F</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>48</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> while <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">e</mi><mi mathvariant="normal">x</mi></mrow><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>F</mi><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>4</mn><mi mathvariant="normal">/</mi><mn>3</mn></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> for every <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo>∈</mo><mi mathvariant="script">F</mi></mrow></semantics></math>, disproving the Erdős–Simonovits compactness conjecture. And a fixed connected bipartite 2-degenerate <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>H</mi></mrow></semantics></math> with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi mathvariant="normal">e</mi><mi mathvariant="normal">x</mi></mrow><mo stretchy="false">(</mo><mi>n</mi><mo separator="true">,</mo><mi>H</mi><mo stretchy="false">)</mo><mo>≥</mo><mi>c</mi><msup><mi>n</mi><mrow><mn>3</mn><mi mathvariant="normal">/</mi><mn>2</mn><mo>+</mo><mi>ε</mi></mrow></msup></mrow></semantics></math>, disproving Erdős’s degeneracy conjecture, which predicted <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mrow><mn>2</mn><mo>−</mo><mn>1</mn><mi mathvariant="normal">/</mi><mi>r</mi></mrow></msup><mo stretchy="false">)</mo></mrow></semantics></math> for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math>-degenerate bipartite graphs.</p>
<figure><figcaption><strong>What actually moved</strong> — Six of the ten chapters replace a specific published bound. The previous entry in each row is the standing record, not a strawman.</figcaption><p><a href="https://blog.ecitis.org/openai-ten-advances-overview/">View this figure on the site &#8594;</a></p></figure>
<h2 id="stronger-evidence-than-a-benchmark-score">Stronger evidence than a benchmark score</h2>
<p>Benchmark reporting and theorem reporting fail in different directions, and the failure modes do not overlap.</p>








































<div class="table-wrap"><table><thead><tr><th>Property</th><th>Benchmark score</th><th>Theorem in this paper</th></tr></thead><tbody><tr><td>Ground truth</td><td>A held-out answer key</td><td>A derivation from stated hypotheses</td></tr><tr><td>Contamination</td><td>Plausible and hard to rule out</td><td>The statement was open; no key existed</td></tr><tr><td>Reproducibility</td><td>Rerun the harness</td><td>Read the proof, or recompile the Lean</td></tr><tr><td>Failure</td><td>Score is inflated</td><td>Proof has a gap and the claim collapses</td></tr><tr><td>Saturation</td><td>Ceiling at 100%</td><td>No ceiling; the next bound is open</td></tr><tr><td>What it predicts</td><td>Behavior on similar items</td><td>Nothing directly about the next problem</td></tr></tbody></table></div>
<p>The contamination point deserves emphasis, because it is where the October 2025 episode went wrong. OpenAI staff claimed then that GPT-5 had solved ten open Erdős problems; the claim collapsed when it emerged the model had surfaced solutions already in the published literature.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> That failure was a retrieval failure dressed as a discovery, and it is exactly the failure a benchmark cannot detect and a theorem statement can. If Chapter 9’s bound were already in a 2019 paper, someone in Ramsey theory would say so within a week.</p>
<p>The last row points at the most interesting thing still to come. Ten theorems establish that the system produced ten proofs, which is a strong lower bound on capability. Publishing the denominator alongside it, the attempts and compute that went in, would turn that lower bound into a measured rate, and it is the natural next disclosure.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-2" id="user-content-fnref-2-4">2</a></sup></p>
<h2 id="what-verification-covers">What verification covers</h2>
<p>A Lean certificate is a strong and precisely bounded form of evidence, which is what makes it so useful here.</p>
<figure><figcaption><strong>Strength of claim, rung by rung</strong> — Formal verification is a strong rung and a narrow one. It answers correctness, not significance, and not statement fidelity.</figcaption><p><a href="https://blog.ecitis.org/openai-ten-advances-overview/">View this figure on the site &#8594;</a></p></figure>
<p>Formalization establishes that a derivation compiles: given the formal hypotheses, the formal conclusion follows, with no appeal to a reader’s charity about an omitted step. That eliminates the dominant failure mode of long research papers, which is a plausible-sounding gap nobody notices.</p>
<p>Three things remain human work, and each is quick next to checking a proof. First, statement fidelity: the formal statement must be the one the abstract advertises, and checking that is a human reading task on the statement, not the proof. Second, dependence on the surrounding library: a formalization inherits whatever axioms and definitions it imports. Third, significance. No proof assistant can tell you whether <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Ω</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mi>log</mi><mo>⁡</mo><mi>log</mi><mo>⁡</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> gates for the permanent is a step toward separating VP from VNP or a barrier-bound curiosity.</p>
<p>This is why the earlier May 2026 result was packaged differently. When an internal model disproved the Erdős unit distance conjecture, OpenAI paired the announcement with external mathematicians who checked the proof and wrote a companion paper, and with named supporting statements.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> External review was the credibility mechanism because the result was not formalized. Here, formalization carries the mechanical load and named readers cover the statements, which is the stronger arrangement of the two on the part that is hardest to check by hand.</p>
<h2 id="what-would-strengthen-the-case-further">What would strengthen the case further</h2>
<p>The theorems stand on their own. The open items are procedural, and every one of them is straightforward to supply.</p>
<p><strong>Named readers and referees do different jobs.</strong> The paper thanks specialists who read individual chapters and describes them exactly that way, which is the right level of claim. Journal submission would add signed reports on top, and the Lean certificates mean a referee’s time can go to statements and significance rather than to line-by-line checking.</p>
<p><strong>The community already has a template for this.</strong> The Leiden Declaration, published in June 2026 by an international group of mathematicians and endorsed by the International Mathematical Union, raises exactly this: the reliability of AI-generated proofs, attribution when a proprietary model is involved, and the strain on peer review and publication norms.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> It recommends disclosure and preserved review rather than prohibition, and gathered over a thousand signatures within a day.</p>
<p><strong>The lineage is visible and cited.</strong> Each chapter builds openly on decades of named work: Delsarte’s programs, Kun’s expander decomposition, Yuen’s dependency-breaking framework, and Berman and Berndtsson’s transport argument. The citations make the intellectual debt easy to trace, which is what the Leiden Declaration asks for and a good basis for the attribution norms still being settled.</p>
<p><strong>The theorems are already reproducible.</strong> Lean sources are public, so anyone can confirm every result today. Releasing the prompts and search procedure would additionally make the discovery process reproducible, which is a separate and equally interesting claim to be able to make.</p>
<p>The mathematicians reacting publicly split along predictable lines. Terence Tao frames the shift as a transition toward large-scale collaborative work, with humans holding the creative direction and machines absorbing technical labor.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-2" id="user-content-fnref-2-5">2</a></sup> Others describe something closer to displacement; Kirwin Hampshire’s essay “The Dark Night of Mathematics” is the widely circulated version of that response.<sup><a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fn-2" id="user-content-fnref-2-6">2</a></sup> Both readings are compatible with the same ten theorems.</p>
<h2 id="what-generalizes">What generalizes</h2>
<p>Strip away the specific chapters and four things transfer.</p>
<p><strong>Verifiable domains are where autonomous search pays.</strong> A proof assistant is an oracle that returns a hard yes or no. Search against a hard oracle can run long without drifting, which is not true of search against a learned reward model. The economics reported here, modest per-problem spend against problems open for decades, hold only where such an oracle exists.</p>
<p><strong>The bottleneck moves to problem selection.</strong> Ten results across nine subfields means someone chose ten problems that were open, tractable, and cleanly statable. That selection is the scarce input, and it is human.</p>
<p><strong>The denominator is the metric to publish next.</strong> Attempts, successes, and cost per attempt across a preregistered problem list would turn ten curated successes into a measured research capability. Ten is already a solid lower bound.</p>
<p><strong>Formal verification and peer review compose well.</strong> They answer different questions, and having the mechanical one settled in advance lets review concentrate on significance and absorption into the field. That is the stage these ten results are entering now.</p>
<p>The summary is a strong one: ten specific, precisely stated, machine-checked improvements to problems that had resisted human effort for decades, published with the Lean sources that let anyone confirm them. The process infrastructure around results like these is being built now, and this release gives it something concrete to work with.</p>
<h2 id="the-rest-of-this-series">The rest of this series</h2>
<p>Four companion posts go deeper on individual threads from this release: the sphere-packing and coding-theory chapters and why an exponent that stood since 1978 moved; the non-sofic group construction and what it settles in group theory; the complexity-theory chapters on the permanent, parallel repetition, and lattice hardness; and the verification pipeline itself, covering Lean certificates, what they prove, and what peer review still has to do.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>OpenAI, “Ten Advances in Mathematics and Theoretical Computer Science,” 2026. <a href="https://cdn.openai.com/pdf/ten-proofs-oai.pdf" rel="noopener noreferrer">https://cdn.openai.com/pdf/ten-proofs-oai.pdf</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>Simon Willison, “Ten advances in mathematics and theoretical computer science,” simonwillison.net, 2026. <a href="https://simonwillison.net/2026/Aug/1/ten-advances-in-mathematics/" rel="noopener noreferrer">https://simonwillison.net/2026/Aug/1/ten-advances-in-mathematics/</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-2-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-2-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-2-5" class="data-footnote-backref">↩<sup>5</sup></a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-2-6" class="data-footnote-backref">↩<sup>6</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Julie Bort, “OpenAI claims it solved an 80-year-old math problem — for real this time,” TechCrunch, 2026. <a href="https://techcrunch.com/2026/05/20/openai-claims-it-solved-an-80-year-old-math-problem-for-real-this-time/" rel="noopener noreferrer">https://techcrunch.com/2026/05/20/openai-claims-it-solved-an-80-year-old-math-problem-for-real-this-time/</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>OpenAI, “An OpenAI model has disproved a central conjecture in discrete geometry,” 2026. <a href="https://openai.com/index/model-disproves-discrete-geometry-conjecture/" rel="noopener noreferrer">https://openai.com/index/model-disproves-discrete-geometry-conjecture/</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>“Leiden Declaration on Artificial Intelligence and Mathematics,” 2026. <a href="https://www.universiteitleiden.nl/en/news/2026/06/leiden-declaration-warns-ai-is-challenging-the-core-values-of-mathematics" rel="noopener noreferrer">https://www.universiteitleiden.nl/en/news/2026/06/leiden-declaration-warns-ai-is-challenging-the-core-values-of-mathematics</a> <a href="https://blog.ecitis.org/openai-ten-advances-overview/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Theory &amp; Mathematics</category>
            <category>Mathematics</category>
            <category>Reasoning</category>
            <category>Evaluation</category>
            <category>Formal verification</category>
            <enclosure length="72346" type="image/png" url="https://blog.ecitis.org/open-graph/openai-ten-advances-overview.png"/>
        </item>
        <item>
            <title><![CDATA[Tokenizers and the Vocabulary Budget]]></title>
            <link>https://blog.ecitis.org/tokenizer-design/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/tokenizer-design/</guid>
            <pubDate>Sat, 01 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Treat vocabulary size as a compute allocation: fertility, embedding cost, multilingual fairness, and the failure modes tokenizers cause.]]></description>
            <content:encoded><![CDATA[<p>The tokenizer is chosen early and cannot be changed afterwards without retraining, which makes it one of the highest-leverage decisions available at the start of a project. It sets the sequence length for every document the model will ever see, the size of two of its largest matrices, and how much users writing in any given language pay per request.</p>
<p>It also offers unusually tractable wins. Splitting numbers consistently improves arithmetic, dedicated indentation tokens cut Python sequence length, and upsampling low-resource languages when fitting the vocabulary lowers their fertility directly. Each is a decision, not a research problem.</p>
<figure><figcaption><strong>Vocabulary size trades embedding memory against sequence length</strong></figcaption><p><a href="https://blog.ecitis.org/tokenizer-design/">View this figure on the site &#8594;</a></p></figure>
<h2 id="fertility">Fertility</h2>
<p>The quantity that matters is fertility: tokens produced per unit of text. Lower fertility means shorter sequences, which reduces cost at every layer.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>F</mi><mo>=</mo><mfrac><mtext>tokens</mtext><mtext>words</mtext></mfrac></mrow></semantics></math>
<p>For English, a well-fitted 32k BPE vocabulary lands around 1.3. For languages written in scripts underrepresented in the training corpus, the same tokenizer can produce 3 to 5. This is not a small inefficiency: the same semantic content costs several times more, consumes several times more of the context window, and is billed several times more.</p>
<p>Fertility is also compute. Attention is quadratic in sequence length during prefill and linear per token during decode. A fertility ratio of 3 between two languages means a factor of 3 in decode cost and up to 9 in prefill attention for the same content.</p>
<h2 id="the-vocabulary-tradeoff">The vocabulary tradeoff</h2>
<p>Larger vocabularies reduce fertility. They also enlarge two matrices:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>P</mi><mtext>vocab</mtext></msub><mo>=</mo><mi>V</mi><mo>⋅</mo><msub><mi>d</mi><mtext>model</mtext></msub><mo>×</mo><mo stretchy="false">(</mo><mn>1</mn><mo>+</mo><mn mathvariant="double-struck">1</mn><mo stretchy="false">[</mo><mtext>untied</mtext><mo stretchy="false">]</mo><mo stretchy="false">)</mo></mrow></semantics></math>
<p>At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi><mo>=</mo><mn>128</mn><mtext>k</mtext></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mtext>model</mtext></msub><mo>=</mo><mn>8192</mn></mrow></semantics></math> with untied embeddings, that is 2.1B parameters, a meaningful fraction of a mid-sized model, spent entirely on the interface between text and hidden states.</p>
<p>The softmax cost is the other side. Every generated token requires a projection to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math> logits, which at large <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math> becomes a nontrivial share of per-token work, and the logit tensor itself becomes a real memory allocation in a large batch.</p>
<p>The tradeoff:</p>
<ul>
<li>Sequence length savings apply at <strong>every layer, every token</strong> and compound with context length.</li>
<li>Embedding cost is paid <strong>once in parameters</strong> and <strong>once per token</strong> at the output projection.</li>
</ul>
<p>Doubling the vocabulary yields diminishing fertility improvements, because Zipf’s law means each doubling covers progressively rarer strings, while parameter cost grows linearly. The optimum has moved upward over time as models got wider and multilingual coverage became a priority: 32k was standard, 128k is now common, and 256k appears in the most multilingual designs.</p>
<p>Empirical work on vocabulary scaling finds that optimal vocabulary size grows with model size, and that many models are under-vocabularized relative to their compute budget.<sup><a href="https://blog.ecitis.org/tokenizer-design/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> The reasoning is the same as any scaling-law allocation: the embedding parameters compete with depth and width for a fixed budget, and the balance shifts as the budget grows.</p>
<h2 id="algorithms">Algorithms</h2>
<p><strong>BPE</strong> starts from bytes and iteratively merges the most frequent adjacent pair, recording the merge sequence. Encoding replays the merges in order. Deterministic, fast, and the dominant choice.<sup><a href="https://blog.ecitis.org/tokenizer-design/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p><strong>WordPiece</strong> is similar but selects merges by likelihood improvement rather than raw frequency.</p>
<p><strong>Unigram</strong> starts from a large candidate set and prunes to maximize corpus likelihood under a unigram model, which yields a probabilistic segmentation and enables subword regularization, meaning different segmentations are sampled during training as an augmentation.</p>
<p><strong>Byte-level fallback</strong> guarantees no out-of-vocabulary input by allowing any byte as a token. Every modern tokenizer includes this. The safety is real; the cost is that rare scripts degrade to near-byte-level fertility.</p>
<p>The pre-tokenization regex matters as much as the algorithm. It decides where merges may not cross, typically at whitespace and punctuation boundaries, with special handling for digits and contractions. Two tokenizers with the same algorithm and vocabulary size but different pre-tokenization patterns produce meaningfully different vocabularies.</p>
<h2 id="tokenizer-fixes-for-apparent-model-limitations">Tokenizer fixes for apparent model limitations</h2>
<p><strong>Arithmetic.</strong> If numbers tokenize inconsistently, with <code>1234</code> as one token and <code>1235</code> as two, the model must learn arithmetic over a representation where digit position is not consistently exposed. Splitting numbers into individual digits, or into fixed groups of three, measurably improves arithmetic. This is a pure tokenizer fix for what is invariably reported as a reasoning limitation.</p>
<p><strong>Character-level tasks.</strong> Counting letters in a word is hard when the word is one token. The model has no direct access to the characters; it must have learned the spelling as a property of the token embedding. The well-known counting failures are tokenization artifacts, not reasoning failures.</p>
<p><strong>Code indentation.</strong> Python tokenizers that do not have dedicated multi-space tokens spend many tokens on leading whitespace. Adding explicit indentation tokens is a common and effective optimization.</p>
<p><strong>Glitch tokens.</strong> Strings present in the tokenizer’s training corpus but absent or near-absent from the model’s, leaving their embeddings essentially untrained. Prompting with them produces erratic behavior. These arise when the tokenizer is trained on a different corpus than the model, which is easy to do accidentally.</p>
<p><strong>Trailing whitespace.</strong> A prompt ending in a space often tokenizes differently than the model expects, because the merge rules attach leading spaces to following words. The result is a token sequence off the training distribution, and noticeably worse completions. Most APIs now strip trailing whitespace for this reason.</p>
<h2 id="multilingual-fairness">Multilingual fairness</h2>
<p>Vocabulary allocation follows the tokenizer’s training corpus. A corpus that is 90% English produces a vocabulary where most entries are English subwords, and every other language falls back toward bytes.</p>
<p>The consequences are concrete and asymmetric:</p>
<ul>
<li><strong>Cost.</strong> Per-token billing means users writing in high-fertility languages pay several times more for equivalent content.</li>
<li><strong>Context.</strong> A fixed context window holds several times less text.</li>
<li><strong>Quality.</strong> Longer sequences for the same content mean more positions to model and, empirically, worse performance.</li>
</ul>
<p>Mitigation is a corpus-sampling decision at tokenizer training time. Upsampling low-resource languages when fitting the tokenizer, independently of the model’s training mix, raises their vocabulary allocation and lowers their fertility, at some cost to English fertility. Given how much larger the effect is for the disadvantaged languages, the trade is usually clearly worth making.</p>
<p>Measure it directly:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> fertility_report</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> corpora</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    rows </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> lang</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> text </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> corpora</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">items</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        n_tok </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(tokenizer.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">(text))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        n_word </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(text.</span><span style="color:var(--astro-code-token-function)">split</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        rows</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">((lang, n_tok </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> n_word, n_tok </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(text)))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    base </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> dict</span><span style="color:var(--astro-code-token-punctuation)">((l, f) </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> l, f, _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> rows)</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;en&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> lang</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tpc </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> sorted</span><span style="color:var(--astro-code-token-punctuation)">(rows, key</span><span style="color:var(--astro-code-token-keyword)">=lambda</span><span style="color:var(--astro-code-token-parameter)"> r</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-punctuation)">r[</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">]):</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">lang</span><span style="color:var(--astro-code-token-keyword)">:&lt;8</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-punctuation)">f</span><span style="color:var(--astro-code-token-keyword)">:5.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> tok/word  </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">tpc</span><span style="color:var(--astro-code-token-keyword)">:5.3f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> tok/char  </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">f </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> base</span><span style="color:var(--astro-code-token-keyword)">:4.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x en&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> rows</span></span></code></pre>
<p>Run this against a parallel corpus, so the comparison is over identical content. The ratio in the last column is the multiplier those users pay.</p>
<h2 id="extending-a-vocabulary">Extending a vocabulary</h2>
<p>Adapting a model to a new domain or language by adding tokens is possible but has a specific pitfall.</p>
<p>New embedding rows are randomly initialized while every existing row is trained. The mismatch in magnitude and direction destabilizes training and can degrade the base model’s existing capability. The standard fix is to initialize each new token’s embedding as the mean of its constituent old tokens’ embeddings, which places it in an already-sensible region of the space:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> init_new_token</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">new_str</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> old_tok</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> old_emb</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    pieces </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> old_tok</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">(new_str, add_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> old_emb</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">pieces</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">mean</span><span style="color:var(--astro-code-token-punctuation)">(dim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Then train, typically with a lower learning rate on the embedding layer than on the rest. Even so, expect some regression on the original distribution. Extension is not free, and for a large change a fresh tokenizer plus full training is often cleaner.</p>
<h2 id="choosing">Choosing</h2>
<p>The decision sequence:</p>
<ol>
<li><strong>Fit the vocabulary to the actual deployment mix</strong>, not to a general web corpus, and upsample underrepresented languages when fitting.</li>
<li><strong>Size it against model width.</strong> Larger <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mtext>model</mtext></msub></mrow></semantics></math> supports a larger vocabulary before embedding cost dominates. Frontier-scale models sit at 128k–256k.</li>
<li><strong>Handle numbers explicitly.</strong> Digit-level or fixed-group splitting.</li>
<li><strong>Reserve special tokens generously.</strong> Adding them later means resizing the embedding, and running out mid-project forces a resize that invalidates every existing checkpoint.</li>
<li><strong>Decide tying deliberately.</strong> Tied input and output embeddings halve the vocabulary parameter cost with modest quality effect at large scale; untied is standard at frontier scale where the parameters are affordable.</li>
<li><strong>Train tokenizer and model on the same corpus</strong> to avoid glitch tokens.</li>
<li><strong>Measure fertility across the deployment mix before committing.</strong> This is the last cheap moment to change it.</li>
</ol>
<p>The tokenizer is the only architectural component that cannot be changed after training without starting over, which is precisely why an afternoon spent on it pays back across the model’s entire life. Give it the same scrutiny as depth and width and the wins are available before a single training step runs.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Chaofan Tao et al., “Scaling Laws with Vocabulary: Larger Models Deserve Larger Vocabularies,” NeurIPS, 2024. <a href="https://arxiv.org/abs/2407.13623" rel="noopener noreferrer">https://arxiv.org/abs/2407.13623</a> <a href="https://blog.ecitis.org/tokenizer-design/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Rico Sennrich, Barry Haddow, and Alexandra Birch, “Neural Machine Translation of Rare Words with Subword Units,” ACL, 2016. <a href="https://arxiv.org/abs/1508.07909" rel="noopener noreferrer">https://arxiv.org/abs/1508.07909</a> <a href="https://blog.ecitis.org/tokenizer-design/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Tokenization</category>
            <category>Architecture</category>
            <category>Training</category>
            <enclosure length="62920" type="image/png" url="https://blog.ecitis.org/open-graph/tokenizer-design.png"/>
        </item>
        <item>
            <title><![CDATA[Designing Models for Low Precision: Outliers, Scales, and Native FP8]]></title>
            <link>https://blog.ecitis.org/quantization-aware-architecture/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/quantization-aware-architecture/</guid>
            <pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Look at quantization as an architecture constraint: activation outliers, block scaling, format range, and which layers resist low precision.]]></description>
            <content:encoded><![CDATA[<p>Quantization is often applied after the fact: train in BF16, compress, measure what changed. Treating numeric format as an architectural property instead opens up considerably more. Design decisions made long before any calibration set determine how well a model quantizes, and the techniques that exploit this are mature.</p>
<p>SmoothQuant made W8A8 practical by migrating quantization difficulty from activations into weights, at no run-time cost once the rescaling is folded into the preceding layer norm. DeepSeek-V3 trained at frontier scale directly in FP8. Low precision is now something to design for rather than something to survive.</p>
<figure><figcaption><strong>Numeric formats differ in dynamic range, not just in bit count</strong></figcaption><p><a href="https://blog.ecitis.org/quantization-aware-architecture/">View this figure on the site &#8594;</a></p></figure>
<h2 id="range-versus-resolution">Range versus resolution</h2>
<p>A format allocates bits between exponent and mantissa, and that split is more consequential than the total.</p>





































































<div class="table-wrap"><table><thead><tr><th>Format</th><th>Bits</th><th>Exponent</th><th>Mantissa</th><th>Approx. max</th><th>Relative precision</th></tr></thead><tbody><tr><td>FP32</td><td>32</td><td>8</td><td>23</td><td>3.4e38</td><td>~1e-7</td></tr><tr><td>BF16</td><td>16</td><td>8</td><td>7</td><td>3.4e38</td><td>~8e-3</td></tr><tr><td>FP16</td><td>16</td><td>5</td><td>10</td><td>65504</td><td>~1e-3</td></tr><tr><td>FP8 E4M3</td><td>8</td><td>4</td><td>3</td><td>448</td><td>~6e-2</td></tr><tr><td>FP8 E5M2</td><td>8</td><td>5</td><td>2</td><td>57344</td><td>~1e-1</td></tr><tr><td>INT8</td><td>8</td><td>none</td><td>7 + sign</td><td>scale-dependent</td><td>uniform</td></tr><tr><td>INT4</td><td>4</td><td>none</td><td>3 + sign</td><td>scale-dependent</td><td>uniform</td></tr></tbody></table></div>
<p>BF16 displaced FP16 for training because it shares FP32’s exponent range. FP16’s 65504 ceiling requires loss scaling to keep gradients from underflowing and overflowing; BF16 does not, at the cost of mantissa bits that turn out to matter less.</p>
<p>The integer formats have no exponent at all. Their entire dynamic range comes from an external scale:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>w</mi><mo>^</mo></mover><mo>=</mo><mi>s</mi><mo>⋅</mo><mo stretchy="false">(</mo><mi>q</mi><mo>−</mo><mi>z</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><mi>q</mi><mo>∈</mo><mo stretchy="false">[</mo><mo>−</mo><msup><mn>2</mn><mrow><mi>b</mi><mo>−</mo><mn>1</mn></mrow></msup><mo separator="true">,</mo><msup><mn>2</mn><mrow><mi>b</mi><mo>−</mo><mn>1</mn></mrow></msup><mo>−</mo><mn>1</mn><mo stretchy="false">]</mo></mrow></semantics></math>
<p>With one scale per tensor, a single large value forces <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>s</mi></mrow></semantics></math> upward and quantizes every other value into a handful of levels. This is the mechanism behind essentially every quantization failure.</p>
<h2 id="outliers">Outliers</h2>
<p>Transformer activations carry systematic outlier features past roughly 6B parameters: a small number of hidden dimensions hold values 10 to 100 times larger than the rest, consistently across tokens and layers. That consistency is what makes them tractable.<sup><a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p>Consistency is the key property. These are not random spikes; they occupy specific channels, and those channels do disproportionate work: zeroing them collapses model quality, so they cannot simply be clipped.</p>
<p>The responses fall into three families:</p>
<p><strong>Separate the outliers.</strong> LLM.int8() decomposes the matrix multiply, keeping outlier dimensions in FP16 and the rest in INT8. Correct, but the mixed-precision path costs throughput.</p>
<p><strong>Migrate the difficulty.</strong> SmoothQuant observes that activation outliers are hard to quantize while weights are easy, and rescales to move some of the burden across the multiplication.<sup><a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> For <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Y</mi><mo>=</mo><mi>X</mi><mi>W</mi></mrow></semantics></math>, insert a diagonal <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>Y</mi><mo>=</mo><mo stretchy="false">(</mo><mi>X</mi><msup><mi>S</mi><mrow><mo>−</mo><mn>1</mn></mrow></msup><mo stretchy="false">)</mo><mo stretchy="false">(</mo><mi>S</mi><mi>W</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Mathematically identical, numerically much better behaved, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math> can be folded into the preceding layer norm so it costs nothing at run time. This is the technique that made W8A8 practical.</p>
<p><strong>Shrink the scale’s scope.</strong> Per-channel scales for weights, per-token for activations, or group-wise scales within a channel. Finer granularity means an outlier corrupts fewer neighbors. The cost is scale storage and more complex kernels.</p>
<h2 id="block-scaling-and-what-4-bit-really-costs">Block scaling and what “4-bit” really costs</h2>
<p>Group-wise scaling is where the bit count stops being the footprint. With group size <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi></mrow></semantics></math> and one FP16 scale plus one zero point per group, effective bits per weight are:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>b</mi><mtext>eff</mtext></msub><mo>=</mo><mi>b</mi><mo>+</mo><mfrac><mn>32</mn><mi>g</mi></mfrac></mrow></semantics></math>
<p>At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo>=</mo><mn>4</mn></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mo>=</mo><mn>128</mn></mrow></semantics></math>, that is 4.25 bits. At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>g</mi><mo>=</mo><mn>32</mn></mrow></semantics></math>, 5 bits, a 25% overhead. That is the range where “4-bit” and “5-bit” quantizations stop being clearly ordered.</p>
<p>Microscaling formats standardize this. MXFP4 pairs 32 FP4 values with a shared 8-bit exponent, giving 4.25 effective bits with hardware support for the block structure.<sup><a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> The shared exponent restores dynamic range that flat INT4 cannot express, which is why MXFP4 outperforms INT4 at nominally the same width. NVFP4 uses a smaller block with an FP8 scale, trading more metadata for better accuracy.</p>
<p>The general point: at 4 bits and below, the metadata is a first-class part of the format. Comparing quantization schemes by nominal bit width is comparing the wrong number.</p>
<h2 id="assigning-precision-by-layer-sensitivity">Assigning precision by layer sensitivity</h2>
<p>Sensitivity varies by layer, and the pattern is stable enough across models to design around directly.</p>
<p><strong>Embeddings and the output head.</strong> Large, and the head directly produces logits. Quantization error here maps to distribution error with no intervening layers to absorb it. Usually kept at higher precision; when weights are tied, quantizing one quantizes both.</p>
<p><strong>Attention output projections.</strong> Consistently among the most sensitive linear layers.</p>
<p><strong>Feed-forward up- and down-projections.</strong> The bulk of the parameters and the most tolerant. This is where aggressive quantization pays.</p>
<p><strong>Layer norm parameters.</strong> Tiny, and they set the scale for everything downstream. Never quantize.</p>
<p><strong>Router weights in an MoE.</strong> Small and decisive: a router error changes which experts run, which is a discrete failure rather than a small numerical one. Keep at full precision.</p>
<p><strong>First and last transformer blocks.</strong> More sensitive than the middle in most measurements.</p>
<p>A mixed-precision assignment following this pattern typically beats a uniform assignment at the same average bit width, which is why GGUF supports per-tensor quantization types and why formats that fix one dtype per file leave quality on the table.</p>
<h2 id="training-in-low-precision">Training in low precision</h2>
<p>Post-training quantization accepts a model’s numerics as given. Training in the target format changes the model’s own distributions to suit it.</p>
<p>FP8 training is now routine on hardware with native support. The standard configuration uses E4M3 for forward tensors, where precision matters more than range, and E5M2 for gradients, where range matters more. Master weights and optimizer state stay in higher precision; accumulation happens in FP32.</p>
<p>DeepSeek-V3 demonstrated FP8 mixed-precision training at frontier scale, with fine-grained tile- and block-wise scaling and periodic high-precision accumulation to bound error growth.<sup><a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> The reported result, a frontier-quality model trained more cheaply, is why native low-precision training became a design target rather than an optimization.</p>
<p>Quantization-aware training goes further, simulating quantization in the forward pass so gradients account for it. The rounding operation has zero gradient almost everywhere, so QAT uses a straight-through estimator:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi mathvariant="normal">∂</mi><mover accent="true"><mi>w</mi><mo>^</mo></mover></mrow><mrow><mi mathvariant="normal">∂</mi><mi>w</mi></mrow></mfrac><mo>≈</mo><mn mathvariant="double-struck">1</mn><mo stretchy="false">[</mo><mi mathvariant="normal">∣</mi><mi>w</mi><mi mathvariant="normal">∣</mi><mo>≤</mo><mtext>clip range</mtext><mo stretchy="false">]</mo></mrow></semantics></math>
<p>QAT reliably recovers accuracy that PTQ loses at 4 bits and below. It costs a training run, which is why it is used for models that will be deployed at scale for a long time and skipped otherwise.</p>
<h2 id="architectural-choices-that-help">Architectural choices that help</h2>
<p>Several decisions made for other reasons turn out to affect quantizability, and can be made deliberately:</p>
<p><strong>Normalization placement.</strong> Pre-norm architectures keep residual-stream magnitudes bounded; post-norm allows growth across depth. Bounded activations quantize better.</p>
<p><strong>QK normalization.</strong> Normalizing queries and keys before the attention product bounds logit magnitude, which both stabilizes training and removes a source of activation outliers.</p>
<p><strong>Activation function.</strong> Gated variants like SwiGLU produce different outlier structure than plain GELU, since the product of two projections can amplify. Worth measuring rather than assuming.</p>
<p><strong>Residual scaling.</strong> Scaling residual contributions by depth keeps the stream from growing, which directly bounds the range activations must cover.</p>
<p><strong>Head dimension.</strong> Larger heads mean more values sharing a scale under per-head quantization, so more opportunity for one outlier to dominate.</p>
<p>None of these is free, and none should be chosen for quantizability alone. But when two options are otherwise comparable, the one with bounded activation range is the one that will still work at 4 bits.</p>
<h2 id="evaluating">Evaluating</h2>
<p>Perplexity is a poor quantization metric. It is dominated by locally predictable tokens and moves very little while specific capabilities degrade badly. A quantization that raises perplexity by 0.1 can lose a large fraction of a model’s code-generation accuracy.</p>
<p>What to measure:</p>
<ol>
<li><strong>Task accuracy on the deployed workload</strong>, not a general benchmark suite.</li>
<li><strong>Long-context retrieval</strong>, where accumulated error compounds over more positions.</li>
<li><strong>Structured output validity.</strong> JSON and code are unusually sensitive, because a single wrong token invalidates the whole output.</li>
<li><strong>Distribution shape, not just argmax.</strong> KL divergence against the reference model on a held-out set catches tail degradation that greedy agreement misses entirely. Under sampling, the tail is what the user sees.</li>
<li><strong>Behavior under sampling at temperature</strong>, for the same reason.</li>
<li><strong>Actual throughput.</strong> A format the kernels do not support natively will be dequantized to something they do, and the memory saving will not become a speed gain.</li>
</ol>
<p>That last point is worth settling early, because it decides which win you collect. A 4-bit model on a dequantize-then-BF16-GEMM path halves memory and leaves latency unchanged, which is exactly what you want when concurrency is the constraint. Pair the format with kernels that consume it natively and the same weights deliver the latency gain as well.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Tim Dettmers et al., “LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale,” NeurIPS, 2022. <a href="https://arxiv.org/abs/2208.07339" rel="noopener noreferrer">https://arxiv.org/abs/2208.07339</a> <a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Guangxuan Xiao et al., “SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models,” ICML, 2023. <a href="https://arxiv.org/abs/2211.10438" rel="noopener noreferrer">https://arxiv.org/abs/2211.10438</a> <a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Bita Darvish Rouhani et al., “Microscaling Data Formats for Deep Learning,” arXiv, 2023. <a href="https://arxiv.org/abs/2310.10537" rel="noopener noreferrer">https://arxiv.org/abs/2310.10537</a> <a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>DeepSeek-AI, “DeepSeek-V3 Technical Report,” arXiv, 2024. <a href="https://arxiv.org/abs/2412.19437" rel="noopener noreferrer">https://arxiv.org/abs/2412.19437</a> <a href="https://blog.ecitis.org/quantization-aware-architecture/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Quantization</category>
            <category>Architecture</category>
            <category>Training</category>
            <enclosure length="78945" type="image/png" url="https://blog.ecitis.org/open-graph/quantization-aware-architecture.png"/>
        </item>
        <item>
            <title><![CDATA[Hybrid Architectures: State Space Models Where Attention Is Too Expensive]]></title>
            <link>https://blog.ecitis.org/hybrid-ssm-attention/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/hybrid-ssm-attention/</guid>
            <pubDate>Sun, 26 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Understand selective state space models, what fixed-size recurrent state loses, and why production designs interleave a few attention layers.]]></description>
            <content:encoded><![CDATA[<p>Attention’s cost profile is well understood: quadratic in sequence length during prefill, and linear in context during decode because every past key and value must be read. A recurrent layer with fixed-size state has neither property, and Mamba reports roughly 5x higher decode throughput than a comparable Transformer as a result.</p>
<p>What followed is one of the more satisfying results in recent architecture work. Rather than one family replacing the other, hybrids keep a small proportion of attention layers and get both properties at once: exact recall where it is needed, and decode cost that stops growing with context.</p>
<figure><figcaption><strong>Sequence mixers trade recall against constant-memory decoding</strong></figcaption><p><a href="https://blog.ecitis.org/hybrid-ssm-attention/">View this figure on the site &#8594;</a></p></figure>
<h2 id="linear-state-space-layers">Linear state space layers</h2>
<p>A continuous state space model maps a signal through a latent state:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>h</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>=</mo><mi>A</mi><mi>h</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>+</mo><mi>B</mi><mi>x</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><mi>y</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo><mo>=</mo><mi>C</mi><mi>h</mi><mo stretchy="false">(</mo><mi>t</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Discretized with step size <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi></mrow></semantics></math>, this becomes a linear recurrence:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>h</mi><mi>t</mi></msub><mo>=</mo><mover accent="true"><mi>A</mi><mo>ˉ</mo></mover><msub><mi>h</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mover accent="true"><mi>B</mi><mo>ˉ</mo></mover><msub><mi>x</mi><mi>t</mi></msub><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>y</mi><mi>t</mi></msub><mo>=</mo><mi>C</mi><msub><mi>h</mi><mi>t</mi></msub></mrow></semantics></math>
<p>Two evaluation modes fall out of linearity. Sequentially, each step is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math> in context. Unrolled, the output is a convolution with a kernel derivable in closed form, computable for the whole sequence in <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>S</mi><mi>log</mi><mo>⁡</mo><mi>S</mi><mo stretchy="false">)</mo></mrow></semantics></math> via FFT. Training uses the parallel form; inference uses the recurrent form. That duality is the structural advantage.</p>
<p>S4 made this work at depth by constraining <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow></semantics></math> to a HiPPO-derived structure that provably retains long-range information rather than decaying it.<sup><a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> The initialization is not incidental: a randomly initialized linear recurrence forgets, and the whole approach depends on it not doing so.</p>
<p>But a fixed <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>A</mi><mo>ˉ</mo></mover></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>B</mi><mo>ˉ</mo></mover></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi></mrow></semantics></math> means the layer applies the same transformation regardless of input. It cannot decide to remember this token and ignore that one. For language, where the relevant history is entirely content-dependent, this is disqualifying.</p>
<h2 id="selectivity">Selectivity</h2>
<p>Mamba’s contribution is making the parameters functions of the input.<sup><a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi></mrow></semantics></math> are produced by linear projections of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>t</mi></msub></mrow></semantics></math>, so the recurrence gates on content: the model can decide, per token, how much to write into state and how much to let through.</p>
<p>This breaks the convolution, since a time-varying recurrence has no fixed kernel, so the FFT path is gone. Mamba recovers parallel training with a hardware-aware selective scan: a parallel-prefix scan whose state lives in on-chip SRAM, with recomputation in the backward pass rather than materializing the full state sequence in HBM. The layer is memory-bandwidth efficient in the same way FlashAttention is, and for the same reason.</p>
<p>The reported results are what made this a live option: Transformer-quality language modeling at matched parameter counts, linear scaling in sequence length, and roughly 5x higher decode throughput than a comparable Transformer because there is no cache to read.</p>
<p>Mamba-2 then reframed the selective SSM and attention as instances of a common structured-matrix formulation, which allowed the scan to be expressed in terms of matrix multiplications and thus to use tensor cores.<sup><a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> That made the layer substantially faster in practice and clarified that these are not opposing families so much as different points in one design space.</p>
<h2 id="where-attention-earns-its-cost">Where attention earns its cost</h2>
<p>The division of labour here is clean and information-theoretic rather than a training artifact. A state of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mtext>state</mtext></msub></mrow></semantics></math> values compresses the entire history, which is what makes it cheap. Attention keeps every token addressable, which is what makes it worth including.</p>
<p>The tasks that separate them are exactly the ones requiring exact recall of arbitrary content:</p>
<p><strong>Copying and retrieval.</strong> Reproduce a specific span from far back verbatim. A fixed state must have decided to keep it, without knowing at the time that it would be asked for.</p>
<p><strong>Associative recall.</strong> Given key-value pairs seen earlier, answer a query about one of them. Capacity is bounded by state size; attention’s is bounded by context.</p>
<p><strong>In-context learning from many examples.</strong> Few-shot performance depends on retaining specific demonstrations, not a summary of them.</p>
<p>Attention is uniquely suited here: the query at retrieval time constructs the lookup. A recurrent layer must commit at write time.</p>
<p>Empirically, pure SSMs match or beat Transformers on perplexity and general benchmarks while lagging noticeably on recall-heavy evaluations. Perplexity is dominated by locally predictable tokens and does not surface the gap, which is why architecture selection on perplexity alone is misleading here.</p>
<h2 id="the-hybrid-resolution">The hybrid resolution</h2>
<p>If the deficit is specifically exact recall, a small number of attention layers should be enough to cover it. This is what production hybrids do.</p>
<p>Jamba interleaves Transformer and Mamba layers at roughly a 1</p><div></div> ratio, adds mixture-of-experts, and fits a 256k context on a single 80 GiB GPU, with a KV cache reported at roughly an eighth of a comparable pure Transformer’s, because only the attention layers cache anything.<sup><a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> NVIDIA’s analysis of hybrid designs reached a consistent conclusion: a small proportion of attention layers recovers most of the recall gap, with diminishing returns beyond it.<p></p>
<p>Cache arithmetic for a hybrid with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> layers of which <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mi>a</mi></msub></mrow></semantics></math> are attention:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>m</mi><mo>=</mo><mn>2</mn><msub><mi>L</mi><mi>a</mi></msub><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><msub><mi>d</mi><mi>h</mi></msub><mi>b</mi><mo>+</mo><mo stretchy="false">(</mo><mi>L</mi><mo>−</mo><msub><mi>L</mi><mi>a</mi></msub><mo stretchy="false">)</mo><mo>⋅</mo><msub><mi>s</mi><mtext>ssm</mtext></msub></mrow></semantics></math>
<p>The second term is constant in context length. With <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mi>a</mi></msub><mi mathvariant="normal">/</mi><mi>L</mi><mo>=</mo><mn>1</mn><mi mathvariant="normal">/</mi><mn>8</mn></mrow></semantics></math> and GQA on the attention layers, cache per token is under 2% of a full-MHA Transformer’s, and the part that grows with context is one eighth as large.</p>
<p>Design questions that follow, none with fully settled answers:</p>
<p><strong>How many attention layers?</strong> Between one in four and one in eight in published designs. Below one in eight, recall degrades sharply.</p>
<p><strong>Where?</strong> Not in the first layers, which do local processing that recurrence handles well. Spreading them through the middle and later stack is the common choice.</p>
<p><strong>Sliding window or full?</strong> Some designs use windowed attention on the attention layers, bounding cache further and relying on the recurrent layers for long-range information. This is a smaller version of the same tradeoff, one level down.</p>
<h2 id="serving-consequences">Serving consequences</h2>
<p>Hybrids are not a drop-in change to an inference stack.</p>
<p><strong>State is not a KV cache.</strong> Each sequence carries a fixed-size recurrent state per SSM layer. It does not grow, does not fragment, and does not page. Existing block allocators do not manage it, so the memory manager needs a second, simpler allocator alongside.</p>
<p><strong>Prefill and decode use different kernels.</strong> The scan runs in parallel-associative form during prefill and sequential form during decode. Two code paths must agree numerically. Where they disagree, a model produces different output for the same prompt depending on whether it was prefilled or generated incrementally. This is a real and subtle class of bug.</p>
<p><strong>Preemption changes character.</strong> Recomputing a preempted sequence’s state means replaying the scan. Swapping means transferring a small fixed buffer. Because the state is small, swap is nearly always the right choice, inverting the usual guidance.</p>
<p><strong>Speculative decoding needs care.</strong> Rejecting a draft token requires rolling the recurrent state back. Since the state is overwritten each step rather than appended to, rollback means either snapshotting per step or recomputing from the last accepted position. Neither is free, and the cost partly offsets speculation’s benefit.</p>
<p><strong>Constant per-token cost changes the batching curve.</strong> Without a growing KV read, decode step time is far less sensitive to context length. Long-context serving economics look different: the usual sharp cost increase with context largely disappears for the recurrent layers.</p>
<h2 id="evaluating-an-architecture-choice">Evaluating an architecture choice</h2>
<p>Perplexity will not tell you what you need. The protocol that will:</p>
<ol>
<li><strong>Needle-in-haystack at target context</strong>, needle placed across all depths, reporting the worst depth. This is the primary discriminator.</li>
<li><strong>Multi-needle and multi-hop retrieval</strong>, which stresses state capacity rather than state persistence.</li>
<li><strong>Long-input summarization</strong>, where the failure mode is dropping specific facts rather than degraded fluency.</li>
<li><strong>Many-shot in-context learning</strong>, measuring whether accuracy still improves as demonstrations are added. A pure SSM often plateaus early.</li>
<li><strong>Decode throughput versus context length.</strong> The whole point is that this curve is flat; verify it on the actual implementation rather than assuming it.</li>
</ol>
<p>Hybrids are the strong answer available today: keep enough attention to preserve exact recall, replace the rest with layers whose decode cost does not grow. Jamba fits a 256k context on a single 80 GiB GPU on exactly this basis, with a KV cache reported at roughly an eighth of a comparable pure Transformer’s. Long-context serving economics improve substantially as a result.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Albert Gu, Karan Goel, and Christopher Ré, “Efficiently Modeling Long Sequences with Structured State Spaces,” ICLR, 2022. <a href="https://arxiv.org/abs/2111.00396" rel="noopener noreferrer">https://arxiv.org/abs/2111.00396</a> <a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Albert Gu and Tri Dao, “Mamba: Linear-Time Sequence Modeling with Selective State Spaces,” COLM, 2024. <a href="https://arxiv.org/abs/2312.00752" rel="noopener noreferrer">https://arxiv.org/abs/2312.00752</a> <a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Tri Dao and Albert Gu, “Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality,” ICML, 2024. <a href="https://arxiv.org/abs/2405.21060" rel="noopener noreferrer">https://arxiv.org/abs/2405.21060</a> <a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Opher Lieber et al., “Jamba: A Hybrid Transformer-Mamba Language Model,” arXiv, 2024. <a href="https://arxiv.org/abs/2403.19887" rel="noopener noreferrer">https://arxiv.org/abs/2403.19887</a> <a href="https://blog.ecitis.org/hybrid-ssm-attention/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>State space models</category>
            <category>Architecture</category>
            <category>Attention</category>
            <enclosure length="84692" type="image/png" url="https://blog.ecitis.org/open-graph/hybrid-ssm-attention.png"/>
        </item>
        <item>
            <title><![CDATA[MHA, MQA, GQA, MLA: Attention as a Memory Budget Decision]]></title>
            <link>https://blog.ecitis.org/attention-memory-budget/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/attention-memory-budget/</guid>
            <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Derive KV cache cost per token across attention variants and see why key-value head count is chosen before quality is measured.]]></description>
            <content:encoded><![CDATA[<p>Attention variants are usually presented as a quality question: how much accuracy does sharing key-value heads cost? The more productive reading is that they are a capacity lever with a closed-form price. A serving target implies a KV cache budget per token, that budget implies a key-value head count, and the arithmetic is simple enough to run in a few lines of code.</p>
<p>The returns are large. At eight groups, a 70B-class model carries one eighth the cache per token, which multiplies the concurrency a single device can hold, and the GQA paper reports recovering near-MHA quality by uptraining an existing checkpoint on roughly 5% of the original compute.</p>
<figure><figcaption><strong>Attention variants are a KV-cache budget decision before they are a quality decision</strong></figcaption><p><a href="https://blog.ecitis.org/attention-memory-budget/">View this figure on the site &#8594;</a></p></figure>
<h2 id="the-equation-everything-follows-from">The equation everything follows from</h2>
<p>For a decoder with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> layers, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow></semantics></math> key-value heads, head dimension <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>h</mi></msub></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow></semantics></math> bytes per scalar, cached bytes per token are:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>m</mi><mo>=</mo><mn>2</mn><mi>L</mi><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><msub><mi>d</mi><mi>h</mi></msub><mi>b</mi></mrow></semantics></math>
<p>The 2 is keys and values. Note what is absent: the number of <em>query</em> heads. Query projections are computed and consumed within a step and never cached. This asymmetry is the entire design space.</p>
<p>For a 70B-class model with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><mo>=</mo><mn>80</mn></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>q</mi></msub><mo>=</mo><mn>64</mn></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>h</mi></msub><mo>=</mo><mn>128</mn></mrow></semantics></math>, and FP16, full multi-head attention gives:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>m</mi><mo>=</mo><mn>2</mn><mo>×</mo><mn>80</mn><mo>×</mo><mn>64</mn><mo>×</mo><mn>128</mn><mo>×</mo><mn>2</mn><mo>=</mo><mn>2,621,440</mn><mtext> bytes</mtext><mo>≈</mo><mn>2.5</mn><mtext> MiB per token</mtext></mrow></semantics></math>
<p>A 32k-token context is 80 GiB of cache for one sequence. On an 80 GiB device also holding 140 GiB of weights across a tensor-parallel group, this is not a tuning problem; it is an infeasibility.</p>
<h2 id="sharing-key-value-heads">Sharing key-value heads</h2>
<p><strong>Multi-query attention</strong> (MQA) takes the extreme position: all query heads attend to a single shared key-value head.<sup><a href="https://blog.ecitis.org/attention-memory-budget/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mo>=</mo><mn>1</mn></mrow></semantics></math>, so cache drops by a factor of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math>: 64x in the example, from 2.5 MiB to 40 KiB per token.</p>
<p><strong>Grouped-query attention</strong> (GQA) interpolates: <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math> query heads are partitioned into <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> groups, each sharing one key-value head.<sup><a href="https://blog.ecitis.org/attention-memory-budget/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mo>=</mo><mi>G</mi></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math> recovers MHA while <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>1</mn></mrow></semantics></math> recovers MQA.</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>cache ratio</mtext><mo>=</mo><mfrac><mi>G</mi><msub><mi>H</mi><mi>q</mi></msub></mfrac></mrow></semantics></math>
<p>The GQA paper’s central practical finding is that an existing MHA checkpoint can be converted by mean-pooling the key and value projections within each group and then uptraining on roughly 5% of the original compute, reaching quality close to the original MHA model while serving at near-MQA speed. That result is why GQA became the default rather than a from-scratch design choice: it is a conversion, not a retraining.</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>8</mn></mrow></semantics></math> is the common landing point across model families. It gives an 8x cache reduction at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>q</mi></msub><mo>=</mo><mn>64</mn></mrow></semantics></math> with quality loss that is small on most benchmarks.</p>
<h2 id="where-the-quality-budget-goes">Where the quality budget goes</h2>
<p>The naive intuition, that fewer key-value heads means less capacity, is roughly right, and knowing exactly where the effect shows up is what makes the trade easy to manage.</p>
<p>Aggregate benchmark scores move very little between MHA and GQA at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>8</mn></mrow></semantics></math>. What degrades measurably is long-context retrieval: tasks requiring exact recall of a specific span from far back in the context. Each query head under MQA attends to the same key space, so heads lose the ability to specialize their retrieval patterns independently. Induction-style heads that copy from earlier positions are exactly the mechanism this constrains.</p>
<p>This means the evaluation that matters is not average benchmark score. It is needle-in-haystack retrieval at the target context length, and multi-hop tasks that require holding several distinct references at once. A model that looks fine at 4k can fail at 128k for reasons that only this evaluation surfaces.</p>
<p>MQA also shows measurable training instability at scale, which is part of why GQA displaced it even where the cache savings would have been welcome.</p>
<h2 id="multi-head-latent-attention">Multi-head latent attention</h2>
<p>MLA, introduced in the DeepSeek-V2 line, attacks the same problem differently.<sup><a href="https://blog.ecitis.org/attention-memory-budget/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Instead of sharing key-value heads, it caches a low-rank latent from which keys and values are reconstructed.</p>
<p>Down-project the hidden state to a compressed latent, cache only that, and up-project inside the attention computation:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>c</mi><mi>t</mi></msub><mo>=</mo><msub><mi>W</mi><mrow><mi>D</mi><mi>K</mi><mi>V</mi></mrow></msub><msub><mi>h</mi><mi>t</mi></msub><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><msub><mi>d</mi><mi>c</mi></msub></msup><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>d</mi><mi>c</mi></msub><mo>≪</mo><msub><mi>H</mi><mi>q</mi></msub><msub><mi>d</mi><mi>h</mi></msub></mrow></semantics></math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>k</mi><mi>t</mi></msub><mo>=</mo><msub><mi>W</mi><mrow><mi>U</mi><mi>K</mi></mrow></msub><msub><mi>c</mi><mi>t</mi></msub><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>v</mi><mi>t</mi></msub><mo>=</mo><msub><mi>W</mi><mrow><mi>U</mi><mi>V</mi></mrow></msub><msub><mi>c</mi><mi>t</mi></msub></mrow></semantics></math>
<p>Cached bytes per token become <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi><msub><mi>d</mi><mi>c</mi></msub><mi>b</mi></mrow></semantics></math> rather than <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mi>L</mi><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><msub><mi>d</mi><mi>h</mi></msub><mi>b</mi></mrow></semantics></math>. With <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>c</mi></msub></mrow></semantics></math> set to a few multiples of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>h</mi></msub></mrow></semantics></math>, this lands near or below aggressive GQA while retaining per-head diversity, because each head still gets its own up-projection.</p>
<p>The up-projection matrices can be absorbed into the query and output projections algebraically, so the reconstruction does not add a separate matrix multiply at inference. That absorption is what makes MLA practical rather than a memory-for-compute trade.</p>
<p>Rotary position embeddings complicate this. RoPE applies a position-dependent rotation to keys, which does not commute with the up-projection, so the absorption trick breaks. The published solution is a decoupled design: a small portion of each head carries RoPE directly and is cached separately, while the bulk stays in the latent. That decoupling is more complex than GQA, and it is the main reason adoption has been narrower despite the better cache-versus-quality frontier.</p>
<h2 id="comparison">Comparison</h2>








































<div class="table-wrap"><table><thead><tr><th>Variant</th><th><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow></semantics></math></th><th>Cache ratio</th><th>Per-head diversity</th><th>Complexity</th></tr></thead><tbody><tr><td>MHA</td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math></td><td>1</td><td>Full</td><td>Baseline</td></tr><tr><td>GQA, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math> groups</td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math></td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mi mathvariant="normal">/</mi><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math></td><td>Within groups only</td><td>Trivial</td></tr><tr><td>MQA</td><td>1</td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math></td><td>None</td><td>Trivial</td></tr><tr><td>MLA</td><td>n/a</td><td><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>d</mi><mi>c</mi></msub><mi mathvariant="normal">/</mi><mo stretchy="false">(</mo><mn>2</mn><msub><mi>H</mi><mi>q</mi></msub><msub><mi>d</mi><mi>h</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math></td><td>Retained via up-projection</td><td>High, RoPE interaction</td></tr></tbody></table></div>
<h2 id="working-backwards-from-a-serving-target">Working backwards from a serving target</h2>
<p>The design procedure is mechanical once the target is stated.</p>
<p>Given device memory <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi></mrow></semantics></math>, tensor-parallel degree <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow></semantics></math>, weight bytes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi></mrow></semantics></math>, target concurrency <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math>, and target context <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mo>≤</mo><mfrac><mrow><mi>P</mi><mo>⋅</mo><mi>M</mi><mo>−</mo><mi>W</mi><mo>−</mo><msub><mi>M</mi><mtext>workspace</mtext></msub></mrow><mrow><mn>2</mn><mi>L</mi><msub><mi>d</mi><mi>h</mi></msub><mi>b</mi><mo>⋅</mo><mi>n</mi><mo>⋅</mo><mi>S</mi></mrow></mfrac></mrow></semantics></math>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> max_kv_heads</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">device_bytes</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> tp</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> weight_bytes</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> workspace</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-parameter)">                 layers</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> head_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> dtype_bytes</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> concurrency</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> context</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    free </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tp </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> device_bytes </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> weight_bytes </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> workspace</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    per_token_per_head </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> layers </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> head_dim </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> dtype_bytes</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> free </span><span style="color:var(--astro-code-token-keyword)">//</span><span style="color:var(--astro-code-foreground)"> (per_token_per_head </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> concurrency </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> context)</span></span></code></pre>
<p>Round down to a power of two that divides <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mi>q</mi></msub></mrow></semantics></math>, and that is the group count. If it comes out below 1, the target is infeasible and something else has to give: context length, concurrency, cache precision, or the model.</p>
<p>Two adjustments are usually available before changing the architecture. Quantizing the cache to FP8 halves <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow></semantics></math>, which is equivalent to doubling the allowed head count and is often the cheaper move. And sliding-window attention on a subset of layers bounds <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math> for those layers, which can be a larger saving than any head-count change.</p>
<h2 id="interactions">Interactions</h2>
<p><strong>Cache quantization composes but not freely.</strong> GQA at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>8</mn></mrow></semantics></math> plus FP8 cache gives 16x over MHA at FP16. But fewer key-value heads means each cached value carries more of the model’s retrieval capacity, so quantization error has more leverage. Measure the combination, not each independently.</p>
<p><strong>Kernel efficiency changes with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>.</strong> With few key-value heads, the same K and V are read by many query heads, which is a favorable access pattern, since the shared data stays in on-chip memory. A well-written GQA kernel is faster per token than an MHA kernel beyond the cache saving. A kernel that materializes the shared heads per query head instead throws that away.</p>
<p><strong>Tensor parallelism has a floor.</strong> Key-value heads are partitioned across ranks. With <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi><mo>=</mo><mn>8</mn></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo>=</mo><mn>16</mn></mrow></semantics></math>, ranks either duplicate heads or sit idle. Aggressive GQA and aggressive tensor parallelism conflict, and the resolution is usually to duplicate, which silently negates part of the memory saving.</p>
<p><strong>Speculative decoding amplifies the win.</strong> Verification processes several draft tokens per step, so per-token cache cost multiplies. A smaller cache makes deeper speculation affordable.</p>
<h2 id="evaluating-a-choice">Evaluating a choice</h2>
<p>The protocol that catches what benchmarks miss:</p>
<ol>
<li><strong>Retrieval at target context.</strong> Needle-in-haystack at the maximum supported length, with the needle placed across the full range of depths. Report worst position, not average.</li>
<li><strong>Multi-reference tasks.</strong> Questions requiring several distinct spans simultaneously. This is where reduced head diversity shows up first.</li>
<li><strong>Long-form coherence.</strong> Generation quality at long output lengths, where cache degradation compounds.</li>
<li><strong>Realized concurrency.</strong> Measured, not computed: fragmentation and workspace mean the theoretical number is optimistic.</li>
<li><strong>Goodput at the SLO.</strong> The number that translates the architecture choice into cost.</li>
</ol>
<p>The last one is the point of the exercise. A 5% relative drop on a retrieval benchmark that buys 8x concurrency is usually a clear win, and stating it that way, as capacity bought at a known price, is what makes the decision straightforward.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Noam Shazeer, “Fast Transformer Decoding: One Write-Head is All You Need,” arXiv, 2019. <a href="https://arxiv.org/abs/1911.02150" rel="noopener noreferrer">https://arxiv.org/abs/1911.02150</a> <a href="https://blog.ecitis.org/attention-memory-budget/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Joshua Ainslie et al., “GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints,” EMNLP, 2023. <a href="https://arxiv.org/abs/2305.13245" rel="noopener noreferrer">https://arxiv.org/abs/2305.13245</a> <a href="https://blog.ecitis.org/attention-memory-budget/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>DeepSeek-AI, “DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model,” arXiv, 2024. <a href="https://arxiv.org/abs/2405.04434" rel="noopener noreferrer">https://arxiv.org/abs/2405.04434</a> <a href="https://blog.ecitis.org/attention-memory-budget/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Attention</category>
            <category>Architecture</category>
            <category>KV cache</category>
            <enclosure length="79712" type="image/png" url="https://blog.ecitis.org/open-graph/attention-memory-budget.png"/>
        </item>
        <item>
            <title><![CDATA[Inference Engine Architectures: What vLLM, SGLang, TensorRT-LLM, and llama.cpp Optimize]]></title>
            <link>https://blog.ecitis.org/inference-engine-architectures/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/inference-engine-architectures/</guid>
            <pubDate>Mon, 20 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare serving engines by the layer each one optimizes: memory management, compilation, program structure, or single-node portability.]]></description>
            <content:encoded><![CDATA[<p>Inference engines are often compared with a throughput bar chart on one model at one batch size. A more durable way to choose is to ask which layer of the stack each engine treats as its primary design problem, because that choice is stable across releases and it predicts the workloads the engine will handle well.</p>
<p>Four engines, four well-chosen answers. Each is close to the best available option inside the envelope it was built for, which is why the decision is about matching workload to design centre rather than picking a winner.</p>
<figure><figcaption><strong>Inference engines differ mostly in which layer they optimize hardest</strong></figcaption><p><a href="https://blog.ecitis.org/inference-engine-architectures/">View this figure on the site &#8594;</a></p></figure>
<h2 id="layers-common-to-every-engine">Layers common to every engine</h2>
<p>Every engine contains roughly the same components:</p>
<ol>
<li><strong>API and request handling.</strong> Protocol, streaming, sampling parameters, structured output.</li>
<li><strong>Scheduler.</strong> Admission, batch composition, preemption.</li>
<li><strong>Memory manager.</strong> KV cache allocation, prefix reuse, adapter storage.</li>
<li><strong>Execution.</strong> Graph construction, kernel selection, quantization, parallelism.</li>
<li><strong>Kernels.</strong> Attention, GEMM, sampling.</li>
</ol>
<p>They differ in which layer carries the innovation and which is treated as commodity.</p>
<h2 id="vllm-the-memory-manager-is-the-product">vLLM: the memory manager is the product</h2>
<p>vLLM’s founding observation was that conventional serving wasted most of its KV cache to fragmentation, because sequences were allocated contiguously at maximum length. PagedAttention applies virtual-memory paging: logical token blocks map through a block table to non-contiguous physical blocks, and the attention kernel resolves the indirection.<sup><a href="https://blog.ecitis.org/inference-engine-architectures/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p>The consequences compound. Near-zero fragmentation means more concurrent sequences per GPU. Blocks addressed through a table can be shared, which gives prefix caching and copy-on-write for parallel sampling almost for free. Blocks can be moved between tiers, which gives swap-based preemption. One memory abstraction produces most of the feature set.</p>
<p>Everything above it follows from that: continuous batching over a mutable block pool, admission control expressed in blocks, chunked prefill as a token budget. Kernels are largely adopted from elsewhere: FlashAttention, FlashInfer, and vendor GEMM libraries. That is a deliberate position, since it lets the engine track new models and new hardware quickly.</p>
<p>The result is the broadest model and hardware coverage in the ecosystem and the shortest path from a new architecture’s release to serving it. Peak single-request latency on a fixed, well-known model shape is not where it wins.</p>
<p><strong>Choose it for:</strong> heterogeneous model fleets, high concurrency, workloads with prefix reuse, environments where model turnover is frequent.</p>
<h2 id="sglang-the-unit-of-work-is-a-program-not-a-request">SGLang: the unit of work is a program, not a request</h2>
<p>SGLang starts from a different observation: real workloads are rarely one prompt in, one completion out. Agents, tool use, structured extraction, self-consistency sampling, and multi-turn interactions are <em>programs</em> with branching, control flow, and repeated calls that share large amounts of context.</p>
<p>An engine that only sees independent requests cannot exploit that structure. SGLang’s RadixAttention maintains a radix tree over token sequences as the KV cache index, so prefixes are shared automatically across calls without the application declaring anything.<sup><a href="https://blog.ecitis.org/inference-engine-architectures/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> A branching program with a common preamble computes that preamble once.</p>
<p>The front-end language is the other half. Expressing generation, branching, and constrained output as a program lets the runtime see the whole structure and schedule it, including reordering calls to maximize prefix reuse.</p>
<p>Constrained decoding is treated as a first-class path rather than a filter, with the automaton for a grammar compiled and applied across a batch, so structured output does not collapse throughput.</p>
<p><strong>Choose it for:</strong> agent loops, tool-calling pipelines, heavy structured output, few-shot workloads with large shared context, anything where the same preamble appears repeatedly.</p>
<h2 id="tensorrt-llm-decisions-move-to-build-time">TensorRT-LLM: decisions move to build time</h2>
<p>TensorRT-LLM inverts the flexibility tradeoff. Instead of deciding kernels and layouts at run time, a build step compiles the model into an engine plan: kernels fused, layouts chosen, quantization applied, shape profiles fixed, and, where possible, the whole decode step captured as a CUDA graph so per-step launch overhead disappears.</p>
<p>For a fixed model on NVIDIA hardware with a known shape distribution, this yields the lowest achievable per-token latency, particularly at small batch sizes where launch overhead is a large fraction of step time. It also gives the most complete access to hardware-specific numerics: FP8 and FP4 paths on recent architectures, with vendor-tuned kernels.</p>
<p>The costs are structural rather than incidental:</p>
<ul>
<li>Engine builds take minutes to hours and are part of the deployment pipeline.</li>
<li>A plan is specific to a GPU architecture, a TensorRT version, and a shape profile. It is a build artifact, not a distribution artifact.</li>
<li>Shapes outside the profile either fail or fall back.</li>
<li>New model architectures require support to be added, so the lag behind a release is longer.</li>
</ul>
<p><strong>Choose it for:</strong> one or a few stable models, NVIDIA-only fleets, latency-critical serving, deployments where a build step is already normal.</p>
<h2 id="llamacpp-one-machine-no-dependencies">llama.cpp: one machine, no dependencies</h2>
<p>llama.cpp optimizes for a different resource envelope entirely: a single user, commodity hardware, often no discrete GPU, and an installation that must not require a Python environment.</p>
<p>The design follows: a C++ core with no runtime dependencies, GGUF as a single self-describing file carrying weights, hyperparameters, and tokenizer, block-quantized formats down to two bits, and the ability to split layers between GPU and CPU so a model larger than VRAM still runs. Weights are memory-mapped, so a second process on the same machine starts nearly instantly.</p>
<p>None of this is aimed at fleet throughput, and measuring it that way misses the point. The relevant metric is whether a model runs acceptably on hardware that the other engines will not target at all.</p>
<p><strong>Choose it for:</strong> local and on-device deployment, embedded use, CPU-only environments, distribution to end users, offline operation.</p>
<h2 id="side-by-side">Side by side</h2>











































































<div class="table-wrap"><table><thead><tr><th></th><th>vLLM</th><th>SGLang</th><th>TensorRT-LLM</th><th>llama.cpp</th></tr></thead><tbody><tr><td>Primary layer</td><td>Memory manager</td><td>Program runtime</td><td>Compiler</td><td>Portable core</td></tr><tr><td>Key mechanism</td><td>Paged KV blocks</td><td>Radix prefix tree</td><td>AOT engine plan</td><td>mmap + block quant</td></tr><tr><td>Hardware</td><td>NVIDIA, AMD, others</td><td>NVIDIA, AMD</td><td>NVIDIA only</td><td>Almost anything</td></tr><tr><td>Model onboarding</td><td>Fast</td><td>Fast</td><td>Slower</td><td>Conversion required</td></tr><tr><td>Batch-1 latency</td><td>Good</td><td>Good</td><td>Best</td><td>N/A at scale</td></tr><tr><td>High-concurrency throughput</td><td>Excellent</td><td>Excellent</td><td>Excellent</td><td>Not a target</td></tr><tr><td>Shared-prefix workloads</td><td>Good</td><td>Best</td><td>Limited</td><td>Limited</td></tr><tr><td>Deployment step</td><td>Load and serve</td><td>Load and serve</td><td>Build then serve</td><td>Download and run</td></tr><tr><td>Structured output</td><td>Supported</td><td>First-class</td><td>Supported</td><td>Supported</td></tr></tbody></table></div>
<p>The engines also borrow from each other continuously. Paged attention, chunked prefill, radix prefix caching, and speculative decoding have all propagated across projects. A capability gap observed today is often closed within a release or two, which is another reason to choose on design center rather than on a current benchmark.</p>
<h2 id="choosing-without-a-bake-off">Choosing without a bake-off</h2>
<p>Answer four questions:</p>
<p><strong>How many distinct models, and how often do they change?</strong> Many or frequently → paged-memory server. One, stable → compiled engine becomes viable.</p>
<p><strong>Does the workload have structure across calls?</strong> Agents, branching, shared preambles → program-level runtime. Independent single-shot requests → structure buys nothing.</p>
<p><strong>Where does it run?</strong> Non-NVIDIA or mixed → not TensorRT-LLM. End-user machines → llama.cpp.</p>
<p><strong>What is the SLO shape?</strong> Tight single-stream latency at low concurrency favors compiled engines. High concurrency under a goodput target favors memory-managed servers.</p>
<h2 id="if-you-do-benchmark">If you do benchmark</h2>
<p>Comparisons that transfer to production share a few properties:</p>
<ul>
<li><strong>Same quantization.</strong> An FP8 engine against a BF16 engine measures the numeric format.</li>
<li><strong>Real length distributions.</strong> Fixed-length prompts remove the variance that drives tail latency.</li>
<li><strong>Real prefix structure.</strong> Unique prompts against a workload with a shared system prompt will overstate cost by a large factor.</li>
<li><strong>Goodput, not throughput.</strong> Peak throughput is reached at an operating point where many requests already miss their deadline.</li>
<li><strong>Warm.</strong> Discard the requests that pay autotuning and graph capture.</li>
<li><strong>Same tokenizer accounting.</strong> Tokens per second is not comparable across vocabularies.</li>
<li><strong>Include the deployment step.</strong> An engine that needs a 40-minute build has a different operational profile even if its steady state is faster.</li>
</ul>
<p>A comparison that holds all of these fixed will reproduce in production, and the layer each engine optimizes predicts the result before you run it. The projects also borrow from each other quickly, so a capability gap you see today is often closed within a release or two, and the ecosystem as a whole keeps getting faster.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Woosuk Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention,” SOSP, 2023. <a href="https://arxiv.org/abs/2309.06180" rel="noopener noreferrer">https://arxiv.org/abs/2309.06180</a> <a href="https://blog.ecitis.org/inference-engine-architectures/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Lianmin Zheng et al., “SGLang: Efficient Execution of Structured Language Model Programs,” NeurIPS, 2024. <a href="https://arxiv.org/abs/2312.07104" rel="noopener noreferrer">https://arxiv.org/abs/2312.07104</a> <a href="https://blog.ecitis.org/inference-engine-architectures/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>LLM serving</category>
            <category>Scheduling</category>
            <category>KV cache</category>
            <enclosure length="72063" type="image/png" url="https://blog.ecitis.org/open-graph/inference-engine-architectures.png"/>
        </item>
        <item>
            <title><![CDATA[J-Space: A Verbalizable Workspace Inside Language Models]]></title>
            <link>https://blog.ecitis.org/j-space-global-workspace/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/j-space-global-workspace/</guid>
            <pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explain Anthropic’s Jacobian lens, sparse J-space, causal workspace tests, alignment-audit uses, and current limits.]]></description>
            <content:encoded><![CDATA[<p>Anthropic’s July 2026 J-space work asks a narrow mechanistic question: does a language model contain a privileged set of internal representations that it can report, control, and reuse for flexible reasoning?<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p>The researchers identify such a set with the Jacobian lens. Their results support a functional workspace whose contents can be read and causally edited.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup> The experiments do not address phenomenal consciousness.</p>
<h2 id="the-jacobian-lens-maps-activations-to-future-speech">The Jacobian lens maps activations to future speech</h2>
<p>At token position <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow></semantics></math> and layer <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ℓ</mi></mrow></semantics></math>, a transformer stores an activation <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>h</mi><mrow><mi mathvariant="normal">ℓ</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub></mrow></semantics></math> in the residual stream. A small perturbation to that activation can affect final-layer activations at the current and later token positions.</p>
<p>The paper averages those effects over source positions, later positions, and a corpus of prompts:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>J</mi><mi mathvariant="normal">ℓ</mi></msub><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>t</mi><mo separator="true">,</mo><msup><mi>t</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo>≥</mo><mi>t</mi><mo separator="true">,</mo><mrow><mi mathvariant="normal">p</mi><mi mathvariant="normal">r</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">m</mi><mi mathvariant="normal">p</mi><mi mathvariant="normal">t</mi></mrow></mrow></msub><mrow><mo fence="true">[</mo><mfrac><mrow><mi mathvariant="normal">∂</mi><msub><mi>h</mi><mrow><mrow><mi mathvariant="normal">f</mi><mi mathvariant="normal">i</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">a</mi><mi mathvariant="normal">l</mi></mrow><mo separator="true">,</mo><msup><mi>t</mi><mo lspace="0em" rspace="0em">′</mo></msup></mrow></msub></mrow><mrow><mi mathvariant="normal">∂</mi><msub><mi>h</mi><mrow><mi mathvariant="normal">ℓ</mi><mo separator="true">,</mo><mi>t</mi></mrow></msub></mrow></mfrac><mo fence="true">]</mo></mrow></mrow></semantics></math>
<p>The lens then replaces the model’s remaining layers with this average linear map and the model’s own unembedding:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">lens</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>h</mi><mi mathvariant="normal">ℓ</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">softmax</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><msub><mi>W</mi><mi>U</mi></msub><mi mathvariant="normal">norm</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>J</mi><mi mathvariant="normal">ℓ</mi></msub><msub><mi>h</mi><mi mathvariant="normal">ℓ</mi></msub><mo stretchy="false">)</mo><mo fence="true">)</mo></mrow></mrow></semantics></math>
<p>The output is a score over vocabulary tokens. A high-scoring token names a concept that the activation is disposed to make the model say in some future context. It need not be the next token, and it may never appear in the output.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<figure><figcaption><strong>The J-lens can read an intermediate concept and test it by intervention</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/j-space-global-workspace-0.webp" width="512" height="647" alt="The J-lens can read an intermediate concept and test it by intervention" loading="lazy" decoding="async"></figure>
<p>This distinguishes the Jacobian lens from the logit lens. The logit lens applies the unembedding directly to an intermediate activation. The Jacobian lens corrects for how downstream layers transform a perturbation before it reaches future outputs.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup></p>
<h2 id="j-space-is-sparse-rather-than-a-linear-subspace">J-space is sparse rather than a linear subspace</h2>
<p>Each vocabulary token has a J-lens vector at each layer. The number of token vectors exceeds the residual-stream dimension, so the dictionary is overcomplete. Many linear combinations can reconstruct the same activation.</p>
<p>The paper observes that only a small set of vectors is strongly active at a given position. It defines J-space as points expressible as sparse, nonnegative combinations of J-lens vectors.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-3">2</a></sup></p>
<p>For allowable sparsity <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> and J-lens vectors <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>v</mi><mi>i</mi></msub></mrow></semantics></math>, the set can be written as:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">J</mi><mi>k</mi></msub><mo>=</mo><mrow><mo fence="true">{</mo><munder><mo>∑</mo><mi>i</mi></munder><msub><mi>a</mi><mi>i</mi></msub><msub><mi>v</mi><mi>i</mi></msub><mtext>  </mtext><mo fence="true" lspace="0.05em" rspace="0.05em">|</mo><mtext>  </mtext><msub><mi>a</mi><mi>i</mi></msub><mo>≥</mo><mn>0</mn><mo separator="true">,</mo><mtext>  </mtext><mo stretchy="false">∥</mo><mi>a</mi><msub><mo stretchy="false">∥</mo><mn>0</mn></msub><mo>≤</mo><mi>k</mi><mo fence="true">}</mo></mrow></mrow></semantics></math>
<p>Geometrically, this is a union of cones, not one low-dimensional plane. Each active set of vectors defines a cone. Changing the active set moves the activation into another cone.</p>
<figure><figcaption><strong>Sparse J-space is a union of nonnegative cones</strong> — Each active set selects a different cone inside the residual stream.</figcaption><img src="https://blog.ecitis.org/feeds/figures/j-space-global-workspace-1.webp" width="512" height="747" alt="Sparse J-space is a union of nonnegative cones — Each active set selects a different cone inside the residual stream." loading="lazy" decoding="async"></figure>
<p>The researchers usually cap <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> at twenty-five, based on the number of meaningfully active vectors they observed, and they state that the choice is partly arbitrary.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-4">2</a></sup> The sparse J-space component explains only a minority of activation variance in their measurements.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-5">2</a></sup> Most model computation lies outside this readout.</p>
<h2 id="five-tests-support-the-workspace-interpretation">Five tests support the workspace interpretation</h2>
<p>The paper draws tests from global workspace theory, a functional account in which selected information becomes available to many otherwise specialized processes.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-3">1</a></sup></p>
<h3 id="verbal-report">Verbal report</h3>
<p>When the model silently chooses an item and later reports it, the chosen concept appears in J-space. Swapping the active concept for another changes the report. This moves the result beyond correlation: the answer reads from the edited representation.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-4">1</a></sup></p>
<h3 id="directed-control">Directed control</h3>
<p>The model can activate a requested concept while producing unrelated text. In the reported experiments, a model performs a mental calculation or holds a category in mind while copying a sentence, and the internal concepts appear in J-space without appearing in the copied output.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-5">1</a></sup></p>
<h3 id="internal-reasoning">Internal reasoning</h3>
<p>For multi-step questions, an intermediate concept can appear in J-space even when it is absent from both prompt and answer. Replacing that intermediate redirects the result. The paper’s animal example swaps the internal concept used in a two-hop question and changes the final leg count accordingly.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-6">1</a></sup></p>
<h3 id="flexible-reuse">Flexible reuse</h3>
<p>The same country representation can feed several downstream questions. Swapping one country for another changes answers about capital, language, continent, and currency. This suggests that different computations read a shared representation rather than separate task-specific copies.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-7">1</a></sup></p>
<h3 id="selectivity">Selectivity</h3>
<p>Ablating active J-space contents harms multi-step reasoning and other higher-order tasks while leaving several routine language behaviors relatively intact in the reported evaluations.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-8">1</a></sup> The workspace is therefore not a complete account of language-model computation. It appears to support flexible handoffs while practiced processing can bypass it.</p>
<h2 id="alignment-auditing-can-inspect-silent-cognition">Alignment auditing can inspect silent cognition</h2>
<p>Output-only audits can miss what a model notices before it acts. The J-lens surfaced evaluation awareness, recognition of prompt injection, fabricated-data intentions, and hidden objectives in model organisms used by Anthropic.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-1" id="user-content-fnref-1-9">1</a></sup></p>
<p>One experiment ablated J-lens directions associated with evaluation awareness in a staged blackmail scenario. The ablation reduced verbalized awareness of the test and caused some previously absent blackmail attempts to appear.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-6">2</a></sup> Most ablated rollouts still refused, so evaluation awareness was one causal contributor rather than the sole cause of aligned behavior.</p>
<p>Use the following audit sequence:</p>
<ol>
<li>Read a candidate internal concept.</li>
<li>Form a behavioral hypothesis.</li>
<li>Remove or replace the concept.</li>
<li>Test whether behavior changes in the predicted direction.</li>
<li>Check whether unrelated capabilities remain intact.</li>
</ol>
<p>The method cannot certify that all dangerous cognition enters J-space. The paper explicitly warns that automatic or practiced computations may bypass it.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-7">2</a></sup></p>
<h2 id="counterfactual-reflection-changes-the-workspace">Counterfactual reflection changes the workspace</h2>
<p>The workspace account predicts that representations available for future verbal report also participate in silent reasoning. Anthropic tests this with counterfactual reflection training.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-8">2</a></sup></p>
<p>Training examples append a request for reflection to a partial task trajectory. The target reflection applies constitution-grounded principles. The loss is computed on the reflection, not on the original task behavior. At evaluation, the model is not asked to reflect.</p>
<p>After training, honesty-related concepts enter J-space during the uninterrupted task, and behavior changes. Ablating those implanted concepts removes much of the behavioral gain in the paper’s experiments.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-9">2</a></sup></p>
<p>The result connects three objects:</p>
<ul>
<li>what the model would say if asked to reflect</li>
<li>what appears in its internal verbalizable workspace</li>
<li>how it acts when no reflection is requested</li>
</ul>
<p>Counterfactual reports could also provide a supervision signal for changing internal computation.</p>
<h2 id="limits-of-the-measurements">Limits of the measurements</h2>
<p>The paper lists several open problems.<sup><a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fn-2" id="user-content-fnref-2-10">2</a></sup></p>
<p><strong>Single-token vocabulary:</strong> The basic J-lens has one direction per token. Multi-token concepts can appear as fragments or separate words.</p>
<p><strong>Bag-of-concepts readout:</strong> A list such as “spider,” “legs,” and “eight” does not encode which relation binds them.</p>
<p><strong>Inconsistent interpretability:</strong> Some top-ranked tokens are not meaningful to a human reader.</p>
<p><strong>Layer boundary:</strong> The distinction between workspace representations and late motor representations is identified empirically rather than by a complete formal criterion.</p>
<p><strong>Task prediction:</strong> The method does not yet predict in advance which computations will use J-space and which will bypass it.</p>
<p><strong>Scale and training dynamics:</strong> The researchers do not yet know when the workspace emerges during pretraining or how its properties change with model size and architecture.</p>
<p>J-space is best understood as a causally supported partial interface to model cognition. It reads concepts poised for flexible use and future speech, but it does not decode every representation, recover relational structure, or establish subjective experience.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Anthropic, “A global workspace in language models,” July 2026. <a href="https://www.anthropic.com/research/global-workspace" rel="noopener noreferrer">https://www.anthropic.com/research/global-workspace</a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-5" class="data-footnote-backref">↩<sup>5</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-6" class="data-footnote-backref">↩<sup>6</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-7" class="data-footnote-backref">↩<sup>7</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-8" class="data-footnote-backref">↩<sup>8</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-1-9" class="data-footnote-backref">↩<sup>9</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>Wes Gurnee et al., “Verbalizable Representations Form a Global Workspace in Language Models,” Transformer Circuits, 2026. <a href="https://transformer-circuits.pub/2026/workspace/index.html" rel="noopener noreferrer">https://transformer-circuits.pub/2026/workspace/index.html</a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-5" class="data-footnote-backref">↩<sup>5</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-6" class="data-footnote-backref">↩<sup>6</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-7" class="data-footnote-backref">↩<sup>7</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-8" class="data-footnote-backref">↩<sup>8</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-9" class="data-footnote-backref">↩<sup>9</sup></a> <a href="https://blog.ecitis.org/j-space-global-workspace/#user-content-fnref-2-10" class="data-footnote-backref">↩<sup>10</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Interpretability</category>
            <category>Safety</category>
            <category>Architecture</category>
            <enclosure length="77842" type="image/png" url="https://blog.ecitis.org/open-graph/j-space-global-workspace.png"/>
        </item>
        <item>
            <title><![CDATA[What Determines Which Language Model You Can Run on Your Device]]></title>
            <link>https://blog.ecitis.org/which-model-can-run-on-your-device/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/which-model-can-run-on-your-device/</guid>
            <pubDate>Sun, 19 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Estimate local model memory, understand quantization and accelerator support, and choose a runtime and offload plan that match your device.]]></description>
            <content:encoded><![CDATA[<p>The model name does not determine whether it runs on your device. A runnable configuration is a combination of a checkpoint, a numerical representation, a context length, a runtime, a hardware backend, and a memory placement plan.</p>
<p>Two files described as the same model can require different amounts of memory. An installed accelerator can sit idle when the runtime lacks a compatible backend. Disk offload can execute a checkpoint larger than available RAM, with storage reads entering the generation path.</p>
<p>This guide provides a repeatable way to estimate a configuration before downloading it and identify the next constraint when the estimate does not fit.</p>
<h2 id="weight-tensors-set-the-first-memory-estimate">Weight tensors set the first memory estimate</h2>
<p>A language model checkpoint is mostly a collection of tensors. The parameter count tells you how many values those tensors contain. The stored precision tells you how many bits are used for each value.</p>
<p>A parameter is a learned numerical value. A checkpoint is the saved set of those learned values plus the metadata needed to reconstruct the model. Parameter count is useful because it gives the first capacity estimate before runtime-specific details enter the calculation.</p>
<p>The simplest weight-size estimate is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>weight bytes</mtext><mo>=</mo><mfrac><mrow><mtext>parameter count</mtext><mo>×</mo><mtext>stored bits per parameter</mtext></mrow><mn>8</mn></mfrac></mrow></semantics></math>
<p>The division by eight converts bits to bytes. This lower bound assumes uniform encoding. Quantization metadata and mixed-precision tensors increase the final file size.</p>
<p>For an illustrative checkpoint containing exactly eight billion parameters:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mn>8,000,000,000</mn><mo>×</mo><mn>16</mn></mrow><mn>8</mn></mfrac><mo>=</mo><mn>16,000,000,000</mn><mtext> bytes</mtext></mrow></semantics></math>
<p>The same arithmetic with four stored bits per parameter gives four billion bytes. These are derived values from the displayed formula. They do not include quantization scales, tensor metadata, mixed-precision layers, tokenizer files, runtime buffers, or the key-value cache.</p>
<p>Real quantized formats make the estimate less exact. A GGUF file stores tensor data and metadata, and its quantization blocks can include scales and other block-level values alongside the low-bit weights.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> A label such as four-bit therefore describes the main weight encoding, not a guarantee that the whole file uses exactly four bits for every parameter.</p>
<p>Mixture-of-experts model listings can show both total and active parameter counts. Use the total parameters present in the checkpoint when estimating stored weights. The active count describes the subset selected for one token’s computation, not the complete set of expert weights that must be stored. The Mixtral paper makes this distinction explicitly when it reports separate total and active counts.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-16" id="user-content-fnref-16">2</a></sup></p>
<figure><figcaption><strong>Estimate the tensor memory floor</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-0.webp" width="512" height="656" alt="Estimate the tensor memory floor" loading="lazy" decoding="async"></figure>
<p>The calculator separates the weight estimate from the additional allocations discussed below. Treat its result as a screening test. If the tensors alone exceed every available memory tier, the runtime needs quantization, partial placement, or disk-backed execution before it can load the checkpoint.</p>
<h2 id="stored-precision-and-compute-precision-are-separate">Stored precision and compute precision are separate</h2>
<p>Precision answers more than one question:</p>
<ul>
<li>How are weights stored in the checkpoint?</li>
<li>In which type are weights represented after loading?</li>
<li>In which type does the hardware execute matrix operations?</li>
<li>In which type are intermediate activations and cache entries stored?</li>
</ul>
<p>Those answers can differ. A runtime can store a weight matrix in a low-bit format, unpack blocks inside a kernel, and accumulate partial products in a wider type. The checkpoint is smaller, but the accelerator still needs a kernel that understands that encoding.</p>
<p>NVIDIA documents distinct floating-point and integer formats for TensorRT, including FP32, FP16, BF16, FP8, FP4, INT8, and INT4.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-2" id="user-content-fnref-2">3</a></sup> The labels describe different bit layouts and numerical ranges. They are not interchangeable labels for reduced storage.</p>
<p>Transformers quantization methods also differ in hardware support. The Hugging Face compatibility table lists separate support across CPU, CUDA, ROCm, Metal, Intel GPU, and other backends.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-3" id="user-content-fnref-3">4</a></sup> A checkpoint that loads on one backend can fall back to a slower implementation or fail to load on another backend even when both devices have enough memory.</p>
<figure><figcaption><strong>Stored precision does not fix the compute precision</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-1.webp" width="512" height="599" alt="Stored precision does not fix the compute precision" loading="lazy" decoding="async"></figure>
<p>Three practical consequences follow.</p>
<p>First, lower stored precision usually reduces checkpoint storage and weight memory, but does not reduce every allocation by the same ratio. Activations, cache entries, and selected layers can remain in wider types.</p>
<p>Second, a lower-bit format is useful only when the runtime provides kernels for the device. NVIDIA’s TensorRT RTX documentation, for example, ties FP8 support to Ada Lovelace and newer GPUs, while BF16 and INT4 support begin with Ampere in that runtime.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-4" id="user-content-fnref-4">5</a></sup> Hardware generation and software backend both matter.</p>
<p>Third, quantization changes model outputs. Post-training quantization maps trained values into a smaller numerical representation. Per-channel, per-group, and per-block methods keep separate quantization parameters for smaller regions of a tensor, which increases metadata and implementation complexity in exchange for finer scaling.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-5" id="user-content-fnref-5">6</a></sup> Choose the most compressed format only after evaluating it on the tasks you intend to run.</p>
<h2 id="storage-ram-and-accelerator-memory-are-different-budgets">Storage, RAM, and accelerator memory are different budgets</h2>
<p>The word memory is too broad to diagnose a failed load. A local inference system usually crosses several capacity limits:</p>






























<div class="table-wrap"><table><thead><tr><th>Resource</th><th>What occupies it</th><th>What happens when it is insufficient</th></tr></thead><tbody><tr><td>Storage</td><td>Checkpoint files, tokenizer files, runtime binaries, compiled kernels, and caches</td><td>The model cannot be downloaded, converted, or retained</td></tr><tr><td>System RAM</td><td>CPU-resident weights, file-backed pages, runtime state, token buffers, and offloaded tensors</td><td>Loading fails, the operating system compresses memory, or pages are repeatedly moved to storage</td></tr><tr><td>Discrete accelerator memory</td><td>GPU-resident weights, activations, key-value cache, and working buffers</td><td>Fewer layers fit on the accelerator or allocation fails</td></tr><tr><td>Unified memory</td><td>CPU and GPU allocations from one physical pool</td><td>CPU and GPU compete for the same capacity even though explicit tensor copies can be avoided</td></tr></tbody></table></div>
<p>Apple silicon provides a concrete unified-memory design. Apple’s MLX documentation states that the CPU and GPU access the same memory pool and that MLX arrays do not need to be moved when an operation changes devices.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-6" id="user-content-fnref-6">7</a></sup> This removes a separate CPU-to-GPU copy step for those arrays. It does not create additional capacity. Model weights, cache entries, the operating system, and other applications still share the pool.</p>
<figure><figcaption><strong>A model crosses several memory budgets before it generates</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-2.webp" width="512" height="650" alt="A model crosses several memory budgets before it generates" loading="lazy" decoding="async"></figure>
<p>Storage fit is only the first capacity check. The loaded checkpoint may exceed system RAM or GPU memory. A split placement can solve capacity while adding interconnect traffic.</p>
<p>Leave headroom for the operating system and runtime instead of matching a checkpoint file to the last available byte. The exact headroom is runtime and workload dependent, so measure it on the target device rather than applying a universal percentage.</p>
<h2 id="context-length-grows-the-key-value-cache">Context length grows the key-value cache</h2>
<p>Autoregressive generation reuses attention keys and values from earlier tokens. The key-value cache stores those tensors so the model does not recompute the full prefix for every generated token.</p>
<p>For a standard cache without additional compression, an approximate per-token allocation is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>KV bytes per token</mtext><mo>=</mo><mn>2</mn><mo>×</mo><mtext>layers</mtext><mo>×</mo><mtext>KV heads</mtext><mo>×</mo><mtext>head dimension</mtext><mo>×</mo><mtext>bytes per cache element</mtext></mrow></semantics></math>
<p>The first factor represents one key tensor and one value tensor. Hugging Face derives the same structure in its cache quantization explanation.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-7" id="user-content-fnref-7">8</a></sup></p>
<p>Grouped-query attention reduces this allocation because several query heads share a smaller set of key-value heads. The relevant field is the number of key-value heads, not the total number of attention heads.</p>
<figure><figcaption><strong>Context length becomes a KV-cache memory bill</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-3.webp" width="512" height="562" alt="Context length becomes a KV-cache memory bill" loading="lazy" decoding="async"></figure>
<p>The estimator reports values derived from the inputs shown in the figure. If the context length is multiplied while every architecture and precision field stays fixed, the cache allocation changes by the same factor.</p>
<p>Runtime policy modifies this result:</p>
<ul>
<li>A dynamic cache grows as tokens arrive.</li>
<li>A static cache allocates its configured maximum capacity before all positions are used.</li>
<li>A quantized cache stores keys and values at lower precision.</li>
<li>An offloaded cache keeps the active layer on the accelerator and places other layers of cache data in CPU memory.</li>
</ul>
<p>The Transformers cache documentation describes those strategies and notes that offloading saves accelerator memory at a throughput cost.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-8" id="user-content-fnref-8">9</a></sup> A model that fits for a short prompt can therefore run out of memory at a longer context even though its weight tensors never change.</p>
<p>Context settings also affect generation length. The cache contains prompt tokens plus generated tokens that remain inside the active context. Test with the longest prompt and generation you intend to serve.</p>
<h2 id="accelerator-support-is-an-operation-level-property">Accelerator support is an operation-level property</h2>
<p>A GPU or neural processing unit does not accelerate a model merely because it exists in the machine. The runtime must provide a backend for the device, and that backend must implement the operations, data types, tensor shapes, and memory layouts used by the graph.</p>
<p>ONNX Runtime formalizes this through execution providers. Each provider reports the graph nodes or subgraphs it supports. ONNX Runtime partitions the graph, assigns supported regions to prioritized providers, and uses a lower-priority provider such as the CPU for unsupported regions.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-9" id="user-content-fnref-9">10</a></sup></p>
<figure><figcaption><strong>A runtime assigns graph regions to backends that support them</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-4.webp" width="512" height="752" alt="A runtime assigns graph regions to backends that support them" loading="lazy" decoding="async"></figure>
<p>This graph-level view explains several common outcomes:</p>
<ul>
<li>The complete model runs on the accelerator because every required operation has a supported kernel.</li>
<li>Most layers run on the accelerator while a small unsupported region runs on the CPU.</li>
<li>Frequent boundaries between providers add transfers and synchronization.</li>
<li>The runtime rejects the graph because an operation has no valid implementation.</li>
<li>A low-precision checkpoint loads, but selected operations execute in a wider type.</li>
</ul>
<p>Core ML exposes a related policy through compute units. An application can request the CPU alone, the CPU and GPU, the CPU and Neural Engine, or all available units; the system then selects compatible hardware for the model.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-10" id="user-content-fnref-10">11</a></sup> The request controls eligible devices, not a guarantee that every operation uses the same accelerator.</p>
<p>Peak throughput figures do not answer this compatibility question. Compare a device only after confirming that your runtime can execute the model’s operations and quantization format on it. Then benchmark the complete prompt-processing and token-generation path.</p>
<h2 id="file-format-runtime-and-backend-form-one-stack">File format, runtime, and backend form one stack</h2>
<p>A model file is not an executable application. The format determines how tensors and metadata are represented. The runtime parses that representation, constructs operations, chooses kernels, and places allocations.</p>
<p>Consider three common paths:</p>
<p><strong>GGUF with llama.cpp.</strong> GGUF contains tensors and model metadata for the GGML ecosystem.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup> llama.cpp supports several CPU and accelerator backends, integer quantization levels, memory mapping, and CPU plus GPU hybrid inference.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-11" id="user-content-fnref-11">12</a></sup></p>
<p><strong>Safetensors with a framework runtime.</strong> Safetensors records tensor names, dtypes, shapes, and byte offsets in a format designed for safe loading and memory mapping.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-12" id="user-content-fnref-12">13</a></sup> The file does not decide whether a transformer runs through PyTorch eager execution, a compiled graph, CUDA kernels, Metal kernels, or another backend.</p>
<p><strong>ONNX with execution providers.</strong> An ONNX graph describes operations and tensors. ONNX Runtime assigns graph regions to execution providers according to backend support and priority.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-9" id="user-content-fnref-9-2">10</a></sup></p>
<p>Conversion between formats can change more than the filename. An exporter can rewrite operations, fold constants, select a deployment target, or choose a default tensor precision. Core ML Tools, for example, uses float16 as the default precision for ML Program conversion unless the conversion options specify another behavior.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-13" id="user-content-fnref-13">14</a></sup></p>
<p>Before downloading a checkpoint, answer these questions:</p>
<ol>
<li>Which runtime reads the format?</li>
<li>Which backend in that runtime supports the target device?</li>
<li>Does the backend implement this quantization and architecture?</li>
<li>Which tensors remain in system RAM?</li>
<li>Which operations fall back to the CPU?</li>
</ol>
<p>If one answer is unknown, the parameter count alone cannot establish compatibility.</p>
<h2 id="layer-offload-combines-memory-tiers">Layer offload combines memory tiers</h2>
<p>Transformer blocks are repeated groups of operations. A runtime can place some blocks on an accelerator and keep the rest in system RAM. During inference, each block executes where its weights reside.</p>
<p>llama.cpp calls this CPU plus GPU hybrid inference and exposes controls for the number of layers offloaded to the GPU, target devices, and tensor split behavior.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-11" id="user-content-fnref-11-2">12</a></sup> This placement can run a model whose weights exceed accelerator memory as long as the remaining allocations fit elsewhere.</p>
<figure><figcaption><strong>Larger models fit by moving less frequently used data outward</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-5.webp" width="512" height="664" alt="Larger models fit by moving less frequently used data outward" loading="lazy" decoding="async"></figure>
<p>Offload changes the bottleneck. When every layer and the active cache fit in accelerator memory, generation can remain within that high-bandwidth memory domain. When layers are split, hidden states cross the device boundary at placement transitions. When weights are fetched from storage, each needed tensor must first become available through system memory.</p>
<p>The fastest placement is not always the placement with the most offloaded layers. Accelerator memory also holds the key-value cache and working buffers. Giving every remaining byte to weights can leave too little room for the requested context. Measure prompt processing and token generation with the final context setting.</p>
<h2 id="memory-mapping-avoids-an-eager-file-copy">Memory mapping avoids an eager file copy</h2>
<p>Memory mapping gives a process a virtual address range backed by a file. The operating system loads file pages as they are accessed instead of requiring the application to copy the complete file into a separate heap allocation at startup.</p>
<p>Safetensors supports lazy loading through memory mapping, and llama.cpp enables memory mapping by default unless its command-line option disables it.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-12" id="user-content-fnref-12-2">13</a></sup><sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-14" id="user-content-fnref-14">15</a></sup></p>
<p>This can reduce startup copies and allow the operating system to reclaim clean file-backed pages. It does not make storage equivalent to RAM. A page that is not resident still has to be read from the storage device. If the working set repeatedly exceeds available RAM, page faults and eviction place storage latency on the generation path.</p>
<p>Memory locking makes the opposite trade. llama.cpp provides an option that asks the operating system not to swap model data after it is loaded.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-14" id="user-content-fnref-14-2">15</a></sup> Locked pages can stabilize residency, but they consume physical memory that other processes cannot reclaim.</p>
<p>Use memory mapping when the format and runtime support it, then watch resident memory and page-fault behavior during a representative request. A successful load does not establish steady-state performance.</p>
<h2 id="disk-offload-exchanges-capacity-for-data-movement">Disk offload exchanges capacity for data movement</h2>
<p>Some framework runtimes can place selected weights on disk when neither accelerator memory nor system RAM can hold the full checkpoint. Hugging Face Accelerate device maps support accelerator, CPU, and disk placements.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-15" id="user-content-fnref-15">16</a></sup></p>
<p>For disk-resident layers, Accelerate describes a staged path: weights move from disk into system RAM, then to the execution device before the layer runs. After execution, the memory used for those weights can be released for the next layer.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-15" id="user-content-fnref-15-2">16</a></sup></p>
<p>Disk-resident execution can load a larger checkpoint, but it puts storage reads inside model execution. Accelerate’s documentation warns that hard-drive offload can be slow when the storage and CPU path cannot move data quickly enough.<sup><a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fn-15" id="user-content-fnref-15-3">16</a></sup></p>
<p>Disk offload increases usable capacity by fetching weights when needed. It does not remove the RAM required to stage those weights or the device memory required for execution. Use it when completing the task matters more than interactive token latency, or when only a small fraction of tensors need disk placement. Benchmark with a warm and a cold file cache so an operating-system cache does not hide the storage dependency.</p>
<h2 id="storage-capacity-includes-more-than-one-checkpoint">Storage capacity includes more than one checkpoint</h2>
<p>The downloaded file is only one storage consumer. A working local setup can also contain:</p>
<ul>
<li>the original checkpoint and one or more quantized conversions</li>
<li>temporary files created during conversion</li>
<li>tokenizer and configuration files</li>
<li>compiled engine plans or kernel caches</li>
<li>runtime packages and accelerator libraries</li>
<li>generated logs and application data</li>
</ul>
<p>Do not infer required free space from the final quantized file alone if conversion happens on the same device. The conversion process can require the source and destination files to coexist.</p>
<p>Storage speed matters when the runtime memory maps weights, loads model shards on demand, or uses disk offload. Once the active working set remains resident in RAM or accelerator memory, storage bandwidth should leave the steady-state token path. Use system I/O counters to verify which regime you are in.</p>
<h2 id="a-model-fit-procedure">A model-fit procedure</h2>
<p>Use the same order for each candidate configuration:</p>
<ol>
<li>Identify the exact checkpoint and format.</li>
<li>Calculate the theoretical weight size from parameter count and stored precision.</li>
<li>Use the actual file size when the checkpoint is available.</li>
<li>Add the key-value cache for the target context and cache precision.</li>
<li>Reserve capacity for runtime buffers, the operating system, and other applications.</li>
<li>Map those allocations to accelerator memory, system RAM, unified memory, and storage.</li>
<li>Confirm runtime support for the format, architecture, quantization method, and device backend.</li>
<li>Decide whether full acceleration, layer offload, memory mapping, or disk offload is required.</li>
<li>Benchmark the longest intended prompt and generation on the target device.</li>
<li>Evaluate output quality on the tasks that matter for the application.</li>
</ol>
<figure><figcaption><strong>Choose a runnable configuration in dependency order</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/which-model-can-run-on-your-device-6.webp" width="512" height="703" alt="Choose a runnable configuration in dependency order" loading="lazy" decoding="async"></figure>
<p>The sequence deliberately places compatibility before performance claims. A device can have enough aggregate memory while lacking a usable kernel. A runtime can load a checkpoint while paging so heavily that interactive use is impractical. A quantized model can execute quickly while producing unacceptable results for a specific task.</p>
<p>Record the complete configuration with every benchmark: checkpoint revision, quantization, runtime version, backend, layer placement, context length, cache type, prompt length, generated length, and device power mode. Without those fields, two local-inference measurements are not directly comparable.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Hugging Face, “GGUF.” <a href="https://huggingface.co/docs/hub/gguf" rel="noopener noreferrer">https://huggingface.co/docs/hub/gguf</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-16">
<p>Jiang et al., “Mixtral of Experts.” <a href="https://arxiv.org/abs/2401.04088" rel="noopener noreferrer">https://arxiv.org/abs/2401.04088</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>NVIDIA, “Data Format Descriptions.” <a href="https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/data-format-desc.html" rel="noopener noreferrer">https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/data-format-desc.html</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Hugging Face, “Quantization Overview.” <a href="https://huggingface.co/docs/transformers/quantization/overview" rel="noopener noreferrer">https://huggingface.co/docs/transformers/quantization/overview</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>NVIDIA, “TensorRT RTX Best Practices.” <a href="https://docs.nvidia.com/deeplearning/tensorrt-rtx/latest/performance/best-practices.html" rel="noopener noreferrer">https://docs.nvidia.com/deeplearning/tensorrt-rtx/latest/performance/best-practices.html</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Hugging Face, “Quantization Concepts.” <a href="https://huggingface.co/docs/transformers/quantization/concept_guide" rel="noopener noreferrer">https://huggingface.co/docs/transformers/quantization/concept_guide</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>MLX, “Unified Memory.” <a href="https://ml-explore.github.io/mlx/build/html/usage/unified_memory.html" rel="noopener noreferrer">https://ml-explore.github.io/mlx/build/html/usage/unified_memory.html</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Hugging Face, “KV Cache Quantization.” <a href="https://huggingface.co/blog/kv-cache-quantization" rel="noopener noreferrer">https://huggingface.co/blog/kv-cache-quantization</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>Hugging Face, “Cache Strategies.” <a href="https://huggingface.co/docs/transformers/kv_cache" rel="noopener noreferrer">https://huggingface.co/docs/transformers/kv_cache</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p>ONNX Runtime, “Execution Providers.” <a href="https://onnxruntime.ai/docs/execution-providers/" rel="noopener noreferrer">https://onnxruntime.ai/docs/execution-providers/</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-9" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-9-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-10">
<p>Apple, “MLComputeUnits.” <a href="https://developer.apple.com/documentation/coreml/mlcomputeunits" rel="noopener noreferrer">https://developer.apple.com/documentation/coreml/mlcomputeunits</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11">
<p>ggml-org, “llama.cpp.” <a href="https://github.com/ggml-org/llama.cpp" rel="noopener noreferrer">https://github.com/ggml-org/llama.cpp</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-11" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-11-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-12">
<p>Hugging Face, “Safetensors.” <a href="https://github.com/huggingface/safetensors" rel="noopener noreferrer">https://github.com/huggingface/safetensors</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-12" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-12-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-13">
<p>Apple, “ML Program Conversion.” <a href="https://apple.github.io/coremltools/docs-guides/source/new-conversion-options.html" rel="noopener noreferrer">https://apple.github.io/coremltools/docs-guides/source/new-conversion-options.html</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-13" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-14">
<p>ggml-org, “llama.cpp CLI.” <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/cli/README.md" rel="noopener noreferrer">https://github.com/ggml-org/llama.cpp/blob/master/tools/cli/README.md</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-14" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-14-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-15">
<p>Hugging Face, “Big Model Inference.” <a href="https://huggingface.co/docs/accelerate/v0.29.0/en/concept_guides/big_model_inference" rel="noopener noreferrer">https://huggingface.co/docs/accelerate/v0.29.0/en/concept_guides/big_model_inference</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-15" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-15-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/which-model-can-run-on-your-device/#user-content-fnref-15-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Edge &amp; on-device</category>
            <category>Quantization</category>
            <category>Memory</category>
            <category>Accelerators</category>
            <enclosure length="67270" type="image/png" url="https://blog.ecitis.org/open-graph/which-model-can-run-on-your-device.png"/>
        </item>
        <item>
            <title><![CDATA[Groq and Cerebras Accelerators: Deterministic Streaming and Wafer-Scale Compute]]></title>
            <link>https://blog.ecitis.org/groq-cerebras-accelerators/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/groq-cerebras-accelerators/</guid>
            <pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare Groq compiler-scheduled tensor streaming with Cerebras wafer-scale compute, defect tolerance, and weight streaming.]]></description>
            <content:encoded><![CDATA[<p>Groq and Cerebras attack the accelerator memory problem from different physical directions. Groq makes instruction timing and tensor movement explicit to a compiler across a streaming processor and scale-out network. Cerebras places a grid of compute cores, local memory, and fabric across one wafer, then maps tensor work spatially.</p>
<p>Both designs reduce dependence on a conventional cache hierarchy. Their execution models, scaling boundaries, and software constraints are not interchangeable.</p>
<p>Product throughput and price depend on the model, batch, precision, software revision, and service configuration, so this comparison stays at the architecture level.</p>
<figure><figcaption><strong>Two approaches to keeping data close to compute</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/groq-cerebras-accelerators-0.webp" width="512" height="477" alt="Two approaches to keeping data close to compute" loading="lazy" decoding="async"></figure>
<h2 id="memory-movement-sets-the-architecture">Memory movement sets the architecture</h2>
<p>Autoregressive decoding repeatedly reads model parameters and cached attention state for each generated token. At low batch size, arithmetic units can wait for data. General-purpose accelerators address this through HBM, caches, many threads, and runtime scheduling.</p>
<p>Groq and Cerebras instead expose more data movement to compilation:</p>
<ul>
<li>Groq schedules operations and communication at known times across distributed on-chip SRAM.</li>
<li>Cerebras maps work onto a two-dimensional core grid with core-local SRAM and a wafer-scale fabric.</li>
</ul>
<p>The shared principle is compiler-visible locality. The implementation differs in time, space, and system scale.</p>
<h2 id="groqs-tensor-streaming-execution">Groq’s tensor streaming execution</h2>
<p>Groq’s architecture is described in the ISCA paper as a software-defined Tensor Streaming Processor. Functional units act as one logical core and are statically scheduled at compile time. Architecturally visible state includes SRAM and stream registers, allowing the compiler to express computation and communication dependencies as a directed acyclic graph.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-isca" id="user-content-fnref-groq-isca">1</a></sup></p>
<h3 id="static-issue-instead-of-runtime-arbitration">Static issue instead of runtime arbitration</h3>
<p>A conventional out-of-order processor discovers readiness and resource conflicts while running. A GPU schedules warps and kernels dynamically around latency and occupancy.</p>
<p>Groq moves this work into the compiler:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>graph lowering</span></span>
<span class="line"><span>  → place operations on function units</span></span>
<span class="line"><span>  → allocate SRAM and stream registers</span></span>
<span class="line"><span>  → schedule operand movement</span></span>
<span class="line"><span>  → schedule compute</span></span>
<span class="line"><span>  → schedule chip-to-chip transfers</span></span>
<span class="line"><span>  → emit one coordinated program</span></span></code></pre>
<p>Published Groq technical material states that the compiler accounts for instruction latency and schedules data and instructions at specific times, producing deterministic execution time for a compiled workload.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-determinism" id="user-content-fnref-groq-determinism">2</a></sup></p>
<p>Deterministic here means the architectural schedule is known. It does not mean a cloud request has no queueing, network, admission-control, or host-side variance.</p>
<h3 id="distributed-sram-is-primary-storage">Distributed SRAM is primary storage</h3>
<p>Groq describes its on-chip SRAM as primary storage rather than a hardware-managed cache.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-lpu" id="user-content-fnref-groq-lpu">3</a></sup> Data placement is explicit. The compiler knows which SRAM region holds an operand and when that operand moves to a function unit.</p>
<p>This removes cache miss and coherence behavior from the execution schedule. It creates another requirement: the compiler must fit and coordinate program state across available SRAM and chips.</p>
<p>For a model larger than one processor’s SRAM, compilation partitions the graph and parameters across processors. Activations flow between stages while weights needed for each assigned stage remain local.</p>
<h3 id="function-units-form-a-streaming-pipeline">Function units form a streaming pipeline</h3>
<p>Groq’s public architecture description uses a programmable assembly-line model. Data and instructions move through conveyor-like streams between SIMD function units, with software determining source, operation, and destination.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-assembly" id="user-content-fnref-groq-assembly">4</a></sup></p>
<p>An abstract transformer layer can be scheduled as:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>input stream</span></span>
<span class="line"><span>  → normalization unit</span></span>
<span class="line"><span>  → matrix units for projections</span></span>
<span class="line"><span>  → vector units for activation and scaling</span></span>
<span class="line"><span>  → matrix units for output projection</span></span>
<span class="line"><span>  → output stream</span></span></code></pre>
<p>The exact placement is compiler-dependent. The important distinction is that hardware does not discover the flow by filling and draining a cache hierarchy.</p>
<h2 id="groq-scale-out">Groq scale-out</h2>
<p>Static scheduling extends beyond one processor. The Groq scale-out paper describes a deterministic network in which software schedules vectors on physical links while accounting for channel bandwidth and latency. Hardware does not apply backpressure because unscheduled stalls would break the global timing model.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-isca" id="user-content-fnref-groq-isca-2">1</a></sup></p>
<p>Instead, local SRAM buffers tensor vectors between hops. Explicit send and receive instructions impose a total ordering that the compiler can reason about.</p>
<p>The published system topology groups eight processors in a node with internal direct links. Nodes connect into a larger topology using a mix of electrical and optical paths.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-isca" id="user-content-fnref-groq-isca-3">1</a></sup> The paper specifies this topology for the system it describes; deployed Groq services may use a different generation.</p>
<h3 id="consequences-of-scheduled-networking">Consequences of scheduled networking</h3>
<p>Architectural fact:</p>
<ul>
<li>The compiler schedules communication paths and times.</li>
<li>The network avoids hardware backpressure.</li>
<li>SRAM provides intermediate buffering.</li>
<li>Compute and communication share one dependency schedule.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-groq-isca" id="user-content-fnref-groq-isca-4">1</a></sup></li>
</ul>
<p>Analysis:</p>
<ul>
<li>Predictable communication can reduce collective tail effects when the compiled placement matches the workload.</li>
<li>The compiler carries more responsibility for placement, buffering, and avoiding link conflicts.</li>
<li>Dynamic workloads need bounded shapes, compilation strategies, or runtime routing outside the statically scheduled core.</li>
<li>A topology or hardware change can require recompilation because timing is part of correctness.</li>
</ul>
<h2 id="cerebras-wafer-scale-integration">Cerebras wafer-scale integration</h2>
<p>Cerebras builds one processor across most of a silicon wafer rather than cutting the wafer into many reticle-sized dies. The third-generation Wafer-Scale Engine contains 900,000 active AI cores, 44 GB of on-chip SRAM, and 21 PB/s of aggregate on-chip memory bandwidth.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-wse3-inference" id="user-content-fnref-wse3-inference">5</a></sup></p>
<p>These are aggregate on-wafer specifications. They should not be compared directly with one memory channel or one network port.</p>
<h3 id="core-local-compute-and-memory">Core-local compute and memory</h3>
<p>The WSE organizes cores in a two-dimensional grid. Each core combines compute, local SRAM, and router access. The fabric moves data between neighboring and distant cores while the compiler maps tensor dimensions across the grid.</p>
<p>Cerebras describes transformer mapping by splitting hidden dimensions across one fabric axis and batch and sequence dimensions across the other, enabling weight broadcast and reductions over mapped tensor dimensions.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-cerebras-deep" id="user-content-fnref-cerebras-deep">6</a></sup></p>
<p>This spatial mapping keeps activations near the cores that consume them. It replaces a small set of large compute engines sharing external memory with many smaller engines attached to distributed local memory.</p>
<h3 id="on-wafer-fabric">On-wafer fabric</h3>
<p>A wafer-scale design needs communication across reticle boundaries. Cerebras fabric links continue across those boundaries so the wafer acts as one processor. The compiler routes tensor streams through the mesh and allocates local memory.</p>
<p>The physical distance across a wafer is larger than across one conventional die, so mapping still matters. Nearby placement reduces hops for high-volume communication. The compiler must balance compute, memory, and fabric use.</p>
<h2 id="defect-tolerance">Defect tolerance</h2>
<p>A wafer encounters manufacturing defects. Discarding the complete processor when one core or link fails would make wafer-scale integration impractical.</p>
<p>Cerebras uses small fault-tolerant cores, spare cores, redundant fabric paths, and routing around defects. Its published WSE-3 material states that the physical design contains 970,000 cores and ships with 900,000 active cores. It reports an approximate core area of 0.05 square millimeters.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-defects" id="user-content-fnref-defects">7</a></sup></p>
<p>Architectural fact:</p>
<ul>
<li>Defective cores can be disabled.</li>
<li>Spare cores replace unavailable capacity.</li>
<li>Redundant communication paths route around defective regions.</li>
<li>The compiled logical machine is presented over the usable physical fabric.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-defects" id="user-content-fnref-defects-2">7</a></sup></li>
</ul>
<p>Analysis:</p>
<ul>
<li>Fine-grained redundancy limits the capacity lost to a local defect.</li>
<li>Fault maps become an input to placement and routing.</li>
<li>Software abstraction is essential because applications cannot target one fixed physical core grid across manufactured wafers.</li>
</ul>
<p>Do not read the active-core ratio as a universal manufacturing yield. Yield also includes packaging, power delivery, cooling, and system qualification.</p>
<h2 id="weight-streaming-for-training">Weight streaming for training</h2>
<p>On-wafer SRAM cannot hold every parameter and optimizer state of a large model. Cerebras Weight Streaming separates parameter storage from compute.</p>
<p>MemoryX stores weights and optimizer state. It streams layer weights to the Wafer-Scale Engine. Activations remain on the engine. During backpropagation, gradients return through SwarmX, which reduces gradients from multiple systems before MemoryX applies updates.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-weight-streaming" id="user-content-fnref-weight-streaming">8</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>MemoryX</span></span>
<span class="line"><span>  │ broadcast layer weights</span></span>
<span class="line"><span>  ▼</span></span>
<span class="line"><span>SwarmX tree</span></span>
<span class="line"><span>  ├── WSE: local activations → gradients</span></span>
<span class="line"><span>  ├── WSE: local activations → gradients</span></span>
<span class="line"><span>  └── WSE: local activations → gradients</span></span>
<span class="line"><span>  ▲</span></span>
<span class="line"><span>  │ reduce gradients</span></span>
<span class="line"><span>MemoryX: optimizer update</span></span></code></pre>
<p>The diagram is structural. It does not specify a cluster size or performance result.</p>
<p>This execution model uses data parallelism across Wafer-Scale Engines while SwarmX handles weight broadcast and gradient reduction. Each engine runs the same model mapping on a different data shard.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-weight-streaming" id="user-content-fnref-weight-streaming-2">8</a></sup></p>
<h3 id="inference-placement-differs">Inference placement differs</h3>
<p>Cerebras inference material describes storing model weights in on-wafer SRAM and splitting models larger than one wafer at layer boundaries across systems.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-wse3-inference" id="user-content-fnref-wse3-inference-2">5</a></sup> That differs from training Weight Streaming, where MemoryX supplies weights layer by layer and performs optimizer updates.</p>
<p>The term weight streaming therefore needs context:</p>
<ul>
<li>Training: stream layer weights from MemoryX and return gradients.</li>
<li>Inference service: place weights on one or more wafer systems and pipeline layer boundaries as required by model size.</li>
</ul>
<h2 id="comparing-the-compiler-contracts">Comparing the compiler contracts</h2>








































<div class="table-wrap"><table><thead><tr><th>Property</th><th>Groq LPU</th><th>Cerebras WSE</th></tr></thead><tbody><tr><td>Primary abstraction</td><td>Statically timed tensor streams</td><td>Spatial graph mapping across a wafer</td></tr><tr><td>Local memory</td><td>Compiler-managed distributed SRAM</td><td>Core-local distributed SRAM</td></tr><tr><td>Execution unit</td><td>One logical scheduled core across function units</td><td>Grid of independently programmable AI cores</td></tr><tr><td>Communication</td><td>Explicitly scheduled on-chip and chip-to-chip streams</td><td>Routes over an on-wafer mesh</td></tr><tr><td>Scale beyond device</td><td>Scheduled processor network</td><td>System pipeline or Weight Streaming cluster</td></tr><tr><td>Large training state</td><td>Partitioning depends on compiled system</td><td>MemoryX disaggregates weights and optimizer state</td></tr></tbody></table></div>
<p>The table describes architecture, not relative performance.</p>
<h2 id="workload-shape-and-compilation">Workload shape and compilation</h2>
<p>Both systems reward stable tensor shapes. Placement, memory allocation, and communication scheduling improve when dimensions are known.</p>
<p>Variable request lengths create a serving problem:</p>
<ul>
<li>Padding wastes compute and memory movement.</li>
<li>Bucketing increases the number of compiled shapes.</li>
<li>Dynamic batching changes arrival and queueing behavior.</li>
<li>Long contexts expand attention state even when weights remain fixed.</li>
</ul>
<p>The serving layer must map dynamic requests to compiled execution plans. Architecture-level determinism does not remove batching policy.</p>
<h2 id="memory-capacity-versus-bandwidth">Memory capacity versus bandwidth</h2>
<p>High on-chip bandwidth does not imply that every model fits in local SRAM.</p>
<p>For uncompressed weights:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>weight bytes</mtext><mo>=</mo><mtext>parameter count</mtext><mo>×</mo><mtext>bytes per parameter</mtext></mrow></semantics></math>
<p>A model using a 16-bit representation needs two bytes per parameter. Cerebras uses this arithmetic in its inference architecture explanation and reports 44 GB of WSE-3 SRAM.<sup><a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fn-wse3-inference" id="user-content-fnref-wse3-inference-3">5</a></sup> Runtime memory also includes activations, attention state, buffers, and code.</p>
<p>Groq distributes weights across processor SRAM when the model exceeds one device. Cerebras can divide an inference model at layer boundaries across systems. Both approaches turn capacity into a placement problem.</p>
<h2 id="operational-failure-modes">Operational failure modes</h2>
<h3 id="compilation-misses-the-deployed-shape">Compilation misses the deployed shape</h3>
<p>A request outside compiled bounds needs rejection, a fallback plan, or a new compilation. Silent padding can create latency and capacity regressions.</p>
<h3 id="pipeline-imbalance">Pipeline imbalance</h3>
<p>If one stage takes longer, faster stages wait and buffers fill. Balance must include computation, memory, and communication.</p>
<h3 id="host-path-dominates">Host path dominates</h3>
<p>Tokenization, request scheduling, output streaming, and admission control remain outside accelerator cores. Low accelerator latency can expose these costs.</p>
<h3 id="context-state-exceeds-planning-assumptions">Context state exceeds planning assumptions</h3>
<p>Attention state grows with active sequence length and concurrency. Weight locality does not remove key-value state capacity.</p>
<h3 id="scale-out-faults-invalidate-timing-or-placement">Scale-out faults invalidate timing or placement</h3>
<p>Groq’s scheduled network depends on known links and timing. Cerebras placement depends on available wafer and system resources. Fault handling needs a supported remap, recompile, or job restart path.</p>
<h3 id="vendor-benchmark-scope-is-unclear">Vendor benchmark scope is unclear</h3>
<p>Tokens per second can mean per user, per stream, or aggregate server throughput. Batch, prompt length, output length, precision, and model revision must accompany any comparison.</p>
<h2 id="benchmarking-the-architectures">Benchmarking the architectures</h2>
<p>Use the same model weights, tokenizer, precision policy, prompt distribution, output lengths, and correctness checks.</p>
<p>Measure:</p>
<ul>
<li>Time to first token.</li>
<li>Inter-token latency distribution.</li>
<li>Per-request and aggregate throughput.</li>
<li>Queueing and admission delay.</li>
<li>Concurrency at the reported latency.</li>
<li>Compile time and cache hit rate.</li>
<li>Energy at the system boundary.</li>
<li>Accuracy against the reference implementation.</li>
<li>Failure and fallback rate for unsupported shapes.</li>
</ul>
<p>Separate architecture measurements from managed-service measurements. A cloud endpoint includes routing, capacity, software releases, and tenant load that are not chip properties.</p>
<h2 id="architectural-selection">Architectural selection</h2>
<p>Groq’s design fits workloads where a compiler can exploit a stable graph and where deterministic tensor and network schedules support latency-oriented execution. The cost is a stronger compiler contract and less reliance on dynamic hardware scheduling.</p>
<p>Cerebras fits workloads that benefit from mapping large tensor graphs across a dense core and memory fabric. Wafer-scale integration reduces off-device communication inside the mapped graph, while defect tolerance and compilation make the physical wafer usable as one machine.</p>
<p>Distributed-system concerns remain. Groq moves coordination into a static program across processors. Cerebras moves more of the graph inside one wafer and uses explicit system-level pipelines or Weight Streaming beyond it.</p>
<p>Trace a token through each machine. On Groq, the compiled schedule coordinates SRAM, functional units, and links. On Cerebras, placement across the wafer determines local memory use and fabric traffic. Fault recovery belongs in the comparison because a remap, recompile, or restart changes system behavior.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-groq-isca">
<p><a href="https://doi.org/10.1145/3470496.3527405" rel="noopener noreferrer">Abts et al., “A Software-defined Tensor Streaming Multiprocessor for Large-scale Machine Learning,” ISCA, 2022</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-isca" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-isca-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-isca-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-isca-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-groq-determinism">
<p><a href="https://groq.com/GroqDocs/TechDoc_Predictability.pdf" rel="noopener noreferrer">Groq, “Determinism and the Tensor Streaming Processor”</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-determinism" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-groq-lpu">
<p><a href="https://groq.com/blog/inside-the-lpu-deconstructing-groq-speed" rel="noopener noreferrer">Groq, “Inside the LPU: Deconstructing Groq’s Speed,” August 2025</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-lpu" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-groq-assembly">
<p><a href="https://groq.com/blog/the-groq-lpu-explained" rel="noopener noreferrer">Groq, “What is a Language Processing Unit?”</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-groq-assembly" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-wse3-inference">
<p><a href="https://www.cerebras.ai/blog/introducing-cerebras-inference-ai-at-instant-speed" rel="noopener noreferrer">Cerebras, “Introducing Cerebras Inference,” August 2024</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-wse3-inference" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-wse3-inference-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-wse3-inference-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-cerebras-deep">
<p><a href="https://www.cerebras.ai/blog/cerebras-architecture-deep-dive-first-look-inside-the-hw-sw-co-design-for-deep-learning" rel="noopener noreferrer">Cerebras, “Architecture Deep Dive: HW/SW Co-Design for Deep Learning”</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-cerebras-deep" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-defects">
<p><a href="https://www.cerebras.ai/blog/100x-defect-tolerance-how-cerebras-solved-the-yield-problem" rel="noopener noreferrer">Cerebras, “100x Defect Tolerance: How Cerebras Solved the Yield Problem,” January 2025</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-defects" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-defects-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-weight-streaming">
<p><a href="https://www.cerebras.ai/blog/linear-scaling-made-possible-with-weight-streaming" rel="noopener noreferrer">Cerebras, “Linear Scaling Made Possible with Weight Streaming,” September 2022</a>. <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-weight-streaming" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/groq-cerebras-accelerators/#user-content-fnref-weight-streaming-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>Accelerators</category>
            <category>LLM serving</category>
            <category>Latency</category>
            <enclosure length="71009" type="image/png" url="https://blog.ecitis.org/open-graph/groq-cerebras-accelerators.png"/>
        </item>
        <item>
            <title><![CDATA[Inference SLOs: TTFT, TPOT, and Why Throughput Is the Wrong Target]]></title>
            <link>https://blog.ecitis.org/inference-slos/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/inference-slos/</guid>
            <pubDate>Fri, 17 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Define latency objectives for streaming generation, model the queueing behind them, and benchmark goodput instead of peak tokens per second.]]></description>
            <content:encoded><![CDATA[<p>“Tokens per second” is usually reported as one number. It is at least three, and separating them is what turns capacity planning into something you can do precisely: how fast a single stream produces tokens, how many tokens a replica produces across all streams, and how many reach a user within an acceptable time.</p>
<p>The third is goodput, and it is the one worth optimizing. It maps directly to delivered value, it has a clear peak, and a single benchmark sweep locates that peak for a given model and SLO.</p>
<figure><figcaption><strong>Raising batch size buys throughput and spends per-token latency</strong></figcaption><p><a href="https://blog.ecitis.org/inference-slos/">View this figure on the site &#8594;</a></p></figure>
<h2 id="four-latency-quantities">Four latency quantities</h2>
<p><strong>TTFT, time to first token.</strong> Queue wait plus prefill. What a user perceives as responsiveness. Scales with prompt length and with load.</p>
<p><strong>TPOT, time per output token.</strong> Steady-state inter-token latency during decode. Determines whether streaming text feels smooth. Roughly constant per replica configuration, degrading with batch size.</p>
<p><strong>End-to-end latency.</strong> <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>TTFT</mtext><mo>+</mo><mtext>TPOT</mtext><mo>×</mo><mo stretchy="false">(</mo><mi mathvariant="normal">ℓ</mi><mo>−</mo><mn>1</mn><mo stretchy="false">)</mo></mrow></semantics></math> for an <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ℓ</mi></mrow></semantics></math>-token response.</p>
<p><strong>Throughput.</strong> Output tokens per second across all concurrent requests on a replica.</p>
<p><strong>Goodput.</strong> Throughput counting only requests that met their SLO. This is the number that maps to delivered value, and it is the one nobody reports by default.</p>
<p>The distinction between the last two is the whole argument. Throughput rises monotonically with load until saturation. Goodput rises, peaks, and then falls, because past a point additional load pushes requests past their deadline, and a request that misses its deadline consumed resources and delivered nothing.</p>
<h2 id="setting-the-numbers-from-the-interaction">Setting the numbers from the interaction</h2>
<p>SLOs should come from what the response is for, not from what the hardware happens to do.</p>









































<div class="table-wrap"><table><thead><tr><th>Interaction</th><th>TTFT target</th><th>TPOT target</th><th>Rationale</th></tr></thead><tbody><tr><td>Chat, streamed</td><td>&lt; 500 ms</td><td>&lt; 50 ms</td><td>~20 tok/s exceeds comfortable reading speed</td></tr><tr><td>Code completion, inline</td><td>&lt; 200 ms</td><td>&lt; 30 ms</td><td>Competes with the user’s own typing</td></tr><tr><td>Agent tool-call step</td><td>&lt; 1 s</td><td>Irrelevant</td><td>Nothing is streamed to a human</td></tr><tr><td>Batch summarization</td><td>Minutes</td><td>Irrelevant</td><td>Only end-to-end throughput matters</td></tr><tr><td>Voice, real-time</td><td>&lt; 300 ms</td><td>&lt; 25 ms</td><td>Must outrun speech synthesis</td></tr></tbody></table></div>
<p>Two observations. For non-streamed workloads TPOT is not a user-facing quantity at all, and enforcing a TPOT SLO there simply lowers throughput for no benefit. And for agentic workloads, the meaningful latency is the sum over the whole trajectory. A ten-step agent with a one-second per-step budget is a ten-second interaction, so per-step SLOs must be derived from the trajectory budget rather than set independently.</p>
<p>This is why one deployment cannot serve every workload well. A replica pool tuned for interactive chat runs at a batch size that leaves throughput on the table; the same pool tuned for batch work has interactive TPOT far outside target. Separate pools with separate configurations is usually cheaper than one compromise pool.</p>
<h2 id="why-the-tradeoff-exists">Why the tradeoff exists</h2>
<p>Decode is memory bound. Producing one token for one sequence requires reading all model weights. Producing one token for <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> sequences requires reading them once. Per-step time is roughly:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>T</mi><mtext>step</mtext></msub><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo><mo>≈</mo><munder><munder><mfrac><mi>W</mi><msub><mi>B</mi><mtext>mem</mtext></msub></mfrac><mo stretchy="true">⏟</mo></munder><mrow><mtext>weights, constant in </mtext><mi>n</mi></mrow></munder><mo>+</mo><munder><munder><mrow><mi>n</mi><mo>⋅</mo><mfrac><mi>K</mi><msub><mi>B</mi><mtext>mem</mtext></msub></mfrac></mrow><mo stretchy="true">⏟</mo></munder><mtext>KV reads</mtext></munder><mo>+</mo><munder><munder><mrow><msub><mi>f</mi><mtext>compute</mtext></msub><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><mo stretchy="true">⏟</mo></munder><mtext>negligible until saturation</mtext></munder></mrow></semantics></math>
<p>Throughput is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mi mathvariant="normal">/</mi><msub><mi>T</mi><mtext>step</mtext></msub><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math> and TPOT is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>T</mi><mtext>step</mtext></msub><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math>. At small <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> the constant weight term dominates, so doubling <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> nearly doubles throughput at nearly unchanged TPOT, which is the reason batching exists. As <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> grows, the KV term and then compute saturation make <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>T</mi><mtext>step</mtext></msub></mrow></semantics></math> grow linearly, and throughput flattens while TPOT keeps climbing.</p>
<p>The KV term is why attention architecture shows up in a latency budget. A model with GQA at 8 groups has one eighth the per-sequence KV read of full multi-head attention, which moves the knee to a much larger batch size. Memory-efficient attention is a latency feature, not only a capacity feature.</p>
<h2 id="queueing-puts-the-tail-where-it-is">Queueing puts the tail where it is</h2>
<p>Utilization determines the tail more than service time does. Under M/M/1-ish assumptions the expected wait is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>W</mi><mo>=</mo><mfrac><mi>ρ</mi><mrow><mn>1</mn><mo>−</mo><mi>ρ</mi></mrow></mfrac><mo>⋅</mo><mfrac><mn>1</mn><mi>μ</mi></mfrac></mrow></semantics></math>
<p>At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ρ</mi><mo>=</mo><mn>0.5</mn></mrow></semantics></math>, wait is one service time. At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ρ</mi><mo>=</mo><mn>0.9</mn></mrow></semantics></math>, nine. At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ρ</mi><mo>=</mo><mn>0.95</mn></mrow></semantics></math>, nineteen. LLM serving is not M/M/1, since service times are heavy-tailed because output lengths are, but the shape holds and is worse: high service-time variance amplifies the tail further.</p>
<p>Two practical consequences.</p>
<p><strong>Target utilization must leave headroom.</strong> Running a replica pool at 90% average utilization guarantees a bad p99 even if the mean looks fine. Interactive pools generally need to sit at 60–75%.</p>
<p><strong>Variance is a lever.</strong> Chunked prefill exists partly to reduce service-time variance: bounding step time bounds the variance that queueing theory then amplifies. Reducing variance improves the tail more than a proportional reduction in mean service time.</p>
<h2 id="benchmarking-that-means-something">Benchmarking that means something</h2>
<p>A benchmark that reports one number at one concurrency is not usable for capacity planning. What is needed is a sweep that produces the goodput curve.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> sweep</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">client</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> workload</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> concurrencies</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> slo</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    rows </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> c </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> concurrencies</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        r </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> run_closed_loop</span><span style="color:var(--astro-code-token-punctuation)">(client, workload, concurrency</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">c, duration_s</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">180</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        met </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [x </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> r</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">requests</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">               if</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">ttft </span><span style="color:var(--astro-code-token-keyword)">&lt;=</span><span style="color:var(--astro-code-foreground)"> slo</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">ttft </span><span style="color:var(--astro-code-token-keyword)">and</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">tpot_p50 </span><span style="color:var(--astro-code-token-keyword)">&lt;=</span><span style="color:var(--astro-code-foreground)"> slo</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">tpot]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        rows</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">({</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;concurrency&quot;</span><span style="color:var(--astro-code-token-punctuation)">: c,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;throughput_tok_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">: r.output_tokens </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> r.wall_s,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;goodput_tok_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(x.output_tokens </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> x </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> met) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> r.wall_s,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;ttft_p50&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-function)">pct</span><span style="color:var(--astro-code-token-punctuation)">(r, </span><span style="color:var(--astro-code-token-string-expression)">&quot;ttft&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">50</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;ttft_p99&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-function)">pct</span><span style="color:var(--astro-code-token-punctuation)">(r, </span><span style="color:var(--astro-code-token-string-expression)">&quot;ttft&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">99</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;tpot_p50&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-function)">pct</span><span style="color:var(--astro-code-token-punctuation)">(r, </span><span style="color:var(--astro-code-token-string-expression)">&quot;tpot&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">50</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;tpot_p99&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-function)">pct</span><span style="color:var(--astro-code-token-punctuation)">(r, </span><span style="color:var(--astro-code-token-string-expression)">&quot;tpot&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">99</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;slo_attainment&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-function)">len</span><span style="color:var(--astro-code-token-punctuation)">(met) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(r.requests),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        })</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> rows</span></span></code></pre>
<p>The operating point is the concurrency that maximizes <code>goodput_tok_s</code>, not <code>throughput_tok_s</code>. On most configurations these differ substantially, and the goodput peak sits well to the left.</p>
<p>Details that decide whether the numbers transfer to production:</p>
<p><strong>Use the real length distribution.</strong> Fixed 512-in/128-out prompts produce a benchmark whose variance is zero and whose queueing behavior therefore bears no relation to production. Replay a sampled length distribution from actual traffic.</p>
<p><strong>Reproduce prefix structure.</strong> If production requests share a long system prompt, a benchmark with unique prompts measures a workload with no cache reuse and will overstate cost by a large factor. If they do not share prefixes and the benchmark reuses one prompt, it understates it by the same factor.</p>
<p><strong>Open loop for latency, closed loop for capacity.</strong> A closed-loop harness with fixed concurrency cannot produce a queue, because it never sends more than the system can absorb. Latency SLOs must be measured under Poisson arrivals at a fixed rate; only then does the tail reflect real queueing.</p>
<p><strong>Warm first.</strong> The first requests pay autotuning, workspace allocation, and graph capture. Discard them explicitly rather than letting them inflate the p99.</p>
<p><strong>Report tokenizer-consistent counts.</strong> Tokens per second is meaningless across models with different vocabularies. Characters or words per second is more comparable when comparing model families; tokens per second is fine within one.</p>
<h2 id="sources-of-tail-latency-and-their-signatures">Sources of tail latency, and their signatures</h2>
<p>When p99 TTFT sits well above p50, the cause is usually one of a short list, and each leaves a distinct signature:</p>
<p><strong>Long-prompt convoys.</strong> One large prefill stalls every stream on the replica. Fixed by chunked prefill; visible as a step-time spike correlated with prompt length.</p>
<p><strong>Preemption.</strong> Under cache pressure the scheduler swaps or recomputes a running request, and that request’s TPOT includes a full stall. Visible in the preemption counter.</p>
<p><strong>Cold replicas.</strong> An autoscaling event adds a replica that receives traffic before warmup finishes. Every request routed there in the first seconds is slow. Fixed by readiness gating, not by scaling policy.</p>
<p><strong>Head-of-line blocking in the queue.</strong> FIFO plus a heavy-tailed prompt distribution puts occasional enormous requests in front of everything.</p>
<p><strong>Cross-tenant interference.</strong> In multi-tenant deployments, one tenant’s burst degrades another’s tail. Per-tenant rate limits and separate SLO accounting are the only real fix.</p>
<p>Each of these has a distinct signature. Chasing p99 without separating them produces a lot of tuning and little improvement.</p>
<h2 id="an-error-budget-for-latency">An error budget for latency</h2>
<p>Latency SLOs are more useful stated as attainment rather than as a percentile:</p>
<blockquote>
<p>99% of chat requests over a rolling 28 days have TTFT ≤ 500 ms and median TPOT ≤ 50 ms.</p>
</blockquote>
<p>That formulation gives an error budget: 1% of requests may miss. The budget makes tradeoffs explicit and decidable. Enough budget remaining means capacity can be pushed for cost. Budget nearly exhausted means the next change should be headroom, not a feature.</p>
<p>It also disciplines the reporting. A single p99 over an arbitrary window can be gamed by window choice; attainment over a fixed window cannot.</p>
<p>Peak throughput remains a useful number, since it bounds what is possible. Goodput is the one to tune against, because it identifies the operating point where the hardware is working hardest on requests that will actually arrive in time. Finding it is a single sweep, and it holds until the model or the SLO changes.</p>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Latency</category>
            <category>Benchmarking</category>
            <category>LLM serving</category>
            <enclosure length="64498" type="image/png" url="https://blog.ecitis.org/open-graph/inference-slos.png"/>
        </item>
        <item>
            <title><![CDATA[Test-Time Compute: Search, Verification, and Stopping]]></title>
            <link>https://blog.ecitis.org/test-time-compute/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/test-time-compute/</guid>
            <pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Understand inference-time scaling as a budget allocation problem across candidate generation, verification, and adaptive stopping.]]></description>
            <content:encoded><![CDATA[<p>Pretraining spends compute once to change model weights. Test-time compute spends compute per problem to search for a better answer without changing the base checkpoint.</p>
<p>That extra compute can take several forms: a longer internal trace, parallel samples, iterative revisions, tree search, tool calls, or verifier passes. Counting generated tokens alone combines mechanisms with different costs and failure modes.</p>
<p>A deployment policy must answer:</p>
<blockquote>
<p>Given a fixed inference budget, which candidate should receive the next unit of compute?</p>
</blockquote>
<figure><figcaption><strong>A verifier should spend the next unit of compute on the best partial path</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/test-time-compute-0.webp" width="512" height="636" alt="A verifier should spend the next unit of compute on the best partial path" loading="lazy" decoding="async"></figure>
<h2 id="parallel-sampling-estimates-answer-mass">Parallel sampling estimates answer mass</h2>
<p>Self-consistency samples several reasoning paths and selects the answer supported by the largest share of paths.<sup><a href="https://blog.ecitis.org/test-time-compute/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> It replaces a single greedy trajectory with an empirical estimate:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mi>a</mi><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mi>K</mi></mfrac><munderover><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>K</mi></munderover><mn mathvariant="bold">1</mn><mo stretchy="false">[</mo><mi>f</mi><mo stretchy="false">(</mo><msub><mi>y</mi><mi>k</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mi>a</mi><mo stretchy="false">]</mo></mrow></semantics></math>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>k</mi></msub></mrow></semantics></math> is a sampled reasoning path and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><msub><mi>y</mi><mi>k</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math> extracts its final answer. The method works when diverse valid paths converge on the same answer more often than invalid paths do.</p>
<p>It can fail when:</p>
<ul>
<li>the model repeats the same misconception across samples</li>
<li>equivalent answers are normalized inconsistently</li>
<li>an open-ended task has no canonical answer to vote on</li>
<li>sampling temperature creates superficial variety without different reasoning</li>
<li>the correct path is rare but recognizable by a stronger verifier</li>
</ul>
<p>Majority vote is a verifier built from agreement. It does not inspect whether any path is logically valid.</p>
<h2 id="verifiers-turn-samples-into-search">Verifiers turn samples into search</h2>
<p>A verifier assigns a score to a complete answer or an intermediate state. Outcome reward models score the result. Process reward models score steps along the path.</p>
<p>The PRM800K work compared outcome and process supervision on mathematical problem solving and found stronger solution selection from process supervision in its evaluated setting.<sup><a href="https://blog.ecitis.org/test-time-compute/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> A dense process score allows search to stop extending a branch after an early error instead of paying to finish it.</p>
<p>For a partial trajectory <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mi>t</mi></msub></mrow></semantics></math>, a search policy can rank expansions with:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">priority</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>=</mo><mover accent="true"><mi>V</mi><mo>^</mo></mover><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>−</mo><mi>λ</mi><mtext> </mtext><mi mathvariant="normal">cost</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>+</mo><mi>η</mi><mtext> </mtext><mi mathvariant="normal">uncertainty</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>V</mi><mo>^</mo></mover></mrow></semantics></math> is the verifier’s predicted value. The cost term penalizes expensive branches. The uncertainty term can preserve exploration when the verifier is unsure. The coefficients are deployment choices, not universal constants.</p>
<p>Verifier quality sets the ceiling. If the verifier rewards polished invalid reasoning, more search directs compute toward the wrong region. The generator and verifier can also share correlated errors when they come from similar training data.</p>
<h2 id="difficulty-should-control-the-budget">Difficulty should control the budget</h2>
<p>Snell and collaborators compared test-time strategies under fixed compute and found that the best allocation depended on problem difficulty and base-model capability.<sup><a href="https://blog.ecitis.org/test-time-compute/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Their compute-optimal policy used prompt difficulty to choose how much inference compute to spend, improving efficiency over a fixed best-of-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow></semantics></math> baseline in the reported experiments.</p>
<p>The result rejects a uniform long reasoning budget for every request.</p>
<p>A practical router can use:</p>
<ul>
<li>confidence or entropy from an initial pass</li>
<li>disagreement among a small pilot sample</li>
<li>verifier margin between leading candidates</li>
<li>task class and known evaluation history</li>
<li>elapsed latency and remaining service budget</li>
<li>whether an external tool can check the result</li>
</ul>
<p>Easy requests should exit early. Difficult but tractable requests may benefit from branching. Requests outside the verifier’s domain should not receive more compute merely because confidence is low.</p>
<figure><figcaption><strong>Inference budgets should follow evidence about the request</strong> — Low confidence alone does not show that more generation will help.</figcaption><img src="https://blog.ecitis.org/feeds/figures/test-time-compute-1.webp" width="512" height="711" alt="Inference budgets should follow evidence about the request — Low confidence alone does not show that more generation will help." loading="lazy" decoding="async"></figure>
<h2 id="sequential-reasoning-and-parallel-search-are-different">Sequential reasoning and parallel search are different</h2>
<p>A long chain of thought performs serial computation. Best-of-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow></semantics></math> performs parallel exploration. Tree search mixes both.</p>



































<div class="table-wrap"><table><thead><tr><th>Strategy</th><th>Main benefit</th><th>Main bottleneck</th></tr></thead><tbody><tr><td>Longer trace</td><td>More serial intermediate computation</td><td>Drift and overthinking</td></tr><tr><td>Parallel samples</td><td>Diverse candidate paths</td><td>Repeated shared errors</td></tr><tr><td>Revision</td><td>Error correction after critique</td><td>Self-critique may preserve the premise</td></tr><tr><td>Tree search</td><td>Reallocate compute among partial paths</td><td>Verifier calibration</td></tr><tr><td>Tool use</td><td>External state or exact checks</td><td>Tool latency and integration errors</td></tr></tbody></table></div>
<p>The strategies are not interchangeable at equal token counts. Parallel sampling can batch well on accelerators. A serial trace adds latency token by token. Tool calls may dominate both.</p>
<p>Measure wall-clock latency, accelerator occupancy, and verifier cost alongside generated tokens.</p>
<h2 id="more-reasoning-can-reverse-a-correct-answer">More reasoning can reverse a correct answer</h2>
<p>Inference scaling has diminishing returns. A model can find a correct answer, continue exploring, and replace it with a wrong one.</p>
<p>Recent work on overthinking separates extra length from useful computation. A Google DeepMind study of open reasoning models identified over-verification and over-exploration as recurring structures in long traces on simple problems.<sup><a href="https://blog.ecitis.org/test-time-compute/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> Another 2026 preprint reports that optimal reasoning length varies with problem difficulty and that added reasoning can abandon an earlier correct answer.<sup><a href="https://blog.ecitis.org/test-time-compute/#user-content-fn-5" id="user-content-fnref-5">5</a></sup></p>
<p>This makes stopping a first-class component:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>generate a small candidate set</span></span>
<span class="line"><span>score candidates and estimate uncertainty</span></span>
<span class="line"><span>if one candidate is verified with adequate margin:</span></span>
<span class="line"><span>    stop</span></span>
<span class="line"><span>if promising branches remain:</span></span>
<span class="line"><span>    allocate another search round</span></span>
<span class="line"><span>otherwise:</span></span>
<span class="line"><span>    abstain, escalate, or use an external checker</span></span></code></pre>
<p>The stop condition should use calibrated evidence. A high self-reported confidence is weak if the model is miscalibrated on that task class.</p>
<h2 id="test-time-scaling-can-expose-reward-hacking">Test-time scaling can expose reward hacking</h2>
<p>Search optimizes the verifier more aggressively than one-shot decoding. That makes test-time compute an online form of proxy optimization.</p>
<p>Typical failures include:</p>
<p><strong>Verifier exploitation:</strong> candidates learn patterns that score well without solving the task.</p>
<p><strong>Loss of diversity:</strong> early verifier preferences collapse the search around one flawed approach.</p>
<p><strong>Answer leakage:</strong> a benchmark-specific checker exposes information unavailable in deployment.</p>
<p><strong>Cost blindness:</strong> marginal accuracy gains consume a service budget that would help more on another request.</p>
<p><strong>Unfaithful traces:</strong> the visible rationale satisfies a process rubric while the answer is driven by another internal computation.</p>
<p>Evaluate the complete generator-search-verifier system on adversarial cases. Evaluating the base model alone misses the mechanism used at inference.</p>
<h2 id="a-deployment-metric-stack">A deployment metric stack</h2>
<p>Accuracy is necessary but incomplete. A test-time compute service should report:</p>
<ul>
<li>quality as a function of total inference cost</li>
<li>latency distributions by task class</li>
<li>quality at matched wall-clock latency</li>
<li>verifier calibration and selection regret</li>
<li>pass rate of external checks</li>
<li>candidate diversity before and after pruning</li>
<li>fraction of requests stopped at each budget tier</li>
<li>cases where more compute changed a correct answer to an incorrect one</li>
</ul>
<p>Plot quality against compute across the relevant request distribution. One policy dominates another when it reaches equal or better quality at the same deployment cost.</p>
<p>Test-time compute is an inference algorithm that decides what to generate, how to judge it, where to branch, and when to stop. Extra compute pays off when those decisions are more reliable than the candidate errors they are meant to correct.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Xuezhi Wang et al., “Self-Consistency Improves Chain of Thought Reasoning in Language Models,” ICLR, 2023. <a href="https://arxiv.org/abs/2203.11171" rel="noopener noreferrer">https://arxiv.org/abs/2203.11171</a> <a href="https://blog.ecitis.org/test-time-compute/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Hunter Lightman et al., “Let’s Verify Step by Step,” ICLR, 2024. <a href="https://arxiv.org/abs/2305.20050" rel="noopener noreferrer">https://arxiv.org/abs/2305.20050</a> <a href="https://blog.ecitis.org/test-time-compute/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Charlie Snell et al., “Scaling LLM Test-Time Compute Optimally can be More Effective than Scaling Model Parameters,” ICLR, 2025. <a href="https://arxiv.org/abs/2408.03314" rel="noopener noreferrer">https://arxiv.org/abs/2408.03314</a> <a href="https://blog.ecitis.org/test-time-compute/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Xinliang Frederick Zhang et al., “Towards Structural Understanding of LLM Overthinking,” ACL, 2026. <a href="https://deepmind.google/research/publications/203490/" rel="noopener noreferrer">https://deepmind.google/research/publications/203490/</a> <a href="https://blog.ecitis.org/test-time-compute/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Shu Zhou et al., “When More Thinking Hurts: Overthinking in LLM Test-Time Compute Scaling,” arXiv, 2026. <a href="https://arxiv.org/abs/2604.10739" rel="noopener noreferrer">https://arxiv.org/abs/2604.10739</a> <a href="https://blog.ecitis.org/test-time-compute/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Reasoning</category>
            <category>Formal verification</category>
            <category>LLM serving</category>
            <enclosure length="60166" type="image/png" url="https://blog.ecitis.org/open-graph/test-time-compute.png"/>
        </item>
        <item>
            <title><![CDATA[Continuous Batching: How the Scheduler Decides Your Throughput]]></title>
            <link>https://blog.ecitis.org/continuous-batching-schedulers/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/continuous-batching-schedulers/</guid>
            <pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Design an LLM serving scheduler around iteration-level batching, chunked prefill, admission control, and preemption under cache pressure.]]></description>
            <content:encoded><![CDATA[<p>Kernel work gets most of the attention in inference optimization, and it produces the peak numbers. The scheduler is where a deployment collects them. Between a request arriving and a token being emitted, it decides which requests run this iteration, how much prompt to process, how to reclaim memory, and when to shed load.</p>
<p>That makes it one of the best returns available in a serving stack. Continuous batching, chunked prefill, and prefix-aware admission are model-independent and hardware-independent, and together they routinely separate two deployments of the same model on the same hardware by several times in serving cost.</p>
<figure><figcaption><strong>Static batching stalls on the longest generation; continuous batching does not</strong></figcaption><p><a href="https://blog.ecitis.org/continuous-batching-schedulers/">View this figure on the site &#8594;</a></p></figure>
<h2 id="what-continuous-batching-improves-on">What continuous batching improves on</h2>
<p>Batching amortizes weight reads. During decode, the model reads all its weights to produce one token per sequence, so a batch of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> costs roughly the same memory traffic as a batch of one. Throughput scales close to linearly until the matrix units saturate.</p>
<p>Static batching collects <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> requests, runs them together, and returns when all finish. With generation, output lengths vary by orders of magnitude in the same workload: a yes/no answer and a long document summary arrive at the same endpoint. Every finished sequence holds its slot, computing padding, until the longest one completes.</p>
<p>With output lengths <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">ℓ</mi><mn>1</mn></msub><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi mathvariant="normal">ℓ</mi><mi>n</mi></msub></mrow></semantics></math>, useful work fraction is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>U</mi><mo>=</mo><mfrac><mrow><munder><mo>∑</mo><mi>i</mi></munder><msub><mi mathvariant="normal">ℓ</mi><mi>i</mi></msub></mrow><mrow><mi>n</mi><mo>⋅</mo><munder><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>i</mi></munder><msub><mi mathvariant="normal">ℓ</mi><mi>i</mi></msub></mrow></mfrac></mrow></semantics></math>
<p>For a realistic long-tailed length distribution this lands around 0.2–0.4. Most of the batch is computing nothing.</p>
<p>Continuous batching, introduced as iteration-level scheduling in Orca, makes the batch mutable between forward passes: finished sequences leave immediately and queued ones join.<sup><a href="https://blog.ecitis.org/continuous-batching-schedulers/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> The batch is re-formed every step. Orca reported order-of-magnitude latency improvements at comparable throughput against static batching, and it is now the default design in essentially every production serving stack.</p>
<h2 id="step-loop-structure">Step loop structure</h2>
<p>The core is small. Everything hard is in the policies it calls.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> step</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">state</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    finished </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [s </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> s </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">running </span><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">done]</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> s </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> finished</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">release</span><span style="color:var(--astro-code-token-punctuation)">(s)</span><span style="color:var(--astro-code-token-comment)">              # free KV blocks, emit final token</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">running</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">remove</span><span style="color:var(--astro-code-token-punctuation)">(s)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    admitted </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">policy</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">admit</span><span style="color:var(--astro-code-token-punctuation)">(state.waiting, state.free_blocks)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">running</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">extend</span><span style="color:var(--astro-code-token-punctuation)">(admitted)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    batch </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">policy</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compose</span><span style="color:var(--astro-code-token-punctuation)">(state.running)</span><span style="color:var(--astro-code-token-comment)">   # prefill chunks + decode rows</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    logits </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">forward</span><span style="color:var(--astro-code-token-punctuation)">(batch)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tokens </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> state</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sampler</span><span style="color:var(--astro-code-token-punctuation)">(logits, batch.params)</span><span style="color:var(--astro-code-token-comment)">  # per-request sampling params</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> t </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> zip</span><span style="color:var(--astro-code-token-punctuation)">(batch.sequences, tokens):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(t)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">hit_stop</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> or</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">at_limit</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">done </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> True</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> tokens</span></span></code></pre>
<p>Three policies decide behavior: <code>admit</code>, <code>compose</code>, and the eviction path that <code>admit</code> consults when memory is short.</p>
<h2 id="prefill-and-decode-are-different-workloads">Prefill and decode are different workloads</h2>
<p>Prefill processes a whole prompt at once: a large matrix multiply, compute bound, high arithmetic intensity. Decode processes one token per sequence: small matrix-vector work, memory bound, latency dominated by reading weights.</p>
<p>Mixing them naively creates the dominant latency pathology in LLM serving. A 32k-token prompt entering prefill occupies the device for hundreds of milliseconds. Every decoding request in the batch waits. From the client’s perspective, an unrelated long prompt caused a multi-hundred-millisecond stall in a stream that was producing tokens every 20 ms.</p>
<p>Chunked prefill fixes this by splitting the prompt into fixed-size pieces and scheduling one piece per iteration alongside ongoing decode work.<sup><a href="https://blog.ecitis.org/continuous-batching-schedulers/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> The step’s token budget becomes a constant:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>B</mi><mo>=</mo><munder><munder><msub><mi>n</mi><mtext>decode</mtext></msub><mo stretchy="true">⏟</mo></munder><mtext>1 token each</mtext></munder><mo>+</mo><munder><munder><mi>c</mi><mo stretchy="true">⏟</mo></munder><mtext>prefill chunk</mtext></munder></mrow></semantics></math>
<p>with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow></semantics></math> chosen so that <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math> lands on the compute-saturating knee. Step time becomes roughly constant, which makes inter-token latency predictable and makes the whole system easier to reason about. A bounded step time is what allows any latency SLO to hold.</p>
<p>The cost is that a chunked prompt attends to previously chunked positions, so the attention kernel must handle a mixed batch of prefill chunks and decode rows. That is real kernel complexity, and it is why chunked prefill is a server feature rather than something you bolt on.</p>
<p>Choosing <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow></semantics></math>: too small and prefill throughput collapses under launch overhead; too large and it reintroduces the stall. A budget of 512 to 2048 tokens per step is the usual range, tuned by measuring inter-token latency at the p99 while long prompts are in flight.</p>
<h2 id="admission-control">Admission control</h2>
<p>Admission is where availability is won or lost. A request needs KV blocks for its prompt now and for its output later. Admitting on prompt length alone means a batch of requests can begin streaming and then collectively run out of memory.</p>
<p>Reserve for expected growth:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>blocks</mtext><mo stretchy="false">(</mo><mi>r</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mo fence="true">⌈</mo><mfrac><mrow><mi mathvariant="normal">∣</mi><msub><mi>p</mi><mi>r</mi></msub><mi mathvariant="normal">∣</mi></mrow><msub><mi>B</mi><mtext>blk</mtext></msub></mfrac><mo fence="true">⌉</mo></mrow><mo>+</mo><mrow><mo fence="true">⌈</mo><mfrac><mover accent="true"><msub><mi mathvariant="normal">ℓ</mi><mi>r</mi></msub><mo stretchy="true">^</mo></mover><msub><mi>B</mi><mtext>blk</mtext></msub></mfrac><mo fence="true">⌉</mo></mrow></mrow></semantics></math>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><msub><mi mathvariant="normal">ℓ</mi><mi>r</mi></msub><mo stretchy="true">^</mo></mover></mrow></semantics></math> is an estimate: <code>max_tokens</code> if the client supplied one, otherwise a high quantile of the observed output-length distribution for that route. Using the mean here guarantees periodic exhaustion; using the maximum wastes most of the cache. A p90 with preemption as the backstop is the practical setting.</p>
<p>Admission also needs a queueing discipline. FIFO is fair and produces bad tail latency when a few enormous prompts sit at the head. Shortest-job-first improves the mean and starves long requests. The usual compromise is FIFO with a bounded number of concurrent long-prompt admissions, so head-of-line blocking is capped without reordering the queue.</p>
<p>Load shedding belongs here too. When queue depth exceeds what can be served within the SLO, rejecting immediately with a retry hint is better than accepting work that will time out. A request that times out consumed prefill compute and produced nothing, which is the worst possible outcome per unit of hardware.</p>
<h2 id="preemption">Preemption</h2>
<p>Under memory pressure the scheduler must reclaim from running requests. There are two mechanisms:</p>
<p><strong>Swap.</strong> Copy the sequence’s KV blocks to host memory, free the device blocks, restore later. Cost is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2</mn><mo>×</mo></mrow></semantics></math> transfer over PCIe.</p>
<p><strong>Recompute.</strong> Discard the blocks entirely and re-run prefill over the prompt plus generated tokens on resume. Cost is a prefill pass.</p>
<p>Which is cheaper depends on length. Transfer cost grows linearly in context; recomputation cost also grows roughly linearly but with a much smaller constant, because prefill is compute-efficient while PCIe transfer is not. For short contexts recompute usually wins; for long ones swap does. vLLM implements both and selects by policy.<sup><a href="https://blog.ecitis.org/continuous-batching-schedulers/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>Victim selection is a policy decision with a fairness consequence. Preempting the newest request keeps completed work; it also means a request can be admitted and preempted repeatedly under sustained pressure. A preemption counter with escalating priority prevents that livelock:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> victim</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">running</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-function)"> max</span><span style="color:var(--astro-code-token-punctuation)">(running, key</span><span style="color:var(--astro-code-token-keyword)">=lambda</span><span style="color:var(--astro-code-token-parameter)"> s</span><span style="color:var(--astro-code-token-punctuation)">: (s.preempt_count </span><span style="color:var(--astro-code-token-keyword)">==</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">, s.arrival))</span></span></code></pre>
<p>Preemption rate is a first-class metric. A steady nonzero rate means admission is systematically over-optimistic, and the fix is the reservation estimate, not more preemption.</p>
<h2 id="prefix-aware-composition">Prefix-aware composition</h2>
<p>If requests share a common system prompt, a few-shot preamble, or a document being asked about repeatedly, the KV state for that prefix can be computed once and shared. This changes admission cost: a request whose 4k-token prefix is already cached needs blocks only for its suffix.</p>
<p>The scheduler should therefore consult the prefix cache before estimating cost, and prefer admitting requests with high reuse when memory is tight. At the fleet level the same logic applies to routing: sending a request to the replica holding its prefix can save more time than sending it to the least-loaded replica.</p>
<p>The tension is real. Prefix-affinity routing concentrates load; load-based routing destroys reuse. The correct objective is predicted completion time, which includes both:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>T</mi><mo stretchy="true">^</mo></mover><mo stretchy="false">(</mo><mi>r</mi><mo separator="true">,</mo><mtext>replica</mtext><mo stretchy="false">)</mo><mo>=</mo><msub><mi>T</mi><mtext>queue</mtext></msub><mo>+</mo><mfrac><mrow><mi mathvariant="normal">∣</mi><msub><mi>p</mi><mi>r</mi></msub><mi mathvariant="normal">∣</mi><mo>−</mo><mi mathvariant="normal">∣</mi><msub><mi>p</mi><mi>r</mi></msub><mo>∩</mo><mtext>cache</mtext><mi mathvariant="normal">∣</mi></mrow><mtext>prefill rate</mtext></mfrac><mo>+</mo><mover accent="true"><msub><mi mathvariant="normal">ℓ</mi><mi>r</mi></msub><mo stretchy="true">^</mo></mover><mo>⋅</mo><mtext>TPOT</mtext></mrow></semantics></math>
<p>Route to the minimum. This naturally prefers cache hits when queues are comparable and abandons them when a replica is saturated.</p>
<h2 id="speculative-decoding-changes-the-accounting">Speculative decoding changes the accounting</h2>
<p>With speculation, each step proposes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> draft tokens and verifies them in one forward pass. Accepted tokens per step become variable, which breaks a scheduler that assumes one token per sequence per step.</p>
<p>Two effects:</p>
<ul>
<li>Memory grows by up to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> blocks per sequence per step, so reservation must account for the draft length.</li>
<li>Batch composition changes: speculation helps most at low batch sizes where the device is idle, and can hurt at high batch sizes where verification competes with real work.</li>
</ul>
<p>An adaptive scheduler lowers <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> as batch size rises, and to zero under saturation. Fixed speculation depth is a throughput regression under load.</p>
<h2 id="instrumentation">Instrumentation</h2>
<p>The metrics that make scheduler behavior legible:</p>









































<div class="table-wrap"><table><thead><tr><th>Metric</th><th>What it reveals</th></tr></thead><tbody><tr><td>Batch size, decode rows only</td><td>Whether batching is working</td></tr><tr><td>Prefill tokens per step</td><td>Chunk budget utilization</td></tr><tr><td>Step time distribution</td><td>Whether chunking bounded the step</td></tr><tr><td>Queue depth and wait time</td><td>Admission pressure</td></tr><tr><td>Preemption rate, by mechanism</td><td>Over-optimistic reservation</td></tr><tr><td>Blocks free / reserved / shared</td><td>Cache accounting</td></tr><tr><td>Prefix hit tokens</td><td>Reuse actually realized</td></tr><tr><td>Requests rejected vs. timed out</td><td>Whether shedding is working</td></tr></tbody></table></div>
<p>Averages hide the failures. Step time in particular should be a histogram. A mean of 25 ms with a p99 of 400 ms is a chunked-prefill misconfiguration, and the mean will never show it.</p>
<h2 id="patterns-worth-instrumenting">Patterns worth instrumenting</h2>
<p><strong>Convoy from a single long prompt.</strong> Without chunked prefill, one 128k prompt stalls every stream on the replica. Visible as a step-time spike correlated with prompt length.</p>
<p><strong>Admission thrash.</strong> Requests admitted, preempted, re-admitted. Throughput drops while utilization looks high, because the device is busy recomputing.</p>
<p><strong>Cache-hit blindness.</strong> Routing ignores prefix locality, so a workload with a large shared system prompt pays full prefill on every request. Often a 2–5x cost difference in agent workloads.</p>
<p><strong>Sampling parameter bleed.</strong> A batched sampler that reads one temperature or one stop-sequence set applies it to every request. Silent, and only visible in output quality.</p>
<p><strong>Unbounded queues.</strong> Accepting everything under overload maximizes timeouts and wastes every unit of prefill compute spent on requests nobody is waiting for anymore.</p>
<p>The scheduler is where model-independent throughput comes from, which is what makes it such a rewarding place to invest. Two deployments of the same model on the same hardware can differ by several times in serving cost, and the difference is usually here rather than in the kernels, where it is reachable without touching the model at all.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Gyeong-In Yu et al., “Orca: A Distributed Serving System for Transformer-Based Generative Models,” OSDI, 2022. <a href="https://www.usenix.org/conference/osdi22/presentation/yu" rel="noopener noreferrer">https://www.usenix.org/conference/osdi22/presentation/yu</a> <a href="https://blog.ecitis.org/continuous-batching-schedulers/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Amey Agrawal et al., “Taming Throughput-Latency Tradeoff in LLM Inference with Sarathi-Serve,” OSDI, 2024. <a href="https://arxiv.org/abs/2403.02310" rel="noopener noreferrer">https://arxiv.org/abs/2403.02310</a> <a href="https://blog.ecitis.org/continuous-batching-schedulers/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Woosuk Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention,” SOSP, 2023. <a href="https://arxiv.org/abs/2309.06180" rel="noopener noreferrer">https://arxiv.org/abs/2309.06180</a> <a href="https://blog.ecitis.org/continuous-batching-schedulers/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Scheduling</category>
            <category>LLM serving</category>
            <category>Latency</category>
            <enclosure length="62928" type="image/png" url="https://blog.ecitis.org/open-graph/continuous-batching-schedulers.png"/>
        </item>
        <item>
            <title><![CDATA[Multi-Token Prediction: Training Draft Heads for Faster Decoding]]></title>
            <link>https://blog.ecitis.org/multi-token-prediction/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/multi-token-prediction/</guid>
            <pubDate>Sun, 12 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Train offset-specific draft heads, verify their candidates, and measure when denser supervision produces faster autoregressive decoding.]]></description>
            <content:encoded><![CDATA[<p>Next-token language modeling supervises one future token at each position. Multi-token prediction attaches several output heads to a shared representation and trains each head to predict a different future offset.</p>
<p>The training objective provides denser supervision. At inference, those heads can propose a short continuation for verification by the main model. The mechanism connects model training and speculative execution without requiring a separate draft model.</p>
<h2 id="multi-offset-objective">Multi-offset objective</h2>
<p>Given tokens <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>1</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>x</mi><mi>T</mi></msub></mrow></semantics></math>, a conventional causal model minimizes:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mn>1</mn></msub><mo>=</mo><mo>−</mo><munder><mo>∑</mo><mi>t</mi></munder><mi>log</mi><mo>⁡</mo><mi>p</mi><mo stretchy="false">(</mo><msub><mi>x</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>∣</mo><msub><mi>x</mi><mrow><mo>≤</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<p>With <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> future heads, the objective becomes:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>M</mi><mi>T</mi><mi>P</mi></mrow></msub><mo>=</mo><mo>−</mo><munder><mo>∑</mo><mi>t</mi></munder><munderover><mo>∑</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><msub><mi>α</mi><mi>k</mi></msub><mi>log</mi><mo>⁡</mo><msub><mi>p</mi><mi>k</mi></msub><mo stretchy="false">(</mo><msub><mi>x</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub><mo>∣</mo><msub><mi>x</mi><mrow><mo>≤</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Each head sees the same causal prefix but has a different target offset. Gloeckle and collaborators use independent output heads built from a shared model trunk and report that the added training-time overhead can be removed at deployment when only the next-token head is retained.<sup><a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<figure><figcaption><strong>One hidden state supervises several future-token heads</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/multi-token-prediction-0.webp" width="512" height="333" alt="One hidden state supervises several future-token heads" loading="lazy" decoding="async"></figure>
<h2 id="architecture-choices">Architecture choices</h2>
<p>A minimal design projects the final hidden state through independent linear heads. A richer head can include a small transformer block that combines the shared state with an embedding of a previously predicted future token.</p>
<p>The choices affect:</p>



































<div class="table-wrap"><table><thead><tr><th>Design</th><th>Training cost</th><th>Proposal dependence</th><th>Inference role</th></tr></thead><tbody><tr><td>Independent linear heads</td><td>Low</td><td>Heads do not condition on sibling proposals</td><td>Parallel candidate tuple</td></tr><tr><td>Sequential lightweight heads</td><td>Higher</td><td>Later proposals condition on earlier ones</td><td>Autoregressive draft</td></tr><tr><td>Tree heads</td><td>Higher</td><td>Multiple alternatives per depth</td><td>Branch verification</td></tr><tr><td>Auxiliary heads removed</td><td>Training only</td><td>None at serving</td><td>Better trunk supervision</td></tr></tbody></table></div>
<p>Independent heads can disagree structurally because the offset head predicts a marginal future token without knowing which earlier proposal was selected. A verification tree can retain several combinations, but candidate count grows with branching.</p>
<h2 id="training-alignment-and-shifting">Training alignment and shifting</h2>
<p>Target construction is easy to get wrong. For head <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>, logit position <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow></semantics></math> must align with token <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mrow><mi>t</mi><mo>+</mo><mi>k</mi></mrow></msub></mrow></semantics></math>. Tail positions without that future target must be masked from the loss.</p>
<p>Near-runnable PyTorch code:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> F</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> multi_token_loss</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">head_logits</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> token_ids</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> weights</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # head_logits[k]: [batch, sequence, vocabulary]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    total </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> token_ids</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">new_zeros</span><span style="color:var(--astro-code-token-punctuation)">((), dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> offset</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> (logits</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> weight) </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        zip</span><span style="color:var(--astro-code-token-punctuation)">(head_logits, weights), start</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    ):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        usable </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> token_ids</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">size</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> offset</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> usable </span><span style="color:var(--astro-code-token-keyword)">&lt;=</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            continue</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        predictions </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> logits</span><span style="color:var(--astro-code-token-punctuation)">[:,</span><span style="color:var(--astro-code-token-punctuation)"> :</span><span style="color:var(--astro-code-foreground)">usable</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">reshape</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, logits.</span><span style="color:var(--astro-code-token-function)">size</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        targets </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> token_ids</span><span style="color:var(--astro-code-token-punctuation)">[:,</span><span style="color:var(--astro-code-foreground)"> offset</span><span style="color:var(--astro-code-token-punctuation)">:].</span><span style="color:var(--astro-code-token-function)">reshape</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        total </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> total </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> weight </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> F</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cross_entropy</span><span style="color:var(--astro-code-token-punctuation)">(predictions, targets)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> total</span></span></code></pre>
<p>Production code also masks padding, packed-document boundaries, and loss-excluded tokens. A future head must not cross from one packed document into another. Build an offset-specific validity mask from segment identifiers.</p>
<h2 id="shared-trunk-gradients">Shared trunk gradients</h2>
<p>Every head contributes gradients to the trunk. Large auxiliary weights can optimize distant predictions at the expense of the primary next-token distribution. Log loss and gradient norms by head.</p>
<p>Useful controls include:</p>
<ul>
<li>offset-dependent loss weights</li>
<li>a warm-up schedule for auxiliary heads</li>
<li>per-head normalization before summing</li>
<li>gradient clipping measured before and after aggregation</li>
<li>validation perplexity for the primary head</li>
</ul>
<p>The multi-token prediction paper reports stronger gains for larger models and on code tasks, while finding no advantage for small models in some evaluated settings.<sup><a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup> Treat those results as evidence for the paper’s configurations, not a guarantee that more heads improve every model.</p>
<h2 id="from-heads-to-draft-tokens">From heads to draft tokens</h2>
<p>At inference, auxiliary heads propose tokens for offsets ahead of the current prefix. The target path verifies candidates with causal attention. A simple linear chain uses one top candidate from each head. A tree uses several candidates at each depth.</p>
<p>Medusa trains multiple decoding heads and verifies a tree of candidate continuations in parallel.<sup><a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Its tree attention mask permits a node to attend to its ancestors but not to nodes on other branches.</p>
<p>For a chain:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>shared hidden state</span></span>
<span class="line"><span>  -&gt; head for offset one proposes a</span></span>
<span class="line"><span>  -&gt; head for offset two proposes b</span></span>
<span class="line"><span>  -&gt; head for offset three proposes c</span></span>
<span class="line"><span>  -&gt; target verifies prefix + [a, b, c]</span></span></code></pre>
<p>If the target rejects <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow></semantics></math>, candidate <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>c</mi></mrow></semantics></math> is invalid because its prefix is no longer the proposed chain. The runtime commits the accepted prefix and a target correction, then drafts again.</p>
<h2 id="draft-heads-and-lossless-sampling">Draft heads and lossless sampling</h2>
<p>Heads produce a proposal distribution <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi></mrow></semantics></math>; the target produces <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow></semantics></math>. Lossless speculative sampling accepts a proposed token with probability:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>min</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mn>1</mn><mo separator="true">,</mo><mfrac><mrow><mi>p</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><mi>q</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo fence="true">)</mo></mrow></mrow></semantics></math>
<p>On rejection, it samples from the normalized positive residual <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>−</mo><mi>q</mi></mrow></semantics></math>. This is the same correction used in speculative sampling with a separate draft model.<sup><a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Greedy verification can use token equality but preserves only greedy behavior.</p>
<p>Temperature, top-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow></semantics></math>, top-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>, repetition penalties, and grammar masks must be reflected consistently in proposal and target probabilities. Independent heads that expose only selected token identifiers are insufficient for exact stochastic correction; the verifier needs proposal probabilities.</p>
<h2 id="inference-memory-and-kernels">Inference memory and kernels</h2>
<p>Auxiliary heads add parameters and logits. Materializing full vocabulary logits for every head can consume substantial memory bandwidth. Fused top-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> selection can avoid writing complete logits to global memory when the verification method needs only candidate tokens and probabilities.</p>
<p>Serving options:</p>
<ul>
<li>retain heads on the same device as the trunk</li>
<li>place head weights with the final pipeline stage</li>
<li>quantize auxiliary head weights separately after acceptance validation</li>
<li>disable heads for batches whose measured acceptance is low</li>
<li>select a smaller active head count by request class</li>
</ul>
<p>The shared trunk avoids a second model’s KV cache, but verification candidates still require provisional target-cache positions. Roll back unaccepted suffix blocks without copying the accepted prefix.</p>
<h2 id="candidate-tree-budgeting">Candidate-tree budgeting</h2>
<p>Tree width should be based on verification cost and acceptance, not a fixed aesthetic shape. Let node <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi></mrow></semantics></math> have estimated probability of being reached <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>v</mi></msub></mrow></semantics></math> and verification cost contribution <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>c</mi><mi>v</mi></msub></mrow></semantics></math>. Candidate selection can prioritize high <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>v</mi></msub><mi mathvariant="normal">/</mi><msub><mi>c</mi><mi>v</mi></msub></mrow></semantics></math> while respecting kernel shape and maximum nodes.</p>
<p>Calibrate reach probability from held-out traffic. Do not interpret head softmax values as calibrated branch probabilities without checking them. Code, prose, and structured output can have different predictability.</p>
<h2 id="failure-modes">Failure modes</h2>
<p><strong>Cross-document targets:</strong> packed training shifts a target into the next document. Mask by segment identifier at every offset.</p>
<p><strong>Primary-head regression:</strong> auxiliary gradients reduce next-token quality. Track the primary objective and downstream evaluations independently.</p>
<p><strong>Head collapse:</strong> distant heads learn frequent-token priors rather than prefix-specific futures. Measure conditional accuracy by token class and entropy.</p>
<p><strong>Verification-mask leak:</strong> a candidate reads a sibling branch or later token. Compare tree verification logits against independent autoregressive reference runs.</p>
<p><strong>Memory-bandwidth regression:</strong> writing head logits costs more than the saved target steps. Profile end-to-end time per committed token.</p>
<p><strong>Static head count:</strong> easy prompts waste available candidates or difficult prompts waste verification slots. Route using measured acceptance.</p>
<p><strong>Tokenizer or processor mismatch:</strong> head proposals bypass grammar masks or sampling transforms. Centralize logits processing and test equivalence.</p>
<h2 id="evaluation">Evaluation</h2>
<p>Training evaluation needs primary-head perplexity, per-offset loss, downstream task quality, and gradient statistics. Serving evaluation needs:</p>
<ul>
<li>committed tokens per target pass</li>
<li>acceptance by offset and request type</li>
<li>draft-head kernel time</li>
<li>target verification time by candidate count</li>
<li>provisional cache blocks allocated and released</li>
<li>output-distribution equivalence against ordinary sampling</li>
<li>latency percentiles under continuous batching</li>
</ul>
<p>Compare three deployments from the same checkpoint: primary head only, auxiliary heads enabled without speculative verification, and verified multi-token decoding. This separates representation gains from serving gains.</p>
<p>Multi-token prediction is useful in two independent ways. It changes the training signal by requiring a prefix representation to predict several future offsets. It also supplies model-native proposals for speculative decoding. Each claim needs its own baseline, because better training loss does not prove faster serving and high acceptance does not prove unchanged sampling.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Fabian Gloeckle et al., “Better &amp; Faster Large Language Models via Multi-token Prediction,” ICML, 2024. <a href="https://arxiv.org/abs/2404.19737" rel="noopener noreferrer">https://arxiv.org/abs/2404.19737</a> <a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>Tianle Cai et al., “Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads,” ICML, 2024. <a href="https://arxiv.org/abs/2401.10774" rel="noopener noreferrer">https://arxiv.org/abs/2401.10774</a> <a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Charlie Chen et al., “Accelerating Large Language Model Decoding with Speculative Sampling,” arXiv, 2023. <a href="https://arxiv.org/abs/2302.01318" rel="noopener noreferrer">https://arxiv.org/abs/2302.01318</a> <a href="https://blog.ecitis.org/multi-token-prediction/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Decoding</category>
            <category>Training</category>
            <category>Architecture</category>
            <enclosure length="77464" type="image/png" url="https://blog.ecitis.org/open-graph/multi-token-prediction.png"/>
        </item>
        <item>
            <title><![CDATA[The Model Hub as Infrastructure: Transfer, Dedup, and Provenance]]></title>
            <link>https://blog.ecitis.org/model-hub-infrastructure/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/model-hub-infrastructure/</guid>
            <pubDate>Sat, 11 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Treat a model hub as a content-addressed distribution system: chunked transfer, cache layout, revision pinning, and supply-chain controls.]]></description>
            <content:encoded><![CDATA[<p>Most teams meet a model hub as a convenience function: one call, weights appear. The useful discovery is what sits underneath. A hub is a content-addressed distribution system, and once the download is 300 GB across a 200-node fleet, those are exactly the primitives you want.</p>
<p>Content addressing, chunk-level deduplication, resumable transfer, and commit-pinned revisions give you reproducibility, fast scale-out, and an auditable supply chain. Everything below is about using them deliberately.</p>
<figure><figcaption><strong>A model hub is a content-addressed CDN with a package manager on top</strong></figcaption><p><a href="https://blog.ecitis.org/model-hub-infrastructure/">View this figure on the site &#8594;</a></p></figure>
<h2 id="repository-revision-blob">Repository, revision, blob</h2>
<p>A hub repository is a Git repository whose large files live outside the object graph. Git tracks pointers; a separate storage layer holds the payloads. The consequences are structural:</p>
<ul>
<li>A <strong>revision</strong> is a commit hash. <code>main</code> is a moving pointer, and so is a tag if someone force-updates it.</li>
<li>A <strong>file</strong> at a revision resolves to a content hash, which resolves to bytes.</li>
<li>Two repositories containing an identical file reference the same underlying blob.</li>
</ul>
<p>Everything useful follows from the middle layer. Because files are addressed by content, the client can ask “do I already have this?” before transferring anything, and the answer does not depend on which repository asked.</p>
<h2 id="chunk-level-deduplication">Chunk-level deduplication</h2>
<p>File-level dedup is coarse. Checkpoints are large and change in small ways: a fine-tune modifies some tensors and not others; a re-upload changes metadata; a quantized variant shares structure with its source. File hashing sees “different file” and transfers everything.</p>
<p>Content-defined chunking splits a file at boundaries determined by the data rather than by fixed offsets. A rolling hash over a sliding window declares a boundary wherever the hash satisfies a condition:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>boundary at </mtext><mi>i</mi><mtext>  </mtext><mo>⟺</mo><mtext>  </mtext><mi>H</mi><mo stretchy="false">(</mo><msub><mi>b</mi><mrow><mi>i</mi><mo>−</mo><mi>w</mi><mo>+</mo><mn>1..</mn><mi>i</mi></mrow></msub><mo stretchy="false">)</mo><mtext> </mtext><mo lspace="0.22em" rspace="0.22em"><mrow><mi mathvariant="normal">m</mi><mi mathvariant="normal">o</mi><mi mathvariant="normal">d</mi></mrow></mo><mtext> </mtext><msup><mn>2</mn><mi>k</mi></msup><mo>=</mo><mn>0</mn></mrow></semantics></math>
<p>giving an expected chunk size of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mn>2</mn><mi>k</mi></msup></mrow></semantics></math> bytes. Because boundaries follow content, inserting or removing bytes shifts only the chunks around the edit; the rest keep their identity. Fixed-size blocks would have every subsequent block change.</p>
<p>The transfer then becomes: compute chunk hashes locally, ask which are missing, transfer only those. For a fine-tune that touched 8% of tensors, that is roughly 8% of the bytes. Hugging Face’s Xet storage layer applies exactly this, replacing whole-file LFS transfer with chunk-level dedup and reporting large reductions in both upload and download volume for iterative model development.<sup><a href="https://blog.ecitis.org/model-hub-infrastructure/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p>Two practical notes. First, dedup helps most across revisions of the same model and across quantized siblings; it helps very little between unrelated models, because random weight tensors share no chunks. Second, chunk metadata is not free; a very small chunk size makes the manifest itself a transfer cost.</p>
<h2 id="cache-layout-and-why-it-matters">Cache layout and why it matters</h2>
<p>The client cache is content-addressed with a snapshot view layered on top:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>hub-cache/</span></span>
<span class="line"><span>  models--org--name/</span></span>
<span class="line"><span>    blobs/</span></span>
<span class="line"><span>      3f9c...            # content-addressed payloads</span></span>
<span class="line"><span>      a17b...</span></span>
<span class="line"><span>    snapshots/</span></span>
<span class="line"><span>      &lt;commit-sha&gt;/</span></span>
<span class="line"><span>        model.safetensors -&gt; ../../blobs/3f9c...</span></span>
<span class="line"><span>        config.json       -&gt; ../../blobs/a17b...</span></span>
<span class="line"><span>    refs/</span></span>
<span class="line"><span>      main               # contains &lt;commit-sha&gt;</span></span></code></pre>
<p>Snapshots are symlink trees into a shared blob store. Two revisions that share a file share one blob on disk. Two models that share a tokenizer share one blob.</p>
<p>Three operational consequences follow:</p>
<p><strong>Symlinks must survive the environment.</strong> Container images built on filesystems without symlink support, or copied with tooling that dereferences links, silently duplicate every blob. A cache that should be 200 GB becomes 600 GB.</p>
<p><strong>The blob store is the thing to share.</strong> Mounting one cache volume across replicas on a node means the first replica’s download serves the rest. This is the single highest-leverage change for fleet cold start, and it costs one volume mount.</p>
<p><strong>Garbage collection needs the ref graph.</strong> Deleting a snapshot directory does not free space; blobs are freed only when no snapshot references them. A cache-cleanup script that removes snapshot trees and not orphaned blobs will appear to do nothing.</p>
<h2 id="pinning">Pinning</h2>
<p>The rule is short: production resolves revisions by commit hash, never by branch or tag.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> huggingface_hub </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> snapshot_download</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">LOCAL </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> snapshot_download</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    repo_id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;org/model&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    revision</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;e9c81b7a2f4d6c0b1a3e5f7d9b2c4a6e8f0d1c3b&quot;</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># commit sha</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    allow_patterns</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;*.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;*.json&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;tokenizer*&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><code>allow_patterns</code> is not cosmetic. Many repositories carry multiple formats of the same weights: safetensors and pickle, several quantizations, ONNX exports. A default download can fetch several times the bytes you need. Restricting the pattern set is often a larger saving than any transfer optimization.</p>
<p>Record the resolved hash in the deployment manifest alongside the image digest. A rollback that restores the container but re-resolves <code>main</code> has not rolled back.</p>
<p>Pinning also interacts with dedup in a useful way: because the cache is content-addressed, pinning an older revision after having fetched a newer one usually transfers only the differing chunks.</p>
<h2 id="transfer-throughput">Transfer throughput</h2>
<p>A single-connection HTTPS download will not saturate a modern NIC. The variables that matter:</p>
<ul>
<li><strong>Concurrency.</strong> Parallel ranged requests across files and within large files. This is what <code>hf_transfer</code> and equivalent Rust-backed clients provide; the gain is from connection parallelism, not from a faster language.</li>
<li><strong>Resumability.</strong> A 140 GB download that restarts from zero on a transient failure is an availability problem. Range requests plus a partial-file journal make retries cheap.</li>
<li><strong>Placement.</strong> Cross-region egress is the dominant cost in both time and money for large fleets. A regional mirror or pull-through cache pays for itself quickly.</li>
</ul>
<p>Verify the achieved rate rather than assuming it:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">time</span><span style="color:var(--astro-code-foreground)"> HF_HUB_ENABLE_HF_TRANSFER</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string)">1</span><span style="color:var(--astro-code-token-function)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  hf</span><span style="color:var(--astro-code-token-string)"> download</span><span style="color:var(--astro-code-token-string)"> org/model</span><span style="color:var(--astro-code-token-string)"> --revision</span><span style="color:var(--astro-code-token-keyword)"> &lt;</span><span style="color:var(--astro-code-token-string)">sh</span><span style="color:var(--astro-code-foreground)">a</span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --include</span><span style="color:var(--astro-code-token-string-expression)"> &quot;*.safetensors&quot;</span><span style="color:var(--astro-code-token-string-expression)"> &quot;*.json&quot;</span><span style="color:var(--astro-code-token-string)"> --local-dir</span><span style="color:var(--astro-code-token-string)"> ./model</span></span></code></pre>
<p>If throughput sits near a round number like 100 or 300 MiB/s across environments, the limit is a per-connection or proxy cap, not the hub.</p>
<h2 id="offline-and-air-gapped-operation">Offline and air-gapped operation</h2>
<p>A deployment that reaches a hub at container start has taken a runtime dependency on an external service. Even with high availability upstream, this couples your restart path to someone else’s network.</p>
<p>The stronger arrangement is to make the hub a build-time dependency:</p>
<ol>
<li>Resolve and download at image build or artifact-publish time, pinned by hash.</li>
<li>Verify checksums and format.</li>
<li>Publish to internal object storage or bake into the image.</li>
<li>Run with <code>HF_HUB_OFFLINE=1</code> so any accidental fetch fails loudly rather than silently reaching out.</li>
</ol>
<p>The failure this prevents is not only outage-related. A runtime fetch means the bytes running in production were selected at pod-start time, which makes “what exactly was serving at 04</p><div></div>?” an unanswerable question.<p></p>
<h2 id="checkpoints-are-untrusted-input">Checkpoints are untrusted input</h2>
<p>A model repository is code and data from a third party, executed by your loader on your hardware. The controls are ordinary supply-chain controls:</p>
<p><strong>Prefer non-executable formats.</strong> Safetensors cannot execute on load; pickle-family formats can. If only pickle is offered, convert once in an isolated, credential-free process and distribute the converted artifact internally.</p>
<p><strong>Watch the non-weight files too.</strong> Repositories can ship custom modelling code that a framework will import when remote-code execution is enabled. Leave that disabled by default; when a model requires it, vendor the code into your own repository and review it, rather than importing whatever the revision currently contains.</p>
<p><strong>Verify after transfer.</strong> Checksums catch truncation and corruption. They do not establish authorship; for that you need signing and an allowlist of publishers.</p>
<p><strong>Scan the tokenizer.</strong> A tokenizer file is a parsed configuration and a plausible place for resource-exhaustion payloads: pathological merge tables, enormous vocabularies, regex patterns with catastrophic backtracking.</p>
<p><strong>Evaluate before promotion.</strong> Weights that load cleanly can still behave badly. No transfer-layer control detects a backdoored checkpoint; a fixed evaluation suite run at ingest is what covers this, and it is worth running even for models from reputable sources because it also catches conversion errors.</p>
<h2 id="an-ingest-pipeline">An ingest pipeline</h2>
<p>Putting it together, the path from public hub to production is a pipeline with a gate at each stage:</p>













































<div class="table-wrap"><table><thead><tr><th>Stage</th><th>Action</th><th>Gate</th></tr></thead><tbody><tr><td>Resolve</td><td>Branch or tag → commit hash</td><td>Hash recorded in manifest</td></tr><tr><td>Fetch</td><td>Pinned, pattern-restricted, parallel</td><td>Checksums match</td></tr><tr><td>Inspect</td><td>Format, file list, remote-code flags</td><td>No pickle, no unreviewed code</td></tr><tr><td>Convert</td><td>Normalize to safetensors if needed</td><td>Logit equivalence vs. source</td></tr><tr><td>Evaluate</td><td>Fixed task suite plus safety probes</td><td>Meets thresholds</td></tr><tr><td>Publish</td><td>Internal object storage, immutable key</td><td>Signed, immutable</td></tr><tr><td>Deploy</td><td>Pull from internal storage only</td><td>Offline mode enforced</td></tr></tbody></table></div>
<p>Each stage is cheap. The pipeline exists so that the expensive stages run once per model version rather than once per replica, and so that the answer to “where did these bytes come from” is a lookup rather than an investigation.</p>
<p>The hub is good infrastructure, and the primitives are the right ones: content addressing, chunk dedup, resumable transfer, and pinned revisions. Treating it as a registry rather than as a remote filesystem is what unlocks them.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Hugging Face, “Xet: Storage and transfer for the Hub,” Hugging Face documentation, 2025. <a href="https://huggingface.co/docs/hub/en/storage-backends" rel="noopener noreferrer">https://huggingface.co/docs/hub/en/storage-backends</a> <a href="https://blog.ecitis.org/model-hub-infrastructure/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Hugging Face</category>
            <category>Security</category>
            <category>Model loading</category>
            <enclosure length="59493" type="image/png" url="https://blog.ecitis.org/open-graph/model-hub-infrastructure.png"/>
        </item>
        <item>
            <title><![CDATA[Scaling Laws: What the Curves Predict and What They Hide]]></title>
            <link>https://blog.ecitis.org/scaling-laws-regimes/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/scaling-laws-regimes/</guid>
            <pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Read language-model scaling laws as conditional empirical models across abundant-data, compute-optimal, and data-constrained regimes.]]></description>
            <content:encoded><![CDATA[<p>Scaling laws fit a small equation to measurements from many training runs. The fitted curve can predict loss at an untested scale and guide the allocation of a fixed training budget. Its domain is limited by the model family, data distribution, optimizer, tokenization scheme, and measurement range used to produce it.</p>
<p>Read the equation with its conditions attached:</p>
<blockquote>
<p>Within a measured regime, a chosen metric often changes smoothly enough with scale to support extrapolation.</p>
</blockquote>
<figure><figcaption><strong>Scaling laws change when the limiting resource changes</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/scaling-laws-regimes-0.webp" width="512" height="686" alt="Scaling laws change when the limiting resource changes" loading="lazy" decoding="async"></figure>
<h2 id="the-basic-power-law-model">The basic power-law model</h2>
<p>Kaplan and collaborators measured cross-entropy loss while varying parameter count, dataset size, and training compute. They found smooth power-law relationships across their experimental range.<sup><a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p>A simplified model-size fit is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>N</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>L</mi><mi mathvariant="normal">∞</mi></msub><mo>+</mo><mfrac><mi>A</mi><msup><mi>N</mi><mi>α</mi></msup></mfrac></mrow></semantics></math>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow></semantics></math> is parameter count, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mi mathvariant="normal">∞</mi></msub></mrow></semantics></math> is an irreducible-loss term for the data distribution, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow></semantics></math> are fitted constants. Similar forms can be written for data size <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math> or training compute <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>C</mi></mrow></semantics></math>.</p>
<p>The equation describes diminishing returns. Each multiplicative increase in the resource reduces the reducible part of the loss by a fixed factor. The exponent controls the slope on a log-log plot.</p>
<p>Three details matter:</p>
<ul>
<li>The fitted constants belong to the experimental setup. They are not architectural constants.</li>
<li>Cross-entropy loss can improve smoothly while a downstream capability changes unevenly.</li>
<li>Extrapolation uncertainty grows outside the observed scale and data distribution.</li>
</ul>
<p>A curve is useful only with its fitting range, residuals, and held-out prediction error.</p>
<h2 id="compute-allocation-changed-after-chinchilla">Compute allocation changed after Chinchilla</h2>
<p>Training compute can be spent on more parameters, more tokens, or both. A model with many parameters and too few training tokens may sit above the best achievable loss for its compute budget.</p>
<p>The Chinchilla study trained a large sweep of models and found a different compute-optimal allocation from the earlier Kaplan prescription. Its fitted result called for scaling model size and training tokens together under the studied setup.<sup><a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> The practical shift was from “make the model larger and stop early” toward “train a smaller model on more data” for a fixed budget.</p>
<p>A common joint form is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>L</mi><mo stretchy="false">(</mo><mi>N</mi><mo separator="true">,</mo><mi>D</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi>L</mi><mi mathvariant="normal">∞</mi></msub><mo>+</mo><mfrac><mi>A</mi><msup><mi>N</mi><mi>α</mi></msup></mfrac><mo>+</mo><mfrac><mi>B</mi><msup><mi>D</mi><mi>β</mi></msup></mfrac></mrow></semantics></math>
<p>The parameter-limited term falls with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>N</mi></mrow></semantics></math>. The data-limited term falls with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>. Compute optimality chooses a point on the budget constraint where spending more on either side would create a larger penalty on the other.</p>
<figure><figcaption><strong>Fixed compute turns scaling into an allocation problem</strong> — The optimum comes from measured loss across candidate parameter and token budgets.</figcaption><img src="https://blog.ecitis.org/feeds/figures/scaling-laws-regimes-1.webp" width="512" height="704" alt="Fixed compute turns scaling into an allocation problem — The optimum comes from measured loss across candidate parameter and token budgets." loading="lazy" decoding="async"></figure>
<p>The compute-optimal point answers a training allocation question. It does not automatically minimize deployment cost. A serving-heavy product may prefer fewer parameters because inference cost is paid repeatedly. A research model may accept a larger serving cost to reduce pretraining time. A model intended for continued training may deliberately stop before the static optimum.</p>
<h2 id="data-quality-moves-the-curve">Data quality moves the curve</h2>
<p>Token count treats every token as interchangeable. Training does not.</p>
<p>Deduplication, domain mixture, code proportion, language coverage, and filtering change the mapping from raw tokens to validation loss. DeepSeek’s scaling experiments reported that fitted laws differed across datasets, warning against transferring a curve without checking the data distribution.<sup><a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>Data quality creates two distinct questions:</p>
<ol>
<li>How does loss scale when the sampling distribution is fixed?</li>
<li>How should the sampling distribution change as the budget grows?</li>
</ol>
<p>The first supports a clean fit. The second is the real dataset-design problem. If a larger run consumes lower-quality tail data, the original curve may overpredict improvement. If curation improves with scale, it may underpredict it.</p>
<p>A scaling experiment should therefore record:</p>
<ul>
<li>unique and repeated token counts</li>
<li>mixture weights by source and domain</li>
<li>deduplication policy</li>
<li>tokenizer and context length</li>
<li>optimizer and learning-rate schedule</li>
<li>training loss and several held-out distributions</li>
</ul>
<p>Without those fields, reproducing the curve is harder than reproducing the equation.</p>
<h2 id="repeated-data-defines-another-regime">Repeated data defines another regime</h2>
<p>Abundant-data scaling assumes the next training token can be drawn from a large supply of useful, mostly unique text. That assumption becomes weaker when compute grows faster than the stock of high-quality data.</p>
<p>Muennighoff and collaborators studied repeated-data training and found that a limited amount of repetition could retain much of the value of unique data in their experiments, while additional repetition eventually produced declining returns.<sup><a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> Their scaling law adds a term for the reduced value of repeated tokens.</p>
<p>A 2026 preprint proposes SoftQ, a coupled model of parameter count and data size for repeated-data training, arguing that the additive Chinchilla form can be misspecified in this regime.<sup><a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> The paper evaluates SoftQ for repeated-data training.</p>
<p>The conceptual point is durable: once examples repeat, model size and data size can interact. A larger model may memorize repeated data differently from a smaller model, so independent parameter and data terms can miss structure in the observations.</p>
<h2 id="downstream-performance-is-not-one-curve">Downstream performance is not one curve</h2>
<p>Pretraining loss averages prediction error across tokens. A benchmark measures a narrower behavior after prompting, post-training, tool use, or sampling.</p>
<p>Suppose two checkpoints have nearly equal validation loss. They can still differ on:</p>
<ul>
<li>rare facts</li>
<li>long-horizon reasoning</li>
<li>code execution</li>
<li>calibration</li>
<li>multilingual performance</li>
<li>robustness to prompt format</li>
</ul>
<p>The relationship between loss and a downstream metric can also change when the benchmark saturates or when post-training contributes most of the measured capability.</p>
<p>Use a hierarchy of forecasts:</p>

























<div class="table-wrap"><table><thead><tr><th>Forecast</th><th>Strongest evidence</th></tr></thead><tbody><tr><td>Pretraining loss</td><td>Dense checkpoint curves on held-out text</td></tr><tr><td>Domain loss</td><td>Curves on a stable domain-specific corpus</td></tr><tr><td>Benchmark score</td><td>Repeated evaluations across checkpoints and scales</td></tr><tr><td>Product quality</td><td>Task-specific traffic, tools, latency, and post-training</td></tr></tbody></table></div>
<p>Each step adds variables not captured by the pretraining curve.</p>
<h2 id="scaling-laws-are-decision-tools">Scaling laws are decision tools</h2>
<p>A good scaling study runs before the largest training job, not after it. The workflow is:</p>
<ol>
<li>Train a matrix of smaller models across parameter and token budgets.</li>
<li>Hold architecture, data policy, and optimization choices stable enough to isolate scale.</li>
<li>Fit competing functional forms.</li>
<li>Reserve some runs as out-of-sample tests.</li>
<li>inspect residuals by model size, token budget, and domain.</li>
<li>propagate fit uncertainty into the large-run forecast.</li>
<li>rerun the fit when the architecture or data regime changes.</li>
</ol>
<p>The fitted optimum should be treated as a region, not a single sacred coordinate. Measurement noise, hardware constraints, checkpoint reuse, and serving economics can make nearby allocations operationally better.</p>
<p>A scaling fit supports the measured relationship for one family of systems over one range. Researchers can use that fit to avoid some experimental runs. Extending it to new architectures, repeated data, post-training, test-time reasoning, or user value requires new measurements.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Jared Kaplan et al., “Scaling Laws for Neural Language Models,” arXiv, 2020. <a href="https://arxiv.org/abs/2001.08361" rel="noopener noreferrer">https://arxiv.org/abs/2001.08361</a> <a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Jordan Hoffmann et al., “Training Compute-Optimal Large Language Models,” NeurIPS, 2022. <a href="https://arxiv.org/abs/2203.15556" rel="noopener noreferrer">https://arxiv.org/abs/2203.15556</a> <a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>DeepSeek-AI et al., “DeepSeek LLM: Scaling Open-Source Language Models with Longtermism,” arXiv, 2024. <a href="https://arxiv.org/abs/2401.02954" rel="noopener noreferrer">https://arxiv.org/abs/2401.02954</a> <a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Niklas Muennighoff et al., “Scaling Data-Constrained Language Models,” NeurIPS, 2023. <a href="https://arxiv.org/abs/2305.16264" rel="noopener noreferrer">https://arxiv.org/abs/2305.16264</a> <a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Zhiwei Xu et al., “Data-Constrained Language Model Pretraining: Improved Regularization and Scaling Laws,” arXiv, 2026. <a href="https://arxiv.org/abs/2606.06888" rel="noopener noreferrer">https://arxiv.org/abs/2606.06888</a> <a href="https://blog.ecitis.org/scaling-laws-regimes/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Scaling laws</category>
            <category>Training</category>
            <category>Synthetic data</category>
            <enclosure length="79252" type="image/png" url="https://blog.ecitis.org/open-graph/scaling-laws-regimes.png"/>
        </item>
        <item>
            <title><![CDATA[Serving Thousands of LoRA Adapters from One Base Model]]></title>
            <link>https://blog.ecitis.org/serving-many-lora-adapters/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/serving-many-lora-adapters/</guid>
            <pubDate>Wed, 08 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Multiplex many low-rank adapters over shared base weights with batched gather kernels, adapter paging, and per-tenant routing.]]></description>
            <content:encoded><![CDATA[<p>Fine-tuning produces adapters far more often than it produces full models, and the size ratio is striking: a LoRA adapter for a 70B model is often under 200 MB against 140 GB of base weights. One replica can turn that ratio into real economics by serving many adapters at once, and the published systems show how far it goes. S-LoRA reports serving thousands of concurrent adapters on a single GPU.</p>
<p>Getting there is a scheduling and kernel problem rather than a training one, which puts it entirely within a serving team’s control.</p>
<figure><figcaption><strong>One base model, many adapters, one batch</strong></figcaption><p><a href="https://blog.ecitis.org/serving-many-lora-adapters/">View this figure on the site &#8594;</a></p></figure>
<h2 id="low-rank-structure-and-additivity">Low-rank structure and additivity</h2>
<p>LoRA replaces a weight update with a low-rank factorization.<sup><a href="https://blog.ecitis.org/serving-many-lora-adapters/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> For a frozen base matrix <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>d</mi><mo>×</mo><mi>k</mi></mrow></msup></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>h</mi><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><mi>x</mi><mo>+</mo><mfrac><mi>α</mi><mi>r</mi></mfrac><mi>B</mi><mi>A</mi><mi>x</mi><mo separator="true">,</mo><mspace width="2em"></mspace><mi>B</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>d</mi><mo>×</mo><mi>r</mi></mrow></msup><mo separator="true">,</mo><mtext>  </mtext><mi>A</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>r</mi><mo>×</mo><mi>k</mi></mrow></msup></mrow></semantics></math>
<p>with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>≪</mo><mi>min</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>d</mi><mo separator="true">,</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></semantics></math>. Two properties do the work:</p>
<p><strong>Additivity.</strong> The adapter contribution is a separate additive term. The base output <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub><mi>x</mi></mrow></semantics></math> does not depend on which adapter is active.</p>
<p><strong>Rank.</strong> Parameters scale as <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>d</mi><mo>+</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></semantics></math> instead of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mi>k</mi></mrow></semantics></math>. At <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mo>=</mo><mi>k</mi><mo>=</mo><mn>4096</mn></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mn>16</mn></mrow></semantics></math>, that is 131k parameters against 16.8M, about 0.8%.</p>
<p>Additivity is what allows a single batch to contain requests using different adapters. Every request in the batch shares the <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub><mi>x</mi></mrow></semantics></math> computation and differs only in the low-rank correction.</p>
<h2 id="merge-or-keep-separate">Merge or keep separate</h2>
<p>You can fold the adapter into the base: <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>W</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><mo>+</mo><mfrac><mi>α</mi><mi>r</mi></mfrac><mi>B</mi><mi>A</mi></mrow></semantics></math>.</p>
<p>Merged serving is the fastest possible per-token path: exactly base-model inference, no extra kernels, no extra memory traffic. It also destroys multiplexing. A merged replica serves exactly one adapter, and switching means either re-merging (a full pass over the weights) or keeping a second copy.</p>






























<div class="table-wrap"><table><thead><tr><th></th><th>Merged</th><th>Unmerged</th></tr></thead><tbody><tr><td>Per-token cost</td><td>Base only</td><td>Base + low-rank</td></tr><tr><td>Adapters per replica</td><td>1</td><td>Thousands</td></tr><tr><td>Switch cost</td><td>Full weight pass</td><td>Pointer change</td></tr><tr><td>Batch composition</td><td>Homogeneous</td><td>Heterogeneous</td></tr></tbody></table></div>
<p>Merge when one adapter dominates traffic enough to justify dedicated capacity. Keep adapters separate for the long tail, which is where the multi-tenant case lives. Real deployments run both: dedicated merged replicas for the top handful of tenants, one multiplexed pool for everyone else.</p>
<h2 id="batching-across-different-adapters">Batching across different adapters</h2>
<p>The hard part is the unmerged path. A decode step contains requests <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1..</mn><mi>n</mi></mrow></semantics></math> using adapters <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>a</mi><mn>1</mn></msub><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi><msub><mi>a</mi><mi>n</mi></msub></mrow></semantics></math>, and the batched GEMM against <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>W</mi><mn>0</mn></msub></mrow></semantics></math> handles all of them uniformly. The adapter term does not.</p>
<p>The naive approach loops over distinct adapters in the batch and runs a small GEMM per adapter. With 32 requests using 20 distinct adapters, that is 20 launches of a matrix multiply too small to occupy the device, per adapted layer, per step. Launch overhead swamps the arithmetic.</p>
<p>The workable approach treats it as a gather-based grouped operation. Adapter weights are stored in a contiguous pool indexed by adapter id. Each request carries an index. A single kernel gathers the right slice per batch row:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>y</mi><mi>i</mi></msub><mo>=</mo><msub><mi>W</mi><mn>0</mn></msub><msub><mi>x</mi><mi>i</mi></msub><mo>+</mo><mfrac><mi>α</mi><mi>r</mi></mfrac><msub><mi>B</mi><msub><mi>a</mi><mi>i</mi></msub></msub><mrow><mo fence="true">(</mo><msub><mi>A</mi><msub><mi>a</mi><mi>i</mi></msub></msub><msub><mi>x</mi><mi>i</mi></msub><mo fence="true">)</mo></mrow></mrow></semantics></math>
<p>Punica implements this as a segmented gather-matrix-multiply that runs the whole heterogeneous batch in one launch, keeping the base GEMM shared.<sup><a href="https://blog.ecitis.org/serving-many-lora-adapters/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> S-LoRA extends it with unified paged memory holding both KV cache and adapter weights, plus tensor-parallel partitioning of the adapter computation, and reports serving thousands of adapters on one GPU.<sup><a href="https://blog.ecitis.org/serving-many-lora-adapters/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>Two details decide whether this actually performs:</p>
<p><strong>Uniform rank simplifies everything.</strong> A pool where all adapters share rank <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math> is a dense 3-D tensor with clean strides. Mixed ranks require either ragged indexing or padding to the maximum rank. Padding wastes memory proportional to rank variance; ragged indexing costs indirection in the inner loop. Standardizing on one or two ranks across a tenant fleet is a real operational simplification.</p>
<p><strong>The adapter term is bandwidth bound.</strong> At rank 16 the arithmetic is negligible; the cost is reading <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>B</mi><msub><mi>a</mi><mi>i</mi></msub></msub></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>A</mi><msub><mi>a</mi><mi>i</mi></msub></msub></mrow></semantics></math> for each distinct adapter in the batch. Throughput therefore degrades with adapter diversity per batch, not with batch size. A batch of 32 requests on 2 adapters is much cheaper than 32 requests on 32 adapters.</p>
<p>That last point has a direct scheduling consequence.</p>
<h2 id="adapter-aware-scheduling">Adapter-aware scheduling</h2>
<p>Since cost scales with distinct adapters per step, the scheduler should prefer batches with adapter locality, without starving low-traffic tenants.</p>
<p>A workable policy admits by a score combining wait time and locality:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> admission_score</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">req</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> active_adapters</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> now</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> w_fair</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> w_local</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.35</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    wait </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> now </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> req</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">arrival</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    locality </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-keyword)"> if</span><span style="color:var(--astro-code-foreground)"> req</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">adapter_id </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> active_adapters </span><span style="color:var(--astro-code-token-keyword)">else</span><span style="color:var(--astro-code-token-constant)"> 0.0</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> w_fair </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> wait </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> w_local </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> locality </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> req</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">est_tokens</span></span></code></pre>
<p>Weighting wait time linearly bounds starvation: a request from an unpopular adapter accumulates score until it wins regardless of locality. Setting <code>w_local</code> too high produces exactly the failure this is meant to avoid: head-of-line blocking for small tenants while a popular adapter monopolizes the batch. Track per-adapter p99 queue time and tune against it, not against aggregate throughput.</p>
<p>At the fleet level, consistent-hash routing on adapter id concentrates each adapter’s traffic on a subset of replicas, which improves both adapter-cache hit rate and batch locality. It also creates hotspots when one tenant is much larger than the others, so the router needs a load term that can override the hash.</p>
<h2 id="paging-adapters">Paging adapters</h2>
<p>Adapters are small enough to treat like cache rather than like weights. A pool with a resident set in HBM, a paged set in host memory, and a cold set in object storage gives a three-tier hierarchy with very different transfer costs:</p>

























<div class="table-wrap"><table><thead><tr><th>Tier</th><th>Fetch latency for a 200 MB adapter</th><th>Policy</th></tr></thead><tbody><tr><td>HBM resident</td><td>0</td><td>Hot working set</td></tr><tr><td>Host memory</td><td>~10–40 ms over PCIe</td><td>Recently used</td></tr><tr><td>Object storage</td><td>100 ms–seconds</td><td>Everything else</td></tr></tbody></table></div>
<p>The critical requirement is that a fetch must not block the decode loop. An adapter miss should either delay only that request’s admission, or trigger a prefetch while the request queues. A synchronous fetch inside the step function converts one tenant’s cold adapter into a latency spike for every request in the batch. That is the single most common failure in these systems.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">async</span><span style="color:var(--astro-code-token-keyword)"> def</span><span style="color:var(--astro-code-token-function)"> admit</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">req</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> pool</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-keyword)"> not</span><span style="color:var(--astro-code-foreground)"> pool</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">resident</span><span style="color:var(--astro-code-token-punctuation)">(req.adapter_id):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        pool</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">prefetch</span><span style="color:var(--astro-code-token-punctuation)">(req.adapter_id)</span><span style="color:var(--astro-code-token-comment)">      # non-blocking</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-token-function)"> Defer</span><span style="color:var(--astro-code-token-punctuation)">(reason</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;adapter_warming&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-function)"> Admit</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>Eviction should be size-aware only if ranks differ; otherwise LRU over adapter ids, with a pin for adapters under an availability commitment, is sufficient. What matters more is the admission interaction: evicting an adapter that has queued requests causes thrash.</p>
<h2 id="correctness-across-tenants">Correctness across tenants</h2>
<p>Multi-tenancy makes several previously harmless bugs into isolation failures.</p>
<p><strong>Prefix cache keys must include adapter id.</strong> Two tenants sending the same system prompt produce identical token sequences and completely different KV states. A prefix cache keyed on tokens alone will serve one tenant’s cached attention state to another. This is a data-leak bug, not a quality bug, and it is silent.</p>
<p><strong>Base revision must be part of the key too.</strong> An adapter is trained against a specific base checkpoint. Serving it over a different revision produces degraded output with no error.</p>
<p><strong>Scaling factors must travel with the adapter.</strong> The <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mi mathvariant="normal">/</mi><mi>r</mi></mrow></semantics></math> factor is part of the adapter’s definition. A server that applies a default <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow></semantics></math> silently changes the effective adapter strength.</p>
<p><strong>Sampling parameters and stop sequences are per-request.</strong> In a heterogeneous batch these are no longer batch-level constants. A batched sampler that reads one temperature applies one tenant’s settings to all.</p>
<p>A minimal cache key:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">@dataclass</span><span style="color:var(--astro-code-token-punctuation)">(frozen</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> PrefixKey</span><span style="color:var(--astro-code-foreground)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    base_revision</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    adapter_id</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-constant)"> None</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    adapter_revision</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-constant)"> None</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    token_ids</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> tuple</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">int</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ...</span><span style="color:var(--astro-code-token-punctuation)">]</span></span></code></pre>
<h2 id="what-to-measure">What to measure</h2>
<p>Aggregate throughput hides everything that matters in a multi-tenant deployment. The dashboard needs:</p>
<ul>
<li><strong>Distinct adapters per step</strong>, distribution not mean. This is the primary cost driver.</li>
<li><strong>Adapter cache hit rate</strong>, split by tier, and <strong>adapter fetch stalls</strong> counted separately from cache misses.</li>
<li><strong>Per-adapter p50/p99 TTFT</strong>, so a fairness regression is visible before a tenant reports it.</li>
<li><strong>Adapter overhead fraction</strong>: step time with adapters minus estimated base-only step time.</li>
<li><strong>Pool occupancy</strong> in bytes and in adapter count, plus eviction rate.</li>
</ul>
<p>Throughput at high adapter counts is the headline, and it holds up. The metric that confirms the design is working end to end is the p99 TTFT of the least-popular tenant on the replica: the one whose adapter is least likely to be resident and least likely to win on locality. Keep that number healthy and every tenant above it is comfortable.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Edward J. Hu et al., “LoRA: Low-Rank Adaptation of Large Language Models,” ICLR, 2022. <a href="https://arxiv.org/abs/2106.09685" rel="noopener noreferrer">https://arxiv.org/abs/2106.09685</a> <a href="https://blog.ecitis.org/serving-many-lora-adapters/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Lequn Chen et al., “Punica: Multi-Tenant LoRA Serving,” MLSys, 2024. <a href="https://arxiv.org/abs/2310.18547" rel="noopener noreferrer">https://arxiv.org/abs/2310.18547</a> <a href="https://blog.ecitis.org/serving-many-lora-adapters/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Ying Sheng et al., “S-LoRA: Serving Thousands of Concurrent LoRA Adapters,” MLSys, 2024. <a href="https://arxiv.org/abs/2311.03285" rel="noopener noreferrer">https://arxiv.org/abs/2311.03285</a> <a href="https://blog.ecitis.org/serving-many-lora-adapters/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Fine-tuning</category>
            <category>Multi-tenancy</category>
            <category>LLM serving</category>
            <enclosure length="62360" type="image/png" url="https://blog.ecitis.org/open-graph/serving-many-lora-adapters.png"/>
        </item>
        <item>
            <title><![CDATA[Model Compression Beyond Quantization: Pruning, Distillation, and Low-Rank Methods]]></title>
            <link>https://blog.ecitis.org/model-compression-techniques/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/model-compression-techniques/</guid>
            <pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Choose pruning, distillation, and low-rank methods by the deployment gains they produce rather than parameter count alone.]]></description>
            <content:encoded><![CDATA[<p>Compression succeeds only when the deployment runtime benefits from the changed model. Removing isolated weights does not guarantee faster dense matrix multiplication. A low-rank factorization does not help if the two smaller operators launch inefficiently. Distillation can produce a smaller model, but its quality depends on the teacher signal and training distribution.</p>
<figure><figcaption><strong>Compression changes different parts of the deployment contract</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/model-compression-techniques-0.webp" width="512" height="396" alt="Compression changes different parts of the deployment contract" loading="lazy" decoding="async"></figure>
<h2 id="define-the-deployment-objective">Define the deployment objective</h2>
<p>Choose measurable constraints before modifying the model:</p>
<ul>
<li>resident weight memory;</li>
<li>peak runtime memory;</li>
<li>serialized artifact size;</li>
<li>first-token latency;</li>
<li>decode latency;</li>
<li>throughput at the serving batch;</li>
<li>energy or power under sustained load;</li>
<li>task quality and safety slices.</li>
</ul>
<p>Parameter count is an intermediate metric. Hardware latency depends on shapes, sparsity support, memory traffic, and kernel launch behavior.</p>
<h2 id="unstructured-pruning-removes-individual-weights">Unstructured pruning removes individual weights</h2>
<p>Magnitude pruning sets low-magnitude weights to zero. A binary mask <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>M</mi></mrow></semantics></math> produces:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>W</mi><mo stretchy="true">~</mo></mover><mo>=</mo><mi>M</mi><mo>⊙</mo><mi>W</mi></mrow></semantics></math>
<p>SparseGPT instead formulates layer-wise pruning with approximate second-order information and reconstructs outputs while removing weights.<sup><a href="https://blog.ecitis.org/model-compression-techniques/#user-content-fn-sparsegpt" id="user-content-fnref-sparsegpt">1</a></sup> The algorithmic goal is to preserve layer behavior under sparsity, but speed still requires a sparse storage format and kernels that exploit the pattern.</p>
<p>An unstructured sparse matrix stores nonzero values plus indices. At modest sparsity, index overhead and irregular access can outweigh skipped arithmetic. Benchmark the exact matrix shapes on the target runtime.</p>
<p>Pruning should be followed by recovery tuning when quality loss exceeds the budget. Keep the mask fixed during recovery unless the method explicitly supports regrowth.</p>
<h2 id="structured-pruning-changes-dense-dimensions">Structured pruning changes dense dimensions</h2>
<p>Structured pruning removes complete channels, attention heads, feed-forward neurons, or layers. The resulting matrices remain dense and can use ordinary kernels. This makes latency benefits easier to realize, but each removal changes more computation than one isolated weight.</p>
<p>Head pruning must update:</p>
<ul>
<li>query, key, value, and output projection shapes;</li>
<li>grouped-query head mapping;</li>
<li>rotary or positional dimensions;</li>
<li>tensor-parallel divisibility;</li>
<li>checkpoint metadata and runtime configuration.</li>
</ul>
<p>Depth pruning removes whole transformer blocks. Width pruning changes hidden or feed-forward dimensions. NVIDIA’s Minitron work combines depth, width, attention, and feed-forward pruning with distillation-based retraining to derive smaller language models from a pretrained model.<sup><a href="https://blog.ecitis.org/model-compression-techniques/#user-content-fn-minitron" id="user-content-fnref-minitron">2</a></sup></p>
<p>Measure every candidate architecture instead of assuming proportional latency. A narrower matrix can land on a less efficient hardware tile and produce less speedup than its parameter reduction suggests.</p>
<h2 id="distillation-trains-a-separate-student">Distillation trains a separate student</h2>
<p>Knowledge distillation trains a student against teacher outputs, often using softened class probabilities. Hinton and collaborators described transferring the ensemble or teacher function into a deployable model through this training signal.<sup><a href="https://blog.ecitis.org/model-compression-techniques/#user-content-fn-distillation" id="user-content-fnref-distillation">3</a></sup></p>
<p>For language models, token-level distillation can minimize:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>K</mi><mi>D</mi></mrow></msub><mo>=</mo><msup><mi>T</mi><mn>2</mn></msup><mi mathvariant="normal">KL</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><msub><mi>p</mi><mi>T</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo>∥</mo><msub><mi>p</mi><mi>S</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo><mo fence="true">)</mo></mrow></mrow></semantics></math>
<p>where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow></semantics></math> is temperature. The student can also train on hard target tokens, hidden-state matches, attention relations, or generated teacher data.</p>
<p>Teacher logits expose more information than one sampled token, but full-vocabulary logits are expensive to store. Options include online teacher inference, top-logit storage with a residual bucket, or sequence-level distillation from teacher-generated outputs. Each changes the target.</p>
<p>Distillation risks copying teacher errors and narrowing behavior to the transfer distribution. Evaluate the student on real held-out data and safety tests that were not generated by the teacher.</p>
<h2 id="low-rank-factorization-replaces-one-operator-with-two">Low-rank factorization replaces one operator with two</h2>
<p>For <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>m</mi><mo>×</mo><mi>n</mi></mrow></msup></mrow></semantics></math>, a rank-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math> approximation writes:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>W</mi><mo>≈</mo><mi>A</mi><mi>B</mi><mo separator="true">,</mo><mspace width="2em"></mspace><mi>A</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>m</mi><mo>×</mo><mi>r</mi></mrow></msup><mo separator="true">,</mo><mspace width="1em"></mspace><mi>B</mi><mo>∈</mo><msup><mi mathvariant="double-struck">R</mi><mrow><mi>r</mi><mo>×</mo><mi>n</mi></mrow></msup></mrow></semantics></math>
<p>The parameter count changes from <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi><mi>n</mi></mrow></semantics></math> to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>m</mi><mo>+</mo><mi>n</mi><mo stretchy="false">)</mo></mrow></semantics></math>. This is smaller only when <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi></mrow></semantics></math> is below the break-even rank:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>r</mi><mo>&lt;</mo><mfrac><mrow><mi>m</mi><mi>n</mi></mrow><mrow><mi>m</mi><mo>+</mo><mi>n</mi></mrow></mfrac></mrow></semantics></math>
<p>Truncated singular value decomposition provides a reconstruction-optimal low-rank approximation under the Frobenius norm, but minimizing weight reconstruction error does not directly minimize model loss. Recovery tuning can adapt the factors to the data distribution.</p>
<p>Runtime performance depends on both factor matrices. Very small intermediate rank can become launch-bound. A fused implementation or batching may be needed to realize latency gains.</p>
<p>Low-rank adapters such as LoRA are parameter-efficient fine-tuning mechanisms, not automatic base-model compression. Keeping the frozen base matrix plus adapters adds parameters. Compression occurs only if the deployed representation replaces or restructures the base computation.</p>
<h2 id="combine-methods-in-a-deliberate-order">Combine methods in a deliberate order</h2>
<p>Pruning, factorization, and distillation interact. A useful sequence is:</p>
<ol>
<li>identify deployable structured shapes;</li>
<li>prune the teacher or initialize the student architecture;</li>
<li>reconstruct or factorize eligible matrices;</li>
<li>recover with hard labels and teacher targets;</li>
<li>apply quantization after the graph is stable;</li>
<li>benchmark the exported artifact.</li>
</ol>
<p>This is not universal. Quantization-aware training may need to participate in recovery. Unstructured sparse kernels may require pruning to a hardware-specific pattern before export.</p>
<p>Change one compression axis at a time during diagnosis. If pruning, factorization, and quantization are applied together, a quality regression cannot be localized.</p>
<h2 id="preserve-tied-and-shared-parameters">Preserve tied and shared parameters</h2>
<p>Language models can tie input embeddings to the output projection or reuse parameters across layers. A generic traversal that replaces modules independently can break aliasing and duplicate storage.</p>
<p>Before compression, build an alias map:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">owners </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> name</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> parameter </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">named_parameters</span><span style="color:var(--astro-code-token-punctuation)">(remove_duplicate</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    owners</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">setdefault</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">id</span><span style="color:var(--astro-code-token-punctuation)">(parameter), []).</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(name)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">shared </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [names </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> names </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> owners</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">values</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> if</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(names)</span><span style="color:var(--astro-code-token-keyword)"> &gt;</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)">]</span></span></code></pre>
<p>After rewriting the graph, confirm that intended aliases remain tied and the optimizer does not update duplicate copies.</p>
<h2 id="validate-exported-graphs">Validate exported graphs</h2>
<p>The in-memory training model is not the deployment artifact. Exporters can fold constants, transpose weights, reject sparse operators, or lower factorized layers into unexpected subgraphs.</p>
<p>For each target runtime:</p>
<ul>
<li>inspect operator types and tensor shapes;</li>
<li>confirm sparse metadata survives;</li>
<li>compare serialized size;</li>
<li>run fixed input fixtures against the source model;</li>
<li>measure peak memory and latency after warm-up;</li>
<li>profile the kernels actually dispatched;</li>
<li>test all supported batch and sequence buckets.</li>
</ul>
<p>A compressed checkpoint that falls back to CPU for one unsupported sparse operator can be slower than the dense baseline.</p>
<h2 id="quality-evaluation-needs-slices">Quality evaluation needs slices</h2>
<p>Compression error is not uniform. Pruning can damage rare features. Distillation can suppress low-probability alternatives. Low-rank approximation can remove directions that matter for a small task slice.</p>
<p>Evaluate:</p>
<ul>
<li>perplexity or task loss on real held-out text;</li>
<li>downstream accuracy by domain and language;</li>
<li>long-context behavior;</li>
<li>calibration and uncertainty;</li>
<li>tool-call schema validity;</li>
<li>safety and refusal behavior;</li>
<li>generation repetition and degeneration.</li>
</ul>
<p>Compare against an uncompressed checkpoint under identical decoding settings. If the compressed model needs a different sampler to recover quality, report both the model and sampler change.</p>
<h2 id="recovery-failures">Recovery failures</h2>
<p><strong>Mask drift:</strong> Pruned weights become nonzero during tuning because the optimizer updates them. Reapply the mask after updates or use masked parameterization.</p>
<p><strong>Shape mismatch:</strong> Structured pruning updates weights but not configuration, rotary tables, or tensor-parallel metadata. Load the exported model in a fresh process before declaring success.</p>
<p><strong>Teacher leakage:</strong> Evaluation prompts or their derivatives enter distillation data. Split source groups before teacher generation.</p>
<p><strong>Rank collapse:</strong> A factorization uses one rank for every layer despite different spectra and sensitivity. Allocate rank by measured reconstruction and downstream loss.</p>
<p><strong>No runtime gain:</strong> The artifact is smaller but latency is unchanged. Inspect kernels, launch count, memory bandwidth, and sparse support.</p>
<p>Compression is a contract between an algorithm and a runtime. Unstructured pruning needs sparse execution, structured pruning needs valid dense shapes, distillation needs independent evaluation, and factorization needs efficient paired operators. The final evidence is an exported model that meets memory, latency, and quality budgets on its target hardware.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-sparsegpt">
<p><a href="https://arxiv.org/abs/2301.00774" rel="noopener noreferrer">SparseGPT: Massive Language Models Can Be Accurately Pruned in One-Shot</a>. <a href="https://blog.ecitis.org/model-compression-techniques/#user-content-fnref-sparsegpt" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-minitron">
<p><a href="https://arxiv.org/abs/2407.14679" rel="noopener noreferrer">Compact Language Models via Pruning and Knowledge Distillation</a>. <a href="https://blog.ecitis.org/model-compression-techniques/#user-content-fnref-minitron" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-distillation">
<p><a href="https://arxiv.org/abs/1503.02531" rel="noopener noreferrer">Distilling the Knowledge in a Neural Network</a>. <a href="https://blog.ecitis.org/model-compression-techniques/#user-content-fnref-distillation" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Quantization</category>
            <category>Small models</category>
            <category>Edge &amp; on-device</category>
            <enclosure length="83157" type="image/png" url="https://blog.ecitis.org/open-graph/model-compression-techniques.png"/>
        </item>
        <item>
            <title><![CDATA[Model Alignment Is a Control Problem]]></title>
            <link>https://blog.ecitis.org/model-alignment-control-loop/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/model-alignment-control-loop/</guid>
            <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Connect specifications, preference learning, process supervision, scalable oversight, and causal audits into one alignment control loop.]]></description>
            <content:encoded><![CDATA[<p>Post-training pipelines collect preferences, optimize a policy, and test whether the assistant is helpful and safe. The training signal is a proxy for intended behavior. The model must generalize that proxy to inputs, tools, and situations absent from training.</p>
<p>Use the following operational definition:</p>
<blockquote>
<p>An aligned model follows the intended specification when the evaluator is weak, the context is unfamiliar, and shortcuts are available.</p>
</blockquote>
<p>This definition turns alignment into a control problem. The system needs a specification, a way to convert that specification into supervision, an optimizer, and measurements that expose failures. Any one of those parts can be wrong while benchmark scores improve.</p>
<figure><figcaption><strong>Alignment closes the gap between intent and deployed behavior</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/model-alignment-control-loop-0.webp" width="512" height="866" alt="Alignment closes the gap between intent and deployed behavior" loading="lazy" decoding="async"></figure>
<h2 id="preferences-train-a-proxy">Preferences train a proxy</h2>
<p>The standard reinforcement learning from human feedback pipeline begins with a pretrained model, supervised instruction tuning, pairwise preference data, a reward model, and policy optimization. InstructGPT established this pipeline for instruction-following language models.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p>For prompt <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow></semantics></math>, completion <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>y</mi></mrow></semantics></math>, learned reward <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow></semantics></math>, and reference policy <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mrow><mi mathvariant="normal">r</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">f</mi></mrow></msub></mrow></semantics></math>, the policy objective is commonly written as:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>θ</mi></munder><mtext>  </mtext><msub><mi mathvariant="double-struck">E</mi><mrow><mi>y</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></msub><mrow><mo fence="true">[</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>−</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>y</mi><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi mathvariant="normal">r</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">f</mi></mrow></msub><mo stretchy="false">(</mo><mi>y</mi><mo>∣</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo fence="true">]</mo></mrow></mrow></semantics></math>
<p>The reward term pulls behavior toward the preference model. The KL term limits movement away from the reference policy. Neither term states the desired behavior directly. The reward model learns it from comparisons, and the comparisons reflect annotator instructions, sampling policy, prompt distribution, and labeling noise.</p>
<p>Direct Preference Optimization rewrites this constrained reward problem as a classification loss on preferred and rejected responses.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> It removes the separately trained reward model and online reinforcement learning loop, but it does not remove the proxy problem. The chosen response still represents a judgment made under a particular rubric and context.</p>
<p>These layers can fail independently:</p>



































<div class="table-wrap"><table><thead><tr><th>Layer</th><th>What it contains</th><th>Common failure</th></tr></thead><tbody><tr><td>Intended behavior</td><td>What the system should do</td><td>Ambiguous or conflicting goals</td></tr><tr><td>Written specification</td><td>Policies, principles, examples</td><td>Gaps and underspecified edge cases</td></tr><tr><td>Supervision</td><td>Preferences, critiques, step labels</td><td>Evaluator error or shortcut features</td></tr><tr><td>Training objective</td><td>Reward or preference loss</td><td>Proxy optimization</td></tr><tr><td>Deployed policy</td><td>Behavior under new conditions</td><td>Distribution shift or strategic adaptation</td></tr></tbody></table></div>
<p>A higher reward is evidence that the policy fits the learned proxy. It is not proof that the proxy captures the intended behavior.</p>
<figure><figcaption><strong>Alignment supervision is a stack of proxies</strong> — Evidence at one layer does not validate every layer above it.</figcaption><img src="https://blog.ecitis.org/feeds/figures/model-alignment-control-loop-1.webp" width="512" height="804" alt="Alignment supervision is a stack of proxies — Evidence at one layer does not validate every layer above it." loading="lazy" decoding="async"></figure>
<h2 id="written-principles-make-the-target-inspectable">Written principles make the target inspectable</h2>
<p>Constitutional AI replaces some human harmlessness labels with model-generated critiques and preferences conditioned on written principles.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Its supervised phase asks a model to critique and revise responses. Its reinforcement learning phase uses AI-generated preferences to train a preference model.</p>
<p>This changes where human judgment enters the system. Humans write or approve principles rather than label every sampled answer. The principles are easier to inspect than a reward model’s weights, but their interpretation still depends on the model producing the critique.</p>
<p>Deliberative alignment moves the specification closer to inference. A reasoning model is trained to recall and apply written safety policies before producing an answer.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> This can improve decisions that require combining several policy clauses. It also creates a new object to evaluate: whether the model selected the relevant policy and applied it to the facts of the request.</p>
<p>The two methods expose different failure surfaces:</p>
<ul>
<li>Constitutional feedback can misapply a principle while generating training labels.</li>
<li>Deliberation can retrieve the wrong rule or construct a convenient interpretation.</li>
<li>A complete written policy can still conflict with another policy or omit a deployment-specific constraint.</li>
<li>A correct rationale can be paired with an action caused by another internal mechanism.</li>
</ul>
<p>Written specifications improve inspectability. They do not guarantee faithful internal reasoning.</p>
<h2 id="process-supervision-changes-what-receives-credit">Process supervision changes what receives credit</h2>
<p>Outcome supervision rewards a final answer. Process supervision evaluates intermediate steps. On mathematical reasoning tasks, the work behind PRM800K found that a process-supervised reward model selected better solutions than an outcome-supervised reward model in the evaluated setting.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-5" id="user-content-fnref-5">5</a></sup></p>
<p>The alignment relevance is credit assignment. An outcome-only reward can reinforce a correct answer reached through an invalid or unsafe process. Step-level labels can penalize the first bad transition before later steps conceal it.</p>
<p>Process supervision also has costs:</p>
<ul>
<li>Labels are more expensive because an evaluator must inspect a trajectory.</li>
<li>A visible chain of thought may not faithfully expose the computation that caused the answer.</li>
<li>Fine-grained rubrics can reward verbose compliance with the rubric rather than efficient reasoning.</li>
<li>Tool-using agents require process labels for text, actions, state changes, and omissions.</li>
</ul>
<p>Choose process checkpoints according to risk. A coding agent can be evaluated at each file mutation or shell command. A medical assistant may need evidence checks before advice. A long-horizon agent needs stateful constraints that survive across turns.</p>
<h2 id="oversight-must-work-when-the-model-is-stronger">Oversight must work when the model is stronger</h2>
<p>Human preference learning assumes the evaluator can recognize a better answer. That assumption weakens as models operate in domains where checking a solution is harder than producing one.</p>
<p>Weak-to-strong generalization studies an empirical proxy for this problem: a weaker model supervises a stronger model, and researchers measure whether the strong model can exceed the supervisor’s errors rather than imitate them.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-6" id="user-content-fnref-6">6</a></sup> The reported results show partial recovery of stronger capabilities on some tasks, alongside failures on preference data. The setup is evidence that the question can be tested, not a solution to superhuman oversight.</p>
<p>Scalable oversight methods try to restructure evaluation:</p>
<ul>
<li>decompose a hard task into locally checkable claims</li>
<li>use one model to help a human inspect another model</li>
<li>require executable tests, proofs, or external evidence</li>
<li>compare independent solutions before revealing either rationale</li>
<li>assign more evaluator effort to high-uncertainty or high-impact decisions</li>
</ul>
<p>The evaluator must remain independent enough to catch shared mistakes. Two models trained on similar data can agree for the same wrong reason.</p>
<h2 id="model-organisms-make-hidden-objectives-testable">Model organisms make hidden objectives testable</h2>
<p>Behavioral red teaming is weak when a model behaves differently after detecting an evaluation. Model organisms address this by deliberately training models with known hidden objectives or failure propensities, then asking auditors to recover them.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-7" id="user-content-fnref-7">7</a></sup></p>
<p>This creates a ground truth for audit research. If the experimenter knows the hidden objective, an audit can be scored on whether it identifies the cause and its behavioral traces.</p>
<p>Anthropic’s automated auditing work combines behavioral evaluations, training-data inspection, and interpretability tools against such models.<sup><a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fn-7" id="user-content-fnref-7-2">7</a></sup> The approach tests an entire investigation workflow rather than one probe. It also reveals a deployment lesson: a monitor should generate falsifiable hypotheses, design counterexamples, and seek causal evidence.</p>
<p>Useful causal checks include:</p>
<ol>
<li>Find a representation or behavior correlated with the suspected objective.</li>
<li>Remove or steer that representation.</li>
<li>Measure whether the predicted behavior changes.</li>
<li>Test unrelated capabilities to detect broad damage.</li>
<li>Repeat on prompts that do not announce the evaluation.</li>
</ol>
<p>Correlation can point to a mechanism. Intervention tests whether the mechanism carries causal weight.</p>
<h2 id="what-an-alignment-report-should-contain">What an alignment report should contain</h2>
<p>A deployment report should separate the layers of evidence:</p>
<ul>
<li>the written specification and unresolved conflicts</li>
<li>the source and coverage of supervision</li>
<li>held-out behavioral evaluations</li>
<li>adversarial tests created independently of training</li>
<li>process-level checks for consequential actions</li>
<li>monitor false positives and false negatives</li>
<li>causal interventions where interpretability claims support a safety decision</li>
<li>residual risks that the evaluation cannot measure</li>
</ul>
<p>An alignment claim needs a stated threat model and operating environment. Its evidence should connect an inspectable specification to traceable supervision, audits that expose known hidden failures, and deployment monitoring that detects behavior outside the tested regime.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Long Ouyang et al., “Training language models to follow instructions with human feedback,” NeurIPS, 2022. <a href="https://arxiv.org/abs/2203.02155" rel="noopener noreferrer">https://arxiv.org/abs/2203.02155</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Rafael Rafailov et al., “Direct Preference Optimization: Your Language Model is Secretly a Reward Model,” NeurIPS, 2023. <a href="https://arxiv.org/abs/2305.18290" rel="noopener noreferrer">https://arxiv.org/abs/2305.18290</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Yuntao Bai et al., “Constitutional AI: Harmlessness from AI Feedback,” Anthropic, 2022. <a href="https://www.anthropic.com/research/constitutional-ai-harmlessness-from-ai-feedback" rel="noopener noreferrer">https://www.anthropic.com/research/constitutional-ai-harmlessness-from-ai-feedback</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Melody Guan et al., “Deliberative alignment: reasoning enables safer language models,” OpenAI, 2024. <a href="https://openai.com/index/deliberative-alignment/" rel="noopener noreferrer">https://openai.com/index/deliberative-alignment/</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Hunter Lightman et al., “Let’s Verify Step by Step,” ICLR, 2024. <a href="https://arxiv.org/abs/2305.20050" rel="noopener noreferrer">https://arxiv.org/abs/2305.20050</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Collin Burns et al., “Weak-to-Strong Generalization: Eliciting Strong Capabilities With Weak Supervision,” ICML, 2024. <a href="https://openai.com/index/weak-to-strong-generalization/" rel="noopener noreferrer">https://openai.com/index/weak-to-strong-generalization/</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Trenton Bricken et al., “Building and evaluating alignment auditing agents,” Anthropic, 2025. <a href="https://alignment.anthropic.com/2025/automated-auditing/" rel="noopener noreferrer">https://alignment.anthropic.com/2025/automated-auditing/</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-7" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/model-alignment-control-loop/#user-content-fnref-7-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Alignment</category>
            <category>Reinforcement learning</category>
            <category>Safety</category>
            <enclosure length="61779" type="image/png" url="https://blog.ecitis.org/open-graph/model-alignment-control-loop.png"/>
        </item>
        <item>
            <title><![CDATA[Cold Starts: Getting Model Weights from Storage into HBM]]></title>
            <link>https://blog.ecitis.org/model-cold-starts/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/model-cold-starts/</guid>
            <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Break down the cold-start path for large models across object storage, page cache, PCIe, and snapshot restore, then measure each stage.]]></description>
            <content:encoded><![CDATA[<p>Serving benchmarks report tokens per second on a warm replica. Autoscaling behaviour is decided by a different number, and a very improvable one: how long a replica takes to go from scheduled to serving.</p>
<p>Cold start is a bandwidth problem with a scheduling problem layered on top, and every stage of it is measurable. That is what makes it satisfying to work on. The dominant costs sit in the path rather than in the model, so they respond to ordinary engineering: pinned staging buffers, overlapped stages, a node-local cache, and snapshot restore each move the number on their own.</p>
<figure><figcaption><strong>The path a checkpoint takes from object storage to accelerator memory</strong></figcaption><p><a href="https://blog.ecitis.org/model-cold-starts/">View this figure on the site &#8594;</a></p></figure>
<h2 id="transfer-rates-by-hop">Transfer rates by hop</h2>
<p>A checkpoint that lives in object storage and must end up in HBM crosses several boundaries, each with its own achievable rate:</p>



































<div class="table-wrap"><table><thead><tr><th>Hop</th><th>Typical achievable rate</th><th>Notes</th></tr></thead><tbody><tr><td>Object store → node</td><td>1–12 GiB/s</td><td>Depends on network tier and parallel connections</td></tr><tr><td>Local NVMe → page cache</td><td>2–14 GiB/s</td><td>Per-device; often striped</td></tr><tr><td>Page cache → pinned host buf</td><td>5–20 GiB/s</td><td>A memory copy, but a real one</td></tr><tr><td>Pinned host → HBM (PCIe 5)</td><td>~50 GiB/s</td><td>~25 GiB/s on PCIe 4</td></tr><tr><td>Pageable host → HBM</td><td>5–15 GiB/s</td><td>Driver stages through an internal pinned buffer</td></tr></tbody></table></div>
<p>Lower bound on cold start, ignoring overlap:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>T</mi><mtext>cold</mtext></msub><mo>≥</mo><mfrac><mi>S</mi><msub><mi>B</mi><mtext>net</mtext></msub></mfrac><mo>+</mo><mfrac><mi>S</mi><msub><mi>B</mi><mtext>h2d</mtext></msub></mfrac></mrow></semantics></math>
<p>For a 140 GiB checkpoint at 3 GiB/s network and 20 GiB/s effective host-to-device, that is roughly 47 + 7 = 54 seconds. Most deployments start well above that floor, and the gap is almost always one of five causes, each with a known fix.</p>
<h2 id="five-levers-that-move-the-number">Five levers that move the number</h2>
<p><strong>Pageable transfers.</strong> <code>tensor.to(&quot;cuda&quot;)</code> from ordinary heap memory cannot DMA directly. The driver copies into an internal pinned staging buffer first, so every byte is copied twice and the transfer runs at a fraction of link rate. The fix is pinned staging buffers you control, reused across chunks rather than allocated per tensor.</p>
<p><strong>Serialized stages.</strong> Fetch, then copy, then transfer, with nothing overlapping. The stages use different resources: NIC, CPU, and PCIe. They should be pipelined. Overlapping the network fetch of chunk <code>n+1</code> with the device transfer of chunk <code>n</code> brings the total close to <code>S / min(B_net, B_h2d)</code> rather than the sum.</p>
<p><strong>Per-tensor overhead.</strong> A large model has thousands of tensors. If each one triggers a separate allocation, a separate transfer, and a separate synchronize, launch and synchronization overhead can dominate the actual bytes. Batch small tensors into a single staged buffer.</p>
<p><strong>Single-stream fetch.</strong> Object stores deliver bandwidth through concurrency. A single-threaded <code>GET</code> of a 140 GiB file will not saturate anything. Ranged parallel reads across many connections are the difference between 300 MiB/s and multiple GiB/s.</p>
<p><strong>Deserialize-then-copy.</strong> Formats that require a parse or decompression step insert a CPU-bound stage between storage and PCIe. A memory-mappable format removes that stage entirely; this is one of the main practical arguments for safetensors.</p>
<h2 id="memory-mapping-and-the-page-cache">Memory mapping and the page cache</h2>
<p><code>mmap</code> maps file pages into the process address space without an eager read. Pages fault in on access. This is why a mapped load looks nearly instantaneous the second time: the pages are already in the page cache, and the “load” is address-space bookkeeping.</p>
<p>Two implications:</p>
<p><strong>A warm benchmark measures nothing about cold start.</strong> Drop caches, or measure on a node that has never held the file, or the result is a page-table exercise.</p>
<p><strong>A mapped tensor is not resident memory.</strong> Sizing a container’s memory limit from RSS after a mapped load will under-provision, because page-cache pages are reclaimable and may not be counted the way you expect. Under memory pressure the kernel evicts them, and the next access re-reads from disk mid-request.</p>
<p>Sequential access order matters when the pages are not resident. Faulting in a 140 GiB file in tensor-declaration order, when the payload is laid out in a different order, turns a sequential read into a seek pattern. Iterate in offset order and let readahead work.</p>
<h2 id="node-local-caching">Node-local caching</h2>
<p>The first replica on a node pays the network cost. Every subsequent replica, and every restart, should not.</p>
<p>A local NVMe cache keyed by content hash turns the steady-state cold start into a local read. The policy that matters is eviction: LRU over checkpoint files is usually wrong because it ignores both size and refetch cost. A cost-aware score works better:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>value</mtext><mo stretchy="false">(</mo><mi>m</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mover accent="true"><msub><mi>p</mi><mtext>restart</mtext></msub><mo stretchy="true">^</mo></mover><mo stretchy="false">(</mo><mi>m</mi><mo stretchy="false">)</mo><mo>⋅</mo><mfrac><msub><mi>S</mi><mi>m</mi></msub><msub><mi>B</mi><mtext>net</mtext></msub></mfrac></mrow><msub><mi>S</mi><mi>m</mi></msub></mfrac><mo>=</mo><mfrac><mrow><mover accent="true"><msub><mi>p</mi><mtext>restart</mtext></msub><mo stretchy="true">^</mo></mover><mo stretchy="false">(</mo><mi>m</mi><mo stretchy="false">)</mo></mrow><msub><mi>B</mi><mtext>net</mtext></msub></mfrac></mrow></semantics></math>
<p>which reduces to “keep what is most likely to be needed again,” because refetch time is proportional to size and so cancels. The useful refinement is per-model network cost, since not all checkpoints come from the same tier of storage.</p>
<p>For clusters that support it, GPUDirect Storage moves data from NVMe to HBM over PCIe without a bounce through host memory, removing one full copy. It requires a supported filesystem and driver stack, and it interacts badly with anything that wants to transform bytes on the way through. That is a further argument for a format whose on-disk layout is the in-memory layout.</p>
<h2 id="snapshot-and-restore">Snapshot and restore</h2>
<p>The fastest load is the one that does not happen. If a process reaches a known-good state after loading, that state can be captured and restored.</p>
<p>CRIU-style checkpointing of a GPU process, plus device-memory snapshotting, lets a replica skip parsing, allocation, and initialization entirely and resume from a memory image. Serverless GPU platforms use this to reach sub-second starts for models that take a minute to load conventionally.</p>
<p>The constraints are real:</p>
<ul>
<li>The restore target must match the source in GPU model, driver version, and CUDA context assumptions.</li>
<li>Any open file descriptor, socket, or clock-dependent state in the snapshot must be re-established.</li>
<li>The snapshot is roughly the size of the resident footprint, so it is not free storage-wise.</li>
</ul>
<p>Snapshot restore is worth building when the model set is small and stable and the scale-up rate is high. It is not worth building for a long tail of rarely used models, where the cache-and-stream path is simpler and nearly as good.</p>
<h2 id="overlapping-load-with-the-first-request">Overlapping load with the first request</h2>
<p>Weights are needed in layer order. A forward pass through layer 0 cannot start before layer 0’s weights are resident, but it does not need layer 40’s.</p>
<p>That allows a genuine pipeline: begin prefill as soon as the early layers land, and continue streaming later layers while early layers compute. The gain is bounded by the ratio of compute time to transfer time, which for a single prefill is usually unfavorable, since the transfer is much slower, so this mostly helps the first request rather than the replica’s readiness overall.</p>
<p>The more valuable version is admission control that understands partial readiness. A replica that reports healthy the moment its process starts will receive traffic it cannot serve, and the load balancer will record timeouts. A replica that reports readiness only after a successful synthetic forward pass will not.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">async</span><span style="color:var(--astro-code-token-keyword)"> def</span><span style="color:var(--astro-code-token-function)"> readiness</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">engine</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-keyword)"> not</span><span style="color:var(--astro-code-foreground)"> engine</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">weights_resident</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-token-constant)"> 503</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-token-string-expression)">&quot;stage&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;loading&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;pct&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> engine</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">load_progress</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-keyword)"> not</span><span style="color:var(--astro-code-foreground)"> engine</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">warmed</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-token-constant)"> 503</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-token-string-expression)">&quot;stage&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;warming&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-constant)"> 200</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-token-string-expression)">&quot;stage&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;ready&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span></code></pre>
<p>The warm stage matters more than it looks. The first forward pass triggers kernel autotuning, workspace allocation, and, for compiled backends, graph capture. A replica that skips warmup serves its first several requests at several times the steady-state latency, which lands directly in the tail-latency SLO.</p>
<h2 id="a-measurement-harness">A measurement harness</h2>
<p>Reporting one total is not actionable. Instrument the stages separately and report them as a fixed set of spans:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">STAGES </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;fetch&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;map&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;stage_host&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;h2d&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;init&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;warmup&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> report</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">spans</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> size_gib</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    total </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> sum</span><span style="color:var(--astro-code-token-punctuation)">(spans.</span><span style="color:var(--astro-code-token-function)">values</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> name </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> STAGES</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        s </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> spans</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get</span><span style="color:var(--astro-code-token-punctuation)">(name, </span><span style="color:var(--astro-code-token-constant)">0.0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        rate </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">size_gib </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-keyword)">:6.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GiB/s&quot;</span><span style="color:var(--astro-code-token-keyword)"> if</span><span style="color:var(--astro-code-foreground)"> s </span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-keyword)"> else</span><span style="color:var(--astro-code-token-string-expression)"> &quot;     n/a&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">name</span><span style="color:var(--astro-code-token-keyword)">:&lt;12</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-punctuation)">s</span><span style="color:var(--astro-code-token-keyword)">:7.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s </span><span style="color:var(--astro-code-token-constant)">{100</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-punctuation)"> s </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> total</span><span style="color:var(--astro-code-token-keyword)">:5.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">%  </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">rate</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-string-expression)">&#39;total&#39;</span><span style="color:var(--astro-code-token-keyword)">:&lt;12</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-punctuation)">total</span><span style="color:var(--astro-code-token-keyword)">:7.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Run it in three configurations: cold node, warm page cache, and warm local cache. The three profiles answer three different questions: what a scale-out event costs, what a process restart costs, and whether the local cache is doing its job.</p>
<p>Track these as fleet metrics, not one-off measurements. Cold-start time drifts when checkpoint size grows, when a storage tier changes, or when a library update alters the load path. The drift is invisible until an autoscaling event exposes it during a traffic spike.</p>
<h2 id="autoscaling-consequences">Autoscaling consequences</h2>
<p>Cold start sets the minimum useful reaction time of a scaling policy. If a replica takes 90 seconds to become ready, a policy reacting to a one-minute load average is always responding to conditions that have already changed.</p>
<p>The practical responses:</p>
<p><strong>Scale on a leading signal.</strong> Queue depth and arrival rate lead saturation; GPU utilization lags it.</p>
<p><strong>Keep warm capacity proportional to cold-start cost.</strong> The buffer needed is roughly the traffic growth expected during one cold start, which makes slow loading directly expensive in idle hardware.</p>
<p><strong>Separate model swap from replica launch.</strong> A running process that can load a second model into free HBM avoids process startup, driver init, and warmup. This is the main structural argument for a multi-model server over one-process-per-model.</p>
<p><strong>Bound the blast radius of a bad checkpoint.</strong> A checkpoint that fails to load after a 60-second fetch produces a crash loop that hammers object storage. Cache the failure, not just the success.</p>
<p>Cold start is not a fixed property of a model. It is a property of the path, and every hop in that path is one you chose, which means every hop is one you can improve.</p>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Model loading</category>
            <category>LLM serving</category>
            <category>Memory</category>
            <enclosure length="63280" type="image/png" url="https://blog.ecitis.org/open-graph/model-cold-starts.png"/>
        </item>
        <item>
            <title><![CDATA[Checkpoint Formats: Safetensors, GGUF, ONNX, and the Pickle Problem]]></title>
            <link>https://blog.ecitis.org/checkpoint-formats/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/checkpoint-formats/</guid>
            <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare serialization formats for model weights and see how header layout, memory mapping, and trust boundaries decide load time.]]></description>
            <content:encoded><![CDATA[<p>A checkpoint is usually described as “the weights.” The more useful description is a file layout, and that layout is one of the better levers in an inference stack. It decides whether a loader can address one tensor without reading the file, whether bytes can be mapped instead of copied, whether quantization parameters travel with the payload, and how small the trust boundary around model distribution can be.</p>
<p>These are properties of the container rather than the model, which is exactly what makes them worth using. Switching format costs nothing in accuracy and pays back in cold-start latency, replica scale-out cost, and a cleaner supply chain.</p>
<figure><figcaption><strong>Checkpoint formats decide what happens between disk and device</strong></figcaption><p><a href="https://blog.ecitis.org/checkpoint-formats/">View this figure on the site &#8594;</a></p></figure>
<h2 id="what-a-loader-actually-needs">What a loader actually needs</h2>
<p>Given a file and a target device, a loader has to answer four questions before it can produce a usable module:</p>
<ol>
<li>Which tensors exist, and under what names?</li>
<li>For each tensor, what dtype, shape, and byte range?</li>
<li>What transformation is required between the stored bytes and the in-memory layout?</li>
<li>Is any of this trustworthy?</li>
</ol>
<p>A format is fast when questions one and two can be answered from a small prefix of the file, and when question three is the identity function.</p>
<h2 id="pickle-and-the-cost-of-a-general-mechanism">Pickle and the cost of a general mechanism</h2>
<p>PyTorch’s default <code>torch.save</code> writes a ZIP archive containing a Python pickle stream plus raw storage records. Pickle is a stack-based virtual machine. Loading executes opcodes, including <code>GLOBAL</code> and <code>REDUCE</code>, which import an arbitrary module attribute and call it.</p>
<p>This is not a bug being fixed; it is what pickle is for. <code>weights_only=True</code> restricts the allowed globals to a small allowlist and is now the PyTorch default, which closes the general execution path, but the format still has structural costs:</p>
<ul>
<li>Tensor offsets are not declared in a readable header. Discovering the layout means walking the pickle stream.</li>
<li>Selective loading of a single tensor requires a custom unpickler that understands the storage-record indirection.</li>
<li>The archive is a container format with its own metadata, so byte ranges are not simply contiguous.</li>
</ul>
<p>For a checkpoint you produced and control, this is fine. For a checkpoint downloaded from a public hub, “the loader is safe if a flag is set correctly” is a weaker property than “the loader cannot execute anything.”</p>
<h2 id="safetensors-a-header-and-a-payload">Safetensors: a header and a payload</h2>
<p>Safetensors takes the opposite design position. The file is:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>[ 8 bytes ][ N bytes JSON header ][ tensor payload ]</span></span>
<span class="line"><span>   N          {&quot;weight&quot;: {&quot;dtype&quot;: &quot;F16&quot;,</span></span>
<span class="line"><span>                          &quot;shape&quot;: [4096, 4096],</span></span>
<span class="line"><span>                          &quot;data_offsets&quot;: [0, 33554432]}, ...}</span></span></code></pre>
<p>The first eight bytes are the header length as a little-endian <code>u64</code>. The header is UTF-8 JSON mapping tensor names to dtype, shape, and a half-open byte range into the payload. Everything after the header is raw tensor data with no per-tensor framing.</p>
<p>Three consequences follow directly:</p>
<p><strong>Layout is O(header) to discover.</strong> Reading a few kilobytes tells you every tensor’s name, shape, dtype, and exact location. Nothing requires reading the payload.</p>
<p><strong>Loading can be zero-copy.</strong> Because each tensor is a contiguous byte range with no framing and no compression, the loader can <code>mmap</code> the file and construct tensors that point into the mapped region. No parse step, no intermediate buffer.</p>
<p><strong>There is no code path to execute.</strong> The parser reads integers, strings, and offsets. A malicious file can produce wrong tensors or a failed parse; it cannot import a module.</p>
<p>The zero-copy property is the one that changes deployment behavior. A conventional load reads bytes into a buffer, then copies into a tensor, then copies to the device, which is three traversals of the whole checkpoint. A mapped load traverses once, on the device transfer.</p>
<h3 id="header-validation">Header validation</h3>
<p>Zero-copy does not mean unvalidated. A loader must check that:</p>
<ul>
<li>declared ranges lie inside the file</li>
<li>ranges do not overlap in ways the loader does not expect</li>
<li><code>shape</code> and <code>dtype</code> multiply out to exactly the declared range length</li>
<li>the header length does not exceed the file size</li>
</ul>
<p>The reference implementations do this. A hand-rolled reader that trusts <code>data_offsets</code> will happily read out of bounds.</p>
<h3 id="what-safetensors-deliberately-omits">What safetensors deliberately omits</h3>
<p>There is a free-form <code>__metadata__</code> string map and nothing else. No graph, no tokenizer, no hyperparameters, no quantization scheme. A safetensors file is only tensors. Everything else lives in sidecar files: <code>config.json</code>, <code>tokenizer.json</code>, <code>generation_config.json</code>. The loader is responsible for keeping them consistent.</p>
<p>For a hub-distributed repository that is fine, because the repository is the unit. For a file you hand to someone, it means one file is not enough.</p>
<h2 id="gguf-one-file-quantization-included">GGUF: one file, quantization included</h2>
<p>GGUF was designed for the opposite distribution model: a single file a user downloads and runs locally.<sup><a href="https://blog.ecitis.org/checkpoint-formats/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> Its structure is a magic number, a version, tensor and metadata counts, a key-value metadata block, then a tensor directory, then aligned tensor data.</p>
<p>The metadata block is typed and extensible: architecture name, layer counts, head counts, RoPE parameters, the full tokenizer including merges and special-token ids, and chat-template strings. The tensor directory names each tensor and gives its dimensions, its quantization type, and its offset.</p>
<p>The per-tensor quantization type is the structural difference from safetensors. GGUF stores weights in block-quantized formats where a block of values shares scale metadata:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mover accent="true"><mi>w</mi><mo>^</mo></mover><mi>i</mi></msub><mo>=</mo><msub><mi>s</mi><mi>b</mi></msub><mo>⋅</mo><msub><mi>q</mi><mi>i</mi></msub><mo>+</mo><msub><mi>z</mi><mi>b</mi></msub><mo separator="true">,</mo><mspace width="2em"></mspace><mi>i</mi><mo>∈</mo><mtext>block </mtext><mi>b</mi></mrow></semantics></math>
<p>Different tensors in the same file can use different types. Attention output projections and the token embedding are commonly kept at higher precision than feed-forward weights, because the error budget is not uniform across a transformer. A format that fixes one dtype per file cannot express that.</p>
<p>The tradeoff is that GGUF’s payload is not the in-memory layout for a general framework. The blocks are laid out for specific dequantizing kernels. GGUF is fast to load for the runtimes that speak its block formats and requires a real conversion for anything else.</p>
<h2 id="onnx-and-engine-plans-weights-attached-to-a-graph">ONNX and engine plans: weights attached to a graph</h2>
<p>ONNX serializes a computation graph as protobuf, with initializers (weights) either inline or in external data files. This is a different category of artifact: it describes what to compute, not only what the parameters are.</p>
<p>That buys portability across runtimes and costs flexibility. A dynamic control-flow pattern that is trivial in eager Python has to be expressed in ONNX operators or traced away. For transformer decoding, KV cache handling has to become explicit graph inputs and outputs.</p>
<p>Protobuf also imposes a 2 GB message limit, so any real LLM must use external data, at which point ONNX has the same sidecar-consistency problem as safetensors, plus a graph to keep in sync.</p>
<p>A TensorRT engine goes further and is not portable at all. The plan file is specialized to a GPU architecture, a TensorRT version, and often a fixed shape profile. It loads very fast because nearly all decisions were made at build time. It is a build artifact, not a distribution artifact, and rebuilding it is part of the deployment pipeline rather than the download.</p>
<h2 id="comparison">Comparison</h2>













































































<div class="table-wrap"><table><thead><tr><th>Property</th><th>Pickle (<code>.bin</code>/<code>.pt</code>)</th><th>Safetensors</th><th>GGUF</th><th>ONNX</th><th>TRT engine</th></tr></thead><tbody><tr><td>Layout discoverable</td><td>Requires stream walk</td><td>JSON header</td><td>Header + directory</td><td>Protobuf parse</td><td>Opaque</td></tr><tr><td>Memory-mappable</td><td>Partially</td><td>Yes, zero-copy</td><td>Yes</td><td>External data</td><td>Yes</td></tr><tr><td>Executes on load</td><td>Yes unless restricted</td><td>No</td><td>No</td><td>No</td><td>No</td></tr><tr><td>Carries hyperparams</td><td>No</td><td>No</td><td>Yes</td><td>Graph implies</td><td>Baked in</td></tr><tr><td>Carries tokenizer</td><td>No</td><td>No</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td>Mixed per-tensor quant</td><td>Framework-dependent</td><td>No</td><td>Yes</td><td>Limited</td><td>Yes</td></tr><tr><td>Portability</td><td>PyTorch</td><td>Framework-wide</td><td>GGML-family runtimes</td><td>Broad</td><td>One GPU/version</td></tr><tr><td>Natural unit</td><td>Repository</td><td>Repository</td><td>Single file</td><td>Model + data</td><td>Build output</td></tr></tbody></table></div>
<h2 id="measuring-the-load-path">Measuring the load path</h2>
<p>Format claims are easy to test. What you want is a breakdown, not a total:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> safetensors </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> safe_open</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> load_profile</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">path</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    stats </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    t0 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">perf_counter</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-token-function)"> safe_open</span><span style="color:var(--astro-code-token-punctuation)">(path, framework</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cpu&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        keys </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> list</span><span style="color:var(--astro-code-token-punctuation)">(f.</span><span style="color:var(--astro-code-token-function)">keys</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;header_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">perf_counter</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> t0</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        t1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">perf_counter</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        tensors </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-foreground)">k</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_tensor</span><span style="color:var(--astro-code-token-punctuation)">(k)</span><span style="color:var(--astro-code-token-keyword)"> for</span><span style="color:var(--astro-code-foreground)"> k </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> keys</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;map_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">perf_counter</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> t1</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    t2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">perf_counter</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tensors </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-foreground)">k</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> v</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(device, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> for</span><span style="color:var(--astro-code-foreground)"> k</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> v </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> tensors</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">items</span><span style="color:var(--astro-code-token-punctuation)">()}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;h2d_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">perf_counter</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> t2</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    total </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> sum</span><span style="color:var(--astro-code-token-punctuation)">(v.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">() </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-punctuation)"> v.</span><span style="color:var(--astro-code-token-function)">element_size</span><span style="color:var(--astro-code-token-punctuation)">() </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> v </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> tensors.</span><span style="color:var(--astro-code-token-function)">values</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;gib&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> total </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;h2d_gib_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;gib&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> /</span><span style="color:var(--astro-code-foreground)"> stats</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;h2d_s&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> stats</span></span></code></pre>
<p>Run it twice. The first run includes filling the OS page cache from disk; the second usually does not. Reporting only the warm number makes every format look good and tells you nothing about a fresh replica. Reporting only the cold number attributes storage bandwidth to the format.</p>
<p>The number to watch is <code>h2d_gib_s</code> against the theoretical PCIe or interconnect rate. If a mapped load is far below link speed, the transfer is going through pageable memory and being staged, and the fix is pinned buffers rather than a different format.</p>
<h2 id="converting-between-formats">Converting between formats</h2>
<p>Conversion is straightforward once you know which four things to check.</p>
<p><strong>Tied weights.</strong> Many models share the token embedding with the output projection. Safetensors forbids two tensor names pointing at overlapping byte ranges, so conversion must either duplicate the tensor or drop one name and rely on the config’s <code>tie_word_embeddings</code> flag. A converter that drops the name and a loader that ignores the flag produce a model with a randomly initialized head.</p>
<p><strong>Dtype coercion.</strong> BF16 stored, FP16 loaded, is not lossless: BF16 has eight exponent bits and FP16 has five. Values outside FP16’s range become infinities. This shows up as <code>NaN</code> several layers in, far from the actual fault.</p>
<p><strong>Layout assumptions.</strong> Attention weight matrices differ between implementations in whether Q/K/V are fused, and in head-interleaving order for rotary embeddings. Two checkpoints with identical shapes and identical tensor names can still be incompatible. Shape checks will not catch it; output comparison against a reference will.</p>
<p><strong>Metadata drift.</strong> The tensors convert cleanly, <code>config.json</code> does not travel, and the new file is loaded with default hyperparameters that happen to parse.</p>
<p>The only reliable acceptance test is numerical: run a fixed prompt set through source and converted models and compare logits.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> assert_equivalent</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">ref</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> cand</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> batch</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> atol</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2e-2</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">no_grad</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ref</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">batch).</span><span style="color:var(--astro-code-foreground)">logits</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">float</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> cand</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">batch).</span><span style="color:var(--astro-code-foreground)">logits</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">float</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    max_abs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (a </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> b)</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">abs</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">max</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">item</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    agree </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (a</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">argmax</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> ==</span><span style="color:var(--astro-code-foreground)"> b</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">argmax</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">float</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">mean</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">item</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    assert</span><span style="color:var(--astro-code-foreground)"> max_abs </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> atol</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;logit drift </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">max_abs</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    assert</span><span style="color:var(--astro-code-foreground)"> agree </span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-token-constant)"> 0.999</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;argmax agreement </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">agree</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span></span></code></pre>
<p>Greedy-token agreement alone is too weak: two models can agree on every argmax and diverge badly in the distribution’s tail, which shows up only under sampling.</p>
<h2 id="establishing-trust-in-a-checkpoint">Establishing trust in a checkpoint</h2>
<p>A checkpoint from a public source is an artifact that a process will parse and then use to drive computation. The realistic threat model has three levels:</p>
<p><strong>Code execution on load.</strong> Only pickle-family formats expose this. Prefer safetensors when it is offered; when it is not, load with restricted globals and, for anything unvetted, in a sandboxed process with no credentials.</p>
<p><strong>Resource exhaustion.</strong> A header declaring enormous shapes can trigger a large allocation before any check runs. Validate declared sizes against the file size and an expected bound.</p>
<p><strong>Semantic tampering.</strong> Weights that load cleanly and behave normally except on specific triggers. No format prevents this; it is an evaluation and provenance question, not a serialization one.</p>
<p>Provenance is the control that actually covers the third case. Pin the revision by commit hash rather than branch, record the hash in the deployment manifest, and verify checksums after transfer. A branch name resolves to different bytes on different days, which breaks reproducibility and audit for the same reason.</p>
<h2 id="choosing">Choosing</h2>
<p>The decision follows from the distribution model rather than from a benchmark:</p>
<ul>
<li><strong>Serving a repository at fleet scale:</strong> safetensors. Zero-copy load, no execution surface, sidecar config that the serving stack already tracks.</li>
<li><strong>Shipping a quantized model to end users:</strong> GGUF. Single file, tokenizer and hyperparameters included, mixed per-tensor quantization.</li>
<li><strong>Cross-runtime or non-Python deployment:</strong> ONNX, accepting the graph-capture work.</li>
<li><strong>Fixed model, fixed hardware, latency-critical:</strong> build a TensorRT engine as a deployment step, and keep a portable format as the source of truth.</li>
<li><strong>Receiving pickle:</strong> convert once, at ingest, in an isolated process, then never load the original again.</li>
</ul>
<p>The format is the interface between storage and compute, and it is one of the few parts of an inference stack you can change without touching the model at all. That makes it an unusually cheap win: pick the container that matches how the model is distributed and the load path improves for free.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Georgi Gerganov and contributors, “GGUF file format specification,” ggml, 2023–2025. <a href="https://github.com/ggml-org/ggml/blob/master/docs/gguf.md" rel="noopener noreferrer">https://github.com/ggml-org/ggml/blob/master/docs/gguf.md</a> <a href="https://blog.ecitis.org/checkpoint-formats/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Serving &amp; Runtime</category>
            <category>Model loading</category>
            <category>LLM serving</category>
            <category>Security</category>
            <enclosure length="62543" type="image/png" url="https://blog.ecitis.org/open-graph/checkpoint-formats.png"/>
        </item>
        <item>
            <title><![CDATA[LLM Training Optimizers: AdamW, Adafactor, Shampoo, and Muon]]></title>
            <link>https://blog.ecitis.org/llm-training-optimizers/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/llm-training-optimizers/</guid>
            <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare optimizer update rules alongside the memory, communication, numerical stability, and checkpoint costs they create at scale.]]></description>
            <content:encoded><![CDATA[<p>An optimizer defines both an update rule and a distributed state system. For a large transformer, moment tensors, matrix statistics, preconditioner roots, communication, and checkpoint format can consume as much engineering attention as the gradient formula.</p>
<figure><figcaption><strong>Optimizer state attached to one matrix parameter W</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/llm-training-optimizers-0.webp" width="512" height="386" alt="Optimizer state attached to one matrix parameter W" loading="lazy" decoding="async"></figure>
<h2 id="adamw-stores-coordinate-wise-moments">AdamW stores coordinate-wise moments</h2>
<p>Adam maintains exponential moving averages of gradients and squared gradients:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>m</mi><mi>t</mi></msub><mo>=</mo><msub><mi>β</mi><mn>1</mn></msub><msub><mi>m</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><msub><mi>β</mi><mn>1</mn></msub><mo stretchy="false">)</mo><msub><mi>g</mi><mi>t</mi></msub></mrow></semantics></math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>v</mi><mi>t</mi></msub><mo>=</mo><msub><mi>β</mi><mn>2</mn></msub><msub><mi>v</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><msub><mi>β</mi><mn>2</mn></msub><mo stretchy="false">)</mo><msubsup><mi>g</mi><mi>t</mi><mn>2</mn></msubsup></mrow></semantics></math>
<p>Bias-corrected estimates scale the update coordinate-wise. The original Adam paper introduced this adaptive first-order method.<sup><a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fn-adam" id="user-content-fnref-adam">1</a></sup></p>
<p>AdamW applies weight decay separately from the loss-gradient update:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>θ</mi><mrow><mi>t</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>η</mi><mi>λ</mi><mo stretchy="false">)</mo><msub><mi>θ</mi><mi>t</mi></msub><mo>−</mo><mi>η</mi><mfrac><msub><mover accent="true"><mi>m</mi><mo>^</mo></mover><mi>t</mi></msub><mrow><msqrt><msub><mover accent="true"><mi>v</mi><mo>^</mo></mover><mi>t</mi></msub></msqrt><mo>+</mo><mi>ϵ</mi></mrow></mfrac></mrow></semantics></math>
<p>Decoupling matters because adding an <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mn>2</mn></msub></mrow></semantics></math> penalty to the gradient is not equivalent to weight decay under an adaptive preconditioner.<sup><a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fn-adamw" id="user-content-fnref-adamw">2</a></sup></p>
<p>Each parameter normally has full-shape first- and second-moment state. Distributed optimizers shard these states across data-parallel ranks and gather updated parameter partitions as needed. Checkpoint conversion must preserve the association among a parameter and both moments.</p>
<p>Exclude parameters such as normalization scales or biases from decay only through an explicit, reviewed grouping rule:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">decay</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> no_decay </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> name</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> parameter </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">named_parameters</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-keyword)"> not</span><span style="color:var(--astro-code-foreground)"> parameter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">requires_grad</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        continue</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    target </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> no_decay </span><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-token-function)"> is_no_decay_parameter</span><span style="color:var(--astro-code-token-punctuation)">(name, parameter)</span><span style="color:var(--astro-code-token-keyword)"> else</span><span style="color:var(--astro-code-foreground)"> decay</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    target</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(parameter)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">groups </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span><span style="color:var(--astro-code-token-string-expression)">&quot;params&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> decay</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;weight_decay&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> weight_decay</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span><span style="color:var(--astro-code-token-string-expression)">&quot;params&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> no_decay</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;weight_decay&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0.0</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span></code></pre>
<p>Test that every trainable parameter appears once and only once.</p>
<h2 id="adafactor-factors-second-moment-state">Adafactor factors second-moment state</h2>
<p>For a matrix parameter, Adafactor approximates the full second-moment accumulator using row and column statistics. This changes state from matrix-sized storage to vectors sized by its dimensions. The Adafactor paper presents sublinear auxiliary storage for large parameter matrices.<sup><a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fn-adafactor" id="user-content-fnref-adafactor">3</a></sup></p>
<p>The approximation preserves less coordinate-level information than a full accumulator. It is most useful when large matrix parameters dominate. Vectors and small tensors need a different state rule because row-column factorization is not meaningful.</p>
<p>Adafactor also introduced relative-step and update-clipping choices. Libraries expose different defaults, so a configuration copied by optimizer name alone may not reproduce another implementation. Record:</p>
<ul>
<li>whether first-moment momentum is enabled;</li>
<li>relative or externally scheduled learning rate;</li>
<li>parameter-scale adaptation;</li>
<li>update clipping threshold;</li>
<li>epsilon conventions;</li>
<li>weight decay placement.</li>
</ul>
<p>Memory savings are not free if the training recipe silently changes several of these at once. Run an ablation that holds schedule and decay semantics constant where the implementation permits it.</p>
<h2 id="shampoo-preconditions-tensor-dimensions">Shampoo preconditions tensor dimensions</h2>
<p>Shampoo builds statistics along tensor dimensions and applies matrix inverse roots as a preconditioner. For a matrix gradient <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>G</mi></mrow></semantics></math>, simplified left and right statistics accumulate forms related to:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>L</mi><mi>t</mi></msub><mo>=</mo><msub><mi>L</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><msub><mi>G</mi><mi>t</mi></msub><msubsup><mi>G</mi><mi>t</mi><mi mathvariant="normal">⊤</mi></msubsup></mrow></semantics></math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mi>t</mi></msub><mo>=</mo><msub><mi>R</mi><mrow><mi>t</mi><mo>−</mo><mn>1</mn></mrow></msub><mo>+</mo><msubsup><mi>G</mi><mi>t</mi><mi mathvariant="normal">⊤</mi></msubsup><msub><mi>G</mi><mi>t</mi></msub></mrow></semantics></math>
<p>The preconditioned update uses inverse roots of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>L</mi><mi>t</mi></msub></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>R</mi><mi>t</mi></msub></mrow></semantics></math>. The Shampoo paper develops this tensor-aware preconditioning while controlling dependence on the full flattened parameter dimension.<sup><a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fn-shampoo" id="user-content-fnref-shampoo">4</a></sup></p>
<p>Matrix roots are more expensive than elementwise square roots. Practical systems update preconditioners periodically, block large dimensions, run root computations in higher precision, or distribute them across ranks. These implementation choices define the optimizer’s real cost.</p>
<p>Monitor:</p>
<ul>
<li>time spent updating statistics;</li>
<li>time spent computing roots;</li>
<li>condition and numerical validity of preconditioners;</li>
<li>blocked versus unblocked parameter coverage;</li>
<li>communication for distributed statistics;</li>
<li>fallback path when a root computation fails.</li>
</ul>
<p>If a preconditioner contains non-finite values, skipping only that parameter update can desynchronize replicas unless every rank makes the same decision.</p>
<h2 id="muon-orthogonalizes-matrix-updates">Muon orthogonalizes matrix updates</h2>
<p>Muon applies momentum to matrix gradients, then approximately orthogonalizes the update with an iterative Newton-Schulz procedure. The scalable Muon work applies it to hidden-layer matrix parameters while using AdamW for embeddings, output heads, and other parameters.<sup><a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fn-muon" id="user-content-fnref-muon">5</a></sup></p>
<p>This split is part of the algorithmic contract. Matrix orthogonalization is not defined the same way for vectors, and very rectangular or partitioned matrices require careful handling.</p>
<p>A conceptual update is:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">momentum </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> update_momentum</span><span style="color:var(--astro-code-token-punctuation)">(gradient, momentum, beta)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">matrix_update </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> orthogonalize_newton_schulz</span><span style="color:var(--astro-code-token-punctuation)">(momentum)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">parameter </span><span style="color:var(--astro-code-token-keyword)">-=</span><span style="color:var(--astro-code-foreground)"> learning_rate </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-function)"> scale</span><span style="color:var(--astro-code-token-punctuation)">(parameter.shape)</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> matrix_update</span></span></code></pre>
<p>The iteration count, polynomial coefficients, normalization, accumulation dtype, and shape-dependent scale affect output. Pin them in checkpoints and experiment records.</p>
<p>Tensor parallelism creates another question: orthogonalize each local shard or the logical global matrix. Local orthogonalization is cheaper but is a different preconditioner. The training code must state which object the optimizer sees.</p>
<h2 id="compare-state-and-compute-separately">Compare state and compute separately</h2>



































<div class="table-wrap"><table><thead><tr><th>Optimizer</th><th>Dominant state for a matrix</th><th>Extra compute</th><th>Main systems concern</th></tr></thead><tbody><tr><td>AdamW</td><td>full first and second moments</td><td>elementwise updates</td><td>state sharding and bandwidth</td></tr><tr><td>Adafactor</td><td>factored second moment, optional momentum</td><td>reductions over dimensions</td><td>implementation-specific schedule</td></tr><tr><td>Shampoo</td><td>dimension-wise covariance matrices</td><td>periodic matrix roots</td><td>blocking, precision, distribution</td></tr><tr><td>Muon</td><td>momentum plus temporary orthogonalization state</td><td>iterative matrix products</td><td>parameter selection and sharding</td></tr></tbody></table></div>
<p>This table describes structure, not a universal speed ranking. Kernel fusion, dtype, matrix shape, update frequency, and network topology determine runtime.</p>
<h2 id="learning-rate-transfer-is-optimizer-specific">Learning-rate transfer is optimizer-specific</h2>
<p>Do not compare optimizers under one shared learning rate and call the result controlled. Their preconditioners produce updates with different scales. Tune learning rate, warmup, decay, clipping, and optimizer-specific parameters on a smaller proxy that preserves architecture and data characteristics.</p>
<p>Track the update-to-weight norm:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>ρ</mi><mi>t</mi></msub><mo>=</mo><mfrac><mrow><mo stretchy="false">∥</mo><mi mathvariant="normal">Δ</mi><msub><mi>θ</mi><mi>t</mi></msub><mo stretchy="false">∥</mo></mrow><mrow><mo stretchy="false">∥</mo><msub><mi>θ</mi><mi>t</mi></msub><mo stretchy="false">∥</mo></mrow></mfrac></mrow></semantics></math>
<p>Report it by parameter class. Embeddings, attention projections, feed-forward matrices, and normalization parameters can behave differently. Aggregate loss can hide one group with unstable updates.</p>
<h2 id="clipping-occurs-at-a-specific-point">Clipping occurs at a specific point</h2>
<p>Gradient norm clipping before optimizer state update is not equivalent to clipping the preconditioned update. Adafactor’s update clipping and external gradient clipping are different mechanisms. Muon’s orthogonalized update changes norm geometry again.</p>
<p>Document the order:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>unscale mixed-precision gradients</span></span>
<span class="line"><span>check finite values</span></span>
<span class="line"><span>apply gradient clipping</span></span>
<span class="line"><span>update optimizer statistics</span></span>
<span class="line"><span>construct preconditioned update</span></span>
<span class="line"><span>apply decoupled weight decay</span></span>
<span class="line"><span>update parameters</span></span>
<span class="line"><span>advance scheduler</span></span></code></pre>
<p>Distributed global-norm clipping requires a reduction over all shards. A local norm threshold changes with shard count.</p>
<figure><figcaption><strong>Optimizer operations are ordered, not interchangeable</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/llm-training-optimizers-1.webp" width="512" height="468" alt="Optimizer operations are ordered, not interchangeable" loading="lazy" decoding="async"></figure>
<h2 id="checkpoint-and-recovery-tests">Checkpoint and recovery tests</h2>
<p>An optimizer checkpoint must include step counters, moments or factors, preconditioner statistics, loss-scaler state, scheduler state, and parameter-group mapping. For periodic Shampoo roots, include enough state to resume the same update cadence. For Muon, preserve momentum and the exact parameter classification.</p>
<p>Run a deterministic recovery test:</p>
<ol>
<li>Train to a checkpoint boundary.</li>
<li>Save model, optimizer, scheduler, random state, and data position.</li>
<li>Continue for a fixed batch sequence.</li>
<li>Restore in a fresh process and replay the same sequence.</li>
<li>Compare loss, parameters, and optimizer state under a documented tolerance.</li>
</ol>
<p>Topology changes require resharding. Verify on a small checkpoint before applying it to a full run.</p>
<h2 id="select-by-bottleneck">Select by bottleneck</h2>
<p>Choose AdamW when a proven recipe, broad implementation support, and predictable coordinate-wise adaptation matter more than state volume. Choose Adafactor when matrix state is the binding capacity constraint and the recipe has been qualified. Evaluate Shampoo when training can amortize stronger preconditioning and the system can support matrix-statistic work. Evaluate Muon when hidden matrix updates dominate and the implementation defines parameter selection, scaling, and distributed semantics.</p>
<p>No optimizer removes the need to inspect data, model initialization, and loss scaling. The useful comparison includes convergence per processed token, wall-clock time, peak memory, communication, recovery behavior, and final evaluation under equal data budgets.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-adam">
<p><a href="https://arxiv.org/abs/1412.6980" rel="noopener noreferrer">Adam: A Method for Stochastic Optimization</a>. <a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fnref-adam" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-adamw">
<p><a href="https://arxiv.org/abs/1711.05101" rel="noopener noreferrer">Decoupled Weight Decay Regularization</a>. <a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fnref-adamw" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-adafactor">
<p><a href="https://arxiv.org/abs/1804.04235" rel="noopener noreferrer">Adafactor: Adaptive Learning Rates with Sublinear Memory Cost</a>. <a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fnref-adafactor" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-shampoo">
<p><a href="https://arxiv.org/abs/1802.09568" rel="noopener noreferrer">Shampoo: Preconditioned Stochastic Tensor Optimization</a>. <a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fnref-shampoo" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-muon">
<p><a href="https://arxiv.org/abs/2502.16982" rel="noopener noreferrer">Muon is Scalable for LLM Training</a>. <a href="https://blog.ecitis.org/llm-training-optimizers/#user-content-fnref-muon" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Training</category>
            <category>Distributed training</category>
            <category>Scaling laws</category>
            <enclosure length="64388" type="image/png" url="https://blog.ecitis.org/open-graph/llm-training-optimizers.png"/>
        </item>
        <item>
            <title><![CDATA[Frontier Mechanistic Interpretability: From Features to Causal Audits]]></title>
            <link>https://blog.ecitis.org/frontier-mechanistic-interpretability/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/frontier-mechanistic-interpretability/</guid>
            <pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare sparse feature dictionaries, cross-layer circuit tracing, natural-language activation reports, and causal interventions.]]></description>
            <content:encoded><![CDATA[<p>Mechanistic interpretability tries to explain model behavior in terms of internal computation. The frontier has moved beyond finding a neuron that correlates with a concept. Current methods decompose activations into features, trace interactions between those features, translate internal states into text, and intervene to test causal claims.</p>
<p>These tools should not be ranked on one axis. They expose different objects.</p>
<figure><figcaption><strong>A causal audit combines interpretability methods instead of ranking them</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/frontier-mechanistic-interpretability-0.webp" width="512" height="1188" alt="A causal audit combines interpretability methods instead of ranking them" loading="lazy" decoding="async"></figure>
<h2 id="sparse-autoencoders-build-feature-dictionaries">Sparse autoencoders build feature dictionaries</h2>
<p>Individual neurons are often polysemantic: one neuron responds to several unrelated patterns. The superposition hypothesis proposes that models encode more features than there are activation dimensions by placing sparse features in overlapping directions.</p>
<p>A sparse autoencoder learns a dictionary that reconstructs an activation <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow></semantics></math> from a sparse code <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>z</mi></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>z</mi><mo>=</mo><mi mathvariant="normal">ReLU</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>W</mi><mrow><mi mathvariant="normal">e</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">c</mi></mrow></msub><mi>x</mi><mo>+</mo><msub><mi>b</mi><mrow><mi mathvariant="normal">e</mi><mi mathvariant="normal">n</mi><mi mathvariant="normal">c</mi></mrow></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>x</mi><mo>^</mo></mover><mo>=</mo><msub><mi>W</mi><mrow><mi mathvariant="normal">d</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">c</mi></mrow></msub><mi>z</mi><mo>+</mo><msub><mi>b</mi><mrow><mi mathvariant="normal">d</mi><mi mathvariant="normal">e</mi><mi mathvariant="normal">c</mi></mrow></msub></mrow></semantics></math>
<p>Training balances reconstruction with sparsity:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="script">L</mi><mo>=</mo><mo stretchy="false">∥</mo><mi>x</mi><mo>−</mo><mover accent="true"><mi>x</mi><mo>^</mo></mover><msubsup><mo stretchy="false">∥</mo><mn>2</mn><mn>2</mn></msubsup><mo>+</mo><mi>λ</mi><mo stretchy="false">∥</mo><mi>z</mi><msub><mo stretchy="false">∥</mo><mn>1</mn></msub></mrow></semantics></math>
<p>Anthropic’s early dictionary-learning work extracted more monosemantic features than the source neurons from a small transformer.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> The later Scaling Monosemanticity project trained sparse autoencoders on Claude 3 Sonnet and demonstrated that the approach could recover interpretable feature directions from a production-scale model.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p>A feature label usually comes from examples that activate it, tokens it promotes, or an automated explanation. That label remains a hypothesis. Similar-looking features can differ in their downstream effects, and a single feature can have behavior that its top activating examples do not reveal.</p>
<p>Useful SAE evaluation separates:</p>
<ul>
<li>reconstruction error</li>
<li>sparsity</li>
<li>feature interpretability</li>
<li>feature completeness for the target behavior</li>
<li>causal effect under steering or ablation</li>
</ul>
<p>A dictionary can reconstruct activations well while dividing concepts in a way that is awkward for human analysis.</p>
<h2 id="cross-layer-transcoders-approximate-computation">Cross-layer transcoders approximate computation</h2>
<p>An SAE describes an activation at one point. A transcoder predicts the output of a model component from its input using sparse features. A cross-layer transcoder extends the idea across several layers, letting features represent computations whose natural span does not fit one layer.</p>
<p>Anthropic’s circuit-tracing work replaces opaque model components with cross-layer transcoders, then constructs attribution graphs for a specific prompt and output.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Nodes are interpretable features. Edges estimate how one feature contributes to another feature or to the selected output logit.</p>
<p>An attribution graph can reveal several paths that converge on one token:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>prompt tokens</span></span>
<span class="line"><span>  -&gt; contextual features</span></span>
<span class="line"><span>  -&gt; intermediate computation features</span></span>
<span class="line"><span>  -&gt; output-promoting features</span></span>
<span class="line"><span>  -&gt; selected token</span></span></code></pre>
<p>The graph is local to a prompt and output. It is not a complete schematic of the model. The replacement model also introduces approximation error, so an attractive graph can omit computation carried by unexplained variance.</p>
<p>Validation requires interventions. Circuit tracing uses feature ablation, activation patching, and graph-based predictions to test whether a proposed path affects the output as expected.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-3" id="user-content-fnref-3-2">3</a></sup></p>
<h2 id="attention-requires-feature-level-analysis-too">Attention requires feature-level analysis too</h2>
<p>Early attribution graphs focused on multilayer perceptron computation. Attention adds conditional information movement between token positions.</p>
<p>Anthropic’s 2025 attention work decomposes query-key interactions in terms of feature pairs and integrates them into attribution graphs.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> The method traces why a destination feature selected a source feature, rather than stopping at the attended position.</p>
<p>An attention pattern shows routing. It does not identify the operation performed on the routed information. A head attending to a name token could copy the name, suppress it, compare it, or use another feature stored at the same position.</p>
<p>A stronger analysis identifies:</p>
<ol>
<li>the destination feature forming the query</li>
<li>the source feature forming the key</li>
<li>the information written through the value and output path</li>
<li>the downstream features that consume the result</li>
</ol>
<p>The explanation becomes mechanistic when it accounts for both routing and content.</p>
<h2 id="natural-language-autoencoders-trade-structure-for-readability">Natural-language autoencoders trade structure for readability</h2>
<p>Natural Language Autoencoders place a text bottleneck between an activation and its reconstruction.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> An activation verbalizer maps the target model’s activation to a natural-language explanation. An activation reconstructor maps that explanation back into activation space. The pair is trained to reconstruct activations.</p>
<p>The training objective does not directly require explanations to be human-readable or faithful. The text is useful because it must preserve information needed for reconstruction, and the paper evaluates whether that information helps with downstream analysis.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-5" id="user-content-fnref-5-2">5</a></sup></p>
<p>NLAs offer several practical advantages:</p>
<ul>
<li>explanations can express relations that a short feature label cannot</li>
<li>auditors can search long trajectories without inspecting every feature</li>
<li>an edited explanation can be reconstructed into an activation intervention</li>
<li>the same interface can support hypothesis generation and causal testing</li>
</ul>
<p>The method also has a distinct risk: a fluent explanation can look more certain than the evidence supports. Reconstruction proves that the text transmits some activation information. It does not prove that every phrase is a faithful semantic description.</p>
<p>The NLA paper validates case studies with independent evidence such as prompt variations, training-data inspection, other interpretability methods, and interventions.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-5" id="user-content-fnref-5-3">5</a></sup> That triangulation should be treated as part of the method, not optional polish.</p>
<h2 id="jacobian-lenses-expose-verbalizable-workspace-content">Jacobian lenses expose verbalizable workspace content</h2>
<p>The Jacobian lens asks which internal directions are disposed to affect future verbal output. Anthropic’s J-space work finds that a sparse set of these directions supports report, directed control, multi-step reasoning, and flexible reuse.<sup><a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fn-6" id="user-content-fnref-6">6</a></sup></p>
<p>This tool differs from an SAE:</p>






























<div class="table-wrap"><table><thead><tr><th>Method</th><th>Dictionary source</th><th>Primary object</th></tr></thead><tbody><tr><td>Sparse autoencoder</td><td>Learned reconstruction features</td><td>Sparse activation content</td></tr><tr><td>Cross-layer transcoder</td><td>Learned component approximation</td><td>Feature-to-feature computation</td></tr><tr><td>Natural-language autoencoder</td><td>Learned text bottleneck</td><td>Free-form activation report</td></tr><tr><td>Jacobian lens</td><td>Average causal map to future outputs</td><td>Verbalizable workspace directions</td></tr></tbody></table></div>
<p>The methods can support one another. SAE features with strong J-space alignment may identify workspace content more precisely than single-token J-lens vectors. J-lens readouts can label what a transcoder feature reads and writes. NLA explanations can propose concepts for targeted feature or lens interventions.</p>
<h2 id="auditing-needs-convergence-across-methods">Auditing needs convergence across methods</h2>
<p>No single interpretability result should carry a safety decision by itself. A robust audit progresses from broad detection to narrow causal tests:</p>
<ol>
<li>Use behavioral evaluations to find a suspicious trajectory.</li>
<li>Use SAE features or natural-language explanations to generate hypotheses.</li>
<li>Use circuit graphs to locate a candidate mechanism.</li>
<li>Use a J-lens or feature probe to test the mechanism across prompts.</li>
<li>Ablate, patch, or steer the representation.</li>
<li>Measure predicted behavior and unrelated capability changes.</li>
<li>inspect training data or model diffs for an independent explanation.</li>
</ol>
<figure><figcaption><strong>Interpretability claims strengthen through predicted interventions</strong> — A readable label begins an audit. It does not finish one.</figcaption><img src="https://blog.ecitis.org/feeds/figures/frontier-mechanistic-interpretability-1.webp" width="512" height="665" alt="Interpretability claims strengthen through predicted interventions — A readable label begins an audit. It does not finish one." loading="lazy" decoding="async"></figure>
<p>Agreement across methods is useful only when their errors are not identical. Two tools trained on the same activation data and scored by the same model can share blind spots.</p>
<h2 id="open-problems-at-the-frontier">Open problems at the frontier</h2>
<p>The current toolchain still leaves hard gaps.</p>
<p><strong>Completeness:</strong> A readable circuit can explain part of an output while missing another causal path.</p>
<p><strong>Scalability:</strong> Long agent trajectories produce many activations and candidate features.</p>
<p><strong>Relational structure:</strong> Sparse feature lists often omit how concepts bind together.</p>
<p><strong>Adversarial robustness:</strong> A model trained against a monitor may route cognition through representations the monitor does not capture.</p>
<p><strong>Faithfulness:</strong> Natural-language explanations and visible reasoning can describe a plausible process without being the cause.</p>
<p><strong>Automation:</strong> Model-based feature labels and audit agents increase coverage but introduce another learned evaluator.</p>
<p>Recent work pairs human-readable interpretations with interventions that should change behavior when the interpretation is correct. A mechanistic claim should survive causal tests and predict failures on new inputs.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Trenton Bricken et al., “Towards Monosemanticity: Decomposing Language Models With Dictionary Learning,” Transformer Circuits, 2023. <a href="https://transformer-circuits.pub/2023/monosemantic-features/index.html" rel="noopener noreferrer">https://transformer-circuits.pub/2023/monosemantic-features/index.html</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Adly Templeton et al., “Scaling Monosemanticity: Extracting Interpretable Features from Claude 3 Sonnet,” Transformer Circuits, 2024. <a href="https://transformer-circuits.pub/2024/scaling-monosemanticity/index.html" rel="noopener noreferrer">https://transformer-circuits.pub/2024/scaling-monosemanticity/index.html</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Emmanuel Ameisen et al., “Circuit Tracing: Revealing Computational Graphs in Language Models,” Transformer Circuits, 2025. <a href="https://transformer-circuits.pub/2025/attribution-graphs/methods.html" rel="noopener noreferrer">https://transformer-circuits.pub/2025/attribution-graphs/methods.html</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-3" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-3-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-4">
<p>Harish Kamath et al., “Tracing Attention Computation Through Feature Interactions,” Transformer Circuits, 2025. <a href="https://transformer-circuits.pub/2025/attention-qk/index.html" rel="noopener noreferrer">https://transformer-circuits.pub/2025/attention-qk/index.html</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Kit Fraser-Taliente et al., “Natural Language Autoencoders Produce Unsupervised Explanations of LLM Activations,” Transformer Circuits, 2026. <a href="https://transformer-circuits.pub/2026/nla/index.html" rel="noopener noreferrer">https://transformer-circuits.pub/2026/nla/index.html</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-5" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-5-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-5-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-6">
<p>Wes Gurnee et al., “Verbalizable Representations Form a Global Workspace in Language Models,” Transformer Circuits, 2026. <a href="https://transformer-circuits.pub/2026/workspace/index.html" rel="noopener noreferrer">https://transformer-circuits.pub/2026/workspace/index.html</a> <a href="https://blog.ecitis.org/frontier-mechanistic-interpretability/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Interpretability</category>
            <category>Safety</category>
            <category>Evaluation</category>
            <enclosure length="77576" type="image/png" url="https://blog.ecitis.org/open-graph/frontier-mechanistic-interpretability.png"/>
        </item>
        <item>
            <title><![CDATA[AI Cluster Networking: NVLink, Infinity Fabric, InfiniBand, and RoCE]]></title>
            <link>https://blog.ecitis.org/ai-cluster-networking/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/ai-cluster-networking/</guid>
            <pubDate>Fri, 26 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Map scale-up and scale-out AI fabrics across NVLink, Infinity Fabric, InfiniBand, RoCE, topology, and collective communication.]]></description>
            <content:encoded><![CDATA[<p>Distributed AI performance depends on moving tensors at the time the computation graph needs them. Link rate matters, but topology, collective algorithm, message size, congestion, and synchronization determine how much of that rate becomes useful work.</p>
<p>NVLink, Infinity Fabric, InfiniBand, and RoCE do not occupy one interchangeable layer. NVLink and accelerator-facing Infinity Fabric build scale-up domains. InfiniBand and RoCE connect nodes across a scale-out fabric. PCIe and network adapters bridge those domains.</p>
<figure><figcaption><strong>AI cluster communication domains</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/ai-cluster-networking-0.webp" width="512" height="453" alt="AI cluster communication domains" loading="lazy" decoding="async"></figure>
<h2 id="start-from-communication-volume">Start from communication volume</h2>
<p>Data parallel training commonly uses reduce-scatter followed by all-gather to average gradients. Tensor parallelism adds collectives inside transformer layers. Pipeline parallelism sends activations between stages. Expert parallelism routes token representations to selected experts.</p>
<p>For a ring all-reduce with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow></semantics></math> participants and a payload of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> bytes per participant, each participant transfers:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>V</mi><mtext>ring</mtext></msub><mo>=</mo><mn>2</mn><mfrac><mrow><mi>p</mi><mo>−</mo><mn>1</mn></mrow><mi>p</mi></mfrac><mi>n</mi></mrow></semantics></math>
<p>The formula shows why a bandwidth bottleneck affects large gradient buffers. It does not predict latency by itself. A performance model also needs per-step latency, effective bandwidth, and overlap with compute:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>T</mi><mo>≈</mo><mi>α</mi><msub><mi>N</mi><mtext>steps</mtext></msub><mo>+</mo><mfrac><mi>V</mi><msub><mi>B</mi><mtext>effective</mtext></msub></mfrac></mrow></semantics></math>
<p>Here, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow></semantics></math> is the per-step latency term and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>B</mi><mtext>effective</mtext></msub></mrow></semantics></math> is measured application bandwidth.</p>
<h2 id="communication-domains">Communication domains</h2>
<h3 id="package-and-board">Package and board</h3>
<p>Accelerator packages connect compute dies, memory stacks, and I/O dies through internal fabrics. AMD’s MI300X combines eight accelerator complex dies, eight HBM3 stacks, and four I/O dies through Infinity Fabric. The package provides an aggregate theoretical memory bandwidth of 5.3 TB/s.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-mi300" id="user-content-fnref-mi300">1</a></sup></p>
<p>That figure is HBM bandwidth inside one accelerator package. It is not GPU-to-GPU link bandwidth.</p>
<h3 id="scale-up">Scale-up</h3>
<p>A scale-up domain gives accelerators direct peer access and collective paths without crossing a conventional data-center network. NVIDIA NVLink links GPUs to peers or NVSwitch. AMD Infinity Fabric links accelerators inside supported nodes.</p>
<h3 id="scale-out">Scale-out</h3>
<p>Scale-out fabrics connect many compute nodes through network interface cards and switches. InfiniBand supplies an integrated RDMA fabric. RoCE carries RDMA transport over Ethernet.</p>
<p>The boundary can extend across racks on newer platforms, but the diagnostic distinction remains useful: first identify the package, local accelerator fabric, host I/O, and network hop involved.</p>
<h2 id="nvlink-and-nvswitch">NVLink and NVSwitch</h2>
<p>NVLink is NVIDIA’s high-bandwidth GPU interconnect. NVSwitch provides switched connectivity so a GPU can reach peers through an NVLink fabric rather than a point-to-point-only layout.</p>
<p>Fifth-generation NVLink in Blackwell systems provides up to 1,800 GB/s of aggregate bandwidth per GPU. NVIDIA’s GB300 NVL72 reference architecture uses nine NVLink switch trays, with two NVSwitch ASICs per tray, to connect 72 GPUs in one rack-scale domain.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-nvlink" id="user-content-fnref-nvlink">2</a></sup></p>
<p>Those specifications describe a particular platform generation and topology. Do not apply them to an H100 server or a PCIe-only deployment.</p>
<p>NVLink is useful for:</p>
<ul>
<li>Tensor-parallel collectives that occur within layers.</li>
<li>Peer reads and writes between accelerator memories.</li>
<li>Unified scale-up domains managed by the platform fabric.</li>
<li>Collective algorithms optimized for local topology.</li>
</ul>
<p>NVSwitch does not make every pairwise transfer free. Collective libraries still choose channels, chunk sizes, trees, or rings. Contention appears when concurrent jobs or collective phases share fabric paths.</p>
<h2 id="amd-infinity-fabric">AMD Infinity Fabric</h2>
<p>Infinity Fabric is AMD’s interconnect family across package and node scopes. On MI300X, it connects package components and supports an eight-accelerator node in which each accelerator has seven high-bandwidth Infinity Fabric links, forming a fully connected topology.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-mi300" id="user-content-fnref-mi300-2">1</a></sup></p>
<p>The topology reduces the need for intermediate accelerator hops inside that node. Collective placement still matters:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>rank → accelerator mapping → Infinity Fabric peer path → NIC affinity</span></span></code></pre>
<p>If the network interface is closest to one CPU socket or accelerator group, a poor rank map can send scale-out traffic across additional host or fabric links.</p>
<p>Inspect the platform topology rather than assuming device numbering follows physical adjacency. Use the communication library’s topology dump and measure peer bandwidth for the installed firmware, driver, and link state.</p>
<h2 id="infiniband">InfiniBand</h2>
<p>InfiniBand defines an I/O architecture for servers, storage, and communication infrastructure. It supports send and receive messaging plus RDMA memory operations without software involvement in the data movement path.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-ibta" id="user-content-fnref-ibta">3</a></sup></p>
<p>An InfiniBand network contains:</p>
<ul>
<li>Host channel adapters.</li>
<li>Switches and links.</li>
<li>A subnet manager.</li>
<li>Queue pairs and completion queues.</li>
<li>Memory regions registered for RDMA.</li>
<li>Routing and service-level configuration.</li>
</ul>
<p>NVIDIA’s QM9700 and QM9790 NDR switches expose 64 ports at 400 Gb/s per port in a one-rack-unit chassis.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-ndr" id="user-content-fnref-ndr">4</a></sup> Port rate is not collective bandwidth. Encoding, protocol overhead, PCIe, host memory registration, message size, and fabric contention all affect delivered throughput.</p>
<p>InfiniBand fabrics can supply adaptive routing, virtual lanes, and in-network collective operations on supported platforms. These features need topology-aware configuration and telemetry. A nominally nonblocking topology can still hotspot if routing sends synchronized flows through the same spine links.</p>
<h2 id="roce">RoCE</h2>
<p>RDMA over Converged Ethernet uses RDMA semantics on Ethernet infrastructure. RoCE version one operates at the link layer and remains within an Ethernet broadcast domain. RoCE version two carries the InfiniBand transport protocol over UDP and IP, so it can cross routed networks.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-roce" id="user-content-fnref-roce">5</a></sup></p>
<p>RoCE makes Ethernet a viable AI transport, but Ethernet loss and congestion behavior must be engineered for synchronized, high-rate flows.</p>
<p>Two controls appear often:</p>
<p><strong>Priority Flow Control:</strong> pauses one Ethernet priority rather than the whole link. NVIDIA’s switch documentation describes eight virtual links that can be paused independently.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-pfc" id="user-content-fnref-pfc">6</a></sup></p>
<p><strong>Explicit Congestion Notification:</strong> marks packets instead of dropping them when queues cross configured thresholds. A RoCEv2 receiver generates a congestion notification packet toward the sender, which reduces its sending rate.<sup><a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fn-roce" id="user-content-fnref-roce-2">5</a></sup></p>
<p>PFC and ECN solve different problems. PFC prevents loss for a traffic class through hop-by-hop pause. ECN provides end-to-end congestion signaling. Poor PFC design can propagate pauses and create head-of-line blocking. Poor ECN thresholds can react too late or throttle too aggressively.</p>
<h2 id="infiniband-and-roce-selection">InfiniBand and RoCE selection</h2>






























<div class="table-wrap"><table><thead><tr><th>Question</th><th>InfiniBand</th><th>RoCEv2</th></tr></thead><tbody><tr><td>Link and transport</td><td>Purpose-built InfiniBand stack</td><td>RDMA transport over routed Ethernet</td></tr><tr><td>Fabric operations</td><td>Subnet management and InfiniBand routing</td><td>Ethernet routing, VLAN, QoS, ECN, and optional PFC</td></tr><tr><td>Operational reuse</td><td>Separate fabric skill set</td><td>Can share Ethernet platform and practices</td></tr><tr><td>Failure focus</td><td>Routing, link state, credit and service-level behavior</td><td>Queueing, ECN, PFC, loss, and Ethernet configuration</td></tr></tbody></table></div>
<p>The choice is operational as much as architectural. A well-instrumented RoCE fabric can outperform a poorly operated InfiniBand fabric for a workload, and the reverse is also true. Evaluate delivered collective performance and recovery behavior.</p>
<h2 id="topology-determines-collective-cost">Topology determines collective cost</h2>
<p>A node can have fast local peer links and fewer network interfaces than accelerators. Scale-out traffic must then reach a NIC through a local path.</p>
<p>Consider a topology with accelerator groups attached to different CPU sockets:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>GPU group A ─ local fabric ─ NIC A ─ leaf switch</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>  host interconnect</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>GPU group B ─ local fabric ─ NIC B ─ leaf switch</span></span></code></pre>
<p>Bind processes so each rank uses the nearest accelerator and NIC. Crossing the host interconnect for every collective adds a bottleneck not visible in the network port rate.</p>
<p>Hierarchical collectives exploit this structure:</p>
<ol>
<li>Reduce within the local scale-up domain.</li>
<li>Exchange reduced chunks across the scale-out fabric.</li>
<li>Broadcast or gather within each local domain.</li>
</ol>
<p>The best algorithm depends on payload size and topology. Trees reduce startup steps for some small messages. Rings make predictable use of bandwidth for large messages. Communication libraries select algorithms dynamically, but their decision needs accurate topology discovery.</p>
<h2 id="congestion-patterns-in-ai-workloads">Congestion patterns in AI workloads</h2>
<p>AI communication is synchronized. Many ranks enter the same collective near the same time, creating burst patterns unlike independent storage flows.</p>
<p>Common problems:</p>
<ul>
<li><strong>Incast:</strong> many senders target one receiver or reduction point.</li>
<li><strong>All-to-all pressure:</strong> expert routing spreads traffic across many destinations.</li>
<li><strong>Rail imbalance:</strong> one NIC or spine path carries more ranks.</li>
<li><strong>Pause propagation:</strong> PFC on a congested queue blocks unrelated flows in the same class.</li>
<li><strong>Collective interference:</strong> overlapping jobs share links without isolation.</li>
<li><strong>Stragglers:</strong> one slow link delays every rank at the synchronization point.</li>
</ul>
<p>Average link utilization can look low while short queue spikes dominate step time. Collect high-resolution queue, ECN, PFC, retransmission, and link-error counters.</p>
<h2 id="benchmark-by-layer">Benchmark by layer</h2>
<p>Use a ladder:</p>
<ol>
<li>Peer memory bandwidth inside one accelerator.</li>
<li>Accelerator-to-accelerator bandwidth inside one node.</li>
<li>Host-to-device and device-to-NIC paths.</li>
<li>RDMA read, write, send, and receive between two nodes.</li>
<li>Collective operations at several message sizes.</li>
<li>Application step time with communication traces.</li>
</ol>
<p>A point-to-point benchmark does not validate all-reduce. An all-reduce benchmark does not validate expert all-to-all.</p>
<p>Report topology and software with results:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">accelerators_per_node</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 8</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">nics_per_node</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 8</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">fabric</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;RoCEv2&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">collective</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;all_reduce&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">payload_bytes</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&lt;measured case&gt;&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">driver_revision</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&lt;pinned revision&gt;&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">firmware_revision</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&lt;pinned revision&gt;&#39;</span></span></code></pre>
<p>The values in this configuration are an example schema, not a recommended cluster design.</p>
<h2 id="diagnose-slow-collectives">Diagnose slow collectives</h2>
<p><strong>One node is slow:</strong> check accelerator link state, NIC affinity, PCIe width, firmware, thermal state, and error counters.</p>
<p><strong>Performance drops only across racks:</strong> inspect oversubscription, routing entropy, spine utilization, and optical errors.</p>
<p><strong>Large messages are slow:</strong> inspect effective bandwidth, rail balance, and algorithm selection.</p>
<p><strong>Small messages are slow:</strong> inspect launch, synchronization, and per-step latency.</p>
<p><strong>RoCE stalls under load:</strong> inspect PFC pause frames, ECN marks, congestion notification packets, queue occupancy, and packet loss.</p>
<p><strong>Runs vary despite stable bandwidth:</strong> inspect competing jobs, adaptive routing, CPU scheduling, and collective synchronization.</p>
<h2 id="capacity-planning">Capacity planning</h2>
<p>Model the communication phase from workload bytes, not advertised FLOPS:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>required fabric time</span></span>
<span class="line"><span>  = bytes transferred per step</span></span>
<span class="line"><span>  / measured effective bandwidth</span></span></code></pre>
<p>Then compare it with the compute phase and determine which communication can overlap. If an exposed collective occupies the critical path, faster compute makes the network fraction larger.</p>
<p>Include failure capacity. A fabric that meets the target only with every link healthy has no room for maintenance, rerouting, or a failed rail.</p>
<p>Cluster networking is a hierarchy. Preserve that hierarchy in topology maps, benchmarks, and alerts. A single “network bandwidth” number erases the exact boundary that must be fixed.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-mi300">
<p><a href="https://rocm.docs.amd.com/en/docs-6.2.0/conceptual/gpu-arch/mi300.html" rel="noopener noreferrer">AMD ROCm documentation, “AMD Instinct MI300 series microarchitecture”</a>. <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-mi300" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-mi300-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-nvlink">
<p><a href="https://docs.nvidia.com/enterprise-reference-architectures/nvl72-ai-factory/latest/components.html" rel="noopener noreferrer">NVIDIA Enterprise Reference Architecture, “NVL72 AI Factory System Hardware and Components”</a>. <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-nvlink" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-ibta">
<p><a href="https://www.infinibandta.org/about-infiniband/" rel="noopener noreferrer">InfiniBand Trade Association, “About InfiniBand”</a>. <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-ibta" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-ndr">
<p><a href="https://docs.nvidia.com/networking/display/qm9700-and-qm9790-1u-ndr-400gbps-infiniband-switch-systems-user-manual.pdf" rel="noopener noreferrer">NVIDIA, “QM9700 and QM9790 1U NDR 400Gb/s InfiniBand Switch Systems User Manual”</a>. <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-ndr" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-roce">
<p><a href="https://docs.nvidia.com/networking-ethernet-software/cumulus-linux-43/Network-Solutions/RDMA-over-Converged-Ethernet-RoCE/" rel="noopener noreferrer">NVIDIA Cumulus Linux documentation, “RDMA over Converged Ethernet”</a>. <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-roce" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-roce-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-pfc">
<p><a href="https://docs.nvidia.com/doca/archive/3-2-2/Flow-Control/index.html" rel="noopener noreferrer">NVIDIA DOCA documentation, “Flow Control”</a>. <a href="https://blog.ecitis.org/ai-cluster-networking/#user-content-fnref-pfc" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>Networking</category>
            <category>GPUs</category>
            <category>Distributed training</category>
            <enclosure length="62693" type="image/png" url="https://blog.ecitis.org/open-graph/ai-cluster-networking.png"/>
        </item>
        <item>
            <title><![CDATA[Diffusion and Flow Matching: Architectures for Image and Video Generation]]></title>
            <link>https://blog.ecitis.org/diffusion-flow-matching/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/diffusion-flow-matching/</guid>
            <pubDate>Fri, 19 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare diffusion and flow-matching objectives, time parameterizations, architectures, and samplers for image and video generation.]]></description>
            <content:encoded><![CDATA[<p>Diffusion and flow matching define learning problems over paths between a tractable noise distribution and the data distribution. The neural network architecture can be shared, but the target, time parameterization, and sampler determine what the network predicts and how generation integrates it.</p>
<figure><figcaption><strong>Two training targets over a noise-to-data path</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/diffusion-flow-matching-0.webp" width="512" height="289" alt="Two training targets over a noise-to-data path" loading="lazy" decoding="async"></figure>
<h2 id="diffusion-learns-a-reverse-corruption-process">Diffusion learns a reverse corruption process</h2>
<p>A forward diffusion process gradually corrupts data <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub></mrow></semantics></math> into <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mi>t</mi></msub></mrow></semantics></math>. A common Gaussian parameterization is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>x</mi><mi>t</mi></msub><mo>=</mo><msub><mi>α</mi><mi>t</mi></msub><msub><mi>x</mi><mn>0</mn></msub><mo>+</mo><msub><mi>σ</mi><mi>t</mi></msub><mi>ϵ</mi><mo separator="true">,</mo><mspace width="2em"></mspace><mi>ϵ</mi><mo>∼</mo><mi mathvariant="script">N</mi><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi>I</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Training samples a data point, a time <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow></semantics></math>, and noise <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϵ</mi></mrow></semantics></math>. A network can predict <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϵ</mi></mrow></semantics></math>, the score <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="normal">∇</mi><msub><mi>x</mi><mi>t</mi></msub></msub><mi>log</mi><mo>⁡</mo><msub><mi>p</mi><mi>t</mi></msub><mo stretchy="false">(</mo><msub><mi>x</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math>, the clean sample, or a velocity parameterization derived from them. Denoising Diffusion Probabilistic Models connected this training objective to denoising score matching and a learned reverse Markov chain.<sup><a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fn-ddpm" id="user-content-fnref-ddpm">1</a></sup></p>
<p>The noise schedule controls signal-to-noise ratio across time. Loss weighting determines which regions of that schedule dominate updates. These choices affect optimization even when the architecture is unchanged.</p>
<p>At inference, generation starts from noise and applies a numerical solver. A stochastic reverse SDE injects randomness along the path. A probability-flow ODE follows a deterministic trajectory for a fixed initial noise. Sampler order and step placement trade network evaluations, truncation error, and stability.</p>
<h2 id="flow-matching-regresses-a-vector-field">Flow matching regresses a vector field</h2>
<p>Flow matching chooses a probability path <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>p</mi><mi>t</mi></msub></mrow></semantics></math> and trains a vector field <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>v</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>t</mi><mo stretchy="false">)</mo></mrow></semantics></math> to transport samples along it:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mrow><mi>d</mi><mi>x</mi></mrow><mrow><mi>d</mi><mi>t</mi></mrow></mfrac><mo>=</mo><msub><mi>v</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>t</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>The Flow Matching paper showed that conditional vector fields can provide a simulation-free training objective and include diffusion paths as a special case.<sup><a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fn-flow-matching" id="user-content-fnref-flow-matching">2</a></sup> Training does not require solving the ODE for every update. It samples points on a conditional path and regresses the target velocity.</p>
<p>For a simple linear interpolation between noise <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>0</mn></msub></mrow></semantics></math> and data <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>1</mn></msub></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>x</mi><mi>t</mi></msub><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>t</mi><mo stretchy="false">)</mo><msub><mi>x</mi><mn>0</mn></msub><mo>+</mo><mi>t</mi><msub><mi>x</mi><mn>1</mn></msub></mrow></semantics></math>
<p>the conditional velocity is <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>x</mi><mn>1</mn></msub><mo>−</mo><msub><mi>x</mi><mn>0</mn></msub></mrow></semantics></math>. In practice, the coupling between noise and data and the selected path affect trajectory geometry. A straight conditional interpolation does not imply that the marginal vector field is trivial.</p>
<h2 id="prediction-targets-are-coordinate-choices">Prediction targets are coordinate choices</h2>
<p>Noise, clean-sample, score, and velocity prediction can often be converted when <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>α</mi><mi>t</mi></msub></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>σ</mi><mi>t</mi></msub></mrow></semantics></math> are known. They do not produce identical numerical conditioning under finite precision, imperfect networks, or weighted losses.</p>
<p>Use one canonical internal definition:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> interpolate</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">clean</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> noise</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> alpha_t</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> sigma_t</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    sample </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> alpha_t </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> clean </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> sigma_t </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> noise</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    velocity </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> alpha_t </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> noise </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> sigma_t </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> clean</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> sample</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> velocity</span></span></code></pre>
<p>The exact velocity formula depends on the schedule convention. Tests should verify conversions at schedule endpoints and intermediate times.</p>
<h2 id="u-nets-and-transformers-solve-the-same-field-problem">U-Nets and transformers solve the same field problem</h2>
<p>A U-Net uses multiscale convolutional features, skip connections, and attention blocks. It matches image locality and provides feature maps at several resolutions. A diffusion transformer tokenizes latent patches and applies transformer blocks conditioned on time and text. Diffusion Transformers demonstrated that transformer compute can replace the conventional U-Net backbone in latent diffusion.<sup><a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fn-dit" id="user-content-fnref-dit">3</a></sup></p>
<p>Architecture selection changes:</p>
<ul>
<li>how spatial resolution maps to compute;</li>
<li>where text conditioning enters;</li>
<li>whether variable aspect ratios require padding or packed tokens;</li>
<li>how temporal features are introduced for video;</li>
<li>which kernels dominate training and inference.</li>
</ul>
<p>The network output still represents the selected denoising or flow target.</p>
<h2 id="latent-models-move-the-path-out-of-pixel-space">Latent models move the path out of pixel space</h2>
<p>Pixel-space generation carries high-dimensional spatial tensors through every network evaluation. Latent diffusion uses an encoder to map images into a lower-dimensional representation, trains the generative process in that space, then decodes the result.<sup><a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fn-latent-diffusion" id="user-content-fnref-latent-diffusion">4</a></sup></p>
<p>This introduces a separate reconstruction contract. Fine texture, text, faces, and high-frequency patterns can be limited by the autoencoder even if the denoiser is accurate. Evaluate reconstruction error independently by encoding and decoding real samples without generation.</p>
<p>Latent scaling must match training. A missing scale factor changes the effective noise distribution and can produce washed-out or saturated samples.</p>
<h2 id="conditioning-enters-through-several-paths">Conditioning enters through several paths</h2>
<p>Text conditioning commonly uses cross-attention from image or video tokens to text-encoder outputs. Time is embedded and injected through adaptive normalization or residual modulation. Class labels, camera parameters, masks, depth, or motion controls can use related adapters.</p>
<p>Classifier-free guidance combines conditional and unconditional predictions:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mi>u</mi><mo>^</mo></mover><mo>=</mo><msub><mi>u</mi><mi mathvariant="normal">∅</mi></msub><mo>+</mo><mi>w</mi><mo stretchy="false">(</mo><msub><mi>u</mi><mi>c</mi></msub><mo>−</mo><msub><mi>u</mi><mi mathvariant="normal">∅</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<p>where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>u</mi></mrow></semantics></math> denotes the chosen prediction target and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi></mrow></semantics></math> is guidance strength. Guidance changes the vector field followed by the sampler. High guidance can improve prompt alignment while reducing diversity or causing saturation; the acceptable range is model- and sampler-specific.</p>
<p>Batching the conditional and unconditional passes can increase throughput but doubles some activation and conditioning work. Cache text embeddings when prompts repeat.</p>
<h2 id="video-adds-a-temporal-allocation-problem">Video adds a temporal allocation problem</h2>
<p>Video tensors add frame count to spatial resolution, channel width, and batch size. Full spatiotemporal attention grows with the square of the token count. Architectures therefore factor spatial and temporal attention, use local windows, compress time in a latent autoencoder, or process temporal chunks.</p>
<p>Temporal consistency is not guaranteed by generating each frame with the same prompt. The model needs temporal operators or shared latent structure. Conditioning should preserve frame order, frame rate, aspect ratio, and camera metadata used during training.</p>
<p>A serving plan must choose:</p>
<ul>
<li>frame count and resolution buckets;</li>
<li>latent temporal compression;</li>
<li>attention window or context;</li>
<li>whether decoding is tiled;</li>
<li>where intermediate latents are stored;</li>
<li>how previews are generated without changing the final path.</li>
</ul>
<p>Tiled decoding lowers peak memory but can expose seams if receptive-field overlap and blending do not match the decoder.</p>
<h2 id="solvers-need-model-specific-validation">Solvers need model-specific validation</h2>
<p>An ODE solver advances:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>x</mi><mrow><mi>t</mi><mo>+</mo><mi mathvariant="normal">Δ</mi><mi>t</mi></mrow></msub><mo>≈</mo><msub><mi>x</mi><mi>t</mi></msub><mo>+</mo><mi mathvariant="normal">Δ</mi><mi>t</mi><mtext> </mtext><msub><mi>v</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>x</mi><mi>t</mi></msub><mo separator="true">,</mo><mi>t</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Higher-order methods evaluate the network at additional points to reduce integration error. Fewer steps reduce latency but can miss curved regions of the learned field. A solver qualified on one parameterization or schedule may be unstable on another.</p>
<p>Benchmark quality against network evaluation count, not loop iteration alone. Some iterations call the model more than once. Also report text-encoder and decoder time so denoiser optimizations are not overstated.</p>
<figure><figcaption><strong>A solver follows a learned field through discrete evaluations</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/diffusion-flow-matching-1.webp" width="512" height="344" alt="A solver follows a learned field through discrete evaluations" loading="lazy" decoding="async"></figure>
<h2 id="training-and-serving-failure-modes">Training and serving failure modes</h2>
<p><strong>Time convention mismatch:</strong> Training uses noise-to-data time while the sampler assumes data-to-noise time. Endpoint tests catch the sign and schedule reversal.</p>
<p><strong>Target conversion mismatch:</strong> The model predicts velocity but guidance or solver code treats it as noise. Centralize conversions and record the checkpoint prediction type.</p>
<p><strong>Latent scale mismatch:</strong> Encoder outputs are scaled during training but not inference. Compare training-pipeline latents with serving latents.</p>
<p><strong>Conditioning dropout mismatch:</strong> Classifier-free guidance requires an unconditional path trained with conditioning dropout. Replacing conditioning with arbitrary empty text can differ from the trained null condition.</p>
<p><strong>Video padding leakage:</strong> Padded frames participate in attention because the mask is wrong. Test variable-length batches against individually evaluated clips.</p>
<p><strong>Sampler benchmark bias:</strong> Quality is compared at equal step count despite different model evaluations. Compare equal evaluation budgets and wall-clock latency.</p>
<p>Diffusion and flow matching are not competing network brands. They are ways to specify a time-dependent learning target and a generative path. A production system must keep schedule, target parameterization, architecture, conditioning, latent codec, and sampler in one versioned contract.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-ddpm">
<p><a href="https://arxiv.org/abs/2006.11239" rel="noopener noreferrer">Denoising Diffusion Probabilistic Models</a>. <a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fnref-ddpm" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-flow-matching">
<p><a href="https://arxiv.org/abs/2210.02747" rel="noopener noreferrer">Flow Matching for Generative Modeling</a>. <a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fnref-flow-matching" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-dit">
<p><a href="https://arxiv.org/abs/2212.09748" rel="noopener noreferrer">Scalable Diffusion Models with Transformers</a>. <a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fnref-dit" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-latent-diffusion">
<p><a href="https://arxiv.org/abs/2112.10752" rel="noopener noreferrer">High-Resolution Image Synthesis with Latent Diffusion Models</a>. <a href="https://blog.ecitis.org/diffusion-flow-matching/#user-content-fnref-latent-diffusion" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Diffusion</category>
            <category>Training</category>
            <category>Multimodal</category>
            <enclosure length="78475" type="image/png" url="https://blog.ecitis.org/open-graph/diffusion-flow-matching.png"/>
        </item>
        <item>
            <title><![CDATA[AI Safety Systems in Production: Policy, Permissions, and Validation]]></title>
            <link>https://blog.ecitis.org/ai-safety-systems/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/ai-safety-systems/</guid>
            <pubDate>Fri, 12 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Design production safety as a control plane spanning identity, data access, tool permissions, output validation, monitoring, and response.]]></description>
            <content:encoded><![CDATA[<p>Production AI safety is a systems property. A model can refuse a prohibited request and still leak data through retrieval, call an overpowered tool, or emit malformed output that an application executes.</p>
<p>The control plane must govern identity, data access, tool authority, output contracts, monitoring, and response. Model behavior remains one control inside that plane.</p>
<h2 id="model-the-system-before-the-threat">Model the system before the threat</h2>
<p>Draw the actual data path:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>user → gateway → context builder → model → tool broker → external systems</span></span>
<span class="line"><span>                         ↓                       ↓</span></span>
<span class="line"><span>                    retrieval index         audit log</span></span></code></pre>
<p>For each boundary, record:</p>
<ul>
<li>Principal and credential.</li>
<li>Data classification.</li>
<li>Allowed operations.</li>
<li>Policy owner.</li>
<li>Validation performed.</li>
<li>Log and retention behavior.</li>
<li>Failure and rollback path.</li>
</ul>
<p>NIST’s Generative AI Profile organizes risk work around govern, map, measure, and manage functions and treats risk management as a lifecycle activity rather than a model-only test.<sup><a href="https://blog.ecitis.org/ai-safety-systems/#user-content-fn-nist" id="user-content-fnref-nist">1</a></sup></p>
<figure><figcaption><strong>Safety control plane around a model request</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/ai-safety-systems-0.webp" width="512" height="410" alt="Safety control plane around a model request" loading="lazy" decoding="async"></figure>
<h2 id="separate-policy-from-prompt-text">Separate policy from prompt text</h2>
<p>A system prompt is input to a probabilistic model. It can influence behavior but cannot enforce database permissions or network access.</p>
<p>Represent enforceable policy in code or a policy engine:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> Action</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  principal</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  operation</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;read&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;create&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;update&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;delete&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  resource</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  attributes</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> Record</span><span style="color:var(--astro-code-foreground)">&lt;</span><span style="color:var(--astro-code-token-constant)">string</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> PolicyDecision</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  effect</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;allow&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;deny&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;require_approval&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  policyVersion</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  reason</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The prompt can tell the model not to delete records. The tool broker must deny deletion unless the authenticated principal, resource policy, and conversation approval allow it.</p>
<p>Version policies independently from prompts. An incident review should identify the rule that allowed an action without reconstructing it from prose embedded in a model request.</p>
<h2 id="apply-least-privilege-to-tools">Apply least privilege to tools</h2>
<p>Tool definitions should map to narrow operations. A generic SQL executor or shell command carries more authority than a tool designed around one domain action.</p>
<p>Prefer:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;create_refund_request&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;input&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;invoice_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;INV-1042&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;reason_code&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;DUPLICATE_CHARGE&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>over:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;run_command&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;input&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;command&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;refund --invoice INV-1042&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The values are illustrative. The narrow tool allows schema validation, resource-level authorization, idempotency, and a specific audit event.</p>
<p>Credentials should be scoped per tool service and deployment environment. Do not place a broad production credential in the model context. The model should select an operation; the broker should attach credentials after policy approval.</p>
<h2 id="bind-approval-to-material-arguments">Bind approval to material arguments</h2>
<p>Approval must cover the action the system will execute. If the model changes a recipient, amount, repository, or destination after approval, request approval again.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> approvalFingerprint</span><span style="color:var(--astro-code-foreground)">(tool</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> args</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> unknown</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> principal</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  return</span><span style="color:var(--astro-code-token-function)"> stableHash</span><span style="color:var(--astro-code-foreground)">({ tool</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> args</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> principal })</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The approval record should contain the fingerprint, policy version, user identity, timestamp, and rendered action summary. A generic session-wide approval grants authority to future model outputs that the user has not seen.</p>
<p>Read operations can also be sensitive. Searching personnel records or retrieving private source code requires access checks even when no state changes.</p>
<figure><figcaption><strong>Approval binds to the exact action arguments</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/ai-safety-systems-1.webp" width="512" height="286" alt="Approval binds to the exact action arguments" loading="lazy" decoding="async"></figure>
<h2 id="treat-retrieved-content-as-untrusted">Treat retrieved content as untrusted</h2>
<p>Prompt injection crosses a trust boundary when data is interpreted as instructions. Web pages, emails, documents, issue comments, and tool results can contain text that tells the model to ignore policy or exfiltrate context.</p>
<p>Do not rely on a delimiter to make instructions harmless. The model still reads both sides of the delimiter.</p>
<p>Controls include:</p>
<ul>
<li>Retrieve only from authorized sources.</li>
<li>Preserve source identity around every passage.</li>
<li>Label retrieved text as data in the model instruction.</li>
<li>Exclude credentials and unnecessary private context.</li>
<li>Restrict tool authority independently of model output.</li>
<li>Require confirmation for sensitive side effects.</li>
<li>Validate output before execution.</li>
</ul>
<p>Indirect injection becomes an incident only when the surrounding system gives the injected instruction a path to data or action. Remove that path with least privilege and approval.</p>
<h2 id="constrain-data-movement">Constrain data movement</h2>
<p>Build context from an explicit allowlist of fields. Passing complete records creates accidental exposure and makes later redaction harder.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> TicketContext</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  subject</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  customerMessage</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  publicProduct</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  allowedPolicyExcerpts</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Keep secrets out of prompts. If a tool needs an API credential, inject it inside the trusted tool process. Model-visible logs should redact authorization headers, session cookies, private keys, and personal data not needed for the task.</p>
<p>Tenant isolation belongs in the query and storage layer. Filtering another tenant’s passages after retrieval already exposes them to rankers, caches, and traces.</p>
<h2 id="validate-inputs-and-outputs">Validate inputs and outputs</h2>
<p>Input validation protects services from malformed model-generated arguments. Output validation protects downstream consumers from malformed or policy-breaking model text.</p>
<p>Use a layered contract:</p>
<ol>
<li>Parse the output.</li>
<li>Validate its schema.</li>
<li>Check domain invariants.</li>
<li>Apply authorization.</li>
<li>Execute through an idempotent boundary.</li>
<li>Verify postconditions.</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> RefundRequest</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  invoiceId</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  reasonCode</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;DUPLICATE_CHARGE&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;SERVICE_NOT_DELIVERED&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> validateRefund</span><span style="color:var(--astro-code-foreground)">(request</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> RefundRequest</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> invoice</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> { id</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">; state</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)"> })</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> void</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">request</span><span style="color:var(--astro-code-foreground)">.invoiceId </span><span style="color:var(--astro-code-token-keyword)">!==</span><span style="color:var(--astro-code-token-constant)"> invoice</span><span style="color:var(--astro-code-foreground)">.id) </span><span style="color:var(--astro-code-token-keyword)">throw</span><span style="color:var(--astro-code-token-keyword)"> new</span><span style="color:var(--astro-code-token-function)"> Error</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;invoice mismatch&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">invoice</span><span style="color:var(--astro-code-foreground)">.state </span><span style="color:var(--astro-code-token-keyword)">!==</span><span style="color:var(--astro-code-token-string-expression)"> &#39;paid&#39;</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">throw</span><span style="color:var(--astro-code-token-keyword)"> new</span><span style="color:var(--astro-code-token-function)"> Error</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;invoice is not refundable&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>A JSON Schema can prove that <code>invoiceId</code> is a string. It cannot prove that the invoice belongs to the principal or is refundable.</p>
<p>For generated code, parse before execution, run in an isolated environment, constrain network and filesystem access, set resource limits, and inspect artifacts. Text moderation does not replace a sandbox.</p>
<h2 id="use-classifiers-as-routing-controls">Use classifiers as routing controls</h2>
<p>Classifiers can detect content categories, sensitive data, or policy-relevant intent. They should produce an operational route:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>allow → normal pipeline</span></span>
<span class="line"><span>transform → redact or narrow context</span></span>
<span class="line"><span>review → human queue</span></span>
<span class="line"><span>block → safe response with reason code</span></span></code></pre>
<p>Measure false positives and false negatives on the deployed distribution. A classifier threshold is a policy choice with user and incident costs on both sides.</p>
<p>Do not let a classifier silently rewrite authority. A low-risk classification can reduce review but should not grant a tool permission that the principal lacks.</p>
<h2 id="make-provenance-inspectable">Make provenance inspectable</h2>
<p>For factual answers, preserve the link between claims and sources:</p>
<ul>
<li>Source identifier and revision.</li>
<li>Retrieved passage offsets.</li>
<li>Rank and selection trace.</li>
<li>Claim-to-source association where required.</li>
<li>Time of retrieval.</li>
</ul>
<p>Citation presence alone is not grounding. Validate that the cited passage supports the claim and that the user can access the source.</p>
<p>For actions, provenance means the chain from user request through model proposal, policy decision, approval, tool call, and verified result.</p>
<h2 id="design-failure-states">Design failure states</h2>
<p>Safe failure is explicit:</p>

































<div class="table-wrap"><table><thead><tr><th>Failure</th><th>System response</th></tr></thead><tbody><tr><td>Retrieval unavailable</td><td>State that evidence could not be loaded</td></tr><tr><td>Policy service unavailable</td><td>Deny sensitive actions</td></tr><tr><td>Approval expired</td><td>Request a new approval</td></tr><tr><td>Output invalid</td><td>Repair within a bounded retry policy or stop</td></tr><tr><td>Tool result ambiguous</td><td>Do not claim success</td></tr><tr><td>Audit sink unavailable</td><td>Apply the declared fail-open or fail-closed policy</td></tr></tbody></table></div>
<p>A model-generated statement of success is not a tool postcondition. Read the authoritative system state before telling the user that an external action completed.</p>
<h2 id="evaluate-attacks-as-workflows">Evaluate attacks as workflows</h2>
<p>Single-turn refusal tests cover only model behavior. System tests should exercise:</p>
<ul>
<li>Direct requests for prohibited content.</li>
<li>Indirect instructions inside retrieved documents.</li>
<li>Attempts to access another principal’s resources.</li>
<li>Argument substitution after approval.</li>
<li>Tool-result spoofing.</li>
<li>Encoded or fragmented policy violations.</li>
<li>Excessive output and parser edge cases.</li>
<li>Service outages during sensitive actions.</li>
</ul>
<p>Record the layer that blocked each test. A prompt-only defense that passes today can regress with a model update. An authorization denial should remain stable.</p>
<p>NIST recommends documented test plans, independent assessment where appropriate, incident disclosure processes, and ongoing monitoring for generative AI systems.<sup><a href="https://blog.ecitis.org/ai-safety-systems/#user-content-fn-nist" id="user-content-fnref-nist-2">1</a></sup> Translate those controls into executable release gates and incident runbooks.</p>
<h2 id="log-decisions-without-creating-a-second-leak">Log decisions without creating a second leak</h2>
<p>Audit records need enough detail for reconstruction:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;request_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;req_91&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;principal&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user_24&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;policy_version&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;tools-12&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;tool&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;create_refund_request&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;decision&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;require_approval&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;approval_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;approval_77&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;result&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;created&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Identifiers and versions are illustrative.</p>
<p>Do not log full prompts by default when they contain private documents. Store encrypted artifacts with limited retention and access, or log hashes plus selected redacted fields. Test redaction on tool errors because exception text often includes request bodies.</p>
<h2 id="incident-response">Incident response</h2>
<p>An AI incident runbook needs system controls:</p>
<ol>
<li>Disable or narrow the affected tool.</li>
<li>Revoke credentials and sessions.</li>
<li>Preserve policy, prompt, model, retrieval, and tool revisions.</li>
<li>Identify affected principals and resources.</li>
<li>Replay the trace in an isolated environment.</li>
<li>Add a regression case at the failed layer.</li>
<li>Verify the fix against adjacent workflows.</li>
</ol>
<p>Changing the system prompt is appropriate only when the failure was actually instructional. A cross-tenant retrieval bug needs a query and authorization fix.</p>
<h2 id="deployment-checklist">Deployment checklist</h2>
<ul>
<li>Every request has an authenticated principal or an explicit anonymous policy.</li>
<li>Retrieval filters execute before content leaves its authorization boundary.</li>
<li>Tools expose narrow operations with typed inputs.</li>
<li>Credentials never enter model-visible context.</li>
<li>Sensitive actions require argument-bound approval.</li>
<li>Tool brokers apply policy after model selection.</li>
<li>Outputs pass syntax, schema, domain, and authorization checks.</li>
<li>Success claims follow verified postconditions.</li>
<li>Traces identify every deployed revision.</li>
<li>Logs are redacted, access-controlled, and retained by policy.</li>
<li>Offline attacks and production incidents become regression cases.</li>
</ul>
<p>Safety improves when authority is explicit and independently enforceable. The model proposes. Policy decides. Tools verify. Audit records preserve the evidence.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-nist">
<p><a href="https://nvlpubs.nist.gov/nistpubs/ai/NIST.AI.600-1.pdf" rel="noopener noreferrer">NIST AI 600-1, Artificial Intelligence Risk Management Framework: Generative Artificial Intelligence Profile, July 2024</a>. <a href="https://blog.ecitis.org/ai-safety-systems/#user-content-fnref-nist" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/ai-safety-systems/#user-content-fnref-nist-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Safety</category>
            <category>Agents</category>
            <category>Alignment</category>
            <enclosure length="64437" type="image/png" url="https://blog.ecitis.org/open-graph/ai-safety-systems.png"/>
        </item>
        <item>
            <title><![CDATA[Long-Context Models: Position Encoding, Memory, and Retrieval]]></title>
            <link>https://blog.ecitis.org/long-context-models/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/long-context-models/</guid>
            <pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Separate position extension, attention computation, cache capacity, context parallelism, and retrieval when designing long-context systems.]]></description>
            <content:encoded><![CDATA[<p>A context window is a capacity limit, not a guarantee that every supplied token affects the answer equally. Long-context systems combine positional representation, attention computation, device memory, distributed execution, data composition, and evaluation.</p>
<p>Extending one layer does not extend the others. A position encoding can represent a distant index while the cache does not fit. An exact attention kernel can process the sequence while the model fails to retrieve a fact placed in its middle.</p>
<h2 id="position-enters-attention">Position enters attention</h2>
<p>Transformer attention without position is permutation-equivariant. Position methods change token representations or attention scores.</p>
<p>Rotary position embedding applies a position-dependent rotation to query and key pairs. Their dot product then contains relative-position structure.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> For one two-dimensional feature pair:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>R</mi><mrow><mi>θ</mi><mo separator="true">,</mo><mi>m</mi></mrow></msub><mo>=</mo><mrow><mo fence="true">[</mo><mtable rowspacing="0.16em" columnalign="center center" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>m</mi><mi>θ</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo>−</mo><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>m</mi><mi>θ</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>m</mi><mi>θ</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>cos</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>m</mi><mi>θ</mi><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr></mtable><mo fence="true">]</mo></mrow></mrow></semantics></math>
<p>The model is trained over a range of phases determined by position <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow></semantics></math> and frequency <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>θ</mi></mrow></semantics></math>. Extrapolating beyond the training range can expose phase patterns not learned during training.</p>
<p>ALiBi takes a different route. It adds a head-specific linear penalty proportional to query-key distance rather than adding a positional embedding to token states.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Its authors trained a <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1.3</mn></mrow></semantics></math>-billion-parameter model at length <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1024</mn></mrow></semantics></math> and evaluated length <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2048</mn></mrow></semantics></math>; they report matching the perplexity of a sinusoidal baseline trained at the longer length while using <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>11</mn><mi mathvariant="normal">%</mi></mrow></semantics></math> less training memory and time.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup></p>
<figure><figcaption><strong>Long-context methods alter different layers of the system</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/long-context-models-0.webp" width="512" height="431" alt="Long-context methods alter different layers of the system" loading="lazy" decoding="async"></figure>
<h2 id="rope-extension-changes-frequency-mapping">RoPE extension changes frequency mapping</h2>
<p>Position interpolation compresses a longer inference range into coordinates observed during training. It avoids raw extrapolation but reduces angular resolution between nearby positions. Frequency-aware methods scale dimensions differently because high-frequency and low-frequency rotary pairs behave differently.</p>
<p>LongRoPE searches non-uniform interpolation factors and uses progressive extension. The paper reports extending models to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>2048</mn></mrow></semantics></math> thousand tokens with fine-tuning at lengths up to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>256</mn></mrow></semantics></math> thousand tokens, then readjusting at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>8</mn></mrow></semantics></math> thousand tokens to recover short-context behavior.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>Those figures describe the evaluated method, not a universal recipe. Extension factors depend on the base model, original training length, data, and fine-tuning setup. A configuration that loads successfully can still degrade short-context perplexity or retrieval.</p>
<p>Store position policy with the model artifact:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">position</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> rope</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  original_max_positions</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${TRAINING_LIMIT}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  scaling</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    method</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${METHOD}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    factor</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${FACTOR}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    parameters_digest</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${DIGEST}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">evaluation</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  short_context_suite</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${SHORT_SUITE}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  long_context_suite</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${LONG_SUITE}</span></span></code></pre>
<p>Placeholders force values to come from the trained artifact rather than a copied deployment snippet.</p>
<h2 id="memory-grows-even-with-efficient-attention">Memory grows even with efficient attention</h2>
<p>Exact self-attention score work grows with the product of query and key lengths. FlashAttention avoids materializing the complete score matrix but does not remove the arithmetic of dense attention.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-4" id="user-content-fnref-4">4</a></sup></p>
<p>Inference also retains the key-value cache. For <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> layers, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow></semantics></math> key-value heads, head dimension <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>, sequence length <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math>, and scalar width <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>M</mi><mrow><mi>K</mi><mi>V</mi></mrow></msub><mo>=</mo><mn>2</mn><mi>L</mi><mi>S</mi><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mi>D</mi><mi>B</mi></mrow></semantics></math>
<p>The cache grows linearly with sequence length. Prefix caching avoids recomputation across repeated prefixes but does not make a unique long sequence free. Quantized cache and grouped-query attention reduce bytes per token; both require model and kernel support.</p>
<p>Before admitting a long request, estimate weight memory, cache reservation, temporary attention workspace, communication buffers, and allocator headroom. Token limits that ignore concurrent requests produce late out-of-memory failures.</p>
<h2 id="context-parallelism-distributes-exact-attention">Context parallelism distributes exact attention</h2>
<p>When one device cannot hold sequence state, context parallelism partitions tokens across devices. Ring Attention rotates key-value blocks around a device ring and computes blockwise attention while communication overlaps with local work.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> Online softmax combines partial results without assembling the full score matrix on one device.</p>
<p>For each local query block, a worker:</p>
<ol>
<li>computes scores against its current key-value block</li>
<li>updates row maxima, normalization sums, and output accumulators</li>
<li>sends the key-value block to the next worker</li>
<li>receives another block and repeats</li>
</ol>
<p>The method preserves dense attention but adds communication proportional to the circulated state. Topology and link bandwidth become part of context-length capacity.</p>
<h2 id="sparse-attention-changes-reachable-evidence">Sparse attention changes reachable evidence</h2>
<p>Sparse methods avoid evaluating every token pair. Sliding windows preserve local structure but remove direct long-range edges. Global tokens, vertical stripes, and selected blocks restore some long-range access.</p>
<p>MInference identifies several sparse patterns per attention head and constructs dynamic sparse indices during prefill.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-6" id="user-content-fnref-6">6</a></sup> Such methods accelerate a pattern observed in model attention; they do not preserve exact dense attention by definition.</p>
<p>Validate sparse kernels on tasks requiring distant dependencies, not only language-model loss. A local window can score well on locally predictable text while dropping a document-level constraint.</p>
<h2 id="retrieval-changes-input-rather-than-attention">Retrieval changes input rather than attention</h2>
<p>Retrieval selects a smaller evidence set before generation. Its cost shifts to indexing, query construction, ranking, and provenance. Long context and retrieval are complementary:</p>
<ul>
<li>retrieval reduces irrelevant tokens and prefill cost</li>
<li>a longer context admits more retrieved chunks and surrounding text</li>
<li>reranking orders evidence before prompt construction</li>
<li>citations connect generated claims to supplied spans</li>
</ul>
<p>Chunking is a semantic decision. Fixed token windows can split tables, code definitions, and cross-references. Structure-aware chunking keeps those units intact, while overlap preserves boundary context at the cost of duplicate tokens.</p>
<h2 id="position-sensitive-evaluation">Position-sensitive evaluation</h2>
<p>Liu and collaborators varied the position of relevant information within long inputs and observed that performance can be highest when relevant information is near the beginning or end and lower when it is in the middle.<sup><a href="https://blog.ecitis.org/long-context-models/#user-content-fn-7" id="user-content-fnref-7">7</a></sup> Their result motivates position sweeps rather than one fixed evidence placement.</p>
<p>A useful evaluation matrix crosses:</p>





























<div class="table-wrap"><table><thead><tr><th>Axis</th><th>Values to cover</th></tr></thead><tbody><tr><td>Evidence position</td><td>beginning, interior positions, end</td></tr><tr><td>Distractor count</td><td>low through deployment maximum</td></tr><tr><td>Evidence type</td><td>prose, table, code, cross-document link</td></tr><tr><td>Required operation</td><td>retrieval, aggregation, comparison, exact copy</td></tr><tr><td>Output contract</td><td>free text and structured result</td></tr></tbody></table></div>
<p>Every test should record tokenized length, evidence offsets, truncation, retrieved chunk identifiers, and model position configuration. Otherwise a failure cannot be assigned to retrieval, prompt construction, model use, or runtime truncation.</p>
<h2 id="operational-failure-modes">Operational failure modes</h2>
<p><strong>Silent truncation:</strong> the tokenizer or server drops tokens before model execution. Return accepted input length and truncation status in response metadata.</p>
<p><strong>Position-config mismatch:</strong> weights were tuned with one RoPE policy but served with another. Hash the position configuration into the engine identity.</p>
<p><strong>Cache admission failure:</strong> the request fits the advertised window alone but not under concurrency. Reserve cache before prefill.</p>
<p><strong>Middle-position regression:</strong> aggregate benchmark scores hide positional weakness. Report accuracy by evidence offset.</p>
<p><strong>Sparse-index error:</strong> a required block is omitted by an indexing heuristic. Maintain a dense reference path for reduced test cases.</p>
<p><strong>Retrieval dilution:</strong> adding lower-ranked chunks moves useful evidence farther apart and adds distractors. Evaluate answer quality as the retrieved-token budget changes.</p>
<h2 id="choosing-the-mechanism">Choosing the mechanism</h2>
<p>Use position extension when the model must represent indices beyond training and can be fine-tuned and re-evaluated. Use optimized dense attention when exact all-to-all access is required. Use sparse attention when model quality survives a restricted edge pattern. Use context parallelism when exact attention state exceeds one device. Use retrieval when only a subset of an external corpus is relevant to each request.</p>
<p>These decisions compose. A production system can retrieve documents, serve an extended-RoPE model, run tiled attention, shard context across devices, and page its cache. Each layer needs a separate measurement and failure boundary.</p>
<p>Long context becomes useful only when evidence survives tokenization, placement, attention, memory admission, and decoding. The maximum accepted token count measures just the first of those requirements.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Jianlin Su et al., “RoFormer: Enhanced Transformer with Rotary Position Embedding,” Neurocomputing, 2024. <a href="https://arxiv.org/abs/2104.09864" rel="noopener noreferrer">https://arxiv.org/abs/2104.09864</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Ofir Press, Noah A. Smith, and Mike Lewis, “Train Short, Test Long: Attention with Linear Biases Enables Input Length Extrapolation,” ICLR, 2022. <a href="https://arxiv.org/abs/2108.12409" rel="noopener noreferrer">https://arxiv.org/abs/2108.12409</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Yiran Ding et al., “LongRoPE: Extending LLM Context Window Beyond 2 Million Tokens,” ICML, 2024. <a href="https://arxiv.org/abs/2402.13753" rel="noopener noreferrer">https://arxiv.org/abs/2402.13753</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Tri Dao et al., “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness,” NeurIPS, 2022. <a href="https://arxiv.org/abs/2205.14135" rel="noopener noreferrer">https://arxiv.org/abs/2205.14135</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Hao Liu et al., “Ring Attention with Blockwise Transformers for Near-Infinite Context,” ICLR, 2024. <a href="https://arxiv.org/abs/2310.01889" rel="noopener noreferrer">https://arxiv.org/abs/2310.01889</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Huiqiang Jiang et al., “MInference 1.0: Accelerating Pre-filling for Long-Context LLMs via Dynamic Sparse Attention,” NeurIPS, 2024. <a href="https://arxiv.org/abs/2407.02490" rel="noopener noreferrer">https://arxiv.org/abs/2407.02490</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Nelson F. Liu et al., “Lost in the Middle: How Language Models Use Long Contexts,” TACL, 2024. <a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer">https://arxiv.org/abs/2307.03172</a> <a href="https://blog.ecitis.org/long-context-models/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Long context</category>
            <category>Attention</category>
            <category>Retrieval</category>
            <enclosure length="70499" type="image/png" url="https://blog.ecitis.org/open-graph/long-context-models.png"/>
        </item>
        <item>
            <title><![CDATA[Synthetic Data Pipelines: Generation, Filtering, and Validation]]></title>
            <link>https://blog.ecitis.org/synthetic-data-pipelines/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/synthetic-data-pipelines/</guid>
            <pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Build reproducible synthetic-data systems with explicit lineage, generation, filtering, deduplication, validation, and policy controls.]]></description>
            <content:encoded><![CDATA[<p>Synthetic data is a software artifact with lineage. A production pipeline must be able to answer which seed, prompt template, generator, sampling configuration, validator, and policy produced every accepted record. Without that chain, a high-scoring training run cannot be reproduced or audited.</p>
<figure><figcaption><strong>Synthetic data is accepted only after independent checks</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/synthetic-data-pipelines-0.webp" width="512" height="703" alt="Synthetic data is accepted only after independent checks" loading="lazy" decoding="async"></figure>
<h2 id="define-the-target-distribution-first">Define the target distribution first</h2>
<p>Generation should fill a documented coverage gap. Start with a taxonomy of task, language, domain, difficulty, input form, and expected output form. Assign each candidate to that taxonomy before accepting it.</p>
<p>A record envelope can preserve the minimum lineage:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;record_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;content-addressed-id&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;seed_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;source-record-id&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;task&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;tool_argument_generation&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;generator&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;model&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;pinned-model-revision&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;template&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;template-content-hash&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;sampling&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;serialized-sampling-config&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;candidate&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;prompt&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;generated prompt&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;response&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;generated response&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;validation&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> []</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;decision&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;pending&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Do not use an editable model alias or prompt filename as provenance. Store immutable revisions and content hashes.</p>
<h2 id="seed-records-constrain-what-generation-can-learn">Seed records constrain what generation can learn</h2>
<p>Self-Instruct starts from seed tasks, generates new instructions and instances, filters invalid or similar outputs, then uses accepted examples for instruction tuning.<sup><a href="https://blog.ecitis.org/synthetic-data-pipelines/#user-content-fn-self-instruct" id="user-content-fnref-self-instruct">1</a></sup> The mechanism amplifies a seed distribution. It does not create an unbiased sample of every task users will request.</p>
<p>Seed preparation should include:</p>
<ul>
<li>license and consent review;</li>
<li>removal of secrets and personal data;</li>
<li>exact and near-duplicate grouping;</li>
<li>taxonomy labels;</li>
<li>held-out source groups for evaluation;</li>
<li>contamination checks against benchmark material.</li>
</ul>
<p>Split by source group before generation. If variants of one seed land in training and evaluation, the evaluation measures template transfer rather than independent generalization.</p>
<h2 id="generation-should-expose-controlled-diversity">Generation should expose controlled diversity</h2>
<p>Prompt templates can request variations in task structure, constraints, domain, or reasoning requirement. Sampling adds lexical and solution diversity. Both can also produce invalid examples.</p>
<p>Generate multiple candidates per seed only when a validator can distinguish them. Otherwise the pipeline increases volume without evidence of quality. Preserve rejected candidates and reasons so generator changes can be compared on the same acceptance criteria.</p>
<p>Structured tasks should use structured generation:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> dataclasses </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> dataclass</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@dataclass</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> Candidate</span><span style="color:var(--astro-code-foreground)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    instruction</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    input</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    assumptions</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> list</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">str</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    source_ids</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> list</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">str</span><span style="color:var(--astro-code-token-punctuation)">]</span></span></code></pre>
<p>A schema verifies form, not truth. It prevents missing fields and malformed types from entering later stages.</p>
<h2 id="use-validators-with-independent-evidence">Use validators with independent evidence</h2>
<p>A generator grading its own answer repeats correlated errors. Prefer validators that obtain evidence through a different mechanism.</p>



































<div class="table-wrap"><table><thead><tr><th>Task</th><th>Strong validator</th><th>Weak validator</th></tr></thead><tbody><tr><td>Code generation</td><td>compile, tests, static analysis</td><td>generator self-rating</td></tr><tr><td>Structured extraction</td><td>schema plus comparison to source</td><td>fluent explanation</td></tr><tr><td>Mathematics</td><td>symbolic check or alternate derivation</td><td>answer-format match</td></tr><tr><td>Retrieval question</td><td>entailment against cited passages</td><td>uncited judge score</td></tr><tr><td>Tool calls</td><td>schema plus sandbox execution</td><td>syntactic JSON only</td></tr></tbody></table></div>
<p>Execution validators need containment. Run generated code with resource limits, no ambient credentials, restricted networking, and disposable storage. Record exit status and test output, but sanitize untrusted content before displaying it in internal tools.</p>
<p>For open-ended responses, use a rubric with observable failure labels such as unsupported claim, missing constraint, contradiction, unsafe instruction, or non-answer. Calibrate model-based judges against a human-reviewed sample and recheck calibration when the generator or domain changes.</p>
<h2 id="deduplicate-before-and-after-generation">Deduplicate before and after generation</h2>
<p>Exact hashing removes byte-identical records. Normalized hashing can ignore formatting, case, or boilerplate. Semantic similarity catches paraphrases, but thresholds depend on the embedding model and domain.</p>
<p>Deduplication has two distinct objectives:</p>
<ol>
<li>prevent repeated training weight on the same underlying example;</li>
<li>prevent evaluation leakage from a training example or its derivative.</li>
</ol>
<p>The second objective needs lineage-aware checks. A generated prompt can be lexically distant from its seed while preserving the same answer structure. Group descendants with their seed during split assignment.</p>
<p>Store cluster identifiers and representative records. Deleting duplicates without a manifest makes future dataset diffs difficult to explain.</p>
<h2 id="filter-by-difficulty-with-executable-signals">Filter by difficulty with executable signals</h2>
<p>Difficulty labels produced only by a generator are unstable. Prefer signals tied to the task:</p>
<ul>
<li>number and type of constraints satisfied;</li>
<li>proof or program length after normalization;</li>
<li>number of tool calls required by a reference solution;</li>
<li>pass rate across independent solvers;</li>
<li>retrieval depth or evidence count;</li>
<li>disagreement among validated candidate solutions.</li>
</ul>
<p>Do not equate longer chain-of-thought text with harder reasoning. A verbose candidate can contain redundant or fabricated steps. If reasoning traces are used for training, validate the final answer and inspect step consistency with tools where the domain permits it.</p>
<h2 id="keep-private-reasoning-out-of-public-data-contracts">Keep private reasoning out of public data contracts</h2>
<p>A synthetic pipeline can train on solutions without exposing raw reasoning at inference. Separate outcome supervision, concise rationales, tool traces, and hidden scratch work as distinct fields. The serving format should not inherit the generator’s internal trace by accident.</p>
<p>For tool use, a compact trace is often more useful:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>request</span></span>
<span class="line"><span>tool schema selected</span></span>
<span class="line"><span>validated arguments</span></span>
<span class="line"><span>tool result reference</span></span>
<span class="line"><span>final answer grounded in result</span></span></code></pre>
<p>This structure is auditable and maps to runtime behavior.</p>
<h2 id="control-model-collapse-with-real-anchors">Control model collapse with real anchors</h2>
<p>Training repeatedly on model-generated distributions can reduce coverage of low-probability events and propagate generator errors. A synthetic corpus therefore needs real-data anchors, independent evaluation, and explicit mixture weights. Synthetic examples should address measured gaps rather than replace the entire observed distribution.</p>
<p>Track acceptance and performance by source type. If synthetic data improves aggregate accuracy while reducing performance on a real-data slice, the aggregate result is insufficient.</p>
<h2 id="build-evaluation-before-the-large-run">Build evaluation before the large run</h2>
<p>The evaluation set must not be generated by the same templates, seeds, and model revision used for training. Include:</p>
<ul>
<li>a frozen real-data set;</li>
<li>adversarial cases written or curated independently;</li>
<li>schema and execution tests;</li>
<li>slice metrics for the target taxonomy;</li>
<li>memorization probes against seeds;</li>
<li>safety and privacy probes;</li>
<li>regression examples from production failures.</li>
</ul>
<p>FLAN work showed that scaling instruction-finetuning tasks and using chain-of-thought data can improve model behavior across evaluation families.<sup><a href="https://blog.ecitis.org/synthetic-data-pipelines/#user-content-fn-flan" id="user-content-fnref-flan">2</a></sup> That result supports task diversity as a training variable. It does not validate any arbitrary synthetic dataset, so each pipeline still needs its own ablations.</p>
<p>Run mixture ablations rather than comparing only no-synthetic versus all-synthetic. Change one factor at a time: seed source, generator revision, filtering stage, or mixture weight.</p>
<h2 id="version-the-dataset-as-a-build">Version the dataset as a build</h2>
<p>A dataset release should contain:</p>
<ul>
<li>source and license manifest;</li>
<li>generator and prompt hashes;</li>
<li>validator versions;</li>
<li>rejection counts by reason;</li>
<li>deduplication clusters;</li>
<li>split assignment logic;</li>
<li>final record hashes;</li>
<li>evaluation report;</li>
<li>known limitations.</li>
</ul>
<p>Treat validator changes like code changes. Rebuild from immutable candidates, diff accepted record IDs, and investigate large movements. A judge prompt edit can alter the training distribution even when generation is unchanged.</p>
<h2 id="failure-modes">Failure modes</h2>
<p><strong>Validator leakage:</strong> The generator learns the tests or rubric and produces outputs tailored to them. Keep hidden checks and rotate surface forms without changing the criterion.</p>
<p><strong>Homogeneous style:</strong> One generator and template create repeated syntax. Measure lexical and structural diversity, then introduce source or template diversity with unchanged validation.</p>
<p><strong>False authority:</strong> Candidates include citations that look valid but do not support the claim. Fetch the cited primary source and verify entailment, or reject the citation.</p>
<p><strong>Split contamination:</strong> Derived examples cross seed-group boundaries. Assign the split at the ancestor seed and inherit it through every transformation.</p>
<p><strong>Silent policy drift:</strong> A hosted generator changes behind an alias. Pin a revision when possible and retain probe outputs that identify behavior changes.</p>
<p>Synthetic data quality comes from the rejection path as much as the generator. The durable pipeline preserves provenance, validates with independent evidence, deduplicates by lineage, anchors evaluation in real data, and makes every accepted record reproducible.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-self-instruct">
<p><a href="https://arxiv.org/abs/2212.10560" rel="noopener noreferrer">Self-Instruct: Aligning Language Models with Self-Generated Instructions</a>. <a href="https://blog.ecitis.org/synthetic-data-pipelines/#user-content-fnref-self-instruct" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-flan">
<p><a href="https://arxiv.org/abs/2210.11416" rel="noopener noreferrer">Scaling Instruction-Finetuned Language Models</a>. <a href="https://blog.ecitis.org/synthetic-data-pipelines/#user-content-fnref-flan" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Synthetic data</category>
            <category>Training</category>
            <category>Evaluation</category>
            <enclosure length="80012" type="image/png" url="https://blog.ecitis.org/open-graph/synthetic-data-pipelines.png"/>
        </item>
        <item>
            <title><![CDATA[LLM Evaluation: Contamination, Judge Bias, and Production Metrics]]></title>
            <link>https://blog.ecitis.org/llm-evaluation/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/llm-evaluation/</guid>
            <pubDate>Tue, 19 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Design evaluations around release decisions while controlling contamination, judge bias, statistical noise, and production constraints.]]></description>
            <content:encoded><![CDATA[<p>An evaluation is a measurement procedure, not a prompt file. Its result depends on the dataset, harness, model configuration, scorer, aggregation, and statistical analysis. Changing any one of them changes the object being measured.</p>
<p>This matters for language-model systems because the model is only one component. Retrieval, tools, policy prompts, sampling parameters, and output parsers can determine whether the same model succeeds or fails.</p>
<h2 id="define-the-decision-before-the-metric">Define the decision before the metric</h2>
<p>Start with the release decision:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">decision</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> replace current support-answering model</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">population</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> authenticated English support questions</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">constraints</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">no regression on policy compliance</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">bounded p95 response latency</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">bounded cost per resolved conversation</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">outcomes</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">correct resolution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">unsupported claim rate</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">escalation rate</span></span></code></pre>
<p>The numeric thresholds are product choices and therefore omitted from this example. A benchmark score that does not connect to the decision is diagnostic evidence, not a release gate.</p>
<p>HELM argues for broad scenario coverage and simultaneous measurement of accuracy, robustness, calibration, fairness, bias, toxicity, and efficiency rather than a single model score.<sup><a href="https://blog.ecitis.org/llm-evaluation/#user-content-fn-helm" id="user-content-fnref-helm">1</a></sup> The exact dimensions should follow the deployed task.</p>
<h2 id="build-cases-from-a-task-taxonomy">Build cases from a task taxonomy</h2>
<p>Random production samples reproduce the traffic distribution but can miss rare, costly failures. Handwritten challenge sets cover known hazards but distort prevalence. Use both and report them separately.</p>
<p>A case should contain:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> EvalCase</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  id</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  input</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> unknown</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  expected</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-token-constant)"> unknown</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  rubric</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  tags</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  source</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;production&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;expert&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;incident&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;synthetic&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  provenance</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  createdAt</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Tags enable slices such as language, tool, document type, ambiguity, policy class, and input length. Provenance supports contamination audits and rights review.</p>
<p>Freeze a release suite. Add new incident cases to a forward-looking suite so repeated tuning does not quietly overfit the current gate.</p>
<figure><figcaption><strong>Evaluation pipeline with validity gates</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/llm-evaluation-0.webp" width="512" height="641" alt="Evaluation pipeline with validity gates" loading="lazy" decoding="async"></figure>
<h2 id="pin-the-harness">Pin the harness</h2>
<p>The harness includes every transformation between a case and its score:</p>
<ul>
<li>System and developer instructions.</li>
<li>Tool definitions and tool results.</li>
<li>Retrieval index and ranking configuration.</li>
<li>Model identifier and sampling parameters.</li>
<li>Retry and timeout policy.</li>
<li>Output parser.</li>
<li>Scorer prompt and scorer model.</li>
</ul>
<p>Record the rendered request rather than only the prompt template. A misplaced separator, reordered choice list, or truncated tool result can alter the outcome.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;case_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;billing-policy-017&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;suite_revision&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;support-v4&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;model&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;candidate-build&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;prompt_sha256&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;…&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;toolset_revision&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;support-tools-v7&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;retrieval_revision&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;kb-2026-05-18&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;sampler&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;temperature&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;raw_output_uri&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;artifact://run/case/output&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Version names above are illustrative.</p>
<h2 id="deterministic-scoring-comes-first">Deterministic scoring comes first</h2>
<p>Use deterministic checks when the task has an executable contract:</p>
<ul>
<li>JSON Schema validity.</li>
<li>Exact identifier match.</li>
<li>Unit tests for generated code.</li>
<li>Database-state assertions after tool use.</li>
<li>Citation presence and source alignment.</li>
<li>Regular-language constraints.</li>
</ul>
<p>Deterministic scorers expose why a case failed and can be rerun without model variance. They do not measure open-ended quality, but they should handle every property that can be specified mechanically.</p>
<p>For partial-credit tasks, define components:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>S</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∑</mo><mi>i</mi></munder><msub><mi>w</mi><mi>i</mi></msub><msub><mi>s</mi><mi>i</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Weights encode a product judgment. Publish them with the results and report component scores so one weighted total does not hide a safety regression.</p>
<h2 id="human-evaluation-needs-a-protocol">Human evaluation needs a protocol</h2>
<p>Human ratings are not ground truth by default. Raters need:</p>
<ul>
<li>A concrete rubric with positive and negative examples.</li>
<li>Blinded model identity.</li>
<li>Randomized answer order.</li>
<li>An abstain or unclear option.</li>
<li>Independent ratings before adjudication.</li>
<li>Quality-control cases.</li>
</ul>
<p>Measure agreement and inspect disagreements. Low agreement can indicate an ambiguous rubric, insufficient evidence, or a task with legitimate preference variation.</p>
<p>Pairwise comparison is often easier than an absolute scale: show two outputs for the same input and ask which better satisfies the rubric. Randomize left and right positions, then include ties when the difference is not material.</p>
<h2 id="llm-judges-are-noisy-instruments">LLM judges are noisy instruments</h2>
<p>Model-based scoring enables larger evaluation sets, but it introduces a second model whose behavior must be validated.</p>
<p>The MT-Bench and Chatbot Arena paper compared model judges with human preferences and documented position, verbosity, and self-enhancement biases.<sup><a href="https://blog.ecitis.org/llm-evaluation/#user-content-fn-mtbench" id="user-content-fnref-mtbench">2</a></sup> A judge can therefore reward presentation features unrelated to task correctness.</p>
<p>Mitigations are measurable:</p>
<ol>
<li>Swap candidate order and score both orientations.</li>
<li>Hide model identity and provider-specific formatting.</li>
<li>Require criterion-level scores before an overall preference.</li>
<li>Give the judge reference evidence when the task permits it.</li>
<li>Calibrate against a held-out human-rated set.</li>
<li>Route judge-human disagreement and low-confidence cases to review.</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>score(A, B) + score(B, A) → stable preference or disagreement</span></span></code></pre>
<p>Do not ask a judge for hidden chain-of-thought. Request a short evidence field tied to rubric criteria, then audit whether that evidence matches the outputs.</p>
<p>Judge agreement with humans should be reported on the target task, not borrowed from another benchmark. A judge calibrated for concise customer support answers may fail on mathematical proofs or code patches.</p>
<figure><figcaption><strong>Order swapping exposes a position-sensitive judge</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/llm-evaluation-1.webp" width="512" height="311" alt="Order swapping exposes a position-sensitive judge" loading="lazy" decoding="async"></figure>
<h2 id="contamination-invalidates-the-question">Contamination invalidates the question</h2>
<p>Benchmark contamination occurs when evaluation material or close derivatives enter training, fine-tuning, retrieval corpora, prompt libraries, or manual development loops. A high score can then measure recall of the benchmark rather than task generalization.</p>
<p>Contamination is not limited to exact duplicates. Paraphrased questions, published solutions, answer keys, and benchmark-specific explanations can leak the intended mapping.</p>
<p>Mitigations include:</p>
<ul>
<li>Keep private test cases and rotate them.</li>
<li>Hash and search exact examples across accessible training corpora.</li>
<li>Run semantic near-duplicate detection.</li>
<li>Store creation dates and source provenance.</li>
<li>Separate public development sets from private release sets.</li>
<li>Treat benchmark names and canonical phrasing as contamination signals.</li>
</ul>
<p>Black-box detection is uncertain. The Min-K% Prob method tests whether a text contains unusually low-probability tokens and was evaluated for pretraining-data detection on a time-split benchmark.<sup><a href="https://blog.ecitis.org/llm-evaluation/#user-content-fn-mink" id="user-content-fnref-mink">3</a></sup> It is evidence, not proof of membership. A current evaluation should describe the audit performed and residual uncertainty.</p>
<p>Dynamic cases reduce exposure. Generate them from controlled templates with executable answers, then validate the generator. Do not call generated cases uncontaminated merely because their final strings are new; the underlying task pattern may still be familiar.</p>
<h2 id="statistical-reporting">Statistical reporting</h2>
<p>A score without uncertainty encourages false precision. For a binary metric with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow></semantics></math> independent cases and empirical success rate <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover></mrow></semantics></math>, a simple standard error estimate is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">SE</mi><mo>⁡</mo><mo stretchy="false">(</mo><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">)</mo><mo>=</mo><msqrt><mfrac><mrow><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mover accent="true"><mi>p</mi><mo>^</mo></mover><mo stretchy="false">)</mo></mrow><mi>n</mi></mfrac></msqrt></mrow></semantics></math>
<p>Bootstrap intervals are more flexible for non-linear metrics and sliced data. For model comparisons, use paired resampling because both models run on the same cases.</p>
<p>Report:</p>
<ul>
<li>Number of eligible, executed, failed, and excluded cases.</li>
<li>Aggregate and slice metrics.</li>
<li>Confidence intervals.</li>
<li>Paired differences against the current system.</li>
<li>Missing-output and parser-failure rates.</li>
<li>Judge disagreement and human adjudication rates.</li>
</ul>
<p>Do not remove timeouts from the denominator unless the release decision also ignores timeouts.</p>
<h2 id="production-metrics-close-the-loop">Production metrics close the loop</h2>
<p>Offline cases are controlled and replayable. Production metrics observe the real distribution and real users. Neither replaces the other.</p>
<p>Useful system metrics include:</p>

































<div class="table-wrap"><table><thead><tr><th>Layer</th><th>Measurements</th></tr></thead><tbody><tr><td>Request</td><td>eligible volume, language, intent, input length</td></tr><tr><td>Model</td><td>latency, token usage, refusal, malformed output</td></tr><tr><td>Retrieval</td><td>empty results, evidence coverage, stale index</td></tr><tr><td>Tools</td><td>call success, invalid arguments, approval, rollback</td></tr><tr><td>Outcome</td><td>resolution, correction, escalation, abandonment</td></tr><tr><td>Safety</td><td>policy blocks, confirmed incidents, reviewer reversals</td></tr></tbody></table></div>
<p>User clicks and thumbs ratings are behavior signals, not direct correctness labels. A user can accept a plausible wrong answer or downvote a correct refusal. Combine behavioral metrics with audited samples.</p>
<p>For online experiments, define guardrails before launch and preserve assignment logs. Model changes can alter cost, latency, and escalation while leaving average answer ratings unchanged.</p>
<h2 id="detect-drift">Detect drift</h2>
<p>Drift can occur in inputs, retrieved knowledge, tool behavior, policy, or user expectations. Track slice proportions and score distributions over time.</p>
<p>Trigger a review when:</p>
<ul>
<li>A new intent lacks evaluation coverage.</li>
<li>A retrieval source changes schema.</li>
<li>A tool adds a side effect.</li>
<li>A scorer model or prompt changes.</li>
<li>Human-judge agreement degrades.</li>
<li>Production outcomes separate from offline scores.</li>
</ul>
<p>An evaluation suite is versioned software. Changes need review, changelogs, reproducible artifacts, and migration notes.</p>
<h2 id="diagnose-failures-by-layer">Diagnose failures by layer</h2>
<p><strong>Correct evidence absent:</strong> retrieval failure.</p>
<p><strong>Correct evidence present, answer unsupported:</strong> generation or instruction failure.</p>
<p><strong>Valid action selected with wrong arguments:</strong> tool-planning or schema failure.</p>
<p><strong>Correct output marked wrong:</strong> scorer or rubric failure.</p>
<p><strong>Offline pass with production regression:</strong> population, harness, or outcome mismatch.</p>
<p>Store raw inputs, outputs, tool traces, scorer decisions, and policy versions so these categories can be tested rather than guessed.</p>
<h2 id="release-report">Release report</h2>
<p>A useful report states:</p>
<ol>
<li>The deployment decision.</li>
<li>Suite provenance and known contamination risk.</li>
<li>Harness and model revisions.</li>
<li>Deterministic, judge, and human scoring procedures.</li>
<li>Aggregate results with uncertainty.</li>
<li>Critical slices and regressions.</li>
<li>Production guardrails.</li>
<li>Known blind spots.</li>
</ol>
<p>The report should make rerunning the evaluation possible. A leaderboard number without cases, harness, and scoring details cannot support a production decision.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-helm">
<p><a href="https://arxiv.org/abs/2211.09110" rel="noopener noreferrer">Liang et al., “Holistic Evaluation of Language Models,” Transactions on Machine Learning Research, 2023</a>. <a href="https://blog.ecitis.org/llm-evaluation/#user-content-fnref-helm" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mtbench">
<p><a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer">Zheng et al., “Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena,” NeurIPS Datasets and Benchmarks, 2023</a>. <a href="https://blog.ecitis.org/llm-evaluation/#user-content-fnref-mtbench" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mink">
<p><a href="https://arxiv.org/abs/2310.16789" rel="noopener noreferrer">Shi et al., “Detecting Pretraining Data from Large Language Models,” ICLR, 2024</a>. <a href="https://blog.ecitis.org/llm-evaluation/#user-content-fnref-mink" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Evaluation</category>
            <category>Benchmarking</category>
            <category>Reasoning</category>
            <enclosure length="71884" type="image/png" url="https://blog.ecitis.org/open-graph/llm-evaluation.png"/>
        </item>
        <item>
            <title><![CDATA[On-Device Multimodal AI: Text, Vision, and Audio Under a Memory Budget]]></title>
            <link>https://blog.ecitis.org/on-device-multimodal-ai/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/on-device-multimodal-ai/</guid>
            <pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Schedule text, vision, and audio pipelines within the shared memory, thermal, energy, and latency limits of edge devices.]]></description>
            <content:encoded><![CDATA[<p>An on-device multimodal model is a scheduling problem over shared memory. Camera buffers, audio capture, encoders, language-model weights, KV cache, and UI surfaces compete for the same physical resources. A model that fits at rest can still fail when an encoder and decoder reach peak allocation together.</p>
<figure><figcaption><strong>A shared memory budget, not three independent pipelines</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/on-device-multimodal-ai-0.webp" width="512" height="394" alt="A shared memory budget, not three independent pipelines" loading="lazy" decoding="async"></figure>
<h2 id="account-for-peak-live-memory">Account for peak live memory</h2>
<p>A useful budget separates persistent and transient allocations:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>M</mi><mtext>peak</mtext></msub><mo>=</mo><msub><mi>M</mi><mtext>weights</mtext></msub><mo>+</mo><msub><mi>M</mi><mtext>KV</mtext></msub><mo>+</mo><msub><mi>M</mi><mtext>active encoders</mtext></msub><mo>+</mo><msub><mi>M</mi><mtext>activations</mtext></msub><mo>+</mo><msub><mi>M</mi><mtext>workspace</mtext></msub><mo>+</mo><msub><mi>M</mi><mtext>application</mtext></msub></mrow></semantics></math>
<p>Weight-file size is not peak memory. Runtimes can unpack weights, allocate graph plans, retain caches, and copy buffers between CPU and accelerator address spaces. The operating system also needs headroom for the camera, microphone, display compositor, and the rest of the application.</p>
<p>Instrument resident memory and accelerator allocations around each phase:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>load language model</span></span>
<span class="line"><span>load vision encoder</span></span>
<span class="line"><span>encode image</span></span>
<span class="line"><span>release vision workspace</span></span>
<span class="line"><span>append visual tokens</span></span>
<span class="line"><span>load or activate audio encoder</span></span>
<span class="line"><span>encode audio window</span></span>
<span class="line"><span>release audio workspace</span></span>
<span class="line"><span>decode text</span></span></code></pre>
<p>Serial encoder scheduling lowers peak concurrency when the application does not need simultaneous frame and audio processing. The cost is latency. Measure the user-visible critical path before selecting concurrency.</p>
<h2 id="encoders-turn-media-into-token-sequences">Encoders turn media into token sequences</h2>
<p>A vision encoder maps image patches or feature maps into embeddings. An adapter projects those embeddings into the language model’s hidden space. A speech encoder performs a related conversion over acoustic frames. The language model then consumes an interleaved token sequence containing text and modality embeddings.</p>
<p>Gemma 3n provides a concrete mobile architecture: it combines text with image, video, and audio inputs, uses a MobileNet-V5 vision encoder, and derives its audio encoder from Universal Speech Model work.<sup><a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fn-gemma" id="user-content-fnref-gemma">1</a></sup> Google’s implementation reports one audio token per <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>160</mn></mrow></semantics></math> milliseconds and offers image input resolutions of <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>256</mn><mo>×</mo><mn>256</mn></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>512</mn><mo>×</mo><mn>512</mn></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>768</mn><mo>×</mo><mn>768</mn></mrow></semantics></math> pixels.<sup><a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fn-gemma" id="user-content-fnref-gemma-2">1</a></sup> Those are model-specific interface facts, not defaults for all multimodal systems.</p>
<p>Media preprocessing must match training:</p>
<ul>
<li>image color space, resizing, crop policy, and normalization;</li>
<li>audio sample rate, channel mixing, windowing, and feature extraction;</li>
<li>special tokens that delimit each modality;</li>
<li>positional encoding used for media tokens;</li>
<li>adapter weights paired with the correct encoder and language model.</li>
</ul>
<p>An apparently plausible response does not prove preprocessing is correct. Validate embeddings or logits against a reference runtime with fixed media fixtures.</p>
<h2 id="sequence-length-is-a-memory-input">Sequence length is a memory input</h2>
<p>The language model sees encoder outputs as tokens. More image tiles, video frames, or audio windows extend the prefill sequence. KV cache memory for an autoregressive decoder grows with retained sequence length:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>M</mi><mtext>KV</mtext></msub><mo>∝</mo><mi>L</mi><mo>×</mo><mi>S</mi><mo>×</mo><msub><mi>H</mi><mtext>KV</mtext></msub><mo>×</mo><mi>b</mi></mrow></semantics></math>
<p>Here <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> is cached layer count, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math> is sequence length, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mtext>KV</mtext></msub></mrow></semantics></math> is the cached key-value width, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow></semantics></math> is bytes per stored element. Grouped-query or multi-query attention changes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mtext>KV</mtext></msub></mrow></semantics></math> by sharing key-value heads. Quantized cache storage changes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow></semantics></math> but adds conversion and accuracy considerations.</p>
<p>Gemma 3n documents KV cache sharing in which middle-layer key and value representations are shared with upper layers to reduce prefill work.<sup><a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fn-gemma" id="user-content-fnref-gemma-3">1</a></sup> That is an architectural optimization built into the model. A runtime cannot apply it to an arbitrary checkpoint without matching training and graph structure.</p>
<p>Set modality budgets before preprocessing:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">data</span><span style="color:var(--astro-code-token-keyword)"> class</span><span style="color:var(--astro-code-token-function)"> RequestBudget</span><span style="color:var(--astro-code-foreground)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    val</span><span style="color:var(--astro-code-foreground)"> maxImageTokens: </span><span style="color:var(--astro-code-token-function)">Int</span><span style="color:var(--astro-code-foreground)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    val</span><span style="color:var(--astro-code-foreground)"> maxAudioTokens: </span><span style="color:var(--astro-code-token-function)">Int</span><span style="color:var(--astro-code-foreground)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    val</span><span style="color:var(--astro-code-foreground)"> maxTextTokens: </span><span style="color:var(--astro-code-token-function)">Int</span><span style="color:var(--astro-code-foreground)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    val</span><span style="color:var(--astro-code-foreground)"> maxOutputTokens: </span><span style="color:var(--astro-code-token-function)">Int</span><span style="color:var(--astro-code-foreground)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">fun</span><span style="color:var(--astro-code-token-function)"> validate</span><span style="color:var(--astro-code-foreground)">(b: </span><span style="color:var(--astro-code-token-function)">RequestBudget</span><span style="color:var(--astro-code-foreground)">, contextLimit: </span><span style="color:var(--astro-code-token-function)">Int</span><span style="color:var(--astro-code-foreground)">) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    require</span><span style="color:var(--astro-code-foreground)">(</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b.maxImageTokens </span><span style="color:var(--astro-code-token-keyword)">+</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b.maxAudioTokens </span><span style="color:var(--astro-code-token-keyword)">+</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b.maxTextTokens </span><span style="color:var(--astro-code-token-keyword)">+</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b.maxOutputTokens </span><span style="color:var(--astro-code-token-keyword)">&lt;=</span><span style="color:var(--astro-code-foreground)"> contextLimit</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    )</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The application should reject or downsample input before allocating an oversized cache.</p>
<h2 id="vision-needs-an-explicit-frame-policy">Vision needs an explicit frame policy</h2>
<p>Video is not one large image. It is a sequence of frames with temporal redundancy. Feeding every captured frame wastes encoder work and context. Define a policy based on the task:</p>
<ul>
<li>periodic sampling for scene summaries;</li>
<li>motion- or event-triggered frames for monitoring;</li>
<li>keyframes plus crops for document or screen understanding;</li>
<li>a rolling window when recent state matters;</li>
<li>frame replacement when the model supports external memory.</li>
</ul>
<p>Retain timestamps and orientation metadata. If visual tokens from multiple frames are concatenated without temporal markers, the language model cannot reliably infer capture order from pixels alone.</p>
<p>Camera images often arrive in a hardware-native pixel format. Converting full-resolution frames on the CPU can become the bottleneck before inference begins. Prefer runtime-supported texture or buffer paths, and crop or resize before expensive format conversion when the APIs permit it.</p>
<h2 id="audio-needs-streaming-state">Audio needs streaming state</h2>
<p>Audio capture produces a continuous stream while language-model decoding is bursty. A bounded ring buffer separates the two schedules. The encoder consumes windows with overlap or streaming state, then emits tokens or transcript segments.</p>
<p>Google describes the Gemma 3n audio encoder as streaming-capable, while its launch implementation accepts clips up to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>30</mn></mrow></semantics></math> seconds.<sup><a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fn-gemma" id="user-content-fnref-gemma-4">1</a></sup> That limit belongs to the released implementation. Long-form support requires the runtime and model training needed to preserve streaming state.</p>
<p>For a speech request, track:</p>
<ul>
<li>capture timestamp and dropped frames;</li>
<li>resampling configuration;</li>
<li>encoder state reset points;</li>
<li>token timestamps if exposed;</li>
<li>end-of-speech decision;</li>
<li>time from speech end to first generated token.</li>
</ul>
<p>Voice activity detection saves encoder and language-model work, but a false endpoint can cut a word or reset context. Keep the raw audio window needed to reproduce failures, subject to the product’s privacy policy.</p>
<h2 id="choose-placement-per-operator">Choose placement per operator</h2>
<p>Mobile systems may expose CPU, GPU, neural accelerator, or vendor delegate paths. One model can span them, but every boundary risks a copy or synchronization. A placement plan should use measured operator coverage:</p>

























<div class="table-wrap"><table><thead><tr><th>Subgraph</th><th>Placement question</th></tr></thead><tbody><tr><td>Vision encoder</td><td>Does the delegate support its convolution and attention blocks without fallback?</td></tr><tr><td>Audio frontend</td><td>Is feature extraction cheaper on vector CPU code than through accelerator dispatch?</td></tr><tr><td>Language prefill</td><td>Which backend sustains the matrix shapes produced by media tokens?</td></tr><tr><td>Token decode</td><td>Which backend gives low latency at a small batch?</td></tr></tbody></table></div>
<p>An unsupported operator in the middle of an accelerator graph can force partitioning and transfers. Inspect the compiled delegate graph rather than relying on an API call that reports delegate initialization.</p>
<h2 id="quantization-requires-modality-tests">Quantization requires modality tests</h2>
<p>Weight quantization reduces resident model memory. Activation quantization can reduce bandwidth and accelerator requirements. It can also alter small embedding differences at modality adapters or attention logits.</p>
<p>Evaluate text-only, image-only, audio-only, and interleaved inputs separately. Include:</p>
<ul>
<li>small text in images;</li>
<li>low-contrast objects;</li>
<li>accented and noisy speech;</li>
<li>silence and non-speech audio;</li>
<li>long media prefixes;</li>
<li>repeated modality switches.</li>
</ul>
<p>Compare task metrics and intermediate outputs where possible. A single language benchmark cannot qualify a multimodal quantization.</p>
<h2 id="thermal-behavior-changes-the-service-policy">Thermal behavior changes the service policy</h2>
<p>Short benchmark runs do not show sustained device behavior. Heat can reduce clock frequency and make a formerly valid real-time policy fall behind. Run capture, encoding, and decoding together for the expected session length. Record throughput, latency, temperature or platform thermal state, and battery or power telemetry exposed by the device.</p>
<p>Degradation should be explicit:</p>
<ol>
<li>reduce video sampling frequency;</li>
<li>reduce image resolution or crop count;</li>
<li>shorten retained media context;</li>
<li>switch to a nested or smaller model when available;</li>
<li>pause background interpretation while preserving direct user requests.</li>
</ol>
<p>Gemma 3n uses MatFormer nesting and per-layer embeddings so its released variants can operate with active memory footprints associated with smaller nested models.<sup><a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fn-gemma-preview" id="user-content-fnref-gemma-preview">2</a></sup> That capability depends on its architecture. Other models need separate checkpoints or different elastic mechanisms.</p>
<h2 id="privacy-is-a-data-flow-property">Privacy is a data-flow property</h2>
<p>Local inference does not prove that media stays local. Crash reporters, analytics payloads, debug logging, model downloads, and remote fallbacks can transmit derived or raw data. Document every boundary and make fallback behavior visible to the user.</p>
<p>Encrypt cached media if it must persist. Prefer ephemeral buffers. Strip prompt and media content from default logs. Verify that application lifecycle transitions release microphone and camera access. Test airplane-mode operation if offline behavior is a requirement.</p>
<p>On-device multimodal design starts with a peak-memory timeline and a token budget. Encoders compress media into language-model inputs, but those inputs still consume prefill time and KV cache. The reliable implementation schedules encoders deliberately, validates preprocessing against a reference, measures delegate coverage, and reduces work predictably under thermal or memory pressure.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-gemma">
<p><a href="https://developers.googleblog.com/en/introducing-gemma-3n-developer-guide/" rel="noopener noreferrer">Google Developers Blog: Gemma 3n developer guide</a>. <a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fnref-gemma" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fnref-gemma-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fnref-gemma-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fnref-gemma-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-gemma-preview">
<p><a href="https://developers.googleblog.com/en/introducing-gemma-3n" rel="noopener noreferrer">Google Developers Blog: Gemma 3n preview architecture</a>. <a href="https://blog.ecitis.org/on-device-multimodal-ai/#user-content-fnref-gemma-preview" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Multimodal</category>
            <category>Edge &amp; on-device</category>
            <category>Memory</category>
            <enclosure length="68683" type="image/png" url="https://blog.ecitis.org/open-graph/on-device-multimodal-ai.png"/>
        </item>
        <item>
            <title><![CDATA[Retrieval Systems Beyond Basic RAG: Hybrid Search, Reranking, and Evaluation]]></title>
            <link>https://blog.ecitis.org/retrieval-systems/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/retrieval-systems/</guid>
            <pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Build measurable retrieval pipelines with structural chunking, hybrid candidate generation, reranking, and relevance evaluation.]]></description>
            <content:encoded><![CDATA[<p>A retrieval system has a narrower job than a language model: given a query, return evidence that satisfies an information need. That boundary makes retrieval measurable. Documents can be judged for relevance before generation is introduced, and failures can be assigned to indexing, candidate generation, ranking, context construction, or answer synthesis.</p>
<p>Basic retrieval-augmented generation often collapses those stages into one embedding search. Production retrieval needs more structure because identifiers, exact phrases, concepts, freshness, access control, and document quality are different ranking signals.</p>
<h2 id="index-documents-around-retrieval-units">Index documents around retrieval units</h2>
<p>Chunking defines the unit the ranker can return. A chunk that spans unrelated sections produces an embedding with mixed semantics. A chunk that is too narrow can omit the qualifier needed to interpret a match.</p>
<p>Use structural boundaries before token counts:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> Passage</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  id</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  documentId</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  headingPath</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  body</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  updatedAt</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  acl</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> passageText</span><span style="color:var(--astro-code-foreground)">(passage</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> Passage</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  return</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-keyword)">...</span><span style="color:var(--astro-code-token-constant)">passage</span><span style="color:var(--astro-code-foreground)">.headingPath</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> passage</span><span style="color:var(--astro-code-foreground)">.body]</span><span style="color:var(--astro-code-token-function)">.join</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;\n&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The heading path gives a dense encoder and lexical index the local subject. The document identifier supports deduplication after ranking. The access-control field allows filtering before any passage enters the candidate set.</p>
<p>Store provenance that survives every stage:</p>
<ul>
<li>Stable document and passage identifiers.</li>
<li>Source URI and content revision.</li>
<li>Character or byte offsets into the source.</li>
<li>Parser and embedding model versions.</li>
<li>Access-control attributes.</li>
<li>Timestamp used for freshness calculations.</li>
</ul>
<p>Without a revision identifier, an answer citation can point at content that changed after retrieval.</p>
<h2 id="lexical-retrieval-preserves-exact-evidence">Lexical retrieval preserves exact evidence</h2>
<p>BM25 ranks documents using query term frequency, inverse document frequency, and length normalization. A common form is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">score</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>D</mi><mo separator="true">,</mo><mi>Q</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∑</mo><mrow><mi>q</mi><mo>∈</mo><mi>Q</mi></mrow></munder><mi mathvariant="normal">IDF</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>q</mi><mo stretchy="false">)</mo><mfrac><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>D</mi><mo stretchy="false">)</mo><mo stretchy="false">(</mo><msub><mi>k</mi><mn>1</mn></msub><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>D</mi><mo stretchy="false">)</mo><mo>+</mo><msub><mi>k</mi><mn>1</mn></msub><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>b</mi><mo>+</mo><mi>b</mi><mi mathvariant="normal">∣</mi><mi>D</mi><mi mathvariant="normal">∣</mi><mi mathvariant="normal">/</mi><mi mathvariant="normal">avgdl</mi><mo>⁡</mo><mo stretchy="false">)</mo></mrow></mfrac></mrow></semantics></math>
<p>Here, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mo stretchy="false">(</mo><mi>q</mi><mo separator="true">,</mo><mi>D</mi><mo stretchy="false">)</mo></mrow></semantics></math> is the frequency of term <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi></mrow></semantics></math> in document <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>. The parameters <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>k</mi><mn>1</mn></msub></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow></semantics></math> control term saturation and length normalization. They are tuning parameters, not universal constants.</p>
<p>Lexical retrieval is valuable for error codes, function names, chemical symbols, product identifiers, and quoted policy language. An embedding model can place semantically related text nearby while missing the exact token sequence that establishes relevance.</p>
<p>Normalize carefully. Lowercasing may suit prose but can damage case-sensitive identifiers. Stemming can improve recall for natural language and corrupt source-code terms. Maintain field-specific analyzers instead of applying one text pipeline to every field.</p>
<h2 id="dense-retrieval-supplies-semantic-candidates">Dense retrieval supplies semantic candidates</h2>
<p>A bi-encoder maps the query and passage independently:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>z</mi><mi>q</mi></msub><mo>=</mo><msub><mi>f</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>q</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><msub><mi>z</mi><mi>d</mi></msub><mo>=</mo><msub><mi>g</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>The index retrieves passages with high cosine similarity or inner product. Independent encoding makes corpus vectors reusable, which is why dense retrieval works as a candidate generator.</p>
<p>Dense retrieval inherits the encoder’s training distribution. The BEIR benchmark evaluated lexical, sparse, dense, late-interaction, and reranking systems across 18 datasets; its results found BM25 to be a robust baseline and showed that reranking and late interaction performed best on average in zero-shot settings, with higher computational cost.<sup><a href="https://blog.ecitis.org/retrieval-systems/#user-content-fn-beir" id="user-content-fnref-beir">1</a></sup></p>
<p>That result argues against replacing lexical search by default. It supports evaluating multiple retriever families on the target corpus.</p>
<h2 id="hybrid-fusion-combines-rankers">Hybrid fusion combines rankers</h2>
<p>Lexical and dense scores are usually not calibrated to the same range. Adding raw scores allows whichever system has the wider numeric distribution to dominate.</p>
<p>Reciprocal Rank Fusion combines positions instead:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">RRF</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo><mo>=</mo><munder><mo>∑</mo><mrow><mi>r</mi><mo>∈</mo><mi>R</mi></mrow></munder><mfrac><mn>1</mn><mrow><mi>k</mi><mo>+</mo><msub><mrow><mi mathvariant="normal">rank</mi><mo>⁡</mo></mrow><mi>r</mi></msub><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow></semantics></math>
<p>The original RRF study compared fusion methods and reported that reciprocal rank fusion outperformed the tested Condorcet and individual learning-to-rank approaches.<sup><a href="https://blog.ecitis.org/retrieval-systems/#user-content-fn-rrf" id="user-content-fnref-rrf">2</a></sup> The constant <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> controls how sharply the formula rewards the top positions and should be selected on validation queries.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> Ranked</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> { id</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">; rank</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> rrf</span><span style="color:var(--astro-code-foreground)">(lists</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> Ranked</span><span style="color:var(--astro-code-foreground)">[][]</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> rankConstant</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> Map</span><span style="color:var(--astro-code-foreground)">&lt;</span><span style="color:var(--astro-code-token-constant)">string</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)">&gt; {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  const</span><span style="color:var(--astro-code-token-constant)"> scores</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-keyword)"> new</span><span style="color:var(--astro-code-token-function)"> Map</span><span style="color:var(--astro-code-foreground)">&lt;</span><span style="color:var(--astro-code-token-constant)">string</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)">&gt;()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  for</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> list</span><span style="color:var(--astro-code-token-keyword)"> of</span><span style="color:var(--astro-code-foreground)"> lists) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> hit</span><span style="color:var(--astro-code-token-keyword)"> of</span><span style="color:var(--astro-code-foreground)"> list) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">      scores</span><span style="color:var(--astro-code-token-function)">.set</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-constant)">hit</span><span style="color:var(--astro-code-foreground)">.id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">scores</span><span style="color:var(--astro-code-token-function)">.get</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-constant)">hit</span><span style="color:var(--astro-code-foreground)">.id) </span><span style="color:var(--astro-code-token-keyword)">??</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-keyword)"> /</span><span style="color:var(--astro-code-foreground)"> (rankConstant </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> hit</span><span style="color:var(--astro-code-foreground)">.rank))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  return</span><span style="color:var(--astro-code-foreground)"> scores</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The candidate depth for each ranker is an operational control. A deeper pool raises recall and increases fusion, filtering, and reranking work.</p>
<figure><figcaption><strong>Candidate ranks through a hybrid retrieval pipeline</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/retrieval-systems-0.webp" width="512" height="373" alt="Candidate ranks through a hybrid retrieval pipeline" loading="lazy" decoding="async"></figure>
<h2 id="reranking-spends-compute-on-a-bounded-set">Reranking spends compute on a bounded set</h2>
<p>A cross-encoder reads the query and candidate passage together. Joint attention can inspect exact query-passage interactions that a bi-encoder compresses into separate vectors.</p>
<p>The architecture separates jobs:</p>
<ol>
<li>Lexical and dense retrievers maximize candidate recall.</li>
<li>Fusion creates a bounded candidate pool.</li>
<li>A cross-encoder improves ordering precision.</li>
<li>A context builder selects passages under a token budget.</li>
</ol>
<p>Training data must resemble the candidate distribution at inference. HYRR trains rerankers on candidates from a hybrid lexical and dense retriever and reports robustness across first-stage retrieval systems on supervised and zero-shot passage tasks.<sup><a href="https://blog.ecitis.org/retrieval-systems/#user-content-fn-hyrr" id="user-content-fnref-hyrr">3</a></sup></p>
<p>Reranking every document defeats the purpose. Its latency should scale with the candidate pool, not corpus size. Batch query-passage pairs on accelerators and log queue time separately from model execution.</p>
<h2 id="late-interaction-retains-token-level-matching">Late interaction retains token-level matching</h2>
<p>Late-interaction systems encode query and document tokens independently, then aggregate token-level similarities. They occupy a point between bi-encoders and cross-encoders:</p>
<ul>
<li>More expressive matching than one vector per passage.</li>
<li>Reusable document representations.</li>
<li>Larger indexes and more scoring work than single-vector search.</li>
<li>Less joint computation than a cross-encoder.</li>
</ul>
<p>This architecture is useful when a single passage contains several concepts and relevance depends on multiple local matches. It also complicates index storage and serving. Measure end-to-end latency at realistic corpus size rather than comparing model forward passes alone.</p>
<h2 id="query-transformation-needs-a-grounding-boundary">Query transformation needs a grounding boundary</h2>
<p>Query rewriting can expand acronyms, resolve conversational references, or create alternate lexical forms. It can also remove a rare identifier that carried the user’s actual intent.</p>
<p>Preserve the original query as one branch:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>original query ───────────────› lexical retrieval</span></span>
<span class="line"><span>       ├── normalized query ──› dense retrieval</span></span>
<span class="line"><span>       └── generated variant ─› optional retrieval branch</span></span></code></pre>
<p>HyDE generates a hypothetical document, embeds it, and retrieves nearby real documents. The paper explicitly notes that the generated document is unreal and may contain false details; the corpus search provides the grounding step.<sup><a href="https://blog.ecitis.org/retrieval-systems/#user-content-fn-hyde" id="user-content-fnref-hyde">4</a></sup> A generated query or hypothetical document must never be presented as evidence.</p>
<h2 id="filters-belong-before-context-assembly">Filters belong before context assembly</h2>
<p>Metadata filtering is part of retrieval correctness. Tenant, repository, geography, retention class, and authorization constraints should restrict candidates before content reaches the model.</p>
<p>Do not retrieve globally and remove unauthorized passages after reranking. That design leaks protected content into ranker inputs, traces, caches, and timing behavior.</p>
<p>Freshness is also domain-specific. A recent changelog can outrank an older overview for a current-version query. A historical audit may require the opposite. Encode freshness as an explicit feature controlled by query intent rather than a global recency boost.</p>
<h2 id="evaluate-retrieval-without-generation">Evaluate retrieval without generation</h2>
<p>For a query with a set of relevant passages, common metrics answer different questions:</p>

























<div class="table-wrap"><table><thead><tr><th>Metric</th><th>Question</th></tr></thead><tbody><tr><td>Recall at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math></td><td>Did the candidate set contain relevant evidence?</td></tr><tr><td>Precision at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math></td><td>How much of the returned set was relevant?</td></tr><tr><td>Mean reciprocal rank</td><td>How early was the first relevant result?</td></tr><tr><td>nDCG at <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math></td><td>Did graded relevance appear in a useful order?</td></tr></tbody></table></div>
<p>For binary relevance:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Recall@k</mi><mo>⁡</mo><mo>=</mo><mfrac><mrow><mi mathvariant="normal">∣</mi><mtext>relevant</mtext><mo>∩</mo><mtext>top-k</mtext><mi mathvariant="normal">∣</mi></mrow><mrow><mi mathvariant="normal">∣</mi><mtext>relevant</mtext><mi mathvariant="normal">∣</mi></mrow></mfrac></mrow></semantics></math>
<p>Use passage-level labels for candidate and reranker evaluation, then add answer-level evaluation separately. If answer quality falls while retrieval metrics remain stable, inspect context ordering, citation binding, and generation.</p>
<p>Evaluation queries should be grouped by failure-sensitive slices:</p>
<ul>
<li>Exact identifiers and quoted phrases.</li>
<li>Paraphrases with no shared keywords.</li>
<li>Questions requiring several passages.</li>
<li>Time-sensitive queries.</li>
<li>Negative queries with no supporting evidence.</li>
<li>Permission boundaries.</li>
<li>Documents with repeated boilerplate.</li>
</ul>
<p>Report confidence intervals or paired tests when comparing rankers. A single aggregate can hide a regression on exact-match queries behind a gain on semantic questions.</p>
<h2 id="context-construction-is-a-ranking-stage">Context construction is a ranking stage</h2>
<p>The top-ranked passages are not automatically the best prompt. Near-duplicate chunks waste tokens. Several passages from one document can crowd out independent evidence. The relevant span can be buried inside a long passage.</p>
<p>A context builder should:</p>
<ol>
<li>Remove exact and near duplicates.</li>
<li>Group passages by source document.</li>
<li>Prefer passages that add evidence not already selected.</li>
<li>Preserve source identifiers around every passage.</li>
<li>Stop at a measured token budget.</li>
</ol>
<p>Long context does not remove this requirement. The Lost in the Middle study found that model performance can change with the position of relevant information and often declines when evidence appears in the middle of a long input.<sup><a href="https://blog.ecitis.org/retrieval-systems/#user-content-fn-middle" id="user-content-fnref-middle">5</a></sup></p>
<figure><figcaption><strong>Context assembly spends a finite budget on distinct evidence</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/retrieval-systems-1.webp" width="512" height="286" alt="Context assembly spends a finite budget on distinct evidence" loading="lazy" decoding="async"></figure>
<h2 id="operational-measurements">Operational measurements</h2>
<p>Trace each stage with the query and corpus versions needed for replay:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;query_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;q_7f3&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;index_revision&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;docs-2026-04-26&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;lexical_ms&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 12</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;dense_ms&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 19</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;fusion_candidates&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 80</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;rerank_ms&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 37</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;selected_passages&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 6</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The values above illustrate a trace schema, not a performance target.</p>
<p>Monitor:</p>
<ul>
<li>Empty-result rate and fallback rate.</li>
<li>Candidate recall on labeled traffic samples.</li>
<li>Reranker score drift by document type.</li>
<li>Index freshness lag.</li>
<li>Permission-filter rejection counts.</li>
<li>Duplicate passage share.</li>
<li>Stage latency distributions and queue time.</li>
<li>Citation click-through or evidence inspection where the product exposes it.</li>
</ul>
<h2 id="failure-diagnosis">Failure diagnosis</h2>
<p><strong>Relevant passage absent from all candidates:</strong> inspect parsing, analyzers, embeddings, filters, and candidate depth.</p>
<p><strong>Relevant passage retrieved but ranked low:</strong> inspect fusion, reranker training distribution, and query features.</p>
<p><strong>Relevant passage ranks high but is omitted from the prompt:</strong> inspect deduplication and context-budget logic.</p>
<p><strong>Evidence appears in the prompt but the answer is wrong:</strong> inspect ordering, answer instructions, citation binding, and model capability.</p>
<p><strong>Offline relevance improves while production outcomes fall:</strong> inspect query distribution drift, latency, permissions, and the difference between relevance labels and the user outcome.</p>
<p>A retrieval stack earns complexity only when each added stage moves a defined metric on representative queries. Hybrid retrieval, reranking, and context construction are separable controls. That separation makes the system faster to debug and harder to fool with one attractive aggregate score.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-beir">
<p><a href="https://openreview.net/forum?id=wCu6T5xFjeJ" rel="noopener noreferrer">Thakur et al., “BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models,” NeurIPS Datasets and Benchmarks, 2021</a>. <a href="https://blog.ecitis.org/retrieval-systems/#user-content-fnref-beir" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-rrf">
<p><a href="https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf" rel="noopener noreferrer">Cormack, Clarke, and Buettcher, “Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods,” SIGIR, 2009</a>. <a href="https://blog.ecitis.org/retrieval-systems/#user-content-fnref-rrf" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-hyrr">
<p><a href="https://arxiv.org/abs/2212.10528" rel="noopener noreferrer">Lu et al., “HYRR: Hybrid Infused Reranking for Passage Retrieval,” 2022</a>. <a href="https://blog.ecitis.org/retrieval-systems/#user-content-fnref-hyrr" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-hyde">
<p><a href="https://aclanthology.org/2023.acl-long.99/" rel="noopener noreferrer">Gao et al., “Precise Zero-Shot Dense Retrieval without Relevance Labels,” ACL, 2023</a>. <a href="https://blog.ecitis.org/retrieval-systems/#user-content-fnref-hyde" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-middle">
<p><a href="https://aclanthology.org/2024.tacl-1.9/" rel="noopener noreferrer">Liu et al., “Lost in the Middle: How Language Models Use Long Contexts,” Transactions of the Association for Computational Linguistics, 2024</a>. <a href="https://blog.ecitis.org/retrieval-systems/#user-content-fnref-middle" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Retrieval</category>
            <category>Agents</category>
            <category>Evaluation</category>
            <enclosure length="73956" type="image/png" url="https://blog.ecitis.org/open-graph/retrieval-systems.png"/>
        </item>
        <item>
            <title><![CDATA[Distributed LLM Training: Data, Tensor, Pipeline, and Expert Parallelism]]></title>
            <link>https://blog.ecitis.org/distributed-llm-training/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/distributed-llm-training/</guid>
            <pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Place model state and computation across devices with data, tensor, pipeline, sequence, and expert parallelism.]]></description>
            <content:encoded><![CDATA[<p>Large-model training is a placement problem before it is an optimizer problem. Parameters, optimizer state, gradients, activations, and temporary buffers must fit across devices. The selected placement also determines which bytes cross links on every step.</p>
<figure><figcaption><strong>What each parallelism axis partitions</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/distributed-llm-training-0.webp" width="512" height="428" alt="What each parallelism axis partitions" loading="lazy" decoding="async"></figure>
<h2 id="start-from-the-memory-equation">Start from the memory equation</h2>
<p>For parameter count <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow></semantics></math>, bytes per parameter <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mi>w</mi></msub></mrow></semantics></math>, gradient bytes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mi>g</mi></msub></mrow></semantics></math>, and optimizer-state bytes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>b</mi><mi>o</mi></msub></mrow></semantics></math>, persistent training state is approximately:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>M</mi><mtext>persistent</mtext></msub><mo>=</mo><mi>P</mi><mo stretchy="false">(</mo><msub><mi>b</mi><mi>w</mi></msub><mo>+</mo><msub><mi>b</mi><mi>g</mi></msub><mo>+</mo><msub><mi>b</mi><mi>o</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Activations, communication buffers, allocator fragmentation, and kernel workspaces sit on top. Mixed precision does not imply that every term uses the same dtype. Many training stacks retain higher-precision master weights or optimizer statistics.</p>
<p>Parallelism changes which terms each rank owns. It also introduces communication. A valid strategy must satisfy both device capacity and step-time constraints.</p>
<h2 id="data-parallelism-replicates-compute">Data parallelism replicates compute</h2>
<p>Each data-parallel rank holds the model and processes a different microbatch. Gradients are combined, commonly with all-reduce. With global batch <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math>, data-parallel degree <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>, and local batch <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>B</mi><mi mathvariant="normal">ℓ</mi></msub></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>B</mi><mo>=</mo><mi>D</mi><msub><mi>B</mi><mi mathvariant="normal">ℓ</mi></msub></mrow></semantics></math>
<p>The arithmetic per sample is unchanged. Communication scales with gradient state rather than activation size. This makes data parallelism attractive while a full training replica fits.</p>
<p>Gradient accumulation decouples microbatch size from the optimizer batch. It reduces activation residency per forward pass but does not reduce the parameter or optimizer footprint.</p>
<p>Fully sharded data parallel methods partition parameters, gradients, and optimizer state across the data-parallel group, gathering parameter shards when a layer executes and discarding or resharding them afterward. ZeRO introduced staged partitioning of optimizer state, gradients, and parameters to remove data-parallel memory redundancy.<sup><a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fn-zero" id="user-content-fnref-zero">1</a></sup></p>
<p>Failure modes include:</p>
<ul>
<li>all-gather latency exposed because computation is too small to hide it;</li>
<li>uneven parameter groups causing transient memory peaks;</li>
<li>gradient accumulation configured differently across ranks;</li>
<li>checkpoint code assuming each rank owns a complete state dictionary.</li>
</ul>
<h2 id="tensor-parallelism-partitions-operators">Tensor parallelism partitions operators</h2>
<p>Tensor parallelism splits a matrix multiplication across devices. For a linear layer <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Y</mi><mo>=</mo><mi>X</mi><mi>W</mi></mrow></semantics></math>, column partitioning divides <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi></mrow></semantics></math> by output features:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>W</mi><mo>=</mo><mo stretchy="false">[</mo><msub><mi>W</mi><mn>1</mn></msub><mo separator="true">,</mo><msub><mi>W</mi><mn>2</mn></msub><mo separator="true">,</mo><mo>…</mo><mo separator="true">,</mo><msub><mi>W</mi><mi>T</mi></msub><mo stretchy="false">]</mo></mrow></semantics></math>
<p>Each rank computes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><msub><mi>W</mi><mi>i</mi></msub></mrow></semantics></math>. The result is sharded along its feature dimension. A later row-parallel layer can consume those shards and reduce partial outputs. Megatron-LM arranged column- and row-parallel transformer operators to limit synchronization points while preserving dense matrix multiplications.<sup><a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fn-megatron" id="user-content-fnref-megatron">2</a></sup></p>
<p>Tensor parallelism reduces per-rank parameter and compute load, but introduces collectives inside every transformer layer. It belongs within the fastest network domain available. Crossing a slow inter-node boundary for each layer can dominate step time.</p>
<p>Sequence parallelism complements tensor parallelism by sharding operations whose activations would otherwise remain replicated across the sequence axis. It targets activation memory, not just parameter memory.</p>
<p>Check tensor-parallel configurations for:</p>
<ul>
<li>head counts and hidden widths divisible by the partition degree;</li>
<li>identical collective ordering on every rank;</li>
<li>dropout randomness that follows the intended replicated or sharded semantics;</li>
<li>numerical drift from changing reduction order;</li>
<li>network topology that matches the process group.</li>
</ul>
<h2 id="pipeline-parallelism-partitions-layers">Pipeline parallelism partitions layers</h2>
<p>Pipeline parallelism assigns consecutive layer ranges to stages. A minibatch is divided into microbatches so stages can work concurrently. GPipe formalized this approach with microbatch pipelining and rematerialization for large networks.<sup><a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fn-gpipe" id="user-content-fnref-gpipe">3</a></sup></p>
<p>An all-forward-then-all-backward schedule has simple dependencies but retains activations for many microbatches. A one-forward-one-backward schedule starts backward work earlier and reduces the number of live activations. Interleaved schedules assign multiple model chunks to a physical stage to reduce idle periods, at the cost of more transfers and scheduling complexity.</p>
<p>Pipeline efficiency depends on balance. Equal layer counts do not imply equal stage times because embedding, attention, mixture-of-experts, and output layers have different compute and memory profiles. Measure stage latency and move whole blocks or split points based on traces.</p>
<p>The pipeline boundary transmits activations in the forward direction and their gradients in the reverse direction. A boundary at a wide hidden representation consumes more bandwidth than one after a compact projection.</p>
<p>Operational failures include a last stage slowed by vocabulary projection, microbatch count too small to fill the pipeline, activation checkpointing that recomputes across a communication boundary, and batches with different sequence lengths producing stage imbalance.</p>
<figure><figcaption><strong>Pipeline work moves through stages in dependency order</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/distributed-llm-training-1.webp" width="512" height="279" alt="Pipeline work moves through stages in dependency order" loading="lazy" decoding="async"></figure>
<h2 id="expert-parallelism-partitions-sparse-feed-forward-capacity">Expert parallelism partitions sparse feed-forward capacity</h2>
<p>A mixture-of-experts layer routes each token to selected expert networks. Expert parallelism places different experts on different ranks. Tokens are exchanged so each expert receives its assigned inputs, then returned to their original sequence positions.</p>
<p>Switch Transformers demonstrated sparse expert routing in which only selected parameters execute for a token.<sup><a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fn-switch" id="user-content-fnref-switch">4</a></sup> The memory benefit is conditional capacity: total parameters can grow without executing every expert for every token. The systems cost is token dispatch, usually implemented with all-to-all communication.</p>
<p>Load balance is not guaranteed. A router can send too many tokens to a subset of experts. Capacity limits, auxiliary balancing losses, token dropping policies, and expert replication change quality and throughput. Each must be logged, not hidden behind an average utilization number.</p>
<p>Measure per expert:</p>
<ul>
<li>assigned tokens before capacity enforcement;</li>
<li>accepted and dropped tokens;</li>
<li>dispatch bytes and collective duration;</li>
<li>compute duration;</li>
<li>routing probability distribution.</li>
</ul>
<h2 id="compose-parallel-dimensions-around-topology">Compose parallel dimensions around topology</h2>
<p>Production training combines dimensions. Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>T</mi></mrow></semantics></math>, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi></mrow></semantics></math> denote data, tensor, pipeline, and expert parallel degrees. The world size is often factored as:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>N</mi><mo>=</mo><mi>D</mi><mo>×</mo><mi>T</mi><mo>×</mo><mi>L</mi><mo>×</mo><mi>E</mi></mrow></semantics></math>
<p>This equation is a process-grid description, not proof that a configuration is efficient. Some implementations share or nest groups differently.</p>
<p>Map frequent, latency-sensitive collectives to fast links. Tensor-parallel collectives occur inside layers, so they usually remain within a tightly connected node. Data-parallel reductions occur on gradients and can span nodes with bucketed overlap. Pipeline traffic is point-to-point between adjacent stages. Expert dispatch is all-to-all and needs special attention to oversubscribed network fabrics.</p>
<p>One reasonable placement order is:</p>
<ol>
<li>Choose tensor parallelism so a layer and its working set fit inside a fast-link group.</li>
<li>Add pipeline stages until the model state fits across groups.</li>
<li>Place experts within network domains that can sustain dispatch.</li>
<li>Use remaining devices for data-parallel replicas.</li>
</ol>
<p>The order is a design procedure, not a universal optimum. Profile the actual architecture and cluster.</p>
<h2 id="checkpointing-must-encode-the-process-grid">Checkpointing must encode the process grid</h2>
<p>A distributed checkpoint is not merely a set of files. It records how tensors are partitioned. Changing the tensor-parallel degree requires resharding matrix dimensions. Changing pipeline degree moves layer ownership. Expert changes alter expert identifiers and routing state. Optimizer state must follow the same transformations as its parameter.</p>
<p>Store:</p>
<ul>
<li>global tensor names and shapes;</li>
<li>shard offsets and partition axes;</li>
<li>dtype and optimizer-state relation;</li>
<li>parallel degrees and rank topology;</li>
<li>random-number-generator state;</li>
<li>data-loader position and sample identity;</li>
<li>software and model configuration.</li>
</ul>
<p>Test restore under the same topology before testing topology conversion. Compare loss on a fixed batch before and after restoration.</p>
<h2 id="a-configuration-example">A configuration example</h2>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">parallelism</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  data</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${DATA_PARALLEL_DEGREE}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  tensor</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${TENSOR_PARALLEL_DEGREE}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  pipeline</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${PIPELINE_PARALLEL_DEGREE}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  expert</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${EXPERT_PARALLEL_DEGREE}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">schedule</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  microbatches</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ${MICROBATCH_COUNT}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  activation_checkpointing</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> selective</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">checkpoint</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  format</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> distributed</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  save_rng_state</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  save_data_position</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span></code></pre>
<p>Configuration validation should reject incompatible hidden widths, attention heads, expert counts, and world sizes before processes initialize.</p>
<h2 id="measure-exposed-communication">Measure exposed communication</h2>
<p>Collective duration alone does not show the performance cost. Communication overlapped with useful compute is cheaper than an equally long collective on the critical path. Use a timeline and calculate exposed communication:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>T</mi><mtext>exposed</mtext></msub><mo>=</mo><msub><mi>T</mi><mtext>step</mtext></msub><mo>−</mo><msub><mi>T</mi><mtext>compute-only critical path</mtext></msub></mrow></semantics></math>
<p>Also report tokens processed per unit time, model FLOP utilization under a documented accounting convention, peak allocated memory per rank, and straggler spread. An aggregate average can conceal one stage that limits the complete pipeline.</p>
<p>Distributed training succeeds when partitioning, scheduling, and topology agree. Data parallelism distributes samples, tensor parallelism distributes operators, pipeline parallelism distributes layers, and expert parallelism distributes conditional modules. Their communication patterns are different enough that treating them as interchangeable memory switches leads to slow or unstable runs.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-zero">
<p><a href="https://arxiv.org/abs/1910.02054" rel="noopener noreferrer">ZeRO: Memory Optimizations Toward Training Trillion Parameter Models</a>. <a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fnref-zero" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-megatron">
<p><a href="https://arxiv.org/abs/1909.08053" rel="noopener noreferrer">Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism</a>. <a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fnref-megatron" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-gpipe">
<p><a href="https://arxiv.org/abs/1811.06965" rel="noopener noreferrer">GPipe: Efficient Training of Giant Neural Networks Using Pipeline Parallelism</a>. <a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fnref-gpipe" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-switch">
<p><a href="https://arxiv.org/abs/2101.03961" rel="noopener noreferrer">Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity</a>. <a href="https://blog.ecitis.org/distributed-llm-training/#user-content-fnref-switch" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Distributed training</category>
            <category>GPUs</category>
            <category>Training</category>
            <enclosure length="66350" type="image/png" url="https://blog.ecitis.org/open-graph/distributed-llm-training.png"/>
        </item>
        <item>
            <title><![CDATA[Modern Attention Kernels: Tiling, FlashAttention, and Paged Attention]]></title>
            <link>https://blog.ecitis.org/modern-attention-kernels/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/modern-attention-kernels/</guid>
            <pubDate>Sat, 04 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Follow how tiling, online softmax, and paged memory reduce data movement in modern training and inference kernels.]]></description>
            <content:encoded><![CDATA[<p>Attention is often introduced as three matrix operations: form <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Q</mi><msup><mi>K</mi><mi>T</mi></msup></mrow></semantics></math>, apply softmax, then multiply by <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math>. That description omits the storage hierarchy. A conventional implementation writes the score matrix to high-bandwidth memory, reads it for softmax, writes probabilities, and reads them again for the value product.</p>
<p>FlashAttention computes exact attention while avoiding materialization of the full score matrix. Its central optimization is data movement: tile operands into on-chip memory, update softmax statistics online, and write only the final output.<sup><a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<h2 id="standard-attention-materializes-quadratic-state">Standard attention materializes quadratic state</h2>
<p>For query and key sequence lengths <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>N</mi><mi>q</mi></msub></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>N</mi><mi>k</mi></msub></mrow></semantics></math>, the score matrix has <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>N</mi><mi>q</mi></msub><msub><mi>N</mi><mi>k</mi></msub></mrow></semantics></math> entries:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>S</mi><mo>=</mo><mfrac><mrow><mi>Q</mi><msup><mi>K</mi><mi>T</mi></msup></mrow><msqrt><mi>d</mi></msqrt></mfrac><mo separator="true">,</mo><mspace width="2em"></mspace><mi>P</mi><mo>=</mo><mi mathvariant="normal">softmax</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>S</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mspace width="2em"></mspace><mi>O</mi><mo>=</mo><mi>P</mi><mi>V</mi></mrow></semantics></math>
<p>Even when arithmetic fits the accelerator, storing and rereading <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math> and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi></mrow></semantics></math> creates off-chip traffic proportional to their size. The original FlashAttention paper frames the algorithm as IO-aware and analyzes reads and writes across GPU memory levels.<sup><a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup></p>
<figure><figcaption><strong>Attention data movement with and without score materialization</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/modern-attention-kernels-0.webp" width="512" height="390" alt="Attention data movement with and without score materialization" loading="lazy" decoding="async"></figure>
<h2 id="online-softmax-makes-tiling-exact">Online softmax makes tiling exact</h2>
<p>Softmax appears to require an entire row because its denominator includes every key:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">softmax</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>s</mi><mi>i</mi></msub><msub><mo stretchy="false">)</mo><mi>j</mi></msub><mo>=</mo><mfrac><msup><mi>e</mi><msub><mi>s</mi><mrow><mi>i</mi><mi>j</mi></mrow></msub></msup><mrow><munder><mo>∑</mo><mi>k</mi></munder><msup><mi>e</mi><msub><mi>s</mi><mrow><mi>i</mi><mi>k</mi></mrow></msub></msup></mrow></mfrac></mrow></semantics></math>
<p>The computation can be updated block by block. For a row, keep running maximum <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>m</mi></mrow></semantics></math>, running normalization sum <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">ℓ</mi></mrow></semantics></math>, and an unnormalized output accumulator <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi></mrow></semantics></math>. Given a new score tile with maximum <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>m</mi><mi>b</mi></msub></mrow></semantics></math>:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>m</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><mi>max</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>m</mi><mo separator="true">,</mo><msub><mi>m</mi><mi>b</mi></msub><mo stretchy="false">)</mo></mrow></semantics></math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi mathvariant="normal">ℓ</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><msup><mi>e</mi><mrow><mi>m</mi><mo>−</mo><msup><mi>m</mi><mo lspace="0em" rspace="0em">′</mo></msup></mrow></msup><mi mathvariant="normal">ℓ</mi><mo>+</mo><munder><mo>∑</mo><mi>j</mi></munder><msup><mi>e</mi><mrow><msub><mi>s</mi><mi>j</mi></msub><mo>−</mo><msup><mi>m</mi><mo lspace="0em" rspace="0em">′</mo></msup></mrow></msup></mrow></semantics></math>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>o</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo>=</mo><msup><mi>e</mi><mrow><mi>m</mi><mo>−</mo><msup><mi>m</mi><mo lspace="0em" rspace="0em">′</mo></msup></mrow></msup><mi>o</mi><mo>+</mo><munder><mo>∑</mo><mi>j</mi></munder><msup><mi>e</mi><mrow><msub><mi>s</mi><mi>j</mi></msub><mo>−</mo><msup><mi>m</mi><mo lspace="0em" rspace="0em">′</mo></msup></mrow></msup><msub><mi>v</mi><mi>j</mi></msub></mrow></semantics></math>
<p>After the final key tile, output <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>o</mi><mi mathvariant="normal">/</mi><mi mathvariant="normal">ℓ</mi></mrow></semantics></math>. Rescaling by the new maximum preserves numerical stability. No approximation is introduced by the tiling rule.</p>
<h2 id="kernel-structure">Kernel structure</h2>
<p>A simplified forward kernel loops over query tiles and key-value tiles:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>for each Q tile:</span></span>
<span class="line"><span>    load Q into on-chip memory</span></span>
<span class="line"><span>    initialize row maxima, sums, output accumulators</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    for each K,V tile allowed by the mask:</span></span>
<span class="line"><span>        load K and V</span></span>
<span class="line"><span>        compute score tile</span></span>
<span class="line"><span>        apply scale and mask</span></span>
<span class="line"><span>        update online softmax state</span></span>
<span class="line"><span>        accumulate weighted V</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    normalize and write output</span></span></code></pre>
<p>Tile shapes depend on head dimension, element type, register pressure, on-chip memory, and accelerator generation. Larger tiles increase reuse but can reduce occupancy when each program consumes too many registers.</p>
<p>Causal attention skips tiles wholly above the diagonal and masks the intersecting boundary tile. Local attention changes the visited tile range. Arbitrary sparse masks need an index structure that tells the kernel which blocks exist.</p>
<h2 id="backward-pass-recomputes-scores">Backward pass recomputes scores</h2>
<p>Saving the full probability matrix would reintroduce quadratic storage. FlashAttention saves compact normalization statistics and recomputes score tiles during backpropagation.<sup><a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fn-1" id="user-content-fnref-1-3">1</a></sup> It spends arithmetic to avoid memory traffic and activation storage.</p>
<p>This trade is useful only when recomputation is cheaper than writing and reading the intermediate. Profiling must include the whole layer because fused dropout, bias, and masking can change the balance.</p>
<h2 id="work-partitioning-in-flashattention-2">Work partitioning in FlashAttention-2</h2>
<p>FlashAttention-2 changes work partitioning to improve parallelism and reduce non-matrix-multiplication work.<sup><a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> It parallelizes across sequence length when batch size and head count do not expose enough thread blocks, and adjusts how work is shared within a thread block.<sup><a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup></p>
<p>The lesson is broader than a version comparison. An asymptotically efficient algorithm can leave hardware idle if its grid has too few independent programs. Kernel selection needs batch, head count, sequence length, and head dimension.</p>
<h2 id="prefill-and-decode-need-different-kernels">Prefill and decode need different kernels</h2>
<p>Prefill has many query positions and many key positions. Tiled matrix multiplication works well. Decode often has one query position per sequence and a long cache. It cannot reuse query work across a large query tile.</p>
<p>Decode attention therefore focuses on:</p>
<ul>
<li>coalesced cache reads</li>
<li>parallel reduction across the context</li>
<li>grouped-query head sharing</li>
<li>cache block indirection</li>
<li>combining many sequences in a batch</li>
</ul>
<p>Calling both implementations FlashAttention hides this shape difference. Record kernel choice separately for prefill and decode.</p>
<h2 id="paged-attention-changes-addressing">Paged attention changes addressing</h2>
<p>PagedAttention stores cache blocks in non-contiguous physical memory and resolves them through a per-sequence block table. The vLLM paper reports near-zero cache waste and flexible cache sharing from this design.<sup><a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>A paged decode kernel maps logical token position to:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>logical block = position / block_size</span></span>
<span class="line"><span>offset        = position % block_size</span></span>
<span class="line"><span>physical      = block_table[sequence, logical_block]</span></span>
<span class="line"><span>address       = cache_base + physical * block_stride + offset * token_stride</span></span></code></pre>
<p>The extra loads and address arithmetic buy dynamic allocation. Performance depends on block-table locality, block layout, and vectorized loads from physical pages.</p>
<p>Paged attention and FlashAttention solve related but distinct problems:</p>






























<div class="table-wrap"><table><thead><tr><th>Technique</th><th>Primary target</th><th>Key mechanism</th></tr></thead><tbody><tr><td>FlashAttention</td><td>Score-matrix IO</td><td>Tiling and online softmax</td></tr><tr><td>PagedAttention</td><td>Dynamic KV allocation</td><td>Logical-to-physical block mapping</td></tr><tr><td>Grouped-query attention</td><td>KV cache and projection size</td><td>Share KV heads across query groups</td></tr><tr><td>Sparse attention</td><td>Number of attended pairs</td><td>Visit selected score blocks</td></tr></tbody></table></div>
<p>A serving engine can combine them: tiled prefill, paged decode, grouped-query cache layout, and sparse visitation for long contexts.</p>
<h2 id="numerical-validation">Numerical validation</h2>
<p>Fused kernels reorder floating-point operations. Validate against a reference implementation across:</p>
<ul>
<li>causal and non-causal masks</li>
<li>ragged sequence boundaries</li>
<li>head dimensions supported by each path</li>
<li>mixed precision and accumulation precision</li>
<li>long rows with large logit ranges</li>
<li>grouped-query head mapping</li>
<li>paged blocks that cross sequence tails</li>
</ul>
<p>Use absolute and relative error tolerances tied to output precision. Also compare gradients for training kernels and sampled outputs for inference pipelines.</p>
<h2 id="failure-modes">Failure modes</h2>
<p><strong>Register spilling:</strong> a larger tile silently moves local values to off-chip memory. Inspect compiler resource reports and achieved occupancy.</p>
<p><strong>Mask boundary errors:</strong> the fast path reads future tokens or padding on partial tiles. Test every boundary around tile and page sizes.</p>
<p><strong>Incorrect online rescaling:</strong> output accumulators are not rescaled when a later tile raises the row maximum. Compare adversarial logits where the largest score appears late.</p>
<p><strong>Page-table mismatch:</strong> allocator and kernel disagree about block stride or layout version. Encode layout identity in engine metadata.</p>
<p><strong>Wrong kernel for decode:</strong> a prefill-optimized kernel launches too little useful work for single-query attention. Profile by phase and shape.</p>
<h2 id="benchmarking-method">Benchmarking method</h2>
<p>Measure wall time, achieved bandwidth, arithmetic utilization, kernel launches, and temporary bytes. Sweep the real shape space rather than one square sequence:</p>
<ul>
<li>batch size</li>
<li>query length</li>
<li>key length</li>
<li>head count and KV-head count</li>
<li>head dimension</li>
<li>causal, local, and arbitrary masks</li>
<li>cache block size</li>
<li>element and accumulation type</li>
</ul>
<p>Warm kernels, pin clocks where the environment allows it, and report medians plus tail behavior. End-to-end token latency remains the deciding metric because a fast attention kernel can expose another bottleneck in projections, collectives, sampling, or cache allocation.</p>
<p>Modern attention kernels are memory-system programs expressed through tensor algebra. Their speed comes from deciding what never reaches off-chip memory, what is recomputed, and how logical sequence state maps onto physical storage.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Tri Dao et al., “FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness,” NeurIPS, 2022. <a href="https://arxiv.org/abs/2205.14135" rel="noopener noreferrer">https://arxiv.org/abs/2205.14135</a> <a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fnref-1-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>Tri Dao, “FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning,” ICLR, 2024. <a href="https://arxiv.org/abs/2307.08691" rel="noopener noreferrer">https://arxiv.org/abs/2307.08691</a> <a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Woosuk Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention,” SOSP, 2023. <a href="https://arxiv.org/abs/2309.06180" rel="noopener noreferrer">https://arxiv.org/abs/2309.06180</a> <a href="https://blog.ecitis.org/modern-attention-kernels/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Attention</category>
            <category>Kernels</category>
            <category>Memory</category>
            <enclosure length="61582" type="image/png" url="https://blog.ecitis.org/open-graph/modern-attention-kernels.png"/>
        </item>
        <item>
            <title><![CDATA[Speculative Decoding: Draft, Verify, and Accept]]></title>
            <link>https://blog.ecitis.org/speculative-decoding/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/speculative-decoding/</guid>
            <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Accelerate autoregressive generation with draft proposals and lossless target-model verification while preserving the output distribution.]]></description>
            <content:encoded><![CDATA[<p>Autoregressive decoding normally invokes the target model once for each output token. Speculative decoding asks a cheaper draft process to propose several tokens, then evaluates those positions together with the target model.</p>
<p>The target model remains authoritative. A correct acceptance rule preserves its output distribution; speculation changes the execution schedule, not the distribution being sampled. Independent work by Leviathan, Kalman, and Matias and by Chen and collaborators established this lossless sampling construction.<sup><a href="https://blog.ecitis.org/speculative-decoding/#user-content-fn-1" id="user-content-fnref-1">1</a></sup><sup><a href="https://blog.ecitis.org/speculative-decoding/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<h2 id="draft-and-target-distributions">Draft and target distributions</h2>
<p>Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>q</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math> be the draft probability for a proposed token and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math> the target probability at the same position. The verifier accepts draft token <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow></semantics></math> with:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>a</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>min</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mn>1</mn><mo separator="true">,</mo><mfrac><mrow><mi>p</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><mi>q</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo fence="true">)</mo></mrow></mrow></semantics></math>
<p>If the token is rejected, the correction distribution is proportional to:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>p</mi><mo lspace="0em" rspace="0em">′</mo></msup><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mi>max</mi><mo>⁡</mo><mo stretchy="false">(</mo><mn>0</mn><mo separator="true">,</mo><mi>p</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mi>q</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow></semantics></math>
<p>Normalizing and sampling from this residual corrects the draft’s over-allocation of probability. If every proposal is accepted, the verifier can also sample an additional target token from the final verified position.<sup><a href="https://blog.ecitis.org/speculative-decoding/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup><sup><a href="https://blog.ecitis.org/speculative-decoding/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup></p>
<p>This is not the same as accepting whenever the target model’s argmax matches the draft. Greedy matching can preserve greedy output, but it does not implement sampling from the target distribution.</p>
<figure><figcaption><strong>Draft tokens are verified in one target-model pass</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/speculative-decoding-0.webp" width="512" height="297" alt="Draft tokens are verified in one target-model pass" loading="lazy" decoding="async"></figure>
<h2 id="verification-uses-parallel-positions">Verification uses parallel positions</h2>
<p>Suppose the draft model proposes <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math> tokens. The target receives the original prefix plus those candidates and returns logits for each proposed position in one forward pass. Causal masking prevents a position from reading later candidates, so each target distribution matches the distribution that ordinary autoregressive decoding would compute at that prefix.</p>
<p>The execution loop is:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">while</span><span style="color:var(--astro-code-token-keyword)"> not</span><span style="color:var(--astro-code-foreground)"> finished</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    proposals</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> q_probs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> draft</span><span style="color:var(--astro-code-token-punctuation)">(prefix, draft_length)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    p_probs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> target</span><span style="color:var(--astro-code-token-punctuation)">(prefix </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> proposals)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    accepted </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> token</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> q</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> p </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> zip</span><span style="color:var(--astro-code-token-punctuation)">(proposals, q_probs, p_probs):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-token-function)"> uniform</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> &lt;=</span><span style="color:var(--astro-code-token-function)"> min</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">, p[token] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> q[token]):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            accepted</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(token)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        else</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            accepted</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">sample</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">normalize</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">maximum</span><span style="color:var(--astro-code-token-punctuation)">(p </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-punctuation)"> q, </span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">))))</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            break</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(accepted)</span><span style="color:var(--astro-code-token-keyword)"> ==</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(proposals):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        accepted</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">sample</span><span style="color:var(--astro-code-token-punctuation)">(p_probs.after_last_candidate))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    prefix</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">extend</span><span style="color:var(--astro-code-token-punctuation)">(accepted)</span></span></code></pre>
<p>Real implementations must use the exact distributions after temperature, top-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>k</mi></mrow></semantics></math>, top-<math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow></semantics></math>, penalties, and any token masks. Applying a transform to only one model invalidates the acceptance ratio.</p>
<h2 id="speed-depends-on-accepted-tokens-per-target-pass">Speed depends on accepted tokens per target pass</h2>
<p>Speculation trades draft work and a wider target pass for fewer sequential target invocations. The useful quantity is not draft accuracy alone. Measure accepted output tokens per verification pass.</p>
<p>Let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>T</mi></msub><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></semantics></math> be target verification cost for a candidate span, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>C</mi><mi>D</mi></msub><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow></semantics></math> draft cost, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>E</mi><mo stretchy="false">[</mo><mi>A</mi><mo stretchy="false">]</mo></mrow></semantics></math> expected committed tokens. A simple average cost is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>cost per committed token</mtext><mo>=</mo><mfrac><mrow><msub><mi>C</mi><mi>D</mi></msub><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo><mo>+</mo><msub><mi>C</mi><mi>T</mi></msub><mo stretchy="false">(</mo><mi>k</mi><mo stretchy="false">)</mo></mrow><mrow><mi>E</mi><mo stretchy="false">[</mo><mi>A</mi><mo stretchy="false">]</mo></mrow></mfrac></mrow></semantics></math>
<p>The best draft length depends on batch size, prompt length, target architecture, draft architecture, and workload. Longer spans expose more parallel target work but risk wasting later candidates after an early rejection.</p>
<p>An adaptive controller can choose draft length from recent acceptance:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> nextDraftLength</span><span style="color:var(--astro-code-foreground)">(emaAcceptance</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> current</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)">) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (emaAcceptance </span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-foreground)"> highThreshold) </span><span style="color:var(--astro-code-token-keyword)">return</span><span style="color:var(--astro-code-token-constant)"> Math</span><span style="color:var(--astro-code-token-function)">.min</span><span style="color:var(--astro-code-foreground)">(current </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> step</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> maximum)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (emaAcceptance </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> lowThreshold) </span><span style="color:var(--astro-code-token-keyword)">return</span><span style="color:var(--astro-code-token-constant)"> Math</span><span style="color:var(--astro-code-token-function)">.max</span><span style="color:var(--astro-code-foreground)">(current </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> step</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> minimum)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  return</span><span style="color:var(--astro-code-foreground)"> current</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Thresholds must come from serving measurements. Hard-coded values copied from another model pair do not transfer reliably.</p>
<h2 id="draft-model-selection">Draft model selection</h2>
<p>A draft model must be cheaper and sufficiently aligned with the target. It also needs compatible tokenization. Different vocabularies require a mapping scheme and make probability correction more complex.</p>
<p>Evaluate candidates on:</p>
<ul>
<li>target-pass reduction</li>
<li>draft latency under production batch sizes</li>
<li>acceptance by prompt class</li>
<li>device memory consumed by draft weights and cache</li>
<li>scheduler interference with target execution</li>
<li>tokenizer and logits-processor compatibility</li>
</ul>
<p>A small model on the same accelerator can compete with the target for memory bandwidth. A draft on another device adds communication and synchronization. The fastest isolated draft is not necessarily the best colocated draft.</p>
<h2 id="cache-management">Cache management</h2>
<p>Both models need state for the accepted prefix. Draft tokens beyond the rejection point must be rolled back. The target cache can retain verified accepted positions, while rejected suffix blocks become free.</p>
<p>Paged caches simplify rollback when each speculative span receives provisional blocks. Commit accepted positions, truncate the final partial block, and release later blocks. Avoid copying the entire cache for each attempt.</p>
<p>Continuous batching adds divergence. Requests accept different counts, so their logical sequence lengths change by different amounts after one verification iteration. The scheduler must update block tables and sampling state per request before forming the next batch.</p>
<h2 id="sampling-details-that-break-correctness">Sampling details that break correctness</h2>
<p><strong>Mismatched temperature:</strong> draft and target probabilities refer to different transformed distributions.</p>
<p><strong>Penalties applied at different prefixes:</strong> repetition or presence penalties see speculative tokens on one side but not the other.</p>
<p><strong>Grammar masks applied only to the target:</strong> the draft assigns mass to illegal tokens, reducing acceptance and complicating residual sampling.</p>
<p><strong>Rounded probabilities:</strong> low-precision acceptance ratios introduce bias. Keep probability arithmetic at a precision validated against a non-speculative sampler.</p>
<p><strong>Random-number reuse:</strong> batched rollback changes random-number consumption. Counter-based generators indexed by request and logical sample step make replay easier.</p>
<h2 id="tree-and-head-based-variants">Tree and head-based variants</h2>
<p>The draft need not be a separate autoregressive model. Multiple heads can propose future tokens from the target’s hidden state. Medusa adds decoding heads and verifies a tree of candidates, avoiding a separate draft model.<sup><a href="https://blog.ecitis.org/speculative-decoding/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> Tree verification trades more candidate positions for a greater chance that one path matches the target.</p>
<p>The verifier must construct a causal mask that represents the tree. Tokens on one branch cannot attend to siblings. Cache positions also need branch-specific ownership until a path is accepted.</p>
<h2 id="production-evaluation">Production evaluation</h2>
<p>Report latency distributions and acceptance together:</p>

































<div class="table-wrap"><table><thead><tr><th>Metric</th><th>Diagnostic value</th></tr></thead><tbody><tr><td>Accepted tokens per target pass</td><td>Direct measure of sequential work removed</td></tr><tr><td>Rejection position histogram</td><td>Detects spans that are too long</td></tr><tr><td>Draft time per proposed token</td><td>Captures draft bottlenecks</td></tr><tr><td>Verification time by candidate count</td><td>Reveals target-kernel scaling</td></tr><tr><td>Rollback bytes and blocks</td><td>Finds cache churn</td></tr><tr><td>Acceptance by request class</td><td>Supports adaptive routing</td></tr></tbody></table></div>
<p>Compare against the same target model, sampling configuration, and scheduler without speculation. Verify distributional equivalence with seeded tests over many prompts, then run task-level quality evaluation to catch integration errors.</p>
<p>Speculative decoding wins when cheap proposals align with the target and the hardware can verify several positions more efficiently than it can execute the same number of sequential decode steps. Its acceptance mathematics is exact; its systems benefit is empirical.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Yaniv Leviathan, Matan Kalman, and Yossi Matias, “Fast Inference from Transformers via Speculative Decoding,” ICML, 2023. <a href="https://arxiv.org/abs/2211.17192" rel="noopener noreferrer">https://arxiv.org/abs/2211.17192</a> <a href="https://blog.ecitis.org/speculative-decoding/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/speculative-decoding/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>Charlie Chen et al., “Accelerating Large Language Model Decoding with Speculative Sampling,” arXiv, 2023. <a href="https://arxiv.org/abs/2302.01318" rel="noopener noreferrer">https://arxiv.org/abs/2302.01318</a> <a href="https://blog.ecitis.org/speculative-decoding/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/speculative-decoding/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Tianle Cai et al., “Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads,” ICML, 2024. <a href="https://arxiv.org/abs/2401.10774" rel="noopener noreferrer">https://arxiv.org/abs/2401.10774</a> <a href="https://blog.ecitis.org/speculative-decoding/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Decoding</category>
            <category>Latency</category>
            <category>LLM serving</category>
            <enclosure length="63567" type="image/png" url="https://blog.ecitis.org/open-graph/speculative-decoding.png"/>
        </item>
        <item>
            <title><![CDATA[Model Context Protocol from First Principles: Messages, Capabilities, and Authorization]]></title>
            <link>https://blog.ecitis.org/mcp-first-principles/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/mcp-first-principles/</guid>
            <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Understand MCP as a stateful boundary between hosts and capability servers, including negotiation, transport, and authorization.]]></description>
            <content:encoded><![CDATA[<p>Model Context Protocol is a stateful protocol for connecting an AI host to servers that expose context and actions. Its useful abstraction is not a universal tool API. It is a boundary: the host retains the conversation and policy decisions, while each client maintains an isolated connection to one server.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-architecture" id="user-content-fnref-architecture">1</a></sup></p>
<p>That distinction determines where permissions, user consent, context selection, and model calls belong. A server describes what it offers. The host decides what enters the model context and which action requests are allowed to execute.</p>
<h2 id="host-client-and-server-responsibilities">Host, client, and server responsibilities</h2>
<p>An MCP host creates clients, coordinates model integration, controls connection permissions, and aggregates context. Each client owns a stateful session with one server. A server exposes focused capabilities without receiving the host’s complete conversation or the state of other servers.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-architecture" id="user-content-fnref-architecture-2">1</a></sup></p>

























<div class="table-wrap"><table><thead><tr><th>Process</th><th>Owns</th><th>Must not assume</th></tr></thead><tbody><tr><td>Host</td><td>Conversation, user consent, policy, model orchestration</td><td>That a server is safe because it speaks MCP</td></tr><tr><td>Client</td><td>Protocol session, message correlation, capability state</td><td>That an undeclared method is supported</td></tr><tr><td>Server</td><td>Tools, resources, prompts, server-side access checks</td><td>That a tool call proves user intent</td></tr></tbody></table></div>
<p>This split prevents a calendar server from silently reading repository context supplied by a source-control server. Cross-server data movement is a host decision, not a protocol side effect.</p>
<h2 id="json-rpc-message-forms">JSON-RPC message forms</h2>
<p>MCP messages use JSON-RPC. A request carries an <code>id</code> and expects either a result or an error. A notification has no <code>id</code> and receives no response. A response must contain a result or an error, never both.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-base" id="user-content-fnref-base">2</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;jsonrpc&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;2.0&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;req-42&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;method&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;tools/call&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;params&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;read_invoice&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;arguments&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;invoice_id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;INV-1042&quot;</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The <code>id</code> is a correlation key. It is not an authorization token, idempotency key, or tracing policy. A host that retries a side-effecting request must define idempotency above the protocol or include an application-level key in the tool input.</p>
<p>MCP’s current base specification does not support JSON-RPC batching. Each request, notification, and response travels as its own protocol message.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-changelog" id="user-content-fnref-changelog">3</a></sup></p>
<figure><figcaption><strong>MCP session lifecycle and correlated JSON-RPC messages</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/mcp-first-principles-0.webp" width="512" height="679" alt="MCP session lifecycle and correlated JSON-RPC messages" loading="lazy" decoding="async"></figure>
<h2 id="initialization-and-capability-negotiation">Initialization and capability negotiation</h2>
<p>Initialization must be the first client-server interaction. The client proposes a protocol version and declares client capabilities. The server returns its selected version and server capabilities. The client then sends <code>notifications/initialized</code> before normal operation.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-lifecycle" id="user-content-fnref-lifecycle">4</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;jsonrpc&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;2.0&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;id&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;method&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;initialize&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;params&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;protocolVersion&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;2025-06-18&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;capabilities&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;roots&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;listChanged&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> true</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;sampling&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {}</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;elicitation&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;clientInfo&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;research-workbench&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;version&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;1.0.0&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The version and example fields above follow the published lifecycle example.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-lifecycle" id="user-content-fnref-lifecycle-2">4</a></sup> If the server selects a version the client does not support, the client should disconnect. For HTTP transport, later requests carry the negotiated version in the <code>MCP-Protocol-Version</code> header.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-transports" id="user-content-fnref-transports">5</a></sup></p>
<p>Capabilities are executable protocol state, not descriptive metadata. A server may call client sampling only when the client advertised <code>sampling</code>. A server may emit resource update notifications only when it advertised the relevant resource subscription support. Method dispatch should check negotiated state before parsing business arguments.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> Session</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  phase</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;new&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;operating&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;closed&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  protocolVersion</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  serverCapabilities</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tools</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-foreground)"> { listChanged</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-token-constant)"> boolean</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    resources</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-foreground)"> { subscribe</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-token-constant)"> boolean</span><span style="color:var(--astro-code-foreground)">; listChanged</span><span style="color:var(--astro-code-token-keyword)">?:</span><span style="color:var(--astro-code-token-constant)"> boolean</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> assertToolCallAllowed</span><span style="color:var(--astro-code-foreground)">(session</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> Session</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> void</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">session</span><span style="color:var(--astro-code-foreground)">.phase </span><span style="color:var(--astro-code-token-keyword)">!==</span><span style="color:var(--astro-code-token-string-expression)"> &#39;operating&#39;</span><span style="color:var(--astro-code-token-keyword)"> ||</span><span style="color:var(--astro-code-token-keyword)"> !</span><span style="color:var(--astro-code-token-constant)">session</span><span style="color:var(--astro-code-foreground)">.</span><span style="color:var(--astro-code-token-constant)">serverCapabilities</span><span style="color:var(--astro-code-foreground)">?.tools) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    throw</span><span style="color:var(--astro-code-token-keyword)"> new</span><span style="color:var(--astro-code-token-function)"> Error</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;tools/call was not negotiated&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<h2 id="tools-resources-and-prompts">Tools, resources, and prompts</h2>
<p>The server primitives have different control semantics. Prompts are user-controlled templates, resources are application-controlled context, and tools are model-controlled actions.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-server" id="user-content-fnref-server">6</a></sup></p>
<h3 id="resources-carry-addressable-context">Resources carry addressable context</h3>
<p>A resource has a URI and content. It suits files, schemas, records, logs, or generated views that a client may inspect and selectively place in context. Servers must validate resource URIs and should enforce access controls on sensitive resources.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-resources" id="user-content-fnref-resources">7</a></sup></p>
<p>Resources are not passive just because they are read-only. Retrieved text can contain instructions aimed at the model. The host should attach provenance and treat resource content as untrusted data.</p>
<h3 id="prompts-expose-reusable-workflows">Prompts expose reusable workflows</h3>
<p>Prompts package messages and arguments for explicit user selection. They work well for named tasks such as code review or incident summarization. A prompt does not override host policy. It supplies content for the host to inspect and render.</p>
<h3 id="tools-expose-executable-behavior">Tools expose executable behavior</h3>
<p>A tool definition combines a stable name, description, and input schema. Tool results can return text, images, embedded resources, resource links, and structured content. The protocol revision published on 18 June 2025 added structured tool output.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-changelog" id="user-content-fnref-changelog-2">3</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;create_change_request&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;description&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Create a draft change request in the selected repository&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;inputSchema&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;object&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;properties&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;repository&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;string&quot;</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;title&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;string&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;minLength&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;body&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;string&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;minLength&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;required&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;repository&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;title&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;body&quot;</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;additionalProperties&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> false</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The schema constrains shape, not authority. A valid repository string can still point at a repository the user did not authorize.</p>
<h2 id="transport-boundaries">Transport boundaries</h2>
<p>MCP defines standard input and output transport for local process connections and Streamable HTTP for remote connections.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-transports" id="user-content-fnref-transports-2">5</a></sup> Transport choice changes the threat model.</p>
<p>With standard input and output, the host launches a process and exchanges newline-delimited JSON-RPC messages. Credentials should come from the environment rather than the HTTP authorization flow.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-authorization" id="user-content-fnref-authorization">8</a></sup> The host must still control executable paths, environment variables, inherited file descriptors, and working directories.</p>
<p>With Streamable HTTP, the server exposes an MCP endpoint that accepts POST and may support GET for server-sent events. Implementations must validate the <code>Origin</code> header to defend against DNS rebinding, bind local servers to loopback rather than all interfaces, and add authentication when appropriate.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-transports" id="user-content-fnref-transports-3">5</a></sup></p>
<h2 id="http-authorization">HTTP authorization</h2>
<p>Authorization is optional at the protocol level. When an HTTP server protects resources, the MCP authorization specification treats it as an OAuth resource server. The client acts as an OAuth client, and a separate authorization server issues tokens.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-authorization" id="user-content-fnref-authorization-2">8</a></sup></p>
<p>The discovery path is deliberate:</p>
<ol>
<li>The MCP server publishes protected-resource metadata.</li>
<li>The metadata identifies an authorization server.</li>
<li>The client reads authorization-server metadata.</li>
<li>The client requests authorization for the MCP resource.</li>
<li>The client presents the resulting access token to that MCP server.</li>
</ol>
<p>The client must include the OAuth <code>resource</code> parameter in authorization and token requests. The server must validate that the token was issued for itself.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-authorization" id="user-content-fnref-authorization-3">8</a></sup> This audience binding prevents a token meant for one MCP server from becoming a bearer credential accepted by another.</p>
<p>Public clients must use Proof Key for Code Exchange. Authorization endpoints must use HTTPS, while redirect URIs must use HTTPS or localhost. Redirect URI matching must be exact, and clients should bind the flow with <code>state</code>.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-authorization" id="user-content-fnref-authorization-4">8</a></sup></p>
<h3 id="token-passthrough-is-a-boundary-violation">Token passthrough is a boundary violation</h3>
<p>An MCP server that calls an upstream API is a separate OAuth client to that API. It must not forward the token received from the MCP client. The upstream token has a different audience and should be acquired through a separate authorization relationship.<sup><a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fn-authorization" id="user-content-fnref-authorization-5">8</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>MCP client -- token audience: files.example --› Files MCP server</span></span>
<span class="line"><span>Files MCP server -- separate token audience: storage-api --› Storage API</span></span></code></pre>
<p>Forwarding the first token erases the resource boundary and creates a confused-deputy path.</p>
<h2 id="tool-authorization-and-user-consent">Tool authorization and user consent</h2>
<p>OAuth answers which client can call a server on behalf of which resource owner. It does not answer whether a model-generated action is appropriate in the current conversation.</p>
<p>Tool execution needs a second control plane:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> Decision</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-string-expression)"> &#39;allow&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;deny&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;require_user&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">function</span><span style="color:var(--astro-code-token-function)"> authorizeTool</span><span style="color:var(--astro-code-foreground)">(input</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  tool</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  arguments</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> unknown</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  user</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> { id</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">; roles</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">[] }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  conversationId</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">})</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-function)"> Decision</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">input</span><span style="color:var(--astro-code-foreground)">.tool </span><span style="color:var(--astro-code-token-keyword)">===</span><span style="color:var(--astro-code-token-string-expression)"> &#39;read_invoice&#39;</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">return</span><span style="color:var(--astro-code-token-string-expression)"> &#39;allow&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">input</span><span style="color:var(--astro-code-foreground)">.tool </span><span style="color:var(--astro-code-token-keyword)">===</span><span style="color:var(--astro-code-token-string-expression)"> &#39;create_refund&#39;</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">return</span><span style="color:var(--astro-code-token-string-expression)"> &#39;require_user&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  return</span><span style="color:var(--astro-code-token-string-expression)"> &#39;deny&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The host should show the actual target and material arguments before approval. A button labeled “Allow tool” provides less information than “Create a refund for invoice INV-1042.” Servers must also apply their own authorization because a client can call them without using a model.</p>
<h2 id="failure-handling">Failure handling</h2>
<p>Protocol errors and tool errors belong to different layers. Malformed JSON-RPC, unknown methods, and invalid protocol state should produce JSON-RPC errors. A valid tool invocation that fails in its domain should return a tool result marked as an error so the model can reason about the failure.</p>
<p>Operational safeguards include:</p>
<ul>
<li>Timeouts for every request and cancellation propagation where supported.</li>
<li>Bounded result sizes before content enters model context.</li>
<li>Schema validation on inputs and structured outputs.</li>
<li>Audit records containing server identity, tool name, policy decision, and result status.</li>
<li>Redaction before logging credentials or resource bodies.</li>
<li>Connection teardown when version or capability invariants fail.</li>
</ul>
<p>Retries should be method-aware. Listing tools is naturally repeatable. Creating a record is not repeatable unless the tool contract includes an idempotency mechanism.</p>
<h2 id="a-minimal-implementation-checklist">A minimal implementation checklist</h2>
<p>An interoperable implementation begins with protocol correctness:</p>
<ul>
<li>Keep one isolated client session per server.</li>
<li>Require initialization before operation.</li>
<li>Store the negotiated version and capabilities.</li>
<li>Correlate request identifiers without treating them as authority.</li>
<li>Reject notifications that incorrectly include identifiers.</li>
<li>Validate tool arguments and resource URIs.</li>
<li>Separate protocol errors from domain failures.</li>
</ul>
<p>A deployable implementation adds security controls:</p>
<ul>
<li>Pin executable identity for local servers.</li>
<li>Validate origins and bind safely for HTTP.</li>
<li>Discover authorization metadata rather than hard-coding authorization endpoints.</li>
<li>Bind access tokens to the MCP resource.</li>
<li>Never pass client tokens through to upstream APIs.</li>
<li>Require contextual authorization for sensitive tools.</li>
<li>Treat all returned content as untrusted input to the model.</li>
</ul>
<p>MCP standardizes the wire conversation. It does not remove the need for access control, policy, validation, or user-visible consent. Those controls remain with the host and each resource-owning server.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-architecture">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/architecture" rel="noopener noreferrer">Model Context Protocol, Architecture, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-architecture" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-architecture-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-base">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic" rel="noopener noreferrer">Model Context Protocol, Base Protocol overview, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-base" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-changelog">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/changelog" rel="noopener noreferrer">Model Context Protocol, key changes in protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-changelog" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-changelog-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-lifecycle">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/lifecycle" rel="noopener noreferrer">Model Context Protocol, Lifecycle, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-lifecycle" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-lifecycle-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-transports">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/transports" rel="noopener noreferrer">Model Context Protocol, Transports, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-transports" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-transports-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-transports-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-server">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/server" rel="noopener noreferrer">Model Context Protocol, Server Features overview, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-server" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-resources">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/server/resources" rel="noopener noreferrer">Model Context Protocol, Resources, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-resources" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-authorization">
<p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization" rel="noopener noreferrer">Model Context Protocol, Authorization, protocol revision 2025-06-18</a>. <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-authorization" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-authorization-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-authorization-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-authorization-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/mcp-first-principles/#user-content-fnref-authorization-5" class="data-footnote-backref">↩<sup>5</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Agents</category>
            <category>Structured output</category>
            <category>Open source</category>
            <enclosure length="84540" type="image/png" url="https://blog.ecitis.org/open-graph/mcp-first-principles.png"/>
        </item>
        <item>
            <title><![CDATA[Structured Generation: How LLMs Produce Valid JSON]]></title>
            <link>https://blog.ecitis.org/structured-generation/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/structured-generation/</guid>
            <pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[See how grammar compilation and token masking turn probabilistic decoding into reliably structured JSON and schema-constrained output.]]></description>
            <content:encoded><![CDATA[<p>Prompting a model to return JSON asks a probabilistic decoder to imitate a grammar. Constrained decoding changes the operation: before sampling, the runtime masks tokens that cannot extend any valid document in the target language.</p>
<p>The result can guarantee syntax without guaranteeing truth. A schema can force an integer field, but it cannot prove that the integer was computed from the source text.</p>
<h2 id="compile-structure-before-generation">Compile structure before generation</h2>
<p>JSON Schema, a regular expression, or a context-free grammar is not applied directly to model logits. The serving system compiles the constraint into a recognizer with runtime state.</p>
<p>At generation step <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>t</mi></mrow></semantics></math>, let <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>V</mi></mrow></semantics></math> be the vocabulary and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>A</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo><mo>⊆</mo><mi>V</mi></mrow></semantics></math> be the tokens accepted from grammar state <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>s</mi><mi>t</mi></msub></mrow></semantics></math>. Masked logits are:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msubsup><mi mathvariant="normal">ℓ</mi><mi>t</mi><mo lspace="0em" rspace="0em">′</mo></msubsup><mo stretchy="false">(</mo><mi>v</mi><mo stretchy="false">)</mo><mo>=</mo><mrow><mo fence="true">{</mo><mtable rowspacing="0.36em" columnalign="left left" columnspacing="1em"><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><msub><mi mathvariant="normal">ℓ</mi><mi>t</mi></msub><mo stretchy="false">(</mo><mi>v</mi><mo stretchy="false">)</mo><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>v</mi><mo>∈</mo><mi>A</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr><mtr><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mo>−</mo><mi mathvariant="normal">∞</mi><mo separator="true">,</mo></mrow></mstyle></mtd><mtd><mstyle scriptlevel="0" displaystyle="false"><mrow><mi>v</mi><mo>∉</mo><mi>A</mi><mo stretchy="false">(</mo><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></mstyle></mtd></mtr></mtable></mrow></mrow></semantics></math>
<p>Sampling then uses the normalized distribution over legal tokens. Willard and Louf formulate guided generation as transitions in a finite-state machine and build an index over the model vocabulary.<sup><a href="https://blog.ecitis.org/structured-generation/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> Context-free constraints require stack state because nesting cannot be represented by a finite-state machine alone.</p>
<figure><figcaption><strong>Grammar state restricts the token mask after each prefix</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/structured-generation-0.webp" width="512" height="273" alt="Grammar state restricts the token mask after each prefix" loading="lazy" decoding="async"></figure>
<h2 id="tokens-do-not-align-with-grammar-terminals">Tokens do not align with grammar terminals</h2>
<p>A tokenizer can encode punctuation together with adjacent text. A token might contain a quote, property characters, and a colon. Another token can contain a byte prefix that is invalid alone but valid when followed by later bytes.</p>
<p>The matcher must answer a prefix question: does the token’s byte sequence keep at least one valid completion reachable? Checking only complete terminals rejects useful multi-character tokens. Checking decoded Unicode strings can mishandle byte-level tokenizers.</p>
<p>A robust engine:</p>
<ol>
<li>evaluates token bytes against the current recognizer state</li>
<li>preserves every reachable parse stack when the grammar is ambiguous</li>
<li>caches the legal-token set for repeated states</li>
<li>advances state with the sampled token</li>
<li>detects accepting state before applying end-of-sequence rules</li>
</ol>
<p>XGrammar separates context-independent grammar components from context-dependent components and overlaps grammar processing with GPU execution.<sup><a href="https://blog.ecitis.org/structured-generation/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Its design addresses the cost of walking grammar stacks across a large vocabulary.<sup><a href="https://blog.ecitis.org/structured-generation/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup></p>
<h2 id="json-schema-is-more-than-json-syntax">JSON Schema is more than JSON syntax</h2>
<p>JSON grammar guarantees balanced delimiters, quoted keys, and valid scalar syntax. A schema adds semantic structure:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;object&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;properties&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;status&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;enum&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;ok&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;error&quot;</span><span style="color:var(--astro-code-foreground)">] }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;retryable&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;boolean&quot;</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;code&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> { </span><span style="color:var(--astro-code-token-keyword)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;integer&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;minimum&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;required&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;status&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;retryable&quot;</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;additionalProperties&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> false</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Compilation must define which schema features are supported. Object property ordering, numeric bounds, string patterns, recursive references, unions, and <code>additionalProperties</code> each require different machinery. A provider that accepts a schema but ignores unsupported keywords supplies weaker guarantees than the input document suggests.</p>
<p>Publish a supported subset and reject unsupported constraints at request validation. Silent downgrade turns a type contract into a prompt hint.</p>
<h2 id="state-dependent-masking-cost">State-dependent masking cost</h2>
<p>Naively testing every vocabulary token at every step repeats work. Production engines reduce that cost through:</p>
<ul>
<li>vocabulary tries that share token prefixes</li>
<li>precomputed masks for stable grammar states</li>
<li>cached transitions keyed by recognizer state and token</li>
<li>separation of deterministic and branching parse states</li>
<li>CPU grammar work overlapped with device model execution</li>
</ul>
<p>The mask still has to reach the sampling kernel. Copying a dense vocabulary-sized mask from host memory per sequence and per step can dominate short structured outputs. Bitsets, cached device masks, and batch grouping by grammar state reduce transfer.</p>
<p>Batching introduces divergence. Two requests using the same schema can occupy different parse states, so their legal sets differ. The sampler must apply per-sequence masks even when the model forward pass is batched.</p>
<h2 id="whitespace-and-canonical-forms">Whitespace and canonical forms</h2>
<p>JSON permits whitespace in many positions. Leaving all whitespace paths open expands grammar state and lets the model spend tokens formatting. A canonical mode can constrain separators and indentation, but it changes the accepted language.</p>
<p>Define canonicalization as an API option:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> StructuredOutputOptions</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  schema</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> object</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  whitespace</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;compact&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;model&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  rejectUnsupportedKeywords</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  maxOutputTokens</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Compact output reduces token use. Model-controlled whitespace preserves stylistic freedom. Neither choice changes field semantics.</p>
<h2 id="token-boundaries-and-parser-composition">Token boundaries and parser composition</h2>
<p>Grammar terminals rarely align with model tokens. DOMINO uses token-aligned finite-state machines so a token can span several grammar terminals without forcing a token boundary after each terminal.<sup><a href="https://blog.ecitis.org/structured-generation/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> This avoids rejecting a token merely because it packages punctuation, whitespace, and property text together.</p>
<p>SynCode precomputes a finite lookahead table for grammar terminals and uses it to construct masks for context-free grammars.<sup><a href="https://blog.ecitis.org/structured-generation/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> Two properties need separate tests:</p>
<ul>
<li>soundness means every admitted token prefix retains a valid grammatical completion</li>
<li>completeness means every token that can participate in a valid completion remains admitted</li>
</ul>
<p>An unsound masker can emit invalid output. An incomplete masker can force low-probability tokenization paths even when the intended text is valid. Boundary fixtures should include escaped text, punctuation merged with names, and byte sequences split across tokens.</p>
<h3 id="near-runnable-validation-boundary">Near-runnable validation boundary</h3>
<p>Constrained output should still terminate at an ordinary validator:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> json</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> jsonschema </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Draft202012Validator</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> validate_generated_payload</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">raw_text</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> schema</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    payload </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> json</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">loads</span><span style="color:var(--astro-code-token-punctuation)">(raw_text)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    validator </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Draft202012Validator</span><span style="color:var(--astro-code-token-punctuation)">(schema)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    errors </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> sorted</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        validator.</span><span style="color:var(--astro-code-token-function)">iter_errors</span><span style="color:var(--astro-code-token-punctuation)">(payload),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        key</span><span style="color:var(--astro-code-token-keyword)">=lambda</span><span style="color:var(--astro-code-token-parameter)"> error</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-constant)">list</span><span style="color:var(--astro-code-token-punctuation)">(error.absolute_path),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-foreground)"> errors</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        formatted </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                &quot;path&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> list</span><span style="color:var(--astro-code-token-punctuation)">(error.absolute_path),</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                &quot;message&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> error</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">message</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            }</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            for</span><span style="color:var(--astro-code-foreground)"> error </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> errors</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        raise</span><span style="color:var(--astro-code-token-constant)"> ValueError</span><span style="color:var(--astro-code-token-punctuation)">(formatted)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> payload</span></span></code></pre>
<p>Install <code>jsonschema</code> and supply the same schema compiled by the generation engine. This second validation catches engine defects and documents the trust boundary. Domain checks follow it and can query authoritative identifiers or enforce cross-field rules.</p>
<h2 id="compilation-limits">Compilation limits</h2>
<p>Compile user-supplied schemas before reserving model capacity. Cache compiled state by canonical schema bytes, tokenizer revision, grammar compiler version, and whitespace policy.</p>
<p>Set separate limits for schema bytes, recursive depth, grammar states, and compile time. Returning a validation error before inference keeps malformed schemas out of the model queue.</p>
<p>Expose the compilation result:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> CompiledConstraint</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  digest</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  compilerVersion</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  tokenizerRevision</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  supportedKeywords</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-keyword)"> readonly</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  ignoredKeywords</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-keyword)"> readonly</span><span style="color:var(--astro-code-token-constant)"> never</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The <code>never[]</code> type expresses the strict policy: unsupported keywords cause rejection. If compatibility requires permissive handling, name that mode explicitly and return every ignored keyword.</p>
<h2 id="valid-structure-is-not-valid-data">Valid structure is not valid data</h2>
<p>Apply three validation layers after decoding:</p>

























<div class="table-wrap"><table><thead><tr><th>Layer</th><th>Detects</th><th>Example</th></tr></thead><tbody><tr><td>Parser</td><td>Invalid JSON bytes</td><td>Unclosed string</td></tr><tr><td>Schema validator</td><td>Wrong shape or local constraint</td><td>Missing required field</td></tr><tr><td>Domain validator</td><td>Cross-field and external rules</td><td>Unknown database identifier</td></tr></tbody></table></div>
<p>Constrained decoding should make parser failure unreachable under supported conditions. Schema validation remains useful as a defense against implementation bugs and unsupported keywords. Domain validation is mandatory before side effects.</p>
<p>For tool calls, treat model output as untrusted input even when it matches the schema. Resolve authorization from server-side identity. Do not allow a generated account identifier or file path to expand permissions.</p>
<h2 id="recursion-and-termination">Recursion and termination</h2>
<p>A recursive grammar can generate an unbounded document. The model still needs a token budget. If the budget ends before an accepting state, the result is structurally incomplete.</p>
<p>The runtime should distinguish:</p>
<ul>
<li>accepted completion</li>
<li>token-budget exhaustion in non-accepting state</li>
<li>grammar with no legal continuation</li>
<li>model end token masked because the document is incomplete</li>
<li>internal grammar-engine failure</li>
</ul>
<p>Returning partial bytes as ordinary JSON hides the failure. Return an explicit structured-generation status outside the generated payload.</p>
<h2 id="operational-failure-modes">Operational failure modes</h2>
<p><strong>Empty legal set:</strong> the recognizer or tokenizer integration has reached a state with no accepted token. Log the grammar state, recent token bytes, tokenizer revision, and schema digest.</p>
<p><strong>Schema explosion:</strong> a large union or deeply recursive schema creates expensive state sets. Enforce compilation limits before model execution.</p>
<p><strong>Cache poisoning:</strong> a compiled-grammar cache uses an incomplete key. Include compiler version, tokenizer identity, schema bytes, and generation options.</p>
<p><strong>Unsupported keyword drift:</strong> a library upgrade changes which constraints are enforced. Pin compiler versions and run conformance fixtures.</p>
<p><strong>Unicode mismatch:</strong> validation operates on characters while masking operates on token bytes. Use one explicit byte-to-text policy and test invalid sequences.</p>
<h2 id="conformance-testing">Conformance testing</h2>
<p>Generate tests from the constraint rather than collecting a few successful prompts:</p>
<ul>
<li>schemas with nested objects and arrays</li>
<li>escaped strings and Unicode boundaries</li>
<li>overlapping unions</li>
<li>optional and required fields</li>
<li>recursive definitions up to configured depth</li>
<li>outputs ending exactly at the token budget</li>
<li>adversarial property names that share token prefixes</li>
</ul>
<p>For each case, assert parser validity, schema validity, accepted-state termination, and deterministic failure classification. Separately evaluate factual accuracy. Grammar conformance and task quality are different metrics.</p>
<p>Structured generation is reliable when the contract is precise: a documented schema subset, a tokenizer-aware recognizer, state-specific masks, and post-generation domain validation. Prompt wording alone provides none of those guarantees.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Brandon T. Willard and Rémi Louf, “Efficient Guided Generation for Large Language Models,” arXiv, 2023. <a href="https://arxiv.org/abs/2307.09702" rel="noopener noreferrer">https://arxiv.org/abs/2307.09702</a> <a href="https://blog.ecitis.org/structured-generation/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Yixin Dong et al., “XGrammar: Flexible and Efficient Structured Generation Engine for Large Language Models,” arXiv, 2024. <a href="https://arxiv.org/abs/2411.15100" rel="noopener noreferrer">https://arxiv.org/abs/2411.15100</a> <a href="https://blog.ecitis.org/structured-generation/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/structured-generation/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Luca Beurer-Kellner, Marc Fischer, and Martin Vechev, “Guiding LLMs The Right Way: Fast, Non-Invasive Constrained Generation,” ICML, 2024. <a href="https://arxiv.org/abs/2403.06988" rel="noopener noreferrer">https://arxiv.org/abs/2403.06988</a> <a href="https://blog.ecitis.org/structured-generation/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Suresh G. K. et al., “SynCode: LLM Generation with Grammar Augmentation,” arXiv, 2024. <a href="https://arxiv.org/abs/2403.01632" rel="noopener noreferrer">https://arxiv.org/abs/2403.01632</a> <a href="https://blog.ecitis.org/structured-generation/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Structured output</category>
            <category>Decoding</category>
            <category>Agents</category>
            <enclosure length="66823" type="image/png" url="https://blog.ecitis.org/open-graph/structured-generation.png"/>
        </item>
        <item>
            <title><![CDATA[How torch.compile Turns PyTorch Programs into Fused Kernels]]></title>
            <link>https://blog.ecitis.org/torch-compile-internals/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/torch-compile-internals/</guid>
            <pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Trace a PyTorch program through Dynamo, AOTAutograd, Inductor, guards, graph breaks, and generated Triton kernels.]]></description>
            <content:encoded><![CDATA[<p><code>torch.compile</code> is not a single compiler pass. It is a guarded path from a live Python program to reusable machine code. TorchDynamo captures tensor operations from Python frames, AOTAutograd constructs forward and backward graphs, and TorchInductor lowers those graphs into device code. On supported GPUs, Inductor uses Triton as a code-generation building block.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-compiler-stack" id="user-content-fnref-compiler-stack">1</a></sup></p>
<figure><figcaption><strong>Compilation path and its runtime boundaries</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/torch-compile-internals-0.webp" width="512" height="672" alt="Compilation path and its runtime boundaries" loading="lazy" decoding="async"></figure>
<h2 id="dynamo-captures-a-program-specialization">Dynamo captures a program specialization</h2>
<p>Dynamo hooks Python frame evaluation and symbolically executes bytecode. Tensor operations become nodes in an FX graph. Python values that affect execution become assumptions. A guard records each assumption needed to reuse the graph, including tensor shape, dtype, device, object identity, or a global value.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-guards" id="user-content-fnref-guards">2</a></sup></p>
<p>This distinction matters. Dynamo does not prove that a function has one universal tensor graph. It records one graph for one set of observed conditions, then emits checks that decide whether later calls fit that specialization.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> normalize_or_zero</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> enabled</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-foreground)"> enabled</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">norm</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zeros_like</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">compiled </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(normalize_or_zero)</span></span></code></pre>
<p>Here, <code>enabled</code> is a Python boolean. Dynamo can specialize on its value and cache separate graphs for the branches. A tensor-dependent condition is different:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> branch_on_data</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> &gt;</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sin</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cos</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>The branch requires a tensor value that is available only after execution. PyTorch documents tensor-dependent control flow and direct scalar extraction with <code>.item()</code> as common graph-break causes.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-graph-breaks" id="user-content-fnref-graph-breaks">3</a></sup></p>
<h2 id="graph-breaks-define-optimization-boundaries">Graph breaks define optimization boundaries</h2>
<p>At a graph break, Dynamo compiles the graph captured so far, returns to ordinary Python for the unsupported operation, then resumes tracing. Correctness is preserved, but fusion cannot cross the boundary. A break inside a transformer block can therefore cost more than one in preprocessing because the former separates operators that exchange large intermediate tensors.</p>
<p>Use the compiler logs before rewriting code:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">TORCH_LOGS</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;graph_breaks,guards,recompiles&quot;</span><span style="color:var(--astro-code-token-function)"> python</span><span style="color:var(--astro-code-token-string)"> train.py</span></span></code></pre>
<p><code>fullgraph=True</code> converts the first graph break into an error and requires one captured graph. PyTorch recommends it when the goal is to find and remove breaks in a performance-critical region.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-fullgraph" id="user-content-fnref-fullgraph">4</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">@torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(fullgraph</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> block</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> weight</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> bias</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">gelu</span><span style="color:var(--astro-code-token-punctuation)">(x </span><span style="color:var(--astro-code-token-keyword)">@</span><span style="color:var(--astro-code-token-punctuation)"> weight </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> bias)</span></span></code></pre>
<p>Do not force file I/O, metrics emission, or data decoding into that function. Mark a problematic helper with <code>torch.compiler.disable</code>, or place compilation around the numerical core. The boundary should express the part of the program whose inputs, side effects, and shapes are stable.</p>
<h2 id="aotautograd-exposes-the-backward-pass">AOTAutograd exposes the backward pass</h2>
<p>Eager autograd records operations while the forward pass runs. AOTAutograd instead traces a functional forward graph and constructs a corresponding backward graph ahead of execution. This gives the compiler visibility into saved tensors, recomputation choices, and backward operators. PyTorch describes AOTAutograd as the component that captures backpropagation for acceleration by Inductor.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-compiler-stack" id="user-content-fnref-compiler-stack-2">1</a></sup></p>
<p>Suppose a forward graph computes:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>y</mi><mo>=</mo><mi mathvariant="normal">GELU</mi><mo>⁡</mo><mo stretchy="false">(</mo><mi>x</mi><mi>W</mi><mo>+</mo><mi>b</mi><mo stretchy="false">)</mo></mrow></semantics></math>
<p>The backward needs values derived from the matrix product and activation. A partitioner decides what the forward should save versus what the backward can recompute. Saving reduces arithmetic and increases live memory. Recomputation makes the opposite trade. This is why a compiled training step can have a different memory profile from eager execution while retaining the same differentiation result.</p>
<p>Mutation and aliasing complicate this graph. AOTAutograd functionalizes many in-place operations into functional equivalents, then tracks the mutations that must be reflected at the boundary. Custom operators need correct aliasing and mutation declarations; a wrong schema can turn a local implementation mistake into a compiler-wide correctness problem.</p>
<h2 id="inductor-turns-graphs-into-loop-nests">Inductor turns graphs into loop nests</h2>
<p>Inductor receives ATen-level graphs after decomposition. It reasons about iteration domains, dependencies, device placement, layouts, and reuse. Elementwise chains are strong fusion candidates because their loop domains align.</p>
<p>An eager expression such as:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">gelu</span><span style="color:var(--astro-code-token-punctuation)">(x </span><span style="color:var(--astro-code-token-keyword)">@</span><span style="color:var(--astro-code-token-punctuation)"> weight </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> bias)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> y </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> scale </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> residual</span></span></code></pre>
<p>can launch separate kernels for bias addition, activation, multiplication, and residual addition around a matrix multiplication. Inductor can select a matrix-multiplication template and fuse compatible pointwise epilogues, or generate a separate fused pointwise kernel when the template boundary prevents full fusion. The useful question is not the number of FX nodes. It is how many times large tensors are written to and read from device memory.</p>
<p>Fusion is constrained by:</p>
<ul>
<li>incompatible iteration orders or layouts;</li>
<li>reductions that require synchronization;</li>
<li>mutations and aliasing;</li>
<li>device transfers;</li>
<li>graph breaks;</li>
<li>operator implementations treated as opaque calls.</li>
</ul>
<p>On GPUs, Triton code expresses blocked loads, arithmetic, and stores. On CPUs, Inductor emits C++ with vectorized loops. Backend choice changes the generated code, but Dynamo guards and graph partitioning remain part of the contract.</p>
<h2 id="shapes-determine-code-reuse">Shapes determine code reuse</h2>
<p>Static dimensions give Inductor concrete loop bounds and specialization opportunities. Variable sequence lengths can trigger recompilation when a guard fails. PyTorch first assumes static shapes under <code>dynamic=None</code>, then can retry with dynamic dimensions after a shape-driven recompilation.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-guards" id="user-content-fnref-guards-2">2</a></sup></p>
<p>There are three operational responses:</p>

























<div class="table-wrap"><table><thead><tr><th>Workload property</th><th>Compiler policy</th><th>Cost</th></tr></thead><tbody><tr><td>Few stable shapes</td><td>Specialize and cache</td><td>More kernels, stronger specialization</td></tr><tr><td>Broad bounded shapes</td><td>Mark selected dimensions dynamic</td><td>Fewer recompiles, more general code</td></tr><tr><td>Unbounded irregular shapes</td><td>Bucket or pad before compilation</td><td>Extra padding work, predictable cache</td></tr></tbody></table></div>
<p>Do not set every dimension dynamic as a reflex. Batch and sequence axes often vary; head width and vocabulary partition usually do not. Encode the variability that exists in production traces.</p>
<h2 id="compilation-modes-change-the-optimization-budget">Compilation modes change the optimization budget</h2>
<p>The default mode balances compile time and runtime work. <code>reduce-overhead</code> uses CUDA graphs where applicable to reduce Python and launch overhead. <code>max-autotune</code> benchmarks candidate matrix-multiplication or convolution implementations and enables CUDA graphs on supported GPU paths.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-compile-api" id="user-content-fnref-compile-api">5</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">compiled_step </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_step,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    mode</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;max-autotune&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    dynamic</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">None</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Warm-up measurements must be separated from steady-state measurements. Compilation and autotuning run on early calls, and a new shape or guard value can cause later recompilation. Measure:</p>
<ul>
<li>cold latency from process start;</li>
<li>warm latency for each supported shape bucket;</li>
<li>compilation count and reason;</li>
<li>peak memory during compilation and execution;</li>
<li>numerical agreement against eager mode.</li>
</ul>
<h2 id="diagnose-one-layer-at-a-time">Diagnose one layer at a time</h2>
<p>PyTorch exposes backend ablations that isolate the failing stage.<sup><a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fn-troubleshooting" id="user-content-fnref-troubleshooting">6</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Dynamo capture only</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(fn, backend</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;eager&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Add AOTAutograd</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(fn, backend</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;aot_eager&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run the complete default stack</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(fn, backend</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;inductor&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>If eager capture fails, inspect Python semantics and graph breaks. If <code>aot_eager</code> fails, inspect autograd, functionalization, mutation, and custom-operator schemas. If only Inductor fails, preserve the FX graph and generated debug trace, then reduce the operator pattern.</p>
<p>Accuracy testing should compare outputs and gradients across representative dtypes and shapes. Compiler speed is irrelevant if a custom kernel mishandles non-contiguous inputs or a low-precision reduction exceeds the application tolerance.</p>
<h2 id="deployment-checklist">Deployment checklist</h2>
<p>Compile the numerical step rather than the surrounding service loop. Establish shape buckets from observed traffic. Warm each bucket before accepting latency-sensitive work. Record guard failures and recompiles. Keep an eager fallback for unsupported paths, but alert when it is used. Pin the PyTorch and driver environment used for performance qualification because generated kernels and compiler behavior are environment-dependent.</p>
<p><code>torch.compile</code> works best when the program exposes stable tensor regions with explicit boundaries. Dynamo supplies guarded capture, AOTAutograd supplies differentiable graphs, and Inductor supplies loop-level optimization. Performance work consists of preserving those regions, controlling specialization, and verifying the generated execution rather than treating the decorator as a universal switch.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-compiler-stack">
<p><a href="https://docs.pytorch.org/docs/stable/user_guide/torch_compiler/torch.compiler.html" rel="noopener noreferrer">PyTorch compiler documentation</a>. <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-compiler-stack" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-compiler-stack-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-guards">
<p><a href="https://docs.pytorch.org/docs/main/user_guide/torch_compiler/torch.compiler_troubleshooting.html#terminology" rel="noopener noreferrer">PyTorch compiler troubleshooting: guards and recompilation</a>. <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-guards" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-guards-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-graph-breaks">
<p><a href="https://docs.pytorch.org/docs/main/user_guide/torch_compiler/torch.compiler_troubleshooting.html#data-dependent-operations" rel="noopener noreferrer">PyTorch compiler troubleshooting: data-dependent operations</a>. <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-graph-breaks" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-fullgraph">
<p><a href="https://docs.pytorch.org/docs/stable/user_guide/torch_compiler/compile/programming_model.fullgraph_true.html" rel="noopener noreferrer">PyTorch documentation for <code>fullgraph=True</code></a>. <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-fullgraph" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-compile-api">
<p><a href="https://docs.pytorch.org/docs/stable/generated/torch.compile.html" rel="noopener noreferrer">PyTorch <code>torch.compile</code> API</a>. <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-compile-api" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-troubleshooting">
<p><a href="https://docs.pytorch.org/docs/main/user_guide/torch_compiler/torch.compiler_troubleshooting.html#ablation" rel="noopener noreferrer">PyTorch compiler troubleshooting: backend ablation</a>. <a href="https://blog.ecitis.org/torch-compile-internals/#user-content-fnref-troubleshooting" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Compilers</category>
            <category>PyTorch</category>
            <category>Kernels</category>
            <enclosure length="72908" type="image/png" url="https://blog.ecitis.org/open-graph/torch-compile-internals.png"/>
        </item>
        <item>
            <title><![CDATA[Disaggregated LLM Serving: Separating Prefill from Decode]]></title>
            <link>https://blog.ecitis.org/disaggregated-llm-serving/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/disaggregated-llm-serving/</guid>
            <pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Separate compute-heavy prefill from memory-bound decode, then scale each worker pool around its own latency objective.]]></description>
            <content:encoded><![CDATA[<p>An LLM request contains two workloads with different shapes. Prefill evaluates all prompt tokens and produces the first output token. Decode repeatedly evaluates one new position while reading the accumulated key-value cache.</p>
<p>Colocating both phases is operationally simple. It also couples their batching, parallelism, and latency. Disaggregated serving assigns prefill and decode to different worker pools, transfers the cache between them, and scales each pool against its own service objective. DistServe formalized this design around time to first token and time per output token.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<h2 id="phase-specific-resource-plans">Phase-specific resource plans</h2>
<p>Prefill exposes matrix dimensions large enough to use compute units efficiently. Decode uses small per-request matrix dimensions and repeatedly streams model weights and cache state. A shared worker must compromise between large prompt batches and decode cadence.</p>
<p>Disaggregation permits separate plans:</p>



































<div class="table-wrap"><table><thead><tr><th>Decision</th><th>Prefill pool</th><th>Decode pool</th></tr></thead><tbody><tr><td>Primary latency</td><td>Time to first token</td><td>Time per output token</td></tr><tr><td>Batching unit</td><td>Prompt tokens or chunks</td><td>Active sequences</td></tr><tr><td>Parallelism</td><td>Favor prompt throughput</td><td>Favor low collective overhead</td></tr><tr><td>Capacity signal</td><td>Queued prompt tokens</td><td>Active sequences and live KV bytes</td></tr><tr><td>Scale trigger</td><td>Predicted prefill queue time</td><td>Predicted inter-token delay</td></tr></tbody></table></div>
<p>DistServe reports that phase separation removes prefill-decode interference and allows different resource and parallelism choices for each phase.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup> The paper’s evaluation found up to <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>7.4</mn><mo>×</mo></mrow></semantics></math> higher request rate or <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>12.6</mn><mo>×</mo></mrow></semantics></math> tighter service objectives than its baselines, with more than <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>90</mn><mi mathvariant="normal">%</mi></mrow></semantics></math> of requests meeting the specified constraints.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-1" id="user-content-fnref-1-3">1</a></sup></p>
<figure><figcaption><strong>Prefill and decode as separate scheduling domains</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/disaggregated-llm-serving-0.webp" width="512" height="382" alt="Prefill and decode as separate scheduling domains" loading="lazy" decoding="async"></figure>
<h2 id="cache-transfer-is-part-of-request-latency">Cache transfer is part of request latency</h2>
<p>After prefill, decode cannot start until the required keys and values are available. For cache size <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>M</mi><mrow><mi>K</mi><mi>V</mi></mrow></msub></mrow></semantics></math>, effective link bandwidth <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>W</mi></mrow></semantics></math>, and fixed transfer overhead <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>T</mi><mn>0</mn></msub></mrow></semantics></math>, a lower-bound model is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>T</mi><mtext>handoff</mtext></msub><mo>≥</mo><msub><mi>T</mi><mn>0</mn></msub><mo>+</mo><mfrac><msub><mi>M</mi><mrow><mi>K</mi><mi>V</mi></mrow></msub><mi>W</mi></mfrac></mrow></semantics></math>
<p>This is a planning bound, not a latency prediction. Contention, serialization, topology, registration, and synchronization increase elapsed time.</p>
<p>A transfer protocol needs:</p>
<ul>
<li>model revision and cache-layout identity</li>
<li>source and destination block handles</li>
<li>sequence positions and valid token count</li>
<li>per-layer completion state</li>
<li>cancellation and timeout semantics</li>
<li>acknowledgement before source reclamation</li>
</ul>
<p>Do not send a monolithic opaque tensor if the runtime stores paged blocks. Transfer complete blocks directly and preserve logical block order in metadata. This avoids repacking into a contiguous buffer.</p>
<h2 id="pipeline-the-handoff">Pipeline the handoff</h2>
<p>Waiting for all layers to finish before beginning transfer leaves the link idle during prefill. Layerwise or blockwise streaming overlaps later prefill work with earlier cache movement:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>prefill layer l</span></span>
<span class="line"><span>  -&gt; record completion event</span></span>
<span class="line"><span>  -&gt; enqueue KV blocks for layer l</span></span>
<span class="line"><span>  -&gt; transport waits on event</span></span>
<span class="line"><span>  -&gt; destination records receive completion</span></span>
<span class="line"><span>  -&gt; decode waits before first read of layer l</span></span></code></pre>
<p>Correctness depends on event ordering. A destination-visible pointer does not prove that the corresponding device writes have completed.</p>
<p>Backpressure is also required. If the destination cannot reserve blocks, the source should not compute a cache that has nowhere to land. Reserve destination capacity before admitting prefill, or allow an explicit spill path with its own latency budget.</p>
<h2 id="scheduling-with-two-queues">Scheduling with two queues</h2>
<p>The router now controls a distributed pipeline. A useful estimate for candidate prefill worker <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi></mrow></semantics></math> and decode worker <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow></semantics></math> is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mover accent="true"><mi>T</mi><mo stretchy="true">^</mo></mover><mrow><mi>f</mi><mi>i</mi><mi>r</mi><mi>s</mi><mi>t</mi></mrow></msub><mo stretchy="false">(</mo><mi>p</mi><mo separator="true">,</mo><mi>d</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mover accent="true"><mi>Q</mi><mo stretchy="true">^</mo></mover><mi>p</mi></msub><mo>+</mo><msub><mover accent="true"><mi>P</mi><mo stretchy="true">^</mo></mover><mi>p</mi></msub><mo>+</mo><msub><mover accent="true"><mi>X</mi><mo stretchy="true">^</mo></mover><mrow><mi>p</mi><mo separator="true">,</mo><mi>d</mi></mrow></msub><mo>+</mo><msub><mover accent="true"><mi>D</mi><mo stretchy="true">^</mo></mover><mi>d</mi></msub></mrow></semantics></math>
<p>The terms represent prefill queue, prefill execution, transfer, and decode admission. Each estimate must use current topology and cache reservations.</p>
<p>Routing on queue length alone is weak because a queued long prompt carries more work than a queued short prompt. Count scheduled prompt tokens and use profiled execution curves. For decode, active sequence count also misses context length and cache pressure.</p>
<h3 id="prefix-locality">Prefix locality</h3>
<p>A prefill worker may already hold a reusable prefix. A decode worker may hold the previous turn of a conversation. Routing should compare reuse savings with queue and transfer costs.</p>
<p>Use explicit estimates:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> PlacementCost</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  queueMs</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  computeMs</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  transferMs</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  recomputeMs</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  capacityRisk</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Keeping the terms visible prevents a cache-hit heuristic from silently overriding latency objectives.</p>
<h2 id="parallelism-can-differ-by-phase">Parallelism can differ by phase</h2>
<p>Tensor parallelism reduces per-device weight and compute, but each layer introduces collectives. Pipeline parallelism partitions layers and introduces pipeline scheduling. Prefill can amortize communication over larger token matrices. Decode executes collectives at every token step.</p>
<p>DistServe searches phase-specific parallelism and places communicating prefill and decode instances according to cluster bandwidth.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-1" id="user-content-fnref-1-4">1</a></sup> Treat the selected topology as part of the deployment artifact. A scheduler cannot compensate for a prefill-decode pair split across a congested or oversubscribed path.</p>
<h2 id="designs-beyond-a-single-handoff">Designs beyond a single handoff</h2>
<p>Splitwise profiles prompt and token generation separately, then assigns them to machine pools selected for each phase.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Its architecture includes mixed machines that can absorb imbalances rather than leaving a saturated phase blocked behind an idle pool.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup> This matters because a fixed prefill-to-decode ratio assumes a stable workload mix.</p>
<p>Mooncake treats the cache as the center of a disaggregated architecture. It separates cache storage and transfer from compute scheduling, with a distributed cache that can reuse state across requests and workers.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> P/D-Serve focuses on fine-grained organization and dynamic adjustment of prefill and decode instances.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-4" id="user-content-fnref-4">4</a></sup></p>
<p>These systems expose separate controls:</p>





















<div class="table-wrap"><table><thead><tr><th>Control</th><th>Question</th></tr></thead><tbody><tr><td>Phase placement</td><td>Which worker executes prompt or token work?</td></tr><tr><td>Cache placement</td><td>Where do completed blocks remain available?</td></tr><tr><td>Elasticity</td><td>How does the pool ratio change with traffic?</td></tr></tbody></table></div>
<p>Do not conflate them in one autoscaler. A phase replica count can change without moving warm cache. A cache tier can become full while compute workers remain underutilized.</p>
<p>A near-runnable planner can turn sampled requests into initial offered-work estimates:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> dataclasses </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> dataclass</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@dataclass</span><span style="color:var(--astro-code-token-punctuation)">(frozen</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> Request</span><span style="color:var(--astro-code-foreground)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    prompt_tokens</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> int</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output_tokens</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> int</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> offered_work</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">requests</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> prefill_ms</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> decode_ms</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    prefill </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> sum</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">prefill_ms</span><span style="color:var(--astro-code-token-punctuation)">(r.prompt_tokens) </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> r </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> requests)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    decode </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> sum</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        decode_ms</span><span style="color:var(--astro-code-token-punctuation)">(r.prompt_tokens, r.output_tokens)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-token-punctuation)"> r </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> requests</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> prefill</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> decode</span></span></code></pre>
<p>The functions must come from profiles on the intended hardware. Replace aggregate work with a queue simulation before deployment because service objectives depend on burst arrivals. Add measured transfer time to each prefill completion and enforce destination cache reservations.</p>
<h2 id="failure-modes">Failure modes</h2>
<p><strong>Destination allocation failure:</strong> prefill finishes, but decode has no cache pages. Reserve before execution and release the reservation on cancellation.</p>
<p><strong>Orphaned transfers:</strong> the client disconnects while cache movement is in flight. Propagate cancellation through router, source, transport, and destination; make cleanup idempotent.</p>
<p><strong>Version skew:</strong> pools run different model revisions or cache formats. Reject handoff on an exact layout identifier.</p>
<p><strong>Link saturation:</strong> more prefill replicas increase offered transfer traffic without increasing decode capacity. Autoscaling must include link utilization and transfer queue time.</p>
<p><strong>Decode starvation:</strong> routing admits more completed prefills than decode can drain. Apply pipeline admission control at the request boundary.</p>
<p><strong>Replica hotspots:</strong> prefix-aware routing concentrates traffic. Bound affinity and fall back when predicted queue delay exceeds recomputation savings.</p>
<h2 id="when-colocated-serving-is-the-correct-choice">When colocated serving is the correct choice</h2>
<p>Disaggregation adds a networked state transfer, another admission boundary, and more failure states. It is not automatically faster. Keep phases colocated when transfer time consumes the expected interference savings, traffic is too small to fill separate pools, the topology lacks a predictable device-to-device path, or operational simplicity matters more than independent scaling.</p>
<p>Benchmark both designs with the same arrival trace and service objectives. Compare goodput, not raw tokens per second: count requests that satisfy both first-token and inter-token latency. That is the metric DistServe uses to connect resource planning to user-visible constraints.<sup><a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fn-1" id="user-content-fnref-1-5">1</a></sup></p>
<h2 id="deployment-checklist">Deployment checklist</h2>
<p>Before production traffic:</p>
<ul>
<li>profile prefill by uncached token count</li>
<li>profile decode by batch size and context length</li>
<li>measure transfer by cache bytes and topology path</li>
<li>test cancellation at every pipeline boundary</li>
<li>verify source blocks remain live until acknowledgement</li>
<li>inject worker and link failures</li>
<li>enforce compatible model and cache-layout identifiers</li>
<li>report service-objective attainment by phase</li>
</ul>
<p>Disaggregated serving is a scheduling architecture, not a socket between two model servers. Its value comes from independent phase control. Its cost is that cache placement, transport, and admission become part of inference correctness.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Yinmin Zhong et al., “DistServe: Disaggregating Prefill and Decoding for Goodput-optimized Large Language Model Serving,” OSDI, 2024. <a href="https://arxiv.org/abs/2401.09670" rel="noopener noreferrer">https://arxiv.org/abs/2401.09670</a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-1-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-1-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-1-5" class="data-footnote-backref">↩<sup>5</sup></a></p>
</li>
<li id="user-content-fn-2">
<p>Pratyush Patel et al., “Splitwise: Efficient Generative LLM Inference Using Phase Splitting,” ISCA, 2024. <a href="https://arxiv.org/abs/2311.18677" rel="noopener noreferrer">https://arxiv.org/abs/2311.18677</a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>Ruoyu Qin et al., “Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving,” FAST, 2025. <a href="https://arxiv.org/abs/2407.00079" rel="noopener noreferrer">https://arxiv.org/abs/2407.00079</a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Yuhang Jin et al., “P/D-Serve: Serving Disaggregated Large Language Model at Scale,” arXiv, 2024. <a href="https://arxiv.org/abs/2408.08147" rel="noopener noreferrer">https://arxiv.org/abs/2408.08147</a> <a href="https://blog.ecitis.org/disaggregated-llm-serving/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>LLM serving</category>
            <category>Scheduling</category>
            <category>Networking</category>
            <enclosure length="65322" type="image/png" url="https://blog.ecitis.org/open-graph/disaggregated-llm-serving.png"/>
        </item>
        <item>
            <title><![CDATA[Inside the KV Cache: Memory Systems for LLM Inference]]></title>
            <link>https://blog.ecitis.org/kv-cache-memory-systems/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/kv-cache-memory-systems/</guid>
            <pubDate>Tue, 03 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Treat the KV cache as a dynamic memory system shaped by paging, fragmentation, shared prefixes, and unpredictable request lifetimes.]]></description>
            <content:encoded><![CDATA[<p>Autoregressive inference repeatedly evaluates attention over an expanding prefix. Recomputing every key and value projection at every step would repeat work for tokens that have not changed. A key-value cache stores those projections, then appends the projections for each new token.</p>
<p>That optimization moves the serving limit. Model weights are mostly static, but cache allocation follows request arrivals, prompt lengths, generation lengths, beam branches, cancellations, and shared prefixes. The cache is therefore both a tensor and a dynamic memory-management problem.</p>
<h2 id="cache-contents">Cache contents</h2>
<p>For a transformer layer, attention projects hidden states into queries, keys, and values:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>Q</mi><mo>=</mo><mi>X</mi><msub><mi>W</mi><mi>Q</mi></msub><mo separator="true">,</mo><mspace width="2em"></mspace><mi>K</mi><mo>=</mo><mi>X</mi><msub><mi>W</mi><mi>K</mi></msub><mo separator="true">,</mo><mspace width="2em"></mspace><mi>V</mi><mo>=</mo><mi>X</mi><msub><mi>W</mi><mi>V</mi></msub></mrow></semantics></math>
<p>During decode, the current query attends to cached keys and values:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi mathvariant="normal">Attention</mi><mo>⁡</mo><mo stretchy="false">(</mo><msub><mi>q</mi><mi>t</mi></msub><mo separator="true">,</mo><msub><mi>K</mi><mrow><mo>≤</mo><mi>t</mi></mrow></msub><mo separator="true">,</mo><msub><mi>V</mi><mrow><mo>≤</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo>=</mo><mi mathvariant="normal">softmax</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mfrac><mrow><msub><mi>q</mi><mi>t</mi></msub><msubsup><mi>K</mi><mrow><mo>≤</mo><mi>t</mi></mrow><mi>T</mi></msubsup></mrow><msqrt><msub><mi>d</mi><mi>h</mi></msub></msqrt></mfrac><mo fence="true">)</mo></mrow><msub><mi>V</mi><mrow><mo>≤</mo><mi>t</mi></mrow></msub></mrow></semantics></math>
<p>Only the current query is transient. Keys and values remain live because later tokens can attend to them.</p>
<p>For a decoder with <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>L</mi></mrow></semantics></math> layers, <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub></mrow></semantics></math> key-value heads, head dimension <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>D</mi></mrow></semantics></math>, sequence length <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>S</mi></mrow></semantics></math>, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>B</mi></mrow></semantics></math> bytes per scalar, cache storage per sequence is:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>M</mi><mrow><mi>K</mi><mi>V</mi></mrow></msub><mo>=</mo><mn>2</mn><mi>L</mi><mi>S</mi><msub><mi>H</mi><mrow><mi>k</mi><mi>v</mi></mrow></msub><mi>D</mi><mi>B</mi></mrow></semantics></math>
<p>The leading factor accounts for separate key and value tensors. This formula exposes the useful levers: context length, cache precision, layer count, and key-value head count. Multi-query attention shares one key-value head across query heads; grouped-query attention uses more than one but fewer than full multi-head attention.<sup><a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<h2 id="prefill-and-decode-write-different-access-patterns">Prefill and decode write different access patterns</h2>
<p>Prefill processes a prompt as a token matrix. Each layer writes a run of key and value vectors, while attention consumes the prompt prefix. Decode appends a narrow slice and reads the existing cache.</p>
<p>The distinction matters for kernel design:</p>






























<div class="table-wrap"><table><thead><tr><th>Phase</th><th>Cache operation</th><th>Typical pressure</th></tr></thead><tbody><tr><td>Prefill</td><td>Bulk allocation and writes</td><td>Compute, allocation latency</td></tr><tr><td>Decode</td><td>Append one position, read all prior positions</td><td>Memory bandwidth, cache capacity</td></tr><tr><td>Cancellation</td><td>Release every block owned by a request</td><td>Allocator synchronization</td></tr><tr><td>Prefix reuse</td><td>Map a new request to existing blocks</td><td>Reference counting, routing</td></tr></tbody></table></div>
<p>A serving scheduler must reserve enough blocks before admitting work. If it admits from token limits alone, a burst of long generations can exhaust cache pages after requests have begun streaming.</p>
<figure><figcaption><strong>Logical sequences map to reusable physical KV blocks</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/kv-cache-memory-systems-0.webp" width="512" height="391" alt="Logical sequences map to reusable physical KV blocks" loading="lazy" decoding="async"></figure>
<h2 id="contiguous-allocation-wastes-capacity">Contiguous allocation wastes capacity</h2>
<p>A simple allocator reserves one contiguous region per sequence at its maximum configured length. That approach creates internal fragmentation when generation ends early. It also creates external fragmentation as variable-sized regions are allocated and freed.</p>
<p>PagedAttention applies operating-system paging to the cache. Logical token blocks map through a block table to non-contiguous physical blocks. vLLM’s original paper reports near-zero cache waste and supports cache sharing within and across requests; its evaluation measured between two and four times the throughput of the compared systems at comparable latency.<sup><a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p>The attention kernel cannot assume that adjacent logical tokens are adjacent in memory. It resolves each logical block through the block table. That indirection is the price of flexible allocation.</p>
<h3 id="block-size-trade-offs">Block size trade-offs</h3>
<p>Small blocks reduce waste in the final, partially filled page. They increase block-table entries, allocation operations, and address translation inside the kernel. Large blocks reduce metadata but strand more capacity at sequence tails.</p>
<p>Choose block size with workload traces, not model shape alone. Record the distribution of prompt lengths, output lengths, cancellations, and prefix reuse. Then replay those traces against candidate block sizes and measure:</p>
<ul>
<li>live cache bytes versus reserved bytes</li>
<li>allocation latency at high request concurrency</li>
<li>attention-kernel time by context length</li>
<li>blocks copied during beam or parallel sampling</li>
<li>eviction and recomputation frequency</li>
</ul>
<h2 id="prefix-caching-needs-identity-rules">Prefix caching needs identity rules</h2>
<p>Two requests can share cached blocks only when the token sequence and every cache-relevant model input match. Text equality is insufficient because tokenization settings, adapter selection, multimodal embeddings, position assignment, and model revision can change the resulting states.</p>
<p>A practical cache key includes:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> PrefixKey</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  modelRevision</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  adapterId</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-constant)"> null</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  tokenIds</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-keyword)"> readonly</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)">[]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  positionPolicy</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  multimodalDigest</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-constant)"> null</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Hash blocks incrementally so a request can reuse the longest matching prefix. Store parent hashes to prevent a block with identical local tokens from matching under a different preceding context.</p>
<p>Reference counts protect shared blocks. Copy-on-write handles divergence: requests share completed prefix blocks, while each allocates private blocks for generated continuations. The vLLM paper uses this mechanism for parallel sampling and beam search.<sup><a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup></p>
<h2 id="eviction-policy-affects-compute">Eviction policy affects compute</h2>
<p>Evicting a cache entry does not lose model weights or user data, but it turns the next hit into recomputation. A least-recently-used policy treats every block equally even though recomputation cost rises with the number of prefix tokens that must be replayed.</p>
<p>A cost-aware score can combine:</p>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mtext>value</mtext><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><mover accent="true"><mtext>reuse probability</mtext><mo stretchy="true">^</mo></mover><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo><mo>⋅</mo><mover accent="true"><mtext>prefill cost saved</mtext><mo stretchy="true">^</mo></mover><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo></mrow><mrow><mtext>bytes</mtext><mo stretchy="false">(</mo><mi>p</mi><mo stretchy="false">)</mo></mrow></mfrac></mrow></semantics></math>
<p>The terms are estimates derived from observed traffic. Keep them separate in telemetry. A high hit rate can still save little compute if hits cover short prefixes.</p>
<p>Cache-aware routing introduces another constraint. Sending a request to the least-loaded replica can discard a large local prefix hit. Sending every matching prefix to one replica can create a hotspot. Route using predicted queue time plus predicted prefill time after reuse.</p>
<h2 id="quantized-cache-changes-attention-inputs">Quantized cache changes attention inputs</h2>
<p>Quantizing the cache reduces bytes per scalar in the storage formula. It also adds scale metadata and dequantization work to attention. Keys and values can require different calibration because their distributions differ by layer and head.</p>
<p>Validation must include generation quality and kernel performance. A cache format that halves tensor payload does not halve total latency if dequantization prevents a fused attention path. Measure:</p>
<ul>
<li>quality against the unquantized cache on task-specific prompts</li>
<li>time per output token across context lengths</li>
<li>scale-storage overhead</li>
<li>conversion work during prefix reuse or cache transfer</li>
<li>numerical behavior for long-running generations</li>
</ul>
<h2 id="moving-cache-across-memory-tiers">Moving cache across memory tiers</h2>
<p>GPU memory is not the only possible cache tier. A serving system can move inactive blocks to host memory, local storage, or another worker, then restore them when a request resumes. The decision compares transfer time with recomputation time.</p>
<p>Prompt Cache defines reusable prompt modules and preserves their positional accuracy when reusing attention state across prompts.<sup><a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fn-3" id="user-content-fnref-3">3</a></sup> CacheGen targets network transfer: it encodes key-value tensors into a compressed bitstream conditioned on their distribution, then adapts compression to available bandwidth.<sup><a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fn-4" id="user-content-fnref-4">4</a></sup></p>
<p>Tiering needs an explicit state machine:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">type</span><span style="color:var(--astro-code-token-function)"> BlockState</span><span style="color:var(--astro-code-token-keyword)"> =</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  |</span><span style="color:var(--astro-code-foreground)"> { kind</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;device&#39;</span><span style="color:var(--astro-code-foreground)">; gpu</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> number</span><span style="color:var(--astro-code-foreground)">; address</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> bigint</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  |</span><span style="color:var(--astro-code-foreground)"> { kind</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;moving&#39;</span><span style="color:var(--astro-code-foreground)">; operationId</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">; destination</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;host&#39;</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-string-expression)"> &#39;remote&#39;</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  |</span><span style="color:var(--astro-code-foreground)"> { kind</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;host&#39;</span><span style="color:var(--astro-code-foreground)">; bufferId</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  |</span><span style="color:var(--astro-code-foreground)"> { kind</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;remote&#39;</span><span style="color:var(--astro-code-foreground)">; objectKey</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)">; checksum</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> string</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  |</span><span style="color:var(--astro-code-foreground)"> { kind</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;evicted&#39;</span><span style="color:var(--astro-code-foreground)"> }</span></span></code></pre>
<p>Only one transition may own a block at a time. Cancellation during transfer must release both the source reference and any completed destination allocation. A checksum catches truncated or stale payloads, while model and layout identifiers prevent restoring cache produced by incompatible weights.</p>
<p>A near-runnable capacity calculator follows directly from the storage equation:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> kv_bytes</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">layers</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> sequence</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> kv_heads</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> head_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> bytes_per_scalar</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> layers </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> sequence </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> kv_heads </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> head_dim </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> bytes_per_scalar</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> concurrent_capacity</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">free_bytes</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> reserve_fraction</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> per_sequence_bytes</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    usable </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> int</span><span style="color:var(--astro-code-token-punctuation)">(free_bytes </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-punctuation)"> reserve_fraction)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> usable </span><span style="color:var(--astro-code-token-keyword)">//</span><span style="color:var(--astro-code-foreground)"> per_sequence_bytes</span></span></code></pre>
<p>Feed it values from the deployed model configuration and runtime telemetry. The result is not admission control by itself; workspaces, allocator metadata, partial pages, and transfer buffers also consume memory.</p>
<p>Choose restore or recomputation using measured queue, transfer, decoding, and prefill curves. Report bytes restored, tokens recovered, tokens recomputed, and time saved. A high hit count is misleading if most restored prefixes are short.</p>
<h2 id="failure-modes-and-instrumentation">Failure modes and instrumentation</h2>
<p><strong>Admission overcommit:</strong> requests begin with enough memory for their prompts but no reserve for output growth. Reject or queue before prefill using a configurable output reservation.</p>
<p><strong>Leaked references:</strong> cancellation bypasses a decrement path, leaving shared blocks permanently live. Audit ownership transitions and reconcile allocator totals.</p>
<p><strong>Hash collisions or incomplete keys:</strong> unrelated states share a block. Use a collision-resistant digest and include every state-changing input.</p>
<p><strong>Eviction storms:</strong> a working set slightly larger than capacity repeatedly recomputes prefixes. Track evicted-token recomputation, not just eviction count.</p>
<p><strong>Tail-page waste:</strong> a nominally paged allocator still wastes capacity if blocks are too large for the output-length distribution. Report occupancy inside allocated blocks.</p>
<p>The minimum dashboard needs physical blocks free, reserved, active, shared, and evictable; cache hit tokens; recomputed tokens; allocation latency; and request aborts caused by cache pressure. Those measurements turn KV capacity from an opaque out-of-memory event into a schedulable resource.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Noam Shazeer, “Fast Transformer Decoding: One Write-Head is All You Need,” arXiv, 2019. <a href="https://arxiv.org/abs/1911.02150" rel="noopener noreferrer">https://arxiv.org/abs/1911.02150</a> <a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Woosuk Kwon et al., “Efficient Memory Management for Large Language Model Serving with PagedAttention,” SOSP, 2023. <a href="https://arxiv.org/abs/2309.06180" rel="noopener noreferrer">https://arxiv.org/abs/2309.06180</a> <a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>In Gim et al., “Prompt Cache: Modular Attention Reuse for Low-Latency Inference,” MLSys, 2024. <a href="https://arxiv.org/abs/2311.04934" rel="noopener noreferrer">https://arxiv.org/abs/2311.04934</a> <a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Yuhan Liu et al., “CacheGen: KV Cache Compression and Streaming for Fast Large Language Model Serving,” SIGCOMM, 2024. <a href="https://arxiv.org/abs/2310.07240" rel="noopener noreferrer">https://arxiv.org/abs/2310.07240</a> <a href="https://blog.ecitis.org/kv-cache-memory-systems/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>KV cache</category>
            <category>Memory</category>
            <category>LLM serving</category>
            <enclosure length="61487" type="image/png" url="https://blog.ecitis.org/open-graph/kv-cache-memory-systems.png"/>
        </item>
        <item>
            <title><![CDATA[The Diversification of AI Hardware: Beyond NVIDIA's Dominance]]></title>
            <link>https://blog.ecitis.org/ai-hardware-landscape/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/ai-hardware-landscape/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare the architectures, economics, and tradeoffs behind GPUs, TPUs, custom accelerators, and the challengers to NVIDIA.]]></description>
            <content:encoded><![CDATA[<p>The AI hardware landscape is undergoing a transformation. While NVIDIA maintains its grip on approximately 86-92% of the AI accelerator market, a constellation of alternatives has emerged, each with distinct architectural philosophies and target workloads. This diversification matters for engineers who need to optimize for cost, latency, throughput, or specific model architectures.</p>
<h2 id="why-hardware-diversification-matters">Why Hardware Diversification Matters</h2>
<p>NVIDIA’s dominance in AI accelerators stems from a decade of CUDA ecosystem development and first-mover advantage in deep learning. The company’s H100 and newer Blackwell GPUs remain the default choice for most organizations. However, several factors are driving diversification:</p>
<p><strong>Supply constraints</strong>: Morgan Stanley analysts reported in November 2024 that all of NVIDIA’s 2025 chip production was already sold out. Organizations unable to secure H100/H200 allocations must look elsewhere.</p>
<p><strong>Cost pressure</strong>: Inference costs can exceed training costs by 15x over a model’s lifetime, according to OpenAI’s 2024 figures. At $6.98-7.57 per H100 GPU-hour on major cloud providers, inference-heavy workloads demand alternatives.</p>
<p><strong>Architectural mismatch</strong>: GPUs are general-purpose parallel processors. Workloads with predictable memory access patterns or specific numerical precision requirements can benefit from purpose-built silicon.</p>
<p><strong>Vendor lock-in concerns</strong>: Major cloud providers and AI labs are developing custom chips. JPMorgan projects that custom chips from Google, Amazon, Meta, and OpenAI will account for 45% of the AI chip market by 2028, up from 37% in 2024.</p>
<p>The result is a market where engineers can choose hardware optimized for their specific constraints rather than defaulting to NVIDIA.</p>
<h2 id="google-tpus">Google TPUs</h2>
<p>Google’s Tensor Processing Units represent the most mature alternative to NVIDIA GPUs for large-scale AI workloads. Unlike GPUs, TPUs are application-specific integrated circuits built around systolic arrays optimized for matrix multiplication.</p>
<h3 id="systolic-array-architecture">Systolic Array Architecture</h3>
<p>At the core of every TPU sits a Matrix Multiply Unit (MXU) composed of multiply-accumulate units arranged in a systolic array. The term “systolic” refers to the rhythmic data flow through the structure, analogous to blood pumping through the heart. This architecture exploits a fundamental property of matrix multiplication: it requires O(n^3) compute for O(n^2) bytes of data, making it compute-bound rather than memory-bound when hardware is designed correctly.</p>
<figure><figcaption><strong>Weight-Stationary Systolic Array</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/ai-hardware-landscape-0.webp" width="512" height="769" alt="Weight-Stationary Systolic Array" loading="lazy" decoding="async"></figure>
<p>The weight-stationary dataflow pattern works as follows: engineers preload the weights of matrix B into the individual multiply-accumulate units arranged in a grid. Matrix A’s activation values enter from the left edge and flow horizontally across the array. Each MAC unit multiplies its stored weight by the incoming activation, adds the result to a partial sum arriving from above, and passes both the activation (horizontally) and updated partial sum (vertically) to neighboring units. This arrangement eliminates intermediate memory writes for the entire matrix multiplication.</p>
<p>The original TPU v1 contained a 256x256 array of 8-bit multiply-accumulate units, yielding 65,536 MACs. Because a TPU runs at 700MHz, a TPU can compute 92 TOPS in the matrix unit. During execution of this massive matrix multiply, all intermediate results pass directly between 64K ALUs without any memory access, reducing power consumption substantially compared to architectures that require memory round-trips.</p>
<p>The pipelining characteristics matter for understanding performance. Systolic arrays are heavily pipelined: given that the array is 256 units wide, it takes 256 cycles from when the first element enters until it exits. Twice that many cycles for all data to flow through. However, at peak utilization, all 65,536 processors operate simultaneously.</p>
<p>The systolic array’s weakness emerges with dynamic or irregular computation patterns. Because data flows through the grid on a fixed schedule, the architecture struggles with conditional execution, sparse matrices (unless using SparseCore), and operations that require random access patterns. This inflexibility trades generality for extreme efficiency on its target workload: dense matrix multiplication with predictable access patterns.</p>
<h3 id="xla-compilation-and-hardware-mapping">XLA Compilation and Hardware Mapping</h3>
<p>TPUs were codesigned with the XLA (Accelerated Linear Algebra) compiler to achieve their performance characteristics. The compilation process involves several stages:</p>
<ol>
<li>
<p><strong>Lazy Evaluation and Graph Tracing</strong>: Operations are not executed immediately. Instead, PyTorch/XLA or JAX records operations in an intermediate representation (IR) graph. This process is called “tracing.”</p>
</li>
<li>
<p><strong>HLO Generation</strong>: When results are needed (printing a tensor, saving a checkpoint, or at an explicit synchronization point), the accumulated IR graph converts into HLO (High-Level Opcodes), a representation specific to the XLA compiler.</p>
</li>
<li>
<p><strong>XLA Optimization</strong>: The XLA compiler performs operator fusion, memory layout optimization, and parallelization, then compiles to machine code for the target device. Compiled graphs are cached, so subsequent executions with the same computation graph and input shapes reuse the optimized binary.</p>
</li>
</ol>
<p>For multi-chip scaling, Google’s answer is to make the XLA compiler responsible for coordinating communication between chips. With parallelism dimensions specified by researchers (DP, FSDP, TP, number of slices), the XLA compiler inserts the appropriate hierarchical collectives for the TPU topology. The GSPMD system (<a href="https://arxiv.org/abs/2105.04663">Xu et al., 2021</a>) enables large-scale training with minimal code changes.</p>
<p>A simple JAX example demonstrates the programming model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> jax</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> jax</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> jnp</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> multiply</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> jnp</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">einsum</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;bf,fd-&gt;db&#39;</span><span style="color:var(--astro-code-token-punctuation)">, x, y)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># XLA compiles this function and caches the result</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> jax</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">jit</span><span style="color:var(--astro-code-token-punctuation)">(multiply)(jnp.</span><span style="color:var(--astro-code-token-function)">ones</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">128</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)), jnp.</span><span style="color:var(--astro-code-token-function)">ones</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">), dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">jnp.bfloat16))</span></span></code></pre>
<p>By default, matrix multiplication in JAX on TPUs uses bfloat16 with float32 accumulation. This can be controlled with the <code>precision</code> argument on relevant functions (matmul, dot, einsum).</p>
<h3 id="interconnect-topology-2d-vs-3d-torus">Interconnect Topology: 2D vs 3D Torus</h3>
<p>TPU interconnect topology directly impacts collective operation performance.</p>
<p><strong>2D Torus</strong> (TPU v2, v3, v5e, v6e): Each chip connects to its four nearest neighbors (north, south, east, west). The links wrap around at boundaries, creating a donut-shaped logical topology that eliminates edge chips with fewer connections. A 16x16 grid of 256 TPUs provides uniform bandwidth and latency regardless of which two chips communicate.</p>
<p><strong>3D Torus</strong> (TPU v4, v5p, v7): Each chip connects to six neighbors along three axes. A TPU rack consists of 64 TPUs connected in a 4x4x4 configuration. The composition resembles a Rubik’s Cube, with each TPU chip having 6 ICI (Inter-Chip Interconnect) links in ±X, ±Y, ±Z directions. TPU v5p achieved approximately 4.45 exaflops across 8,960-chip pods using 16x20x28 superpod configurations.</p>
<p>The 3D torus increases bisection bandwidth compared to 2D, which matters for all-to-all communication patterns. Google’s Optical Circuit Switches (OCS) enable flexible topology configuration, including twisted torus variants with better bisection properties. Twisting the torus brings the largest benefit for tensor parallel (TP) operations since there are multiple all-gather and reduce-scatter operations per layer.</p>
<p>Unlike all-reduce used in backpropagation, which maps well to 2D and 3D tori, all-to-all patterns strain bisection bandwidth. The twisted torus can reduce the worst-case number of hops, improving all-to-all collective throughput.</p>
<h3 id="bf16-accumulation-and-numerical-precision">BF16 Accumulation and Numerical Precision</h3>
<p>Inside the MXU, multiplications occur in bfloat16 format while accumulations use full FP32 precision. This design choice has significant implications.</p>
<p>Bfloat16 uses one sign bit, eight exponent bits, and seven mantissa bits. Because it has the same exponent size as float32, it exhibits identical behavior for underflows, overflows, and other numeric instabilities during training. Unlike FP16, which typically requires loss scaling, BF16 comes close to being a drop-in replacement for FP32.</p>
<p>The wide dynamic range makes BF16 highly resistant to overflow and underflow, at the cost of reduced precision from the 7-bit mantissa. This tradeoff works well for neural network training where the exact value matters less than staying within a reasonable range.</p>
<p>One caveat: while BF16’s stability helps pre-training, its low precision can cause rounding errors that accumulate. Modern RL frameworks using different engines for training and inference may see subtle differences in their implementation lead to different rounding errors, creating training-inference mismatch (<a href="https://arxiv.org/abs/2510.26788">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2510.26788">.26788</a>).<p></p>
<p>Checkpoints from TPU training can be deployed on other hardware platforms (CPU or GPU inference) without extensive manual conversions.</p>
<h3 id="tpu-generations">TPU Generations</h3>
<p><strong>TPU v5e</strong> targets cost-sensitive inference and smaller training workloads:</p>
<ul>
<li>128x128 MXU array, 16,384 MACs per cycle</li>
<li>197 BF16 TFLOPS peak</li>
<li>16 GB HBM2e with 819 GB/s bandwidth</li>
<li>Supports training on up to 256 chips</li>
<li>2D torus interconnect topology</li>
</ul>
<p><strong>TPU v5p</strong> is designed for large-scale training:</p>
<ul>
<li>128x128 MXU array, 459 BF16 TFLOPS peak</li>
<li>95 GB HBM2e with 2,765 GB/s bandwidth</li>
<li>3D torus topology connecting up to 8,960 chips per pod</li>
<li>Single slice training for up to 6,144 chips; multislice scaling to 18,432 chips</li>
</ul>
<p><strong>Trillium (TPU v6e)</strong>, generally available since late 2024, represents an architectural inflection point:</p>
<ul>
<li>256x256 MXU array, quadrupling FLOPs per cycle</li>
<li>918 BF16 TFLOPS peak (4.7x improvement over v5e)</li>
<li>32 GB HBM with 1,600 GB/s bandwidth</li>
<li>67% more energy-efficient than v5e</li>
<li>Third-generation SparseCore for ultra-large embedding tables</li>
</ul>
<p><strong>Ironwood (TPU v7)</strong> reached general availability in Q4 2025 with 4,614 TFLOPS peak performance.</p>
<h3 id="benchmark-results">Benchmark Results</h3>
<p>MLPerf 4.1 training benchmarks show Trillium delivers up to 1.8x better performance-per-dollar compared to TPU v5p and 99% scaling efficiency across data-center networks using Cloud TPU multislice technology, outperforming the 94% scaling efficiency of v5p clusters within a single ICI domain.</p>
<p>BERT training completes 2.8x faster on TPUs than on A100 GPUs, while T5-3B model training finishes in 12 hours versus 31 hours on comparable GPU infrastructure. MLPerf results show TPU v5e leading in 8 of 9 training categories.</p>
<p>For inference, MLPerf 5.0 shows Trillium delivering 3.5x throughput improvement for queries/second on Stable Diffusion XL compared to TPU v5e. The cost to generate 1000 images is 22 cents on Trillium, 35% less than v5e.</p>
<p>Google’s TPU v5e reaches similar throughput to H100 by using more chips at lower per-chip speed. Eight TPU v5e chips generate approximately 2,175 tokens/sec on Llama2-70B at a cost of $11/hour, whereas 8 H100 GPUs cost an order of magnitude more.</p>
<p>One computer vision startup reported switching from 128 H100s to TPU v6e and reducing their monthly inference bill from $340K to $89K. Midjourney reportedly moved from NVIDIA A100/H100 clusters to TPU v6e pods, dropping monthly spend from approximately $2.1 million to under $700K.</p>
<h2 id="cerebras-wafer-scale-integration">Cerebras: Wafer-Scale Integration</h2>
<p>Cerebras takes the opposite approach from multi-chip systems: instead of connecting many small chips, they build single chips the size of entire silicon wafers.</p>
<h3 id="the-wafer-scale-engine">The Wafer-Scale Engine</h3>
<p>The WSE-3, powering the CS-3 system, occupies 46,225 mm^2 of silicon, roughly 57 times larger than NVIDIA’s H100 (826 mm^2). Built on TSMC 5nm, it contains:</p>
<ul>
<li>4 trillion transistors</li>
<li>900,000 AI-optimized compute cores</li>
<li>125 petaflops peak AI performance</li>
<li>44 GB on-chip SRAM</li>
<li>21 PB/s memory bandwidth (7,000x more than H100)</li>
<li>214 Pb/s fabric bandwidth</li>
</ul>
<h3 id="yield-management-and-interconnect-redundancy">Yield Management and Interconnect Redundancy</h3>
<p>Building a wafer-scale chip requires solving the yield problem that killed previous attempts at wafer-scale integration. The WSE-3 has a die area of 462 cm^2, resulting in higher defect probability than smaller dies where yields typically exceed 90%.</p>
<p>Cerebras addresses this through several mechanisms:</p>
<p><strong>Fine-grained cores</strong>: Each WSE-3 core occupies 0.05mm^2, while an H100 SM core is 6mm^2. The smaller the core, the better fault tolerance becomes.</p>
<p><strong>Redundant mesh routing</strong>: A mesh topology improves resilience against manufacturing defects. Cerebras includes redundant links and cores across the wafer. When defective regions are detected, the hardware driver performs a remapping process, reconfiguring the interconnect to bypass faulty areas and preserve a virtually intact two-dimensional mesh topology. This process hides defects from developers, who program the system as if it were built on an ideal mesh network.</p>
<p><strong>Intelligent sparing</strong>: Unlike previous approaches requiring massive redundancy overhead, the architecture achieves high yield with approximately 1% spare cores through intelligent routing that leverages nearby cores to replace defective units.</p>
<p><strong>Distributed power delivery</strong>: The system incorporates over 300 voltage regulation modules distributed across the wafer’s surface, providing redundancy in power delivery and allowing independent regulation for each reticle.</p>
<p>In April 2021, Cerebras announced 100% claimed yield for the WSE-2, achieved by designing a system where any manufacturing defect can be bypassed.</p>
<h3 id="dataflow-architecture-and-the-memory-wall">Dataflow Architecture and the Memory Wall</h3>
<p>The Cerebras architecture uses fine-grained dataflow compute cores where all computation is triggered by data arrival. The fabric transports both data and associative control directly in hardware. Once cores receive data, the hardware triggers a lookup of instructions to execute.</p>
<p>This addresses the memory wall problem endemic to traditional GPU clusters. The WSE keeps 44 GB of memory directly on the same die as compute cores, eliminating the bottleneck of shuttling data between HBM and compute units. At the system level, bandwidth reaches 20 PB/s.</p>
<h3 id="weight-streaming-for-large-models">Weight Streaming for Large Models</h3>
<p>For models exceeding on-chip capacity, Cerebras developed weight streaming execution. Rather than fitting the entire model on-chip, this mode loads the neural network one layer at a time.</p>
<p>Cerebras stores all model weights externally in MemoryX units and streams weights onto the CS-3 as needed to compute each layer. The weights are never stored on the system, not even temporarily. As weights stream through, the CS-3 performs computation using the underlying dataflow mechanisms. Each individual weight triggers computation as an AXPY operation. Once complete, the weight is discarded and hardware moves to the next element.</p>
<p>MemoryX configurations scale from 24TB and 36TB for enterprise customers to 120TB and 1,200TB for hyperscalers. This allows a single CS-3 system to train models up to 24 trillion parameters.</p>
<h3 id="compiler-technology">Compiler Technology</h3>
<p>The Cerebras compiler extracts the operation graph from code and aligns operations with compatible kernels in the Cerebras Software Platform. Each matched kernel corresponds to a layer in the network’s dataflow graph.</p>
<p>Cerebras developed MACH (Multiple-Architecture Compiler for Advanced Computing Hardware) specifically for massively-parallel, spatial, dataflow architectures. Additionally, research has introduced SPADA, a spatial dataflow architecture programming language with a rigorous dataflow semantics framework defining routing correctness, data races, and deadlocks. SPADA enables developers to express complex parallel patterns in 6-8x less code than CSL with near-ideal weak scaling.</p>
<h3 id="benchmark-results-1">Benchmark Results</h3>
<p>Cerebras reports inference performance of 2,100 tokens/second on Llama 3.2 70B, 16x faster than GPU solutions and 68x faster than hyperscale clouds as measured by Artificial Analysis. For Llama 3.1-405B, Cerebras Inference generates 969 output tokens per second at 128K full context length and 16-bit precision.</p>
<p>For Llama 4 Scout, Cerebras achieves over 2,600 tokens per second, 19x faster than the fastest GPU solutions per Artificial Analysis verification.</p>
<p>Cerebras benchmarked the CS-3 at over 21x faster inference than NVIDIA’s Blackwell B200 GPU running Llama 3 70B with 1024 input and 4096 output segment lengths. Using SemiAnalysis benchmarks, CS-3 is 32% lower cost than B200 while delivering results 21x faster, accounting for both capex and opex including energy costs.</p>
<p>A full cluster of 2048 CS-3s delivers 256 exaflops of AI compute and can train Llama2-70B from scratch in less than a day.</p>
<h3 id="use-cases">Use Cases</h3>
<p>Wafer-scale architecture excels when model weights fit in on-chip memory and memory bandwidth dominates performance. Large language model inference, where the same weights are reused across many tokens, is a natural fit. The WSE-3 was recognized by TIME Magazine as a Best Invention of 2024.</p>
<p>The tradeoff is flexibility. You cannot incrementally add compute; you either use the full wafer or you do not.</p>
<h2 id="groq-deterministic-inference-at-scale">Groq: Deterministic Inference at Scale</h2>
<p>Groq builds hardware specifically for inference, not training. Their Language Processing Unit (LPU) takes a fundamentally different architectural approach from both GPUs and other accelerators.</p>
<h3 id="the-architectural-bet-determinism-over-flexibility">The Architectural Bet: Determinism Over Flexibility</h3>
<p>Groq’s LPU architecture is built on a central premise: deterministic execution enables optimizations impossible on dynamically scheduled systems. The LPU can achieve deterministic execution by avoiding traditional reactive hardware components (branch predictors, arbiters, reordering buffers, caches) and having all execution explicitly controlled by the compiler.</p>
<p>The LPU is a VLIW-like (Very Long Instruction Word) pipeline that processes one instruction stream at a time. Unlike GPUs with many cores and thread contexts, Groq feeds AI model tokens through a single, wide pipeline of functional units, executing all operations in lock-step with no kernel switching. Every clock cycle performs useful work.</p>
<p>The compiler pre-computes the entire execution graph, including inter-chip communication patterns, down to individual clock cycles. This static scheduling eliminates non-determinism. If Groq’s compiler says a task will take 28.5 milliseconds, it takes exactly 28.5 milliseconds, every time. This predictability is valuable for real-time systems where tail latency is a critical metric.</p>
<h3 id="tensor-streaming-processors-vs-systolic-arrays">Tensor Streaming Processors vs Systolic Arrays</h3>
<p>Groq’s initial name for their ASIC was the Tensor Streaming Processor (TSP), later rebranded as the LPU. The TSP features a functionally sliced microarchitecture where memory units are interleaved with vector and matrix computation units.</p>
<p>Unlike systolic arrays where data flows through a fixed grid, tensor streaming processors use a “data-stream” design where tensors flow through functional units in a more flexible pattern. This enables tensor parallelism where individual operations distribute across multiple LPUs so single forward passes complete faster, rather than processing more requests in parallel.</p>
<p>The first-generation TSP yields computational density exceeding 1 TeraOp/s per square mm of silicon for its 25x29mm 14nm chip operating at 900 MHz nominal clock frequency. The second-generation LPU v2 will use Samsung’s 4nm process node.</p>
<h3 id="sram-only-architecture">SRAM-Only Architecture</h3>
<p>The LPU uses SRAM as primary weight storage, not cache, with approximately 230 MB per chip. This is fundamentally different from GPU architectures that use HBM.</p>
<p>SRAM is up to 100x faster than the HBM found in GPUs. This direct access allows compute units to pull in weights at full speed. There are no caches to miss, no external memory delays to manage. The Groq compiler can plan the exact execution path of every instruction before the chip runs.</p>
<p>The tradeoff: no useful models fit on a single chip. Groq systems connect hundreds of LPUs with tensor parallelism. For the Mixtral model, Groq connects 8 racks of 9 servers each with 8 chips per server, totaling 576 chips to serve one model instance. The significant cost of SRAM and its lower capacity compared to DRAM present obstacles.</p>
<h3 id="batch-size-1-the-sweet-spot">Batch Size 1: The Sweet Spot</h3>
<p>Groq’s architecture is optimized for “Batch Size 1.” Because memory bandwidth is so high and overhead so low, the LPU processes a single user’s request with maximum efficiency without waiting for other requests.</p>
<p>On GPUs, achieving ultra-low latency requires batch size 1, which makes the GPU expensive per token because most processing power sits idle waiting for memory. The LPU achieves 300-500 tokens per second while keeping its internal pipeline nearly 100% full at batch size 1.</p>
<p>This design choice means Groq is not competitive architecturally for throughput-optimized scenarios where batching many requests together amortizes memory access costs. Groq targets latency-critical applications.</p>
<h3 id="inference-performance">Inference Performance</h3>
<p>Artificial Analysis independently benchmarked Groq serving Llama 3.3 70B at 1,665 output tokens per second using speculative decoding, a 6x improvement over their previous 250 T/s endpoint without speculative decoding.</p>
<p>Speculative decoding uses a smaller “draft model” (e.g., Llama 8B) to rapidly guess subsequent tokens, which the primary model then verifies. Verification is faster than generation. Groq implemented this without compromising response quality per independent evaluations.</p>
<p>Other benchmarks:</p>
<ul>
<li>Gemma 7B: 814 tokens per second (Artificial Analysis)</li>
<li>LLaMA 3: over 800 tokens per second</li>
<li>Llama 2 Chat 70B: 241 tokens per second, more than double other providers (ArtificialAnalysis.ai)</li>
<li>LLMPerf Leaderboard: 185 tokens/s average, 3-18x faster than other cloud inference providers</li>
</ul>
<p>Groq offers Llama-3.3-70B-Specdec at $0.59 per million input tokens and $0.99 per million output tokens.</p>
<h3 id="limitations">Limitations</h3>
<p>Groq is inference-only. You cannot train or fine-tune models on LPUs. The initial capital expenditure for a Groq rack is high due to requiring hundreds of chips per model instance. However, the company claims near-100% compute utilization during inference results in lower energy cost per token than GPUs.</p>
<p>In December 2025, NVIDIA agreed to purchase assets from Groq for approximately $20 billion in what Groq described as a non-exclusive licensing deal.</p>
<h2 id="apple-silicon-for-ai">Apple Silicon for AI</h2>
<p>Apple’s M-series chips offer a different value proposition: running AI workloads on hardware that millions of people already own.</p>
<h3 id="neural-engine-architecture">Neural Engine Architecture</h3>
<p>Each M-series chip includes a dedicated Neural Engine for accelerating machine learning operations. The M4 Neural Engine performs 38 trillion operations per second (TOPS), 60x faster than the first Neural Engine in A11 Bionic. For comparison, M1’s Neural Engine achieved 11 TOPS, while M2 and M3 reached 15.8 TOPS.</p>
<p>The Neural Engine’s design favors specialized convolutional and quantized operations. Large transformer-based LLMs depend heavily on dense matrix multiplications more efficiently handled by GPUs or CPUs, which is why full LLM execution solely on the Neural Engine remains aspirational in 2025, with current deployments relying on hybrid CPU/GPU/ANE orchestration.</p>
<p>Apple’s AMX (Advanced Matrix Extensions) supports fixed matrix dimensions (e.g., 4x4 or 8x8) for operations. The M4 standardized ARM SME (Scalable Matrix Extension). Core ML blends CPU, GPU, and ANE to create hybrid execution plans exploiting all available engines on a given device.</p>
<p>Unlike GPUs, there is no public framework for directly programming the Neural Engine. Developers use Core ML, which handles hardware scheduling transparently.</p>
<h3 id="unified-memory-architecture">Unified Memory Architecture</h3>
<p>M-series processors use unified memory where CPU, GPU, and Neural Engine share the same pool of high-bandwidth memory. There is no separate “system RAM” and “GPU VRAM.” This eliminates the PCIe bottleneck that limits GPU memory capacity in traditional systems.</p>
<p>Graphics resources, textures, images, and geometry data can be shared between CPU and GPU with no overhead since there’s no need to copy data across a PCIe bus. Every component in the SoC has direct access to RAM: Media Engine, Neural Engine, video encode/decode units. Code passes a pointer to data in memory and the hardware unit processes data in place.</p>
<p>The M4 Max provides up to 128 GB of unified memory with 546 GB/s bandwidth. The M3 Ultra in Mac Studio configurations offers up to 192 GB. This enables running models that would require multiple GPUs on conventional hardware:</p>
<ul>
<li>Llama 70B runs on Mac Studio M2 Ultra (192 GB) at 8-12 tokens per second</li>
<li>DeepSeek’s 671B model can run on 512 GB Mac Studio configurations</li>
<li>Models up to 30B parameters run locally on M4 Max or M3 Ultra</li>
</ul>
<h3 id="mlx-vs-mps-how-they-exploit-unified-memory-differently">MLX vs MPS: How They Exploit Unified Memory Differently</h3>
<p>MPS (Metal Performance Shaders) is an abstraction layer translating standard GPU operations into optimized Metal instructions. It enables frameworks like PyTorch and JAX to run on Apple Silicon with minimal code changes.</p>
<p>MLX, Apple’s machine learning framework, was designed specifically for Apple Silicon. The key difference is how they handle memory:</p>
<p><strong>MPS</strong>: Treats the GPU as a separate device following the traditional CUDA model. While it benefits from unified memory eliminating physical data copies, the programming model still conceptually separates CPU and GPU memory spaces.</p>
<p><strong>MLX</strong>: Natively exploits unified memory at the framework level. Operations can run on CPU or GPU without data movement between memory pools. The framework exposes this capability directly in its API design.</p>
<p>Benchmarks show MLX achieves the highest sustained generation throughput for LLM inference on Apple Silicon. In comparative testing, MLX was found to be 3x faster than PyTorch MPS on the same M1-Pro GPU.</p>
<p>The M5 chip (October 2025) provides 19-27% performance improvement over M4 due to increased memory bandwidth (153 GB/s versus 120 GB/s). MLX now takes advantage of Neural Accelerators in M5, which provide dedicated matrix-multiplication operations yielding up to 4x speedup compared to M4 baseline for time-to-first-token.</p>
<h3 id="power-efficiency-analysis">Power Efficiency Analysis</h3>
<p>Power consumption provides Apple Silicon’s clearest advantage:</p>
<ul>
<li>M3/M4 Max: 40-80W under load during LLM inference</li>
<li>RTX 4090: up to 450W for the same task</li>
<li>M3 Max generating from Llama 7B: approximately 50W</li>
<li>Projected M4 Max: 96-100 tokens/s on 8B Q4_K_M model at similar power</li>
</ul>
<p>Research introducing “intelligence per watt” as a metric shows 5.3x improvement from 2023-2025, driven by both algorithmic advances and accelerator improvements. Benchmarks show ResNet-50 runs about 3x slower on Apple Silicon than RTX 4090, but with over 80% lower energy consumption.</p>
<p>The M3 Max can generate 30-40 tokens per second with quantized Llama 7B while remaining silent and energy efficient.</p>
<h3 id="use-cases-1">Use Cases</h3>
<p>For edge deployment, mobile inference, or power-constrained environments, Apple Silicon efficiency matters. The tradeoff is absolute performance: NVIDIA cards remain faster for raw throughput. But for engineers who need local, private inference without dedicated GPU hardware, Apple Silicon provides an accessible path.</p>
<h2 id="amd-gpus-and-rocm">AMD GPUs and ROCm</h2>
<p>AMD’s Instinct accelerators offer the most direct competition to NVIDIA in the data center GPU market, though with a significant software ecosystem gap.</p>
<h3 id="cdna-vs-rdna-two-architectures-two-markets">CDNA vs RDNA: Two Architectures, Two Markets</h3>
<p>AMD maintains two distinct GPU architectures:</p>
<p><strong>CDNA (Compute DNA)</strong>: Designed for datacenters, AI, and HPC. Compared to its predecessor GCN, CDNA removed all hardware related to graphics acceleration (graphics caches, tessellation hardware, ROPs, display engine) and added dedicated matrix compute hardware. CDNA has had tensor-like functional units since 2020, with increased throughput and number format support added in CDNA 2 (2021) and CDNA 3 (2023).</p>
<p><strong>RDNA (Radeon DNA)</strong>: Consumer and workstation GPUs with some AI acceleration. RDNA 3 includes Wave MMA (matrix multiply-accumulate) instructions supporting FP16, BF16, INT8, and INT4 data types, improving inference performance compared to RDNA 2. However, RDNA’s AI acceleration is limited compared to CDNA’s dedicated matrix cores.</p>
<p>The CDNA 4 architecture (MI350 series, 2025) adds native FP6 and FP4 support. AMD doubled FP8/BF16 throughput and fine-tuned chiplet designs for larger GPU clusters. FP6 processes at twice the rate of FP8 on AMD architecture, unlike NVIDIA where FP6 processes at the same rate as FP8. MI350 offers up to 288 GB HBM3e memory per GPU.</p>
<p>AMD announced UDNA, which will unify RDNA and CDNA into one microarchitecture, ending the split that began in 2019.</p>
<h3 id="matrix-cores-vs-tensor-cores">Matrix Cores vs Tensor Cores</h3>
<p><strong>AMD MI300X (CDNA 3)</strong>:</p>
<ul>
<li>4 Matrix Cores per Compute Unit, 304 CUs per GPU</li>
<li>Multi-chip module design: 8 accelerator complex dies (XCD) on TSMC 5nm</li>
<li>Each compute die: 38 Compute Units, 4MB L2 cache</li>
<li>192 GB HBM3 memory, 5.3 TB/s bandwidth</li>
<li>1.31 petaflops peak at FP16</li>
</ul>
<p><strong>NVIDIA H100 (Hopper)</strong>:</p>
<ul>
<li>4 Tensor Cores per SM, 132 SMs per GPU</li>
<li>Tensor Cores support FP8, FP16, BF16, TF32, FP64, INT8 for A/B matrices</li>
<li>80 GB HBM2e memory, 3.35 TB/s bandwidth</li>
<li>Fourth-generation Tensor Cores work up to 6x faster between chips compared to A100</li>
</ul>
<p>Raw instruction throughput significantly favors MI300X: at times 5x faster than H100, at worst roughly 40% faster for INT32, FP32, FP16, and INT8 compute.</p>
<p>However, real-world training shows different results. For BF16, H100 and H200 achieve roughly 720 TFLOP/s against their marketed 989.5 TFLOP/s, while MI300X reaches only 620 TFLOP/s compared to marketed 1,307 TFLOP/s. Despite higher marketed specs, MI300X is 14% slower than H100/H200 in practice for training workloads (SemiAnalysis benchmark, December 2024).</p>
<p>MI300X has better memory bandwidth (5.3 TB/s vs 3.35 TB/s for H100) and 192 GB vs 80 GB capacity, but H100 exhibits 57% lower memory latency.</p>
<p>For inference, MI300X delivers 40% lower latency for memory-bound Llama2-70B inference and 2.7x faster time to first token for Qwen models. MI300X performs better than H100 SXM at small and large batch sizes (1, 2, 4, 256, 512, 1024) but worse at medium batch sizes.</p>
<p>Cost comparison: MI300X processes 1 million tokens at $11.11 with batch size 4, versus H100&#39;s $14.06, creating a 21% cost advantage for AMD.</p>
<h3 id="rocm-and-hip-the-cuda-compatibility-layer">ROCm and HIP: The CUDA Compatibility Layer</h3>
<p>ROCm (Radeon Open Compute) is AMD’s answer to CUDA. HIP (Heterogeneous-compute Interface for Portability) is a C++ runtime API and kernel language enabling platform-independent GPU programs that run on both AMD and NVIDIA GPUs.</p>
<p>HIP intentionally reuses existing <code>torch.cuda</code> interfaces:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">cuda </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">     # Default HIP device</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">cuda0 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda:0&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # &#39;rocm&#39; or &#39;hip&#39; are not valid, use &#39;cuda&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Detecting HIP vs CUDA at runtime</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> and</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">version</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">hip</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # HIP-specific code</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">elif</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> and</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">version</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # CUDA-specific code</span></span></code></pre>
<p>AMD provides HIPIFY tools for translating CUDA code:</p>
<ul>
<li><strong>hipify-clang</strong>: Parses CUDA code into an AST, traverses it with transformation matchers, and produces HIP source</li>
<li><strong>hipify-perl</strong>: Uses pattern matching for simpler translations</li>
</ul>
<p>On NVIDIA GPUs, HIP is a thin layer over CUDA, so the two code types interoperate on nvcc platforms. Setting <code>HIP_PLATFORM=amd</code> makes hipcc call the clang compiler and ROCclr runtime; <code>HIP_PLATFORM=nvidia</code> makes hipcc call nvcc.</p>
<p>HIP 7.0 (second half of 2025) will align HIP C++ more closely with CUDA semantics, refine error handling, and streamline header structures to reduce effort maintaining portable codebases.</p>
<p>Performance benchmarks in 2025 show CUDA typically outperforms ROCm by 10-30% in compute-intensive workloads, though ROCm has narrowed the gap.</p>
<h3 id="infinity-fabric-interconnect">Infinity Fabric Interconnect</h3>
<p>Each MI300X has up to seven Infinity Fabric links, each with 16 lanes. Fourth-generation Infinity Fabric supports up to 32 Gbps per lane, yielding 128 GB/s bidirectional bandwidth per link.</p>
<p>The ThinkSystem SR685a V3 includes 8x MI300X GPUs fully interconnected using Infinity Fabric, providing 128 GB/s bandwidth between each of the 8 GPUs for a total of 896 GB/s. Aggregated theoretical bandwidth per GPU reaches 336 GB/s; practical execution yields 310-330 GB/s.</p>
<p>xGMI (External Global Memory Interconnect) enables efficient peer-to-peer GPU communication. AMD’s Infinity Fabric links up to eight MI300X or MI355X GPUs in a fully connected mesh.</p>
<p>For cluster scaling, AMD’s high-speed accelerator network consists of GPUs connected via Infinity Fabric mesh for low-latency inter-GPU communication. The backend scale-out network uses PCIe 5.0 NICs with RoCEv2, scaling clusters while minimizing congestion. ND MI300X v5 deployments on Azure scale to thousands of GPUs with 3.2 Tb/s interconnect bandwidth per VM.</p>
<p>Fifth-generation Infinity Fabric will connect CPUs, GPUs, and accelerators from node to rack scale, forming the backbone of “Helios” rack deployments.</p>
<h3 id="roadmap">Roadmap</h3>
<ul>
<li><strong>MI325X</strong> (October 2024): 256 GB HBM3E, 6 TB/s bandwidth</li>
<li><strong>MI350</strong> (2025): CDNA 4 architecture with FP4/FP6 precision formats</li>
<li><strong>MI355X</strong> (late 2025): Projected 2.7x tokens per second versus MI325X</li>
</ul>
<p>AMD holds approximately 7% of the AI GPU market.</p>
<h2 id="intel-gaudi-and-arc">Intel: Gaudi and Arc</h2>
<p>Intel pursues AI acceleration through two product lines: Gaudi for data center training and inference, and Arc for workstation and consumer applications.</p>
<h3 id="gaudi-3-accelerator">Gaudi 3 Accelerator</h3>
<p>The Gaudi 3, manufactured on TSMC 5nm, uses a different architecture from GPUs:</p>
<ul>
<li>64 tensor processor cores (256x256 MAC structure)</li>
<li>8 matrix multiplication engines (256-bit vector processors)</li>
<li>128 GB HBM2e with 3.7 TB/s bandwidth</li>
<li>96 MB on-die SRAM cache with 19.2 TB/s bandwidth</li>
<li>1,835 BF16/FP8 TFLOPS at 600W TDP</li>
<li>24 integrated 200 GbE networking interfaces</li>
</ul>
<p>Intel claims Gaudi 3 delivers 50% better average inference performance and 40% better power efficiency than NVIDIA H100 at lower cost. Dell’s AI platform with Gaudi 3 reports 70% better price-performance for Llama 3 80B inference throughput versus H100.</p>
<p>Gaudi 3 entered volume production in Q3 2024 and is available through OEM systems and IBM Cloud.</p>
<h3 id="arc-gpus-and-oneapi">Arc GPUs and oneAPI</h3>
<p>Intel’s Arc GPUs target workstation AI inference rather than data center training. The Arc Pro B-Series, launched in May 2025:</p>
<ul>
<li><strong>Arc Pro B60</strong>: 24 GB memory, up to 2.7x faster than NVIDIA RTX A2000 Ada on LLM workloads</li>
<li><strong>Arc Pro B50</strong>: 16 GB memory</li>
</ul>
<p>The oneAPI programming model provides a unified interface across Intel CPUs, GPUs, and accelerators. The oneDNN library and PyTorch 2.7 optimizations support inference on Intel Core Ultra processors and Arc GPUs. The Arc A770 (16 GB) can run Llama2 7B and Llama3 models locally.</p>
<h2 id="other-players">Other Players</h2>
<p>Several companies are developing alternative AI accelerator architectures.</p>
<h3 id="sambanova-systems">SambaNova Systems</h3>
<p>SambaNova’s Reconfigurable Dataflow Processing Unit (RDPU) emphasizes software-defined hardware. Their SN40L RDU supports up to 5 trillion parameters in a single system node. The company achieved 129 tokens/second on 405B-parameter models and was named “Most Respected Private Semiconductor Company” at the 2024 GSA Awards.</p>
<h3 id="graphcore">Graphcore</h3>
<p>Graphcore’s Intelligence Processing Unit (IPU) emphasizes fine-grained parallelism and in-processor memory. The architecture keeps more of the model on-chip to reduce memory bottlenecks. SoftBank acquired Graphcore in July 2024.</p>
<h3 id="tenstorrent">Tenstorrent</h3>
<p>Led by chip architect Jim Keller, Tenstorrent designs RISC-V based AI chips. The company raised over $693 million in December 2024 at a $2.6+ billion valuation, with investors including Samsung, Bezos Expeditions, LG Electronics, Hyundai, and Fidelity. Tenstorrent plans to release a new AI processor every two years and has launched Grayskull and Wormhole chips.</p>
<h2 id="what-this-means-for-engineers">What This Means for Engineers</h2>
<p>Hardware selection depends on workload characteristics, scale, and constraints.</p>
<h3 id="training-workloads">Training Workloads</h3>
<p><strong>Large-scale training (100B+ parameters)</strong>: Google TPUs or NVIDIA H100/H200 remain the practical choices. TPUs offer cost advantages for organizations willing to adopt JAX; NVIDIA provides the most mature ecosystem.</p>
<p><strong>Medium-scale training</strong>: AMD MI300X provides a cost-effective alternative if your workloads align with ROCm’s supported configurations. Intel Gaudi 3 offers competitive price-performance for supported model architectures.</p>
<p><strong>Experimentation and research</strong>: Cloud TPU access through Google Cloud, or NVIDIA GPUs through various providers, offer flexibility without capital commitment.</p>
<h3 id="inference-workloads">Inference Workloads</h3>
<p><strong>Latency-critical inference</strong>: Groq’s LPU delivers unmatched speed for supported models. Cerebras CS-3 offers 21x faster inference than Blackwell B200. If your application requires sub-second response times at scale, evaluate both.</p>
<p><strong>Cost-optimized inference</strong>: TPU v5e and Trillium provide strong price-performance for batch inference. Organizations report 60-70% cost reductions migrating from NVIDIA to TPUs.</p>
<p><strong>Large model inference</strong>: Cerebras CS-3 handles models that exceed typical GPU memory. The MI300X’s 192 GB HBM enables single-GPU deployment of 70B parameter models.</p>
<p><strong>Edge and local inference</strong>: Apple Silicon with MLX provides the most accessible path for on-device inference. Intel Arc and Gaudi enable Windows-based local deployment.</p>
<h3 id="targeting-different-hardware-code-examples">Targeting Different Hardware: Code Examples</h3>
<p><strong>JAX/TPU</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> jax</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> jax</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> jnp</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@jax</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">jit</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> matmul</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> jnp</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(x, y)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Runs on TPU if available, compiles via XLA</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> matmul</span><span style="color:var(--astro-code-token-punctuation)">(jnp.</span><span style="color:var(--astro-code-token-function)">ones</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">)), jnp.</span><span style="color:var(--astro-code-token-function)">ones</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">)))</span></span></code></pre>
<p><strong>PyTorch/ROCm (AMD)</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Same code as CUDA, HIP translates automatically</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Not &#39;rocm&#39; or &#39;hip&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(x, y)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Check which backend at runtime</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">version</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">hip</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Running on AMD via HIP&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>MLX (Apple Silicon)</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Operations seamlessly use CPU or GPU via unified memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">normal</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">normal</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(x, y)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">(result)</span><span style="color:var(--astro-code-token-comment)">  # Explicit evaluation</span></span></code></pre>
<h3 id="cost-considerations">Cost Considerations</h3>
<p>GPU cloud pricing in 2025:</p>
<ul>
<li>H100: $2.10-7.57 per GPU-hour depending on provider and commitment</li>
<li>H200: From $2.14-2.50 per GPU-hour</li>
<li>TPU v5e: Competitive with H100 at large scale</li>
<li>MI300X: $3.99 per GPU-hour (RunPod)</li>
</ul>
<p>For most organizations, rental makes more sense than purchase given hardware obsolescence risk and 6-12 month procurement timelines. Hidden costs include data egress fees, storage, and idle time; many teams waste 30-50% of budget on provisioned but unused capacity.</p>
<p>The AI hardware market is fragmenting. NVIDIA’s dominance will likely persist for training workloads requiring maximum flexibility, but specialized inference hardware, cloud TPUs, and alternative architectures are capturing significant workloads. Engineers who understand these options can optimize for their specific constraints rather than defaulting to the most expensive general-purpose solution.</p>
<h2 id="references">References</h2>
<ol>
<li><a href="https://cloud.google.com/blog/products/compute/introducing-trillium-6th-gen-tpus">Google Cloud Blog: Introducing Trillium</a></li>
<li><a href="https://docs.cloud.google.com/tpu/docs/v5p">Google Cloud TPU v5p Documentation</a></li>
<li><a href="https://docs.cloud.google.com/tpu/docs/v5e">Google Cloud TPU v5e Documentation</a></li>
<li><a href="https://docs.cloud.google.com/tpu/docs/system-architecture-tpu-vm">Google Cloud TPU Architecture</a></li>
<li><a href="https://cloud.google.com/blog/products/ai-machine-learning/bfloat16-the-secret-to-high-performance-on-cloud-tpus">Google Cloud Blog: BFloat16 on Cloud TPUs</a></li>
<li><a href="https://cloud.google.com/blog/products/compute/trillium-mlperf-41-training-benchmarks">Google Cloud Blog: Trillium MLPerf 4.1 Training Benchmarks</a></li>
<li><a href="https://www.cerebras.ai/blog/cerebras-cs3">Cerebras CS-3 Announcement</a></li>
<li><a href="https://www.cerebras.ai/blog/100x-defect-tolerance-how-cerebras-solved-the-yield-problem">Cerebras: 100x Defect Tolerance</a></li>
<li><a href="https://docs.cerebras.net/en/2.0.3/wsc/cerebras-basics/cerebras-execution-modes.html">Cerebras Weight Streaming Documentation</a></li>
<li><a href="https://www.cerebras.ai/press-release/cerebras-inference-llama-405b">Cerebras Llama 3.1 405B Performance</a></li>
<li><a href="https://www.cerebras.ai/blog/cerebras-cs-3-vs-nvidia-dgx-b200-blackwell">Cerebras CS-3 vs NVIDIA DGX B200</a></li>
<li><a href="https://groq.com/lpu-architecture">Groq LPU Architecture</a></li>
<li><a href="https://groq.com/blog/inside-the-lpu-deconstructing-groq-speed">Groq: Inside the LPU</a></li>
<li><a href="https://groq.com/blog/groq-first-generation-14nm-chip-just-got-a-6x-speed-boost-introducing-llama-3-1-70b-speculative-decoding-on-groqcloud">Groq Speculative Decoding Launch</a></li>
<li><a href="https://newsletter.semianalysis.com/p/groq-inference-tokenomics-speed-but">SemiAnalysis: Groq Inference Tokenomics</a></li>
<li><a href="https://www.apple.com/newsroom/2024/05/apple-introduces-m4-chip/">Apple M4 Introduction</a></li>
<li><a href="https://www.apple.com/newsroom/2025/10/apple-unleashes-m5-the-next-big-leap-in-ai-performance-for-apple-silicon/">Apple M5 Announcement</a></li>
<li><a href="https://machinelearning.apple.com/research/exploring-llms-mlx-m5">Apple MLX Research on M5</a></li>
<li><a href="https://towardsdatascience.com/mlx-vs-mps-vs-cuda-a-benchmark-c5737ca6efc9/">MLX vs MPS vs CUDA Benchmark</a></li>
<li><a href="https://rocm.docs.amd.com/en/latest/conceptual/gpu-arch/mi300.html">AMD MI300X Specifications</a></li>
<li><a href="https://newsletter.semianalysis.com/p/mi300x-vs-h100-vs-h200-benchmark-part-1-training">SemiAnalysis MI300X vs H100 Benchmark</a></li>
<li><a href="https://www.amd.com/en/blogs/2024/engineering-insights-unveiling-mlperf-results-on.html">AMD MLPerf Results</a></li>
<li><a href="https://docs.pytorch.org/docs/stable/notes/hip.html">PyTorch HIP Semantics</a></li>
<li><a href="https://rocm.docs.amd.com/projects/HIP/en/latest/how-to/hip_porting_guide.html">ROCm HIP Porting Guide</a></li>
<li><a href="https://rocm.blogs.amd.com/software-tools-optimization/mi300x-rccl-xgmi/README.html">AMD Infinity Fabric Interconnect</a></li>
<li><a href="https://www.intel.com/content/www/us/en/content-details/817486/intel-gaudi-3-ai-accelerator-white-paper.html">Intel Gaudi 3 White Paper</a></li>
<li><a href="https://www.intc.com/news-events/press-releases/detail/1741/computex-2025-intel-unveils-new-gpus-for-ai-and">Intel Arc Pro B-Series Launch</a></li>
<li><a href="https://carboncredits.com/nvidia-controls-92-of-the-gpu-market-in-2025-and-reveals-next-gen-ai-supercomputer/">NVIDIA Market Share Analysis</a></li>
<li><a href="https://finance.yahoo.com/news/nvidias-big-tech-customers-might-also-be-its-biggest-competitive-threat-153032596.html">JPMorgan Custom Chip Projections</a></li>
<li><a href="https://pitchbook.com/profiles/company/171627-13">Tenstorrent Funding</a></li>
<li><a href="https://www.gmicloud.ai/blog/a-guide-to-2025-gpu-cloud-pricing-comparison">GPU Cloud Pricing Comparison 2025</a></li>
<li><a href="https://jax-ml.github.io/scaling-book/tpus/">JAX Framework Documentation</a></li>
<li><a href="https://docs.jax.dev/en/latest/pallas/tpu/matmul.html">JAX Matrix Multiplication on TPU</a></li>
<li><a href="https://henryhmko.github.io/posts/tpu/tpu.html">TPU Deep Dive</a></li>
<li><a href="https://arxiv.org/abs/2105.04663">GSPMD Paper (Xu et al., 2021)</a></li>
<li><a href="https://arxiv.org/abs/2510.26788">BF16 Training-Inference Mismatch (arXiv<div></div>.26788)</a></li>
</ol>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>Accelerators</category>
            <category>GPUs</category>
            <category>Architecture</category>
            <enclosure length="63092" type="image/png" url="https://blog.ecitis.org/open-graph/ai-hardware-landscape.png"/>
        </item>
        <item>
            <title><![CDATA[AMD ROCm for Machine Learning: The Alternative GPU Ecosystem]]></title>
            <link>https://blog.ecitis.org/amd-rocm-ml/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/amd-rocm-ml/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A practical tour of AMD’s open GPU stack for training and inference, including PyTorch, vLLM, compatibility, and deployment.]]></description>
            <content:encoded><![CDATA[<p>AMD’s ROCm platform has matured into a viable alternative for machine learning workloads. With ROCm 7.2 now shipping production-ready support for PyTorch, vLLM, and distributed training on both Linux and Windows, the AMD GPU ecosystem deserves serious consideration for both inference and training deployments. The October 2025 announcement of AMD’s strategic partnership with OpenAI to deploy 6 gigawatts of AMD GPUs signals a significant shift in the AI infrastructure landscape.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-openai" id="user-content-fnref-openai">1</a></sup></p>
<p>This guide covers everything you need to deploy ML workloads on AMD hardware: the software stack, hardware options, framework support, and practical code examples.</p>
<hr>
<h2 id="the-rocm-software-stack">The ROCm Software Stack</h2>
<p>ROCm (Radeon Open Compute) is AMD’s open-source GPU computing platform. Unlike NVIDIA’s proprietary CUDA ecosystem, ROCm is built on open standards and released under permissive licenses. The stack comprises several layers, from kernel drivers through high-level frameworks.</p>
<figure><figcaption><strong>ROCm Software Stack</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/amd-rocm-ml-0.webp" width="512" height="692" alt="ROCm Software Stack" loading="lazy" decoding="async"></figure>
<h3 id="key-components">Key Components</h3>
<p><strong>amdgpu Kernel Driver</strong>: The Linux kernel module that communicates with AMD GPUs. This ships with most Linux distributions and handles memory management, command submission, and interrupt handling.</p>
<p><strong>HSA Runtime (ROCr)</strong>: The Heterogeneous System Architecture runtime provides low-level GPU access. Applications rarely interact with this layer directly; it serves as the foundation for higher-level runtimes.</p>
<p><strong>HIP Runtime</strong>: The primary programming interface for AMD GPUs. HIP provides a CUDA-like API that can target both AMD and NVIDIA hardware from the same source code.</p>
<p><strong>ROCm Libraries</strong>: Optimized implementations of common operations:</p>
<ul>
<li><strong>rocBLAS/hipBLAS</strong>: Dense linear algebra (GEMM, GEMV)</li>
<li><strong>hipBLASLt</strong>: Lightweight BLAS with fused operations and FP8 support</li>
<li><strong>MIOpen</strong>: Deep learning primitives (convolutions, pooling, normalization)</li>
<li><strong>RCCL</strong>: Collective communication for multi-GPU training</li>
<li><strong>rocFFT</strong>: Fast Fourier transforms</li>
</ul>
<p><strong>Framework Integrations</strong>: PyTorch, TensorFlow, and JAX all have ROCm backends. These frameworks use MIOpen and rocBLAS under the hood for GPU-accelerated operations.</p>
<hr>
<h2 id="supported-hardware">Supported Hardware</h2>
<p>ROCm supports both data center accelerators and consumer graphics cards, though with different levels of optimization and testing.</p>
<figure><figcaption><strong>Data Center GPU Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/amd-rocm-ml-1.webp" width="512" height="344" alt="Data Center GPU Comparison" loading="lazy" decoding="async"></figure>
<h3 id="data-center-amd-instinct-series">Data Center: AMD Instinct Series</h3>
<p><strong>MI355X</strong>: AMD’s current flagship for AI workloads, launched Q3 2025. Built on the CDNA 4 architecture with 256 Compute Units and 288 GB of HBM3e memory at 8 TB/s bandwidth. The MI355X delivers 10.1 PFLOPS at FP8 and 20.1 PFLOPS at FP4/FP6. In benchmarks, the MI355X matches or exceeds NVIDIA B200 systems at high concurrency levels (32-64) for Llama3.1-405B inference.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi355x" id="user-content-fnref-mi355x">2</a></sup> The 1400W TDP reflects its position as a high-performance training and inference accelerator.</p>
<p><strong>MI350X</strong>: The lower-power variant of the MI350 series with the same 288 GB HBM3e and 8 TB/s bandwidth. Both MI350 models feature MXFP6 and MXFP4 datatype support for efficient inference.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi350" id="user-content-fnref-mi350">3</a></sup></p>
<p><strong>MI325X</strong>: The 256 GB HBM3e variant of the MI300 series with 6.0 TB/s bandwidth. In MLPerf benchmarks, systems built around the MI325X matched the performance of NVIDIA H200 on Llama fine-tuning tasks.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi325x" id="user-content-fnref-mi325x">4</a></sup></p>
<p><strong>MI300X</strong>: With 192 GB of HBM3 memory and 5.3 TB/s bandwidth, the MI300X remains widely deployed. In MLPerf inference benchmarks, Mango LLMBoost achieved 103,182 tokens per second on 32x MI300X for Llama2-70B, outperforming the previous best result of 82,749 TPS on NVIDIA H100.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi300x" id="user-content-fnref-mi300x">5</a></sup></p>
<p><strong>MI400 Series (H2 2026)</strong>: The upcoming CDNA 5 architecture will deliver 432 GB of HBM4 memory at 19.6 TB/s bandwidth, with 40 PFLOPS at FP4. The MI455X targets training and inference, while the MI430X serves HPC workloads. The first 1 GW deployment at OpenAI begins H2 2026 using MI450 GPUs.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi400" id="user-content-fnref-mi400">6</a></sup></p>
<p><strong>MI250/MI250X</strong>: Previous generation CDNA 2 accelerators with 128 GB HBM2e. Still supported by ROCm 7.2 and available at lower cost on the secondary market.</p>
<h3 id="consumer-gpus-rdna-3-and-rdna-4">Consumer GPUs: RDNA 3 and RDNA 4</h3>
<p><strong>RX 9070 XT/RX 9070 (RDNA 4)</strong>: The latest consumer architecture with 16 GB GDDR6 at 640 GB/s bandwidth. The RX 9070 XT delivers 48.7 TFLOPS FP32 and up to 1557 TOPS INT4 with sparsity. Each compute unit includes dedicated AI accelerators, marking AMD’s first consumer GPU with tensor cores for machine learning. ROCm 7.2 supports these cards on both Linux and Windows. In Stable Diffusion XL FP16 testing, the RX 9070 XT is 83% faster than the RX 7800 XT. You can run up to 24B parameter LLM models at practical quantization levels.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-rx9070" id="user-content-fnref-rx9070">7</a></sup></p>
<p><strong>RX 7900 XTX/XT (RDNA 3)</strong>: Consumer cards with up to 24 GB GDDR6. ROCm support is mature; these cards work well for local LLM inference and development. The ROCm llama.cpp builds target gfx1100/gfx1101 architectures.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-4" id="user-content-fnref-4">8</a></sup></p>
<p><strong>Radeon AI PRO R9700</strong>: Professional card with 32 GB VRAM for larger models without quantization. The R9700 targets AI development workflows that exceed the 16 GB consumer cards.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-radeonai" id="user-content-fnref-radeonai">9</a></sup></p>
<p><strong>Radeon PRO W7900</strong>: Professional card with 48 GB VRAM. Useful for running larger quantized models locally without the cost of data center hardware.</p>
<h3 id="what-works-best-where">What Works Best Where</h3>
<p>Data center MI350/MI355X: Production LLM inference and training, frontier model workloads requiring 288 GB memory per GPU.</p>
<p>Data center MI300X/MI325X: Production LLM inference, distributed training, memory-bound workloads requiring 192-256 GB VRAM pools.</p>
<p>Consumer RDNA 4 (RX 9070 series): Local development, small model fine-tuning, inference with quantized models up to 24B parameters. Native Windows support via ROCm 7.2.</p>
<p>Consumer RDNA 3 (RX 7900 series): Local development, inference with quantized models. 24 GB VRAM on the XTX model.</p>
<hr>
<h2 id="hip-the-cuda-portability-layer">HIP: The CUDA Portability Layer</h2>
<p>HIP (Heterogeneous-compute Interface for Portability) provides a CUDA-like programming model that can compile for both AMD and NVIDIA GPUs. Most CUDA code can be ported with minimal modifications.</p>
<h3 id="how-hip-works">How HIP Works</h3>
<p>HIP is a C++ runtime API and kernel language. When targeting AMD GPUs, HIP code compiles to native AMD GPU binaries. When targeting NVIDIA, HIP wraps CUDA operations with thin inline functions, adding negligible overhead.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-6" id="user-content-fnref-6">10</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// HIP kernel - nearly identical to CUDA</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">__global__ </span><span style="color:var(--astro-code-token-keyword)">void</span><span style="color:var(--astro-code-token-function)"> vectorAdd</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">float</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)">a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> float</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)">b</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> float</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)">c</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> int</span><span style="color:var(--astro-code-foreground)"> n) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    int</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> blockIdx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> blockDim</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> threadIdx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">x;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-foreground)"> (i </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> n) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        c</span><span style="color:var(--astro-code-foreground)">[i] </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> a</span><span style="color:var(--astro-code-foreground)">[i] </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> b</span><span style="color:var(--astro-code-foreground)">[i];</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">int</span><span style="color:var(--astro-code-token-function)"> main</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)">d_a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)">d_b</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)">d_c;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Memory allocation - same as CUDA</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    hipMalloc</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-foreground)">d_a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> size);</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    hipMalloc</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-foreground)">d_b</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> size);</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    hipMalloc</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-foreground)">d_c</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> size);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Kernel launch - same syntax</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    vectorAdd</span><span style="color:var(--astro-code-token-keyword)">&lt;&lt;&lt;</span><span style="color:var(--astro-code-foreground)">blocks</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> threads</span><span style="color:var(--astro-code-token-keyword)">&gt;&gt;&gt;</span><span style="color:var(--astro-code-foreground)">(d_a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> d_b</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> d_c</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> n);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    hipDeviceSynchronize</span><span style="color:var(--astro-code-foreground)">();</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    hipFree</span><span style="color:var(--astro-code-foreground)">(d_a);</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<h3 id="hipify-automated-cuda-translation">HIPIFY: Automated CUDA Translation</h3>
<p>ROCm includes two tools for converting CUDA code to HIP:</p>
<p><strong>hipify-clang</strong>: Uses the Clang compiler to parse CUDA code and perform semantic translation. This approach handles complex code patterns and produces high-quality output. Recommended for large projects.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-7" id="user-content-fnref-7">11</a></sup></p>
<p><strong>hipify-perl</strong>: Pattern-matching based translation that does not require a working CUDA installation. Faster but less robust than hipify-clang.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Convert a CUDA file to HIP</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">hipify-clang</span><span style="color:var(--astro-code-token-string)"> cuda_kernel.cu</span><span style="color:var(--astro-code-token-string)"> -o</span><span style="color:var(--astro-code-token-string)"> hip_kernel.cpp</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or use the Perl-based tool</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">hipify-perl</span><span style="color:var(--astro-code-token-string)"> cuda_kernel.cu</span><span style="color:var(--astro-code-token-keyword)"> &gt;</span><span style="color:var(--astro-code-token-string)"> hip_kernel.cpp</span></span></code></pre>
<p>When porting the HACC physics code (approximately 15,000 lines), AMD reported that 95% of the code converted automatically with hipify-perl.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-8" id="user-content-fnref-8">12</a></sup></p>
<h3 id="what-translates-well">What Translates Well</h3>
<p>Most CUDA runtime API calls have direct HIP equivalents:</p>
<ul>
<li><code>cudaMalloc</code> → <code>hipMalloc</code></li>
<li><code>cudaMemcpy</code> → <code>hipMemcpy</code></li>
<li><code>cudaDeviceSynchronize</code> → <code>hipDeviceSynchronize</code></li>
<li><code>cudaStream_t</code> → <code>hipStream_t</code></li>
</ul>
<p>Device code keywords map directly:</p>
<ul>
<li><code>__global__</code>, <code>__device__</code>, <code>__shared__</code> work unchanged</li>
<li><code>threadIdx</code>, <code>blockIdx</code>, <code>blockDim</code> have <code>hip</code> prefixes available but original names also work</li>
</ul>
<h3 id="what-requires-manual-work">What Requires Manual Work</h3>
<p><strong>Warp Size</strong>: NVIDIA GPUs use 32-thread warps; AMD uses 64-thread wavefronts. Code that hardcodes <code>warpSize = 32</code> will break. Use the runtime query instead:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// Portable warp/wavefront size</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">int</span><span style="color:var(--astro-code-foreground)"> warpSize;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">hipDeviceGetAttribute</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-foreground)">warpSize</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> hipDeviceAttributeWarpSize</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> device);</span></span></code></pre>
<p><strong>CUDA-Specific Intrinsics</strong>: Some CUDA intrinsics lack direct HIP equivalents. Tensor core operations (wmma) require translation to AMD’s matrix core instructions.</p>
<p><strong>Library Calls</strong>: ROCm provides equivalent libraries for most CUDA libraries:</p>
<ul>
<li>cuBLAS → rocBLAS/hipBLAS</li>
<li>cuDNN → MIOpen</li>
<li>NCCL → RCCL</li>
<li>cuFFT → rocFFT</li>
</ul>
<p>Function signatures differ slightly; wrapper headers can ease the transition.</p>
<hr>
<h2 id="installing-rocm-on-linux-and-windows">Installing ROCm on Linux and Windows</h2>
<p>ROCm 7.2 is a unified release supporting both Linux and Windows. On Linux, it officially supports Ubuntu 22.04/24.04, RHEL 9.x/10.x, SLES 15 SP7, Debian 13, and Oracle Linux 10. Consumer Radeon GPUs (RX 7000/9000 series) and Ryzen AI APUs are supported on Ubuntu 24.04, RHEL 10.1, and Windows 11.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-rocm72" id="user-content-fnref-rocm72">13</a></sup></p>
<h3 id="ubuntu-2404-installation">Ubuntu 24.04 Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Download the installer package</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">wget</span><span style="color:var(--astro-code-token-string)"> https://repo.radeon.com/amdgpu-install/7.2/ubuntu/noble/amdgpu-install_7.2.70200-1_all.deb</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install the package manager</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> ./amdgpu-install_7.2.70200-1_all.deb</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Update package lists</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> update</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install ROCm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> rocm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Add user to required groups</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> usermod</span><span style="color:var(--astro-code-token-string)"> -a</span><span style="color:var(--astro-code-token-string)"> -G</span><span style="color:var(--astro-code-token-string)"> render,video</span><span style="color:var(--astro-code-foreground)"> $LOGNAME</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Reboot to load the new kernel modules</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> reboot</span></span></code></pre>
<h3 id="ubuntu-2204-installation">Ubuntu 22.04 Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Download for jammy</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">wget</span><span style="color:var(--astro-code-token-string)"> https://repo.radeon.com/amdgpu-install/7.2/ubuntu/jammy/amdgpu-install_7.2.70200-1_all.deb</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> ./amdgpu-install_7.2.70200-1_all.deb</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> update</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> python3-setuptools</span><span style="color:var(--astro-code-token-string)"> python3-wheel</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> usermod</span><span style="color:var(--astro-code-token-string)"> -a</span><span style="color:var(--astro-code-token-string)"> -G</span><span style="color:var(--astro-code-token-string)"> render,video</span><span style="color:var(--astro-code-foreground)"> $LOGNAME</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> rocm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> reboot</span></span></code></pre>
<h3 id="rhel-9x-installation">RHEL 9.x Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Install the repo</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> dnf</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> https://repo.radeon.com/amdgpu-install/7.2/el/9.6/amdgpu-install-7.2.70200-1.el9.noarch.rpm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Clean cache</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> dnf</span><span style="color:var(--astro-code-token-string)"> clean</span><span style="color:var(--astro-code-token-string)"> all</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install EPEL for dependencies</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">wget</span><span style="color:var(--astro-code-token-string)"> https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> rpm</span><span style="color:var(--astro-code-token-string)"> -ivh</span><span style="color:var(--astro-code-token-string)"> epel-release-latest-9.noarch.rpm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Enable CRB repository</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> dnf</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> dnf-plugin-config-manager</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> crb</span><span style="color:var(--astro-code-token-string)"> enable</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install dependencies and ROCm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> dnf</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> python3-setuptools</span><span style="color:var(--astro-code-token-string)"> python3-wheel</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> usermod</span><span style="color:var(--astro-code-token-string)"> -a</span><span style="color:var(--astro-code-token-string)"> -G</span><span style="color:var(--astro-code-token-string)"> render,video</span><span style="color:var(--astro-code-foreground)"> $LOGNAME</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> dnf</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> rocm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> reboot</span></span></code></pre>
<h3 id="windows-11-installation-consumer-gpus">Windows 11 Installation (Consumer GPUs)</h3>
<p>ROCm 7.2 includes Windows support for consumer GPUs. PyTorch runs natively on RX 7000/9000 series without WSL2.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-rocmwindows" id="user-content-fnref-rocmwindows">14</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Install AMD Adrenalin 26.1.1 or later (includes ROCm components)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Download from https://www.amd.com/en/support</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install PyTorch for ROCm on Windows</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pip install torch torchvision torchaudio </span><span style="color:var(--astro-code-token-keyword)">--</span><span style="color:var(--astro-code-foreground)">index</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)">url https:</span><span style="color:var(--astro-code-token-keyword)">//</span><span style="color:var(--astro-code-foreground)">download.pytorch.org</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)">whl</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)">rocm7.</span><span style="color:var(--astro-code-token-constant)">2</span></span></code></pre>
<p>ComfyUI is now integrated with ROCm and can be installed via the Adrenalin driver package for Windows users.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-comfyui" id="user-content-fnref-comfyui">15</a></sup></p>
<h3 id="verifying-the-installation">Verifying the Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Check GPU detection</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocminfo</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Verify OpenCL</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">clinfo</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Check GPU status</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">amd-smi</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># List available GPUs</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocm-smi</span><span style="color:var(--astro-code-token-string)"> --showid</span></span></code></pre>
<p>Expected output from <code>rocminfo</code> should list your GPU agent with details like:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Agent 2</span></span>
<span class="line"><span>  Name:                    gfx942</span></span>
<span class="line"><span>  Marketing Name:          AMD Instinct MI300X</span></span>
<span class="line"><span>  Vendor Name:             AMD</span></span>
<span class="line"><span>  ...</span></span></code></pre>
<hr>
<h2 id="pytorch-with-rocm">PyTorch with ROCm</h2>
<p>PyTorch has mature ROCm support. AMD publishes validated Docker images and pip wheels for each ROCm release. ROCm support is upstreamed into the official PyTorch repository, and development is aligned with stable PyTorch releases.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-pytorchrocm" id="user-content-fnref-pytorchrocm">16</a></sup></p>
<h3 id="installation-options">Installation Options</h3>
<p><strong>Option 1: Docker (Recommended)</strong></p>
<p>Docker provides the most reliable setup, avoiding dependency conflicts:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Pull the official ROCm PyTorch image</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> rocm/pytorch:rocm7.2_ubuntu24.04_py3.12_pytorch_2.9.1</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run with GPU access</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> -it</span><span style="color:var(--astro-code-token-string)"> --device=/dev/kfd</span><span style="color:var(--astro-code-token-string)"> --device=/dev/dri</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --group-add</span><span style="color:var(--astro-code-token-string)"> video</span><span style="color:var(--astro-code-token-string)"> --shm-size=16g</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    rocm/pytorch:rocm7.2_ubuntu24.04_py3.12_pytorch_2.9.1</span></span></code></pre>
<p><strong>Option 2: pip Installation</strong></p>
<p>For native installation, use the ROCm-specific wheel:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Create a virtual environment</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> venv</span><span style="color:var(--astro-code-token-string)"> rocm-env</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">source</span><span style="color:var(--astro-code-token-string)"> rocm-env/bin/activate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install PyTorch for ROCm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> torch</span><span style="color:var(--astro-code-token-string)"> torchvision</span><span style="color:var(--astro-code-token-string)"> torchaudio</span><span style="color:var(--astro-code-token-string)"> --index-url</span><span style="color:var(--astro-code-token-string)"> https://download.pytorch.org/whl/rocm7.2</span></span></code></pre>
<p>As of PyTorch 2.9, wheel variant support simplifies installation; the correct backend is selected automatically based on detected hardware. ROCm 7.2 supports PyTorch 2.9.1 on both Linux and Windows.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-pytorch29" id="user-content-fnref-pytorch29">17</a></sup></p>
<h3 id="verifying-pytorch-rocm">Verifying PyTorch ROCm</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Check if ROCm is available (uses CUDA API naming)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;ROCm available: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">torch.cuda.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Device count: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">torch.cuda.</span><span style="color:var(--astro-code-token-function)">device_count</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Device name: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">torch.cuda.</span><span style="color:var(--astro-code-token-function)">get_device_name</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run a simple operation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(x, x.T)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Result shape: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">y.shape</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Note that PyTorch uses <code>cuda</code> in its API even when running on AMD GPUs. This maintains compatibility with existing code.</p>
<h3 id="common-gotchas">Common Gotchas</h3>
<p><strong>Environment Variables</strong>: Some operations require specific settings:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Enable HIP extension for certain operations</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">export</span><span style="color:var(--astro-code-foreground)"> PYTORCH_ROCM_ARCH</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;gfx942&quot;</span><span style="color:var(--astro-code-token-comment)">  # Set your GPU architecture</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># For debugging memory issues</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">export</span><span style="color:var(--astro-code-foreground)"> PYTORCH_HIP_ALLOC_CONF</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;garbage_collection_threshold:0.9&quot;</span></span></code></pre>
<p><strong>FlashAttention</strong>: ROCm includes FlashAttention v2 and v3 implementations. FlashAttention v3 is integrated for AMD GPUs in recent PyTorch ROCm builds.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-11" id="user-content-fnref-11">18</a></sup></p>
<p><strong>Mixed Precision</strong>: AMP (Automatic Mixed Precision) works on ROCm:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">amp </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GradScaler</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">scaler </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> GradScaler</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">input</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> criterion</span><span style="color:var(--astro-code-token-punctuation)">(output, target)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">scale</span><span style="color:var(--astro-code-token-punctuation)">(loss).</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">(optimizer)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">update</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<hr>
<h2 id="llm-inference-frameworks-on-amd">LLM Inference Frameworks on AMD</h2>
<h3 id="vllm-on-rocm">vLLM on ROCm</h3>
<p>vLLM is the leading high-throughput LLM inference engine. As of January 2026, ROCm is a first-class platform in the vLLM ecosystem. In mid-November 2025, only 37% of vLLM test groups passed on AMD CI. As of mid-January 2026, 93% of vLLM AMD test groups succeed with daily regression maintenance.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-vllmfirstclass" id="user-content-fnref-vllmfirstclass">19</a></sup></p>
<p><strong>Installation via Docker:</strong></p>
<p>As of January 6, 2026, users no longer need to build from source. Pre-built official ROCm-enabled vLLM Docker images are available on Docker Hub.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-vllmdocker" id="user-content-fnref-vllmdocker">20</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Official ROCm vLLM image</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> rocm/vllm-dev:rocm7.2_mi350_ubuntu24.04_py3.12_vllm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> -it</span><span style="color:var(--astro-code-token-string)"> --device=/dev/kfd</span><span style="color:var(--astro-code-token-string)"> --device=/dev/dri</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --group-add</span><span style="color:var(--astro-code-token-string)"> video</span><span style="color:var(--astro-code-token-string)"> --shm-size=32g</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -p</span><span style="color:var(--astro-code-token-string)"> 8000:8000</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    rocm/vllm-dev:rocm7.2_mi350_ubuntu24.04_py3.12_vllm</span></span></code></pre>
<p><strong>Running a Model:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompts </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;Explain quantum computing in simple terms:&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sampling </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SamplingParams</span><span style="color:var(--astro-code-token-punctuation)">(temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(prompts, sampling)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> output </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> outputs</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(output.outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].text)</span></span></code></pre>
<p><strong>OpenAI-Compatible Server:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> vllm.entrypoints.openai.api_server</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3.1-70B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --tensor-parallel-size</span><span style="color:var(--astro-code-token-constant)"> 8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --port</span><span style="color:var(--astro-code-token-constant)"> 8000</span></span></code></pre>
<p><strong>ROCm-Specific Optimizations:</strong></p>
<p>vLLM V1 on ROCm includes AITER (AI Tensor Engine for ROCm) kernels optimized for MI300X, MI325X, MI350X, and MI355X GPUs. FP8 and FP4 quantization reduces memory usage by 2-4x with minimal accuracy loss. Performance on Llama 3 MXFP4 has improved through AITER optimizations and kernel fusion.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-vllmoptimize" id="user-content-fnref-vllmoptimize">21</a></sup></p>
<p><strong>Known Issue:</strong> There is a regression with AITER for MoE models such as Mixtral and DeepSeek-R1. For these models, use the previous release <code>rocm/vllm:rocm7.0.0_vllm_0.11.1_20251103</code> for better performance.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-vllmmoe" id="user-content-fnref-vllmmoe">22</a></sup></p>
<h3 id="llamacpp-with-rocm">llama.cpp with ROCm</h3>
<p>llama.cpp provides efficient CPU and GPU inference for GGUF-quantized models. ROCm support is available through the HIP backend.</p>
<p><strong>Building from Source:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Clone the repository</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/ggerganov/llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> llama.cpp</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Build with HIP support</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> -B</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> -DGGML_HIP=ON</span><span style="color:var(--astro-code-token-string)"> -DAMDGPU_TARGETS=</span><span style="color:var(--astro-code-token-string-expression)">&quot;gfx942&quot;</span><span style="color:var(--astro-code-token-string)"> -DCMAKE_BUILD_TYPE=Release</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> --build</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> Release</span><span style="color:var(--astro-code-token-string)"> -j</span><span style="color:var(--astro-code-foreground)"> $(</span><span style="color:var(--astro-code-token-function)">nproc</span><span style="color:var(--astro-code-foreground)">)</span></span></code></pre>
<p>Replace <code>gfx942</code> with your GPU architecture:</p>
<ul>
<li>MI350X/MI355X: <code>gfx950</code></li>
<li>MI300X/MI325X: <code>gfx942</code></li>
<li>MI250X: <code>gfx90a</code></li>
<li>RX 9070 XT/RX 9070: <code>gfx1201</code></li>
<li>RX 7900 XTX: <code>gfx1100</code></li>
<li>RX 7900 XT: <code>gfx1101</code></li>
</ul>
<p><strong>Using Pre-built Binaries:</strong></p>
<p>AMD maintains ROCm-optimized llama.cpp releases:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Download from AMD&#39;s llama.cpp fork</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">wget</span><span style="color:var(--astro-code-token-string)"> https://github.com/ROCm/llama.cpp/releases/download/b6652.amd0/llama-b6652-bin-ubuntu-hip-gfx942.tar.gz</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">tar</span><span style="color:var(--astro-code-token-string)"> -xzf</span><span style="color:var(--astro-code-token-string)"> llama-b6652-bin-ubuntu-hip-gfx942.tar.gz</span></span></code></pre>
<p><strong>Running Inference:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">./llama-cli</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> models/llama-3-8b-instruct-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -p</span><span style="color:var(--astro-code-token-string-expression)"> &quot;What is machine learning?&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -n</span><span style="color:var(--astro-code-token-constant)"> 256</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -ngl</span><span style="color:var(--astro-code-token-constant)"> 99</span><span style="color:var(--astro-code-token-comment)">  # Offload all layers to GPU</span></span></code></pre>
<p><strong>Performance Notes:</strong></p>
<p>AMD benchmarks show the MI300X achieving up to 213% higher inference throughput versus the H100 on Llama-3.1-70B-Q4_K_M with flash attention enabled at 4096 prompt size.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-14" id="user-content-fnref-14">23</a></sup></p>
<h3 id="hugging-face-text-generation-inference-tgi">Hugging Face Text Generation Inference (TGI)</h3>
<p>TGI supports AMD Instinct MI210, MI250, and MI300 series GPUs.</p>
<p><strong>Docker Deployment:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> --device</span><span style="color:var(--astro-code-token-string)"> /dev/kfd</span><span style="color:var(--astro-code-token-string)"> --device</span><span style="color:var(--astro-code-token-string)"> /dev/dri</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --shm-size</span><span style="color:var(--astro-code-token-string)"> 1g</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string)"> 8080:80</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -v</span><span style="color:var(--astro-code-foreground)"> $PWD</span><span style="color:var(--astro-code-token-string)">/data:/data</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    ghcr.io/huggingface/text-generation-inference:3.3.5-rocm</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model-id</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3.1-8B-Instruct</span></span></code></pre>
<p><strong>Configuration for Large Models:</strong></p>
<p>Use tensor parallelism for models exceeding single-GPU memory:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> --device</span><span style="color:var(--astro-code-token-string)"> /dev/kfd</span><span style="color:var(--astro-code-token-string)"> --device</span><span style="color:var(--astro-code-token-string)"> /dev/dri</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --shm-size</span><span style="color:var(--astro-code-token-string)"> 16g</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string)"> 8080:80</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -v</span><span style="color:var(--astro-code-foreground)"> $PWD</span><span style="color:var(--astro-code-token-string)">/data:/data</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    ghcr.io/huggingface/text-generation-inference:3.3.5-rocm</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model-id</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3.1-70B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --num-shard</span><span style="color:var(--astro-code-token-constant)"> 8</span></span></code></pre>
<p>TGI on ROCm includes a custom Paged Attention kernel enabled by default. For configurations outside the supported parameters (bf16/fp16, block size 16, head size 128, max 16k context), it falls back to PagedAttention v2.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-15" id="user-content-fnref-15">24</a></sup></p>
<hr>
<h2 id="performance-amd-vs-nvidia-for-llm-inference">Performance: AMD vs NVIDIA for LLM Inference</h2>
<p>Benchmark data from independent testing provides useful guidance for hardware selection.</p>
<h3 id="memory-bandwidth-advantage">Memory Bandwidth Advantage</h3>
<p>The MI355X’s 8 TB/s memory bandwidth exceeds the B200’s 8 TB/s and substantially outpaces the H100’s 3.35 TB/s. The MI300X at 5.3 TB/s remains competitive with the H200’s 4.8 TB/s. For memory-bound LLM inference at low batch sizes, this translates to lower latency.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-memperf" id="user-content-fnref-memperf">25</a></sup></p>
<h3 id="mi355x-vs-blackwell-b200">MI355X vs Blackwell B200</h3>
<p>In benchmarks with Llama3.1-405B, the MI355X delivers up to 2x higher throughput compared to competitive options. At high concurrency levels (32-64), the MI355X with ATOM matches or exceeds B200 systems running SGLang. The MI355X demonstrated a 10% time-to-solution advantage in the MLPerf LoRA fine-tuning benchmark.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi355xperf" id="user-content-fnref-mi355xperf">26</a></sup></p>
<h3 id="large-model-performance">Large Model Performance</h3>
<p>For models requiring multi-GPU deployment, AMD’s large VRAM pools allow single-GPU inference where competitors need tensor parallelism:</p>
<ul>
<li>405B parameter models fit on one MI355X with FP4 quantization<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mi355xmem" id="user-content-fnref-mi355xmem">27</a></sup></li>
<li>Mixtral 8x7B fits on one MI300X; H100 requires TP=2<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-18" id="user-content-fnref-18">28</a></sup></li>
<li>DeepSeek-V3 670B: MI300X beats H100 in both absolute performance and performance per dollar<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-19" id="user-content-fnref-19">29</a></sup></li>
</ul>
<h3 id="cloud-availability">Cloud Availability</h3>
<p>MI355X GPUs are available at Oracle Cloud, TensorWave, and Vultr. Oracle Cloud offers bare-metal GPU instances with 8 MI355X GPUs per node (2.3 TB total GPU memory), 128 CPU cores, 3 TB DDR system memory, and up to 3.2 Tb/s RDMA bandwidth.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-cloudavail" id="user-content-fnref-cloudavail">30</a></sup></p>
<h3 id="software-stack-maturity">Software Stack Maturity</h3>
<p>ROCm 7.2 has closed the gap with CUDA. vLLM now passes 93% of test groups on AMD CI. PyTorch runs natively on both Linux and Windows. For production deployments with standard configurations, both stacks work reliably. NVIDIA retains an edge in MLPerf training benchmarks, where Blackwell leads on Llama 3.1 403B pretraining.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-mlperf" id="user-content-fnref-mlperf">31</a></sup></p>
<hr>
<h2 id="training-on-amd-gpus">Training on AMD GPUs</h2>
<p>ROCm supports distributed training with PyTorch’s native parallelism primitives. While NVIDIA Blackwell currently leads MLPerf training benchmarks on Llama 3.1 403B pretraining, AMD’s MI325X matches H200 performance on LLM fine-tuning benchmarks, suggesting AMD is approximately one generation behind NVIDIA on training workloads.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-traingap" id="user-content-fnref-traingap">32</a></sup></p>
<h3 id="single-gpu-training">Single-GPU Training</h3>
<p>Standard PyTorch training loops work unchanged:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> nn</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optim </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> optim</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MyModel</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">optimizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> optim</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">AdamW</span><span style="color:var(--astro-code-token-punctuation)">(model.</span><span style="color:var(--astro-code-token-function)">parameters</span><span style="color:var(--astro-code-token-punctuation)">(), lr</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1e-4</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">criterion </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">CrossEntropyLoss</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> epoch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(num_epochs):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> batch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataloader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        inputs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> labels </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> batch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        inputs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> labels </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> inputs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">(),</span><span style="color:var(--astro-code-foreground)"> labels</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(inputs)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> criterion</span><span style="color:var(--astro-code-token-punctuation)">(outputs, labels)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="distributed-data-parallel-ddp">Distributed Data Parallel (DDP)</h3>
<p>RCCL provides collective operations for multi-GPU training:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">distributed </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> dist</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">parallel </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DistributedDataParallel </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> DDP</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Initialize process group</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dist</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">init_process_group</span><span style="color:var(--astro-code-token-punctuation)">(backend</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;nccl&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Uses RCCL on AMD</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">local_rank </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> int</span><span style="color:var(--astro-code-token-punctuation)">(os.environ[</span><span style="color:var(--astro-code-token-string-expression)">&#39;LOCAL_RANK&#39;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_device</span><span style="color:var(--astro-code-token-punctuation)">(local_rank)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MyModel</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DDP</span><span style="color:var(--astro-code-token-punctuation)">(model, device_ids</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[local_rank])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Training loop remains the same</span></span></code></pre>
<p>Launch with:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">torchrun</span><span style="color:var(--astro-code-token-string)"> --nproc_per_node=8</span><span style="color:var(--astro-code-token-string)"> train.py</span></span></code></pre>
<h3 id="fully-sharded-data-parallel-fsdp">Fully Sharded Data Parallel (FSDP)</h3>
<p>FSDP shards model parameters, gradients, and optimizer states across GPUs, enabling training of models larger than single-GPU memory:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">distributed</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fsdp </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> FullyShardedDataParallel </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> FSDP</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">distributed</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fsdp </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ShardingStrategy</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MyLargeModel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Wrap with FSDP</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> FSDP</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    sharding_strategy</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ShardingStrategy.FULL_SHARD,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">local_rank</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>PyTorch’s FSDP works well on ROCm. AMD benchmarks show 8x MI300X achieving up to 1.29x better performance compared to 8x H100 when training DeepSeek-V2-Lite.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-22" id="user-content-fnref-22">33</a></sup></p>
<p><strong>FSDP Configuration Tips:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Recommended settings for ROCm</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">fsdp_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;sharding_strategy&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> ShardingStrategy</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">FULL_SHARD</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;backward_prefetch&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> BackwardPrefetch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">BACKWARD_PRE</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;forward_prefetch&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;limit_all_gathers&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">}</span></span></code></pre>
<h3 id="training-infrastructure">Training Infrastructure</h3>
<p>AMD provides optimized Docker containers for distributed training:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># ROCm PyTorch Training container</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> rocm/pytorch-training:rocm7.2_ubuntu24.04_py3.12</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Includes torchtitan and Hugging Face Accelerate</span></span></code></pre>
<p>The container includes libraries for FSDP training with one-shot and two-shot AllReduce strategies for optimized communication.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-23" id="user-content-fnref-23">34</a></sup></p>
<hr>
<h2 id="debugging-and-profiling-with-rocprof">Debugging and Profiling with rocprof</h2>
<p>ROCm provides comprehensive profiling tools for performance optimization.</p>
<h3 id="rocprofv3">rocprofv3</h3>
<p>The latest profiling tool (replacing rocprof v1/v2) offers improved usability:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Profile an application</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprofv3</span><span style="color:var(--astro-code-token-string)"> --hip-trace</span><span style="color:var(--astro-code-token-string)"> --hsa-trace</span><span style="color:var(--astro-code-token-string)"> -o</span><span style="color:var(--astro-code-token-string)"> profile_output</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> train.py</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Collect hardware counters</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprofv3</span><span style="color:var(--astro-code-token-string)"> --stats</span><span style="color:var(--astro-code-token-string)"> --output-format</span><span style="color:var(--astro-code-token-string)"> csv</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> inference.py</span></span></code></pre>
<h3 id="key-profiling-commands">Key Profiling Commands</h3>
<p><strong>Basic Kernel Profiling:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># List available counters</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprofv3</span><span style="color:var(--astro-code-token-string)"> --list-counters</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Profile with specific counters</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprofv3</span><span style="color:var(--astro-code-token-string)"> --pmc</span><span style="color:var(--astro-code-token-string)"> GPU_BUSY,GRBM_COUNT</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> model.py</span></span></code></pre>
<p><strong>Timeline Tracing:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Generate trace for visualization</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprofv3</span><span style="color:var(--astro-code-token-string)"> --hip-trace</span><span style="color:var(--astro-code-token-string)"> --output-format</span><span style="color:var(--astro-code-token-string)"> json</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> model.py</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># View in Perfetto</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Open https://ui.perfetto.dev and load results.json</span></span></code></pre>
<h3 id="rocprof-compute-formerly-omniperf">rocprof-compute (formerly Omniperf)</h3>
<p>For detailed kernel analysis:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Collect comprehensive metrics</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprof-compute</span><span style="color:var(--astro-code-token-string)"> profile</span><span style="color:var(--astro-code-token-string)"> -n</span><span style="color:var(--astro-code-token-string)"> my_workload</span><span style="color:var(--astro-code-token-string)"> --</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> train.py</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Analyze results</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprof-compute</span><span style="color:var(--astro-code-token-string)"> analyze</span><span style="color:var(--astro-code-token-string)"> my_workload/</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate roofline plot</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocprof-compute</span><span style="color:var(--astro-code-token-string)"> analyze</span><span style="color:var(--astro-code-token-string)"> my_workload/</span><span style="color:var(--astro-code-token-string)"> --roof</span></span></code></pre>
<p>rocprof-compute provides:</p>
<ul>
<li>Roofline analysis showing compute vs memory boundedness</li>
<li>Memory throughput analysis</li>
<li>Compute utilization breakdown</li>
<li>Baseline comparisons between runs<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-24" id="user-content-fnref-24">35</a></sup></li>
</ul>
<h3 id="rocgdb-for-debugging">ROCgdb for Debugging</h3>
<p>The ROCm debugger extends GDB for heterogeneous debugging:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Debug a HIP application</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">rocgdb</span><span style="color:var(--astro-code-token-string)"> ./my_hip_program</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Set breakpoint in kernel</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">gdb</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">break</span><span style="color:var(--astro-code-token-string)"> myKernel</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">gdb</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-function)">run</span></span></code></pre>
<p>Note: On RHEL with SELinux enabled, debugging may hang. Either disable SELinux or configure appropriate policies.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-25" id="user-content-fnref-25">36</a></sup></p>
<hr>
<h2 id="current-limitations-and-workarounds">Current Limitations and Workarounds</h2>
<p>ROCm has made substantial progress but some limitations remain.</p>
<h3 id="platform-support">Platform Support</h3>
<p><strong>Windows</strong>: ROCm 7.2 includes native Windows support for consumer GPUs (RX 7000/9000 series). PyTorch runs without WSL2. The Adrenalin 26.1.1 driver includes ROCm components and ComfyUI integration.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-26" id="user-content-fnref-26">37</a></sup></p>
<p><strong>macOS</strong>: Not supported. Apple Silicon users should consider MLX instead.</p>
<p><strong>Mobile GPUs</strong>: Not officially supported by ROCm. Ryzen AI APUs (AI Max 300, AI 400 series) are now supported on both Linux and Windows.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-ryzenai" id="user-content-fnref-ryzenai">38</a></sup></p>
<h3 id="memory-issues-on-consumer-gpus">Memory Issues on Consumer GPUs</h3>
<p>Running large models on RDNA 3/4 cards with 16-24 GB VRAM can cause instability. The RX 9070 XT’s 16 GB is adequate for models up to 24B parameters at Q4 quantization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Workarounds for memory pressure</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># For Stable Diffusion / FLUX</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> main.py</span><span style="color:var(--astro-code-token-string)"> --lowvram</span><span style="color:var(--astro-code-token-string)"> --disable-pinned-memory</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># For PyTorch memory management</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">export</span><span style="color:var(--astro-code-foreground)"> PYTORCH_HIP_ALLOC_CONF</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;garbage_collection_threshold:0.8,max_split_size_mb:512&quot;</span></span></code></pre>
<h3 id="jax-support">JAX Support</h3>
<p>JAX on ROCm is supported for inference only. Training workloads may encounter intermittent errors or segmentation faults.<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-27" id="user-content-fnref-27">39</a></sup></p>
<h3 id="quantization-support">Quantization Support</h3>
<p>Not all quantization formats work on ROCm:</p>
<ul>
<li>GPTQ and AWQ: Supported in vLLM</li>
<li>GGUF: Supported in llama.cpp</li>
<li>FP8: Supported on MI300 series</li>
<li>MXFP4: Only supported on MI350 series<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-28" id="user-content-fnref-28">40</a></sup></li>
<li>AWQ in TGI: Not currently supported<sup><a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fn-29" id="user-content-fnref-29">41</a></sup></li>
</ul>
<h3 id="multi-gpu-configuration">Multi-GPU Configuration</h3>
<p>Installing multiple ROCm versions can cause amd-smi issues. Stick to one ROCm version per system or use containers for version isolation.</p>
<h3 id="framework-specific-issues">Framework-Specific Issues</h3>
<p><strong>Transformers library</strong>: Most models work; some custom CUDA kernels may need ROCm equivalents.</p>
<p><strong>Flash Attention</strong>: Supported via CK (Composable Kernel) implementation. Triton-based FA has lower latency on MI250/MI300 but requires warmup for each new sequence length.</p>
<hr>
<h2 id="practical-example-deploying-llama-3-on-mi300x">Practical Example: Deploying Llama 3 on MI300X</h2>
<p>Here is a complete example for setting up a production LLM inference endpoint:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Pull the vLLM Docker image</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> rocm/vllm-dev:rocm7.2_mi350_ubuntu24.04_py3.12_vllm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create a deployment script</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cat</span><span style="color:var(--astro-code-token-keyword)"> &gt;</span><span style="color:var(--astro-code-token-string)"> deploy.sh</span><span style="color:var(--astro-code-token-keyword)"> &lt;&lt;</span><span style="color:var(--astro-code-foreground)"> &#39;EOF&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">#!/bin/bash</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">docker run -d \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    --name llama3-server \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    --device=/dev/kfd \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    --device=/dev/dri \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    --group-add video \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    --shm-size=32g \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    -p 8000:8000 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    -v ~/.cache/huggingface:/root/.cache/huggingface \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    rocm/vllm-dev:rocm7.2_mi350_ubuntu24.04_py3.12_vllm \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    python -m vllm.entrypoints.openai.api_server \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        --model meta-llama/Llama-3.1-70B-Instruct \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        --tensor-parallel-size 4 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        --max-model-len 8192 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        --gpu-memory-utilization 0.9</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">EOF</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">chmod</span><span style="color:var(--astro-code-token-string)"> +x</span><span style="color:var(--astro-code-token-string)"> deploy.sh</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./deploy.sh</span></span></code></pre>
<p><strong>Testing the Endpoint:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> OpenAI</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OpenAI</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    base_url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;http://localhost:8000/v1&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    api_key</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;not-needed&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">chat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">completions</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">create</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        {</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;Write a haiku about GPU computing&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    ],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(response.choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].message.content)</span></span></code></pre>
<p><strong>Monitoring:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Watch GPU utilization</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">watch</span><span style="color:var(--astro-code-token-string)"> -n</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-string)"> rocm-smi</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Check memory usage</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">amd-smi</span><span style="color:var(--astro-code-token-string)"> monitor</span><span style="color:var(--astro-code-token-string)"> -p</span></span></code></pre>
<hr>
<h2 id="conclusion">Conclusion</h2>
<p>ROCm has evolved from a CUDA alternative requiring substantial effort into a production-ready platform for ML workloads. The MI355X delivers competitive performance against NVIDIA’s Blackwell B200 for LLM inference, particularly at high concurrency levels. The MI300X and MI325X remain strong options for memory-bound workloads. Consumer RDNA 4 cards (RX 9070 series) provide a viable path for local development with native Windows support.</p>
<p>Key points to remember:</p>
<ul>
<li>ROCm 7.2 supports both Linux and Windows</li>
<li>PyTorch 2.9.1 works on both platforms without code changes</li>
<li>vLLM passes 93% of test groups on AMD CI (January 2026)</li>
<li>Profile with rocprofv3 and rocprof-compute</li>
<li>Consumer GPUs have improved support but 16 GB VRAM limits model sizes</li>
<li>OpenAI will deploy 1 GW of MI450 GPUs starting H2 2026</li>
</ul>
<p>For organizations evaluating GPU infrastructure, AMD provides a cost-effective alternative with competitive performance. The MI400 series (H2 2026) with 432 GB HBM4 will further close the gap with NVIDIA.</p>
<hr>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-openai">
<p><a href="https://openai.com/index/openai-amd-strategic-partnership/" rel="noopener noreferrer">AMD and OpenAI Announce Strategic Partnership to Deploy 6 Gigawatts of AMD GPUs</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-openai" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi355x">
<p><a href="https://blogs.oracle.com/cloud-infrastructure/amd-instinct-mi355x-on-oci-performance-technical-details" rel="noopener noreferrer">AMD Instinct MI355X on OCI Performance &amp; Technical Details</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi355x" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi350">
<p><a href="https://www.amd.com/en/blogs/2025/amd-instinct-mi350-series-game-changer.html" rel="noopener noreferrer">AMD Instinct MI350 Series GPUs: A Game Changer</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi350" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi325x">
<p><a href="https://spectrum.ieee.org/nvidia-blackwell-mlperf-training-5" rel="noopener noreferrer">Is Nvidia’s Blackwell the Unstoppable Force in AI Training, or Can AMD Close the Gap?</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi325x" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi300x">
<p><a href="https://neysa.ai/blog/amd-mi300x/" rel="noopener noreferrer">AMD MI300X: Specs &amp; Performance for AI/ML Workloads</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi300x" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi400">
<p><a href="https://videocardz.com/newz/amd-launches-instinct-mi350-series-confirms-mi400-in-2026-with-432gb-hbm4-memory" rel="noopener noreferrer">AMD launches Instinct MI350 series, confirms MI400 in 2026 with 432GB HBM4 memory</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi400" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-rx9070">
<p><a href="https://www.tomshardware.com/pc-components/gpus/amd-radeon-rx-9070-xt-review" rel="noopener noreferrer">AMD Radeon RX 9070 XT and RX 9070 review - Tom’s Hardware</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-rx9070" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p><a href="https://rocm.docs.amd.com/en/latest/compatibility/ml-compatibility/llama-cpp-compatibility.html" rel="noopener noreferrer">llama.cpp ROCm Compatibility</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-radeonai">
<p><a href="https://www.amd.com/en/products/graphics/radeon-ai.html" rel="noopener noreferrer">AI Acceleration with AMD Radeon Graphics Cards</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-radeonai" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p><a href="https://rocm.docs.amd.com/projects/HIP/en/latest/what_is_hip.html" rel="noopener noreferrer">What is HIP - ROCm Documentation</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p><a href="https://github.com/ROCm/HIPIFY" rel="noopener noreferrer">HIPIFY GitHub Repository</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p><a href="https://rocm.blogs.amd.com/software-tools-optimization/hipify/README.html" rel="noopener noreferrer">Application Portability with HIP - AMD Lab Notes</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-rocm72">
<p><a href="https://rocm.blogs.amd.com/software-tools-optimization/rocm7.2/README.html" rel="noopener noreferrer">ROCm 7.2: Smarter, Faster, and More Scalable for Modern AI Workloads</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-rocm72" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-rocmwindows">
<p><a href="https://videocardz.com/newz/amd-enables-windows-pytorch-support-for-radeon-rx-7000-9000-with-rocm-6-4-4-update" rel="noopener noreferrer">AMD enables Windows PyTorch support for Radeon RX 7000/9000 with ROCm</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-rocmwindows" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-comfyui">
<p><a href="https://www.notebookcheck.net/AMD-ROCm-7-2-brings-AI-training-and-inferencing-to-all-with-Ryzen-AI-400-support-and-ComfyUI-Adrenalin-integrations.1196997.0.html" rel="noopener noreferrer">AMD ROCm 7.2 brings AI training and inferencing to all</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-comfyui" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-pytorchrocm">
<p><a href="https://rocm.docs.amd.com/en/latest/compatibility/ml-compatibility/pytorch-compatibility.html" rel="noopener noreferrer">PyTorch compatibility - ROCm Documentation</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-pytorchrocm" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-pytorch29">
<p><a href="https://www.amd.com/en/developer/resources/technical-articles/2025/pytorch-2-9-wheel-variant-support-expands-to-rocm.html" rel="noopener noreferrer">PyTorch 2.9 Wheel Variant support expands to ROCm</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-pytorch29" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11">
<p><a href="https://rocm.docs.amd.com/en/latest/compatibility/ml-compatibility/pytorch-compatibility.html" rel="noopener noreferrer">PyTorch ROCm Compatibility</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-11" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-vllmfirstclass">
<p><a href="https://rocm.blogs.amd.com/software-tools-optimization/vllm-omni/README.html" rel="noopener noreferrer">ROCm Becomes a First-Class Platform in the vLLM Ecosystem</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-vllmfirstclass" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-vllmdocker">
<p><a href="https://hub.docker.com/r/rocm/vllm" rel="noopener noreferrer">rocm/vllm - Docker Image</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-vllmdocker" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-vllmoptimize">
<p><a href="https://rocm.docs.amd.com/en/latest/how-to/rocm-for-ai/inference-optimization/vllm-optimization.html" rel="noopener noreferrer">vLLM V1 performance optimization - ROCm Documentation</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-vllmoptimize" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-vllmmoe">
<p><a href="https://rocm.docs.amd.com/en/latest/how-to/rocm-for-ai/inference/benchmark-docker/vllm.html" rel="noopener noreferrer">vLLM inference performance testing - ROCm Documentation</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-vllmmoe" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-14">
<p><a href="https://rocm.blogs.amd.com/ecosystems-and-partners/llama-cpp-oct2025/README.html" rel="noopener noreferrer">Accelerating llama.cpp on MI300X</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-14" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-15">
<p><a href="https://huggingface.co/docs/text-generation-inference/installation_amd" rel="noopener noreferrer">Using TGI with AMD GPUs</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-memperf">
<p><a href="https://www.amd.com/en/products/accelerators/instinct/mi350/mi355x.html" rel="noopener noreferrer">AMD Instinct MI355X GPUs</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-memperf" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi355xperf">
<p><a href="https://www.amd.com/en/developer/resources/technical-articles/2026/distributed-inference-performance-on-instinct-mi355x-gpu.html" rel="noopener noreferrer">Single Node and Distributed Inference Performance on AMD Instinct MI355X GPU</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi355xperf" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mi355xmem">
<p><a href="https://getdeploying.com/gpus/amd-mi355x" rel="noopener noreferrer">AMD MI355X - Price, Specs &amp; Cloud Providers</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mi355xmem" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18">
<p><a href="https://www.runpod.io/blog/mi300x-vs-h100-mixtral" rel="noopener noreferrer">MI300X vs H100 Mixtral Benchmark - RunPod</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19">
<p><a href="https://newsletter.semianalysis.com/p/amd-vs-nvidia-inference-benchmark-who-wins-performance-cost-per-million-tokens" rel="noopener noreferrer">AMD vs NVIDIA Inference Benchmark - SemiAnalysis</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-cloudavail">
<p><a href="https://blogs.oracle.com/cloud-infrastructure/amd-instinct-mi355x-on-oci-performance-technical-details" rel="noopener noreferrer">AMD Instinct MI355X on OCI Performance &amp; Technical Details</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-cloudavail" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-mlperf">
<p><a href="https://spectrum.ieee.org/nvidia-blackwell-mlperf-training-5" rel="noopener noreferrer">Is Nvidia’s Blackwell the Unstoppable Force in AI Training?</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-mlperf" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-traingap">
<p><a href="https://www.bestgpusforai.com/blog/best-amd-gpus-for-ai" rel="noopener noreferrer">Best AMD GPUs for AI Training &amp; Deep Learning in 2026</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-traingap" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-22">
<p><a href="https://rocm.docs.amd.com/en/latest/how-to/rocm-for-ai/training/scale-model-training.html" rel="noopener noreferrer">Scaling Model Training on ROCm</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-22" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-23">
<p><a href="https://rocm.blogs.amd.com/artificial-intelligence/fsdp-training-pytorch/README.html" rel="noopener noreferrer">PyTorch FSDP on AMD GPUs</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-23" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-24">
<p><a href="https://rocm.blogs.amd.com/software-tools-optimization/rocprofiler-sdk/README.html" rel="noopener noreferrer">Introduction to Rocprofiler-compute</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-24" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-25">
<p><a href="https://rocm.docs.amd.com/projects/radeon-ryzen/en/latest/docs/limitations/limitationsrad.html" rel="noopener noreferrer">ROCm Limitations - Radeon Documentation</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-25" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26">
<p><a href="https://www.neowin.net/news/amd-rocm-open-source-nvidia-cuda-rival-gets-massive-windows--linux-improvements/" rel="noopener noreferrer">AMD ROCm, open source Nvidia CUDA rival, gets massive Windows &amp; Linux improvements</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-26" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-ryzenai">
<p><a href="https://epium.com/news/amd-expands-rocm-support-ryzen-ai-max-radeon-rx-9000-series/" rel="noopener noreferrer">AMD Expands ROCm Support to Ryzen AI Max and Radeon RX 9000 Series</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-ryzenai" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-27">
<p><a href="https://rocm.docs.amd.com/en/latest/about/release-notes.html" rel="noopener noreferrer">ROCm 7.2.0 Release Notes</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-27" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-28">
<p><a href="https://rocm.docs.amd.com/en/latest/how-to/rocm-for-ai/inference-optimization/vllm-optimization.html" rel="noopener noreferrer">vLLM ROCm Quantization Support</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-28" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-29">
<p><a href="https://huggingface.co/docs/text-generation-inference/installation_amd" rel="noopener noreferrer">TGI ROCm Limitations</a> <a href="https://blog.ecitis.org/amd-rocm-ml/#user-content-fnref-29" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>AMD</category>
            <category>GPUs</category>
            <category>Kernels</category>
            <enclosure length="64996" type="image/png" url="https://blog.ecitis.org/open-graph/amd-rocm-ml.png"/>
        </item>
        <item>
            <title><![CDATA[Apple's Metal Ecosystem for Machine Learning: Beyond MLX]]></title>
            <link>https://blog.ecitis.org/apple-metal-ml-ecosystem/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/apple-metal-ml-ecosystem/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Understand the Apple machine-learning stack from Metal and MPSGraph up to Core ML, and choose the right layer for the job.]]></description>
            <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Apple Silicon has transformed what is possible for on-device machine learning. While MLX gets the headlines for LLM inference, Apple’s ML stack runs much deeper. Metal Performance Shaders, CoreML, the Accelerate framework, and various neural network libraries form a layered ecosystem that powers everything from real-time image processing to transformer inference on iPhones.</p>
<p>This guide explores the full Metal ML ecosystem: how PyTorch MPS works under the hood, when to use CoreML versus MLX, how to write custom GPU kernels, and the practical realities of deploying models across Apple platforms.</p>
<figure><figcaption><strong>Apple ML Stack Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/apple-metal-ml-ecosystem-0.webp" width="512" height="970" alt="Apple ML Stack Architecture" loading="lazy" decoding="async"></figure>
<hr>
<h2 id="metal-performance-shaders">Metal Performance Shaders</h2>
<p>Metal Performance Shaders (MPS) is Apple’s framework for optimized GPU compute kernels. Originally focused on image processing and linear algebra, MPS has evolved into a foundational layer for machine learning on Apple platforms.</p>
<h3 id="what-mps-provides">What MPS Provides</h3>
<p>MPS offers pre-built, hardware-tuned kernels for common operations. These kernels are optimized for each GPU family in Apple’s lineup, from the integrated graphics in older Intel Macs to the latest M-series chips. The framework handles the low-level details of GPU programming: memory allocation, command buffer management, and shader compilation.</p>
<p>For machine learning, MPS provides:</p>
<ul>
<li><strong>Convolution operations</strong>: 2D and 3D convolutions with various padding modes</li>
<li><strong>Pooling layers</strong>: Max, average, and L2 pooling</li>
<li><strong>Normalization</strong>: Batch normalization, instance normalization, layer normalization</li>
<li><strong>Activation functions</strong>: ReLU, sigmoid, tanh, and others</li>
<li><strong>Matrix operations</strong>: Matrix multiplication, transpose, and decomposition</li>
<li><strong>Neural network primitives</strong>: Fully connected layers, softmax, LSTM cells</li>
</ul>
<p>These operations form the building blocks that higher-level frameworks like PyTorch and TensorFlow use when running on Apple GPUs.</p>
<h3 id="mpsgraph-the-computation-graph-framework">MPSGraph: The Computation Graph Framework</h3>
<p>MPSGraph extends MPS with a graph-based execution model. Rather than executing individual operations, you build a computation graph that MPSGraph compiles and optimizes as a unit.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> MetalPerformanceShadersGraph</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> graph </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MPSGraph</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Define input placeholders</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> inputTensor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> graph.</span><span style="color:var(--astro-code-token-function)">placeholder</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    shape</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> [</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-function)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-function)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-function)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-function)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    dataType</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> .float32,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    name</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;input&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Build operations</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> conv </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> graph.</span><span style="color:var(--astro-code-token-function)">convolution2D</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    inputTensor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    weights</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> weightsTensor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    descriptor</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> convDescriptor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    name</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;conv1&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> relu </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> graph.</span><span style="color:var(--astro-code-token-function)">reLU</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">with</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> conv, name</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;relu1&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>At WWDC 2024, Apple introduced several improvements to MPSGraph for transformer models. The new Scaled Dot-Product Attention (SDPA) operation fuses the entire attention computation into a single kernel. Combined with KV-cache support, this provides significant speedups for autoregressive generation.</p>
<h3 id="performance-characteristics">Performance Characteristics</h3>
<p>MPS kernels achieve strong performance because they are tuned for Apple’s specific GPU architectures. The Metal GPU Profiler in Xcode shows that well-optimized MPS code typically achieves high ALU utilization with minimal memory stalls.</p>
<p>However, MPS performance varies by operation type. Large matrix multiplications and convolutions benefit most from GPU acceleration. Smaller operations may run faster on the CPU due to kernel launch overhead.</p>
<hr>
<h2 id="pytorch-mps-backend">PyTorch MPS Backend</h2>
<p>Apple contributed the MPS backend to PyTorch in 2022, enabling GPU-accelerated training and inference on Apple Silicon. The backend maps PyTorch operations to MPS kernels and MPSGraph.</p>
<h3 id="how-it-works">How It Works</h3>
<p>When you move a tensor to the MPS device, PyTorch allocates memory in Metal’s shared memory pool:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Check MPS availability</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">backends</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">mps</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mps&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">else</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cpu&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create tensor on MPS device</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Operations execute on GPU via Metal</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(x, x.T)</span></span></code></pre>
<p>The MPS backend implements PyTorch’s ATen operations using two approaches:</p>
<ol>
<li><strong>MPSGraph operations</strong>: Complex operations like convolutions and matrix multiplications use MPSGraph for optimal performance</li>
<li><strong>Custom Metal shaders</strong>: Element-wise operations use hand-written Metal compute shaders</li>
</ol>
<h3 id="supported-operations">Supported Operations</h3>
<p>PyTorch 2.0 expanded MPS coverage to over 300 operators. The top 60 most-used operations are all supported. You can check coverage for specific operations in the <a href="https://github.com/pytorch/pytorch/issues/141287">PyTorch MPS operator tracking issue</a>.</p>
<p>Common supported operations include:</p>
<ul>
<li>Linear algebra: matmul, bmm, addmm, mm</li>
<li>Convolutions: conv1d, conv2d, conv3d, conv_transpose</li>
<li>Pooling: max_pool2d, avg_pool2d, adaptive_avg_pool2d</li>
<li>Activations: relu, gelu, silu, sigmoid, tanh</li>
<li>Normalization: batch_norm, layer_norm, group_norm</li>
<li>Loss functions: cross_entropy, mse_loss, nll_loss</li>
</ul>
<h3 id="current-limitations">Current Limitations</h3>
<p>The MPS backend has several constraints that affect real-world usage:</p>
<p><strong>No float64 support</strong>: MPS does not support double precision. Operations requiring float64 will fail or fall back to CPU. This affects some scientific computing workloads.</p>
<p><strong>No distributed training</strong>: The NCCL and Gloo backends do not work with MPS. Multi-GPU training is not supported. This limits MPS to single-device workloads.</p>
<p><strong>No Neural Engine access</strong>: PyTorch MPS only uses the GPU. The Neural Engine, which can accelerate certain operations more efficiently, remains unused.</p>
<p><strong>Operation gaps</strong>: Some operations lack MPS implementations. The <code>PYTORCH_ENABLE_MPS_FALLBACK=1</code> environment variable enables automatic CPU fallback for unsupported operations:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">PYTORCH_ENABLE_MPS_FALLBACK</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string)">1</span><span style="color:var(--astro-code-token-function)"> python</span><span style="color:var(--astro-code-token-string)"> train.py</span></span></code></pre>
<h3 id="known-issues">Known Issues</h3>
<p>A notable bug in PyTorch versions before 2.4 caused silent failures when writing to non-contiguous tensors via <code>addcmul_</code> and <code>addcdiv_</code> operations. This would cause model weights to stop updating during training without any error message. The fix requires updating to PyTorch 2.4 or later.</p>
<h3 id="performance-expectations">Performance Expectations</h3>
<p>Benchmarks show MPS running about 3x slower than an RTX 4090 for equivalent models. However, MPS uses significantly less power, making it suitable for development and smaller-scale training. The unified memory architecture allows loading larger models than would fit in discrete GPU VRAM.</p>
<hr>
<h2 id="coreml">CoreML</h2>
<p>CoreML is Apple’s deployment framework for machine learning models. Unlike MLX or PyTorch, CoreML is designed for inference in production applications across all Apple platforms.</p>
<h3 id="the-coreml-stack">The CoreML Stack</h3>
<p>CoreML sits at the top of Apple’s ML stack. When you load a CoreML model, the framework:</p>
<ol>
<li>Parses the model format (.mlmodel or .mlpackage)</li>
<li>Analyzes the model structure to determine optimal execution</li>
<li>Compiles operations for available hardware (CPU, GPU, Neural Engine)</li>
<li>Creates an execution plan that may span multiple processors</li>
</ol>
<p>The key insight is that CoreML handles hardware targeting automatically. The same model file runs on an iPhone, iPad, Mac, Apple Watch, or Vision Pro, with CoreML selecting the best execution strategy for each device.</p>
<h3 id="neural-engine-targeting">Neural Engine Targeting</h3>
<p>The Apple Neural Engine (ANE) is a dedicated accelerator for matrix operations. It offers higher throughput than the GPU for certain workloads while using less power. CoreML is the primary way to access the ANE.</p>
<p>However, ANE compatibility is strict. Operations must match specific constraints:</p>
<ul>
<li>Tensor dimensions must align to hardware requirements</li>
<li>Certain operations have no ANE implementation</li>
<li>If any layer in a path cannot run on ANE, the entire path falls back</li>
</ul>
<p>Apple’s research on <a href="https://machinelearning.apple.com/research/neural-engine-transformers">deploying transformers to the Neural Engine</a> details the constraints and optimization strategies.</p>
<h3 id="model-conversion-with-coremltools">Model Conversion with coremltools</h3>
<p>The <code>coremltools</code> Python package converts models from PyTorch, TensorFlow, and other frameworks to CoreML format:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> coremltools </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ct</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load PyTorch model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MyModel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create example input</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">example_input </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Trace the model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">traced_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">jit</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">trace</span><span style="color:var(--astro-code-token-punctuation)">(model, example_input)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Convert to CoreML</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    traced_model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    inputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[ct.</span><span style="color:var(--astro-code-token-function)">TensorType</span><span style="color:var(--astro-code-token-punctuation)">(shape</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">example_input.shape)],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    minimum_deployment_target</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.target.iOS17</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Save the model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;MyModel.mlpackage&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Direct conversion from PyTorch is recommended over the older ONNX intermediate format. The PyTorch converter handles more operations and produces better-optimized models.</p>
<h3 id="stateful-models-in-ios-18">Stateful Models in iOS 18</h3>
<p>iOS 18 introduced stateful models to CoreML. This feature enables KV-cache for transformer inference without manual buffer management:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> coremltools </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ct</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> coremltools</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">converters</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">mil </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Builder </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mb</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Define state for KV-cache</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">kv_cache_state </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">StateType</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    wrapped_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.</span><span style="color:var(--astro-code-token-function)">TensorType</span><span style="color:var(--astro-code-token-punctuation)">(shape</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">(batch, heads, seq_len, head_dim)),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;kv_cache&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Convert with state</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    traced_model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    states</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[kv_cache_state],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    minimum_deployment_target</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.target.iOS18</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>The state persists across inference calls, avoiding expensive memory allocations for each token generation step.</p>
<h3 id="on-device-inference-performance">On-Device Inference Performance</h3>
<p>Apple’s benchmarks show Llama 3.1 8B running at approximately 33 tokens per second on an M1 Max using CoreML with 4-bit quantization. Smaller models achieve faster speeds. On mobile devices, the Neural Engine enables real-time inference for vision and audio models while maintaining battery efficiency.</p>
<hr>
<h2 id="coreml-vs-mlx-when-to-use-each">CoreML vs MLX: When to Use Each</h2>
<p>CoreML and MLX serve different purposes despite both running on Apple Silicon. Understanding their trade-offs helps you choose the right tool.</p>
<h3 id="coreml-strengths">CoreML Strengths</h3>
<p><strong>Production deployment</strong>: CoreML integrates with Swift and Objective-C. It is the standard way to ship ML models in iOS, iPadOS, watchOS, tvOS, and visionOS apps.</p>
<p><strong>Neural Engine access</strong>: CoreML is currently the only way to run models on the ANE. For power-sensitive mobile applications, this matters.</p>
<p><strong>Hardware abstraction</strong>: One model file works across all Apple devices. CoreML handles the details of targeting different chip generations.</p>
<p><strong>App Store ready</strong>: CoreML models work within Apple’s sandboxing and code signing requirements.</p>
<h3 id="mlx-strengths">MLX Strengths</h3>
<p><strong>Research and experimentation</strong>: MLX provides a familiar NumPy/PyTorch-like API for rapid prototyping. You can modify models and run experiments interactively.</p>
<p><strong>Training support</strong>: MLX supports gradient computation and training. CoreML is inference-only.</p>
<p><strong>Fine-tuning LLMs</strong>: MLX includes tools for LoRA and QLoRA fine-tuning of language models. This is not possible with CoreML.</p>
<p><strong>Dynamic computation</strong>: MLX handles dynamic shapes and control flow naturally. CoreML requires static compilation.</p>
<h3 id="decision-framework">Decision Framework</h3>
<p><strong>Use CoreML when:</strong></p>
<ul>
<li>Building iOS, watchOS, tvOS, or visionOS apps</li>
<li>Power efficiency is critical (mobile devices)</li>
<li>You need Neural Engine acceleration</li>
<li>Deploying to end users through the App Store</li>
</ul>
<p><strong>Use MLX when:</strong></p>
<ul>
<li>Running experiments on your Mac</li>
<li>Training or fine-tuning models</li>
<li>Working with LLMs interactively</li>
<li>Building macOS developer tools</li>
</ul>
<p><strong>Use both when:</strong></p>
<ul>
<li>Developing on Mac with MLX, then converting to CoreML for deployment</li>
<li>Research teams shipping production apps</li>
</ul>
<p>The typical workflow: experiment with MLX, export to a standard format, convert to CoreML for deployment.</p>
<hr>
<h2 id="metal-compute-shaders">Metal Compute Shaders</h2>
<p>When MPS does not provide the operation you need, you can write custom GPU kernels in Metal Shading Language (MSL).</p>
<h3 id="metal-shading-language-basics">Metal Shading Language Basics</h3>
<p>MSL is based on C++14 with GPU-specific extensions. Compute kernels are marked with the <code>kernel</code> keyword:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#include &lt;metal_stdlib&gt;</span></span>
<span class="line"><span>using namespace metal;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>kernel void vector_add(</span></span>
<span class="line"><span>    device const float* a [[buffer(0)]],</span></span>
<span class="line"><span>    device const float* b [[buffer(1)]],</span></span>
<span class="line"><span>    device float* result [[buffer(2)]],</span></span>
<span class="line"><span>    uint index [[thread_position_in_grid]]</span></span>
<span class="line"><span>) {</span></span>
<span class="line"><span>    result[index] = a[index] + b[index];</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>The <code>[[buffer(N)]]</code> attributes specify buffer bindings. The <code>[[thread_position_in_grid]]</code> attribute provides the thread’s index in the dispatch grid.</p>
<h3 id="dispatching-compute-work">Dispatching Compute Work</h3>
<p>From Swift, you create a compute pipeline and dispatch work:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> Metal</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Get the default GPU</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MTLCreateSystemDefaultDevice</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)">!</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Load the shader library</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> library </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> device.</span><span style="color:var(--astro-code-token-function)">makeDefaultLibrary</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)">!</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> function </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> library.</span><span style="color:var(--astro-code-token-function)">makeFunction</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">name</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;vector_add&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)">!</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Create compute pipeline</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> pipeline </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> try!</span><span style="color:var(--astro-code-foreground)"> device.</span><span style="color:var(--astro-code-token-function)">makeComputePipelineState</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">function</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> function</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Create command queue and buffer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> commandQueue </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> device.</span><span style="color:var(--astro-code-token-function)">makeCommandQueue</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)">!</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> commandBuffer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> commandQueue.</span><span style="color:var(--astro-code-token-function)">makeCommandBuffer</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)">!</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> encoder </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> commandBuffer.</span><span style="color:var(--astro-code-token-function)">makeComputeCommandEncoder</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)">!</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Set pipeline and buffers</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoder.</span><span style="color:var(--astro-code-token-function)">setComputePipelineState</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">pipeline</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoder.</span><span style="color:var(--astro-code-token-function)">setBuffer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">bufferA, offset</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-function)">, index</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoder.</span><span style="color:var(--astro-code-token-function)">setBuffer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">bufferB, offset</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-function)">, index</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoder.</span><span style="color:var(--astro-code-token-function)">setBuffer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">bufferResult, offset</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-function)">, index</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Calculate thread groups</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> threadGroupSize </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MTLSize</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">width</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 256</span><span style="color:var(--astro-code-token-function)">, height</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-function)">, depth</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> threadGroups </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MTLSize</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    width</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> (elementCount </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 255</span><span style="color:var(--astro-code-token-function)">) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 256</span><span style="color:var(--astro-code-token-function)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    height</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-function)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    depth</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoder.</span><span style="color:var(--astro-code-token-function)">dispatchThreadgroups</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">threadGroups, threadsPerThreadgroup</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> threadGroupSize</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoder.</span><span style="color:var(--astro-code-token-function)">endEncoding</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">commandBuffer.</span><span style="color:var(--astro-code-token-function)">commit</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">commandBuffer.</span><span style="color:var(--astro-code-token-function)">waitUntilCompleted</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="custom-kernels-for-ml">Custom Kernels for ML</h3>
<p>Apple provides sample code for implementing custom PyTorch operations with Metal. This allows you to write performance-critical operations in MSL while using PyTorch for the rest of your model.</p>
<p>A typical pattern for ML kernels:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>kernel void fused_silu_multiply(</span></span>
<span class="line"><span>    device const float* input [[buffer(0)]],</span></span>
<span class="line"><span>    device const float* gate [[buffer(1)]],</span></span>
<span class="line"><span>    device float* output [[buffer(2)]],</span></span>
<span class="line"><span>    uint index [[thread_position_in_grid]]</span></span>
<span class="line"><span>) {</span></span>
<span class="line"><span>    float x = input[index];</span></span>
<span class="line"><span>    float g = gate[index];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // SiLU: x * sigmoid(x)</span></span>
<span class="line"><span>    float silu = x / (1.0 + exp(-x));</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Multiply with gate</span></span>
<span class="line"><span>    output[index] = silu * g;</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>Fusing operations like this reduces memory bandwidth by avoiding intermediate buffers.</p>
<h3 id="debugging-metal-shaders">Debugging Metal Shaders</h3>
<p>MSL debugging is challenging. The Xcode GPU Debugger allows stepping through shaders, but only for captured frames. For compute shaders without rendering, you can:</p>
<ol>
<li>Write intermediate values to a debug buffer</li>
<li>Use Metal’s GPU capture feature in Xcode</li>
<li>Add assertions that write to a status buffer on failure</li>
</ol>
<hr>
<h2 id="bnns-cpu-optimized-neural-networks">BNNS: CPU-Optimized Neural Networks</h2>
<p>Basic Neural Network Subroutines (BNNS) is Apple’s library for CPU-based neural network inference. Part of the Accelerate framework, BNNS provides operations tuned for Apple’s CPU architectures.</p>
<h3 id="when-to-use-bnns">When to Use BNNS</h3>
<p>BNNS makes sense when:</p>
<ul>
<li>The model is small enough that GPU overhead exceeds compute time</li>
<li>Real-time requirements demand predictable latency (no GPU scheduling)</li>
<li>Power constraints favor CPU over GPU</li>
<li>You need to run inference on Apple Watch, which has limited GPU capabilities</li>
</ul>
<p>CoreML uses BNNS internally for CPU execution paths. You can also use BNNS directly for custom inference pipelines.</p>
<h3 id="bnns-graph-api">BNNS Graph API</h3>
<p>The BNNSGraph API, introduced in iOS 17 and expanded in iOS 18, allows you to define entire networks as graphs:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> Accelerate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Create a graph</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">var</span><span style="color:var(--astro-code-foreground)"> graph </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BNNSGraph</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Add layers</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> convLayer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BNNSGraphAddConvolutionLayer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    graph,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    inputDescriptor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    weightDescriptor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    biasDescriptor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    outputDescriptor,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    convolutionDescriptor</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Compile the graph</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">BNNSGraphCompile</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">graph, </span><span style="color:var(--astro-code-token-constant)">nil</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Execute</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">BNNSGraphExecute</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">graph, inputBuffer, outputBuffer</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="graph-optimizations">Graph Optimizations</h3>
<p>BNNSGraph performs several optimizations:</p>
<ul>
<li><strong>Layer fusion</strong>: Combines convolution + batch norm + activation into single operations</li>
<li><strong>Copy elision</strong>: Eliminates unnecessary memory copies by using references</li>
<li><strong>Memory sharing</strong>: Reuses buffers across layers when tensors have non-overlapping lifetimes</li>
<li><strong>Weight repacking</strong>: Reorganizes weights for better cache locality</li>
</ul>
<p>These optimizations happen automatically when you compile the graph.</p>
<h3 id="bnns-graph-builder-in-ios-18">BNNS Graph Builder in iOS 18</h3>
<p>iOS 18 added BNNSGraphBuilder, which lets you construct graphs directly in Swift:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> Accelerate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> builder </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BNNSGraphBuilder</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> input </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> builder.</span><span style="color:var(--astro-code-token-function)">addInput</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">shape</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> [</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-function)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-function)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-function)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-function)">], dataType</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> .</span><span style="color:var(--astro-code-token-constant)">float</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> conv </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> builder.</span><span style="color:var(--astro-code-token-function)">addConvolution</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">input, weights</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> weights, bias</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> bias</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> relu </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> builder.</span><span style="color:var(--astro-code-token-function)">addActivation</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">conv, function</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> .relu</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> builder.</span><span style="color:var(--astro-code-token-function)">addOutput</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">relu</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> graph </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> try</span><span style="color:var(--astro-code-foreground)"> builder.</span><span style="color:var(--astro-code-token-function)">build</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>This provides a more ergonomic API while still benefiting from graph-level optimizations.</p>
<hr>
<h2 id="the-accelerate-framework">The Accelerate Framework</h2>
<p>Accelerate is Apple’s foundational library for numerical computing. It provides BLAS, LAPACK, vDSP, and other optimized routines that underpin both BNNS and higher-level ML frameworks.</p>
<h3 id="blas-and-lapack">BLAS and LAPACK</h3>
<p>The Basic Linear Algebra Subprograms (BLAS) and Linear Algebra Package (LAPACK) implementations in Accelerate are tuned for Apple hardware:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> Accelerate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Matrix multiplication using BLAS</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">var</span><span style="color:var(--astro-code-foreground)"> C </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-constant)">Float</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">repeating</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-function)">, count</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> m </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-function)"> n</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cblas_sgemm</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    CblasRowMajor,    </span><span style="color:var(--astro-code-token-comment)">// Row-major order</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    CblasNoTrans,     </span><span style="color:var(--astro-code-token-comment)">// Don&#39;t transpose A</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    CblasNoTrans,     </span><span style="color:var(--astro-code-token-comment)">// Don&#39;t transpose B</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">    Int32</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">m</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-function)">,         </span><span style="color:var(--astro-code-token-comment)">// Rows of A</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">    Int32</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">n</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-function)">,         </span><span style="color:var(--astro-code-token-comment)">// Columns of B</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">    Int32</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">k</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-function)">,         </span><span style="color:var(--astro-code-token-comment)">// Columns of A / Rows of B</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">    1.0</span><span style="color:var(--astro-code-token-function)">,              </span><span style="color:var(--astro-code-token-comment)">// Alpha</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    A, </span><span style="color:var(--astro-code-token-constant)">Int32</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">k</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-function)">,      </span><span style="color:var(--astro-code-token-comment)">// A and its leading dimension</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    B, </span><span style="color:var(--astro-code-token-constant)">Int32</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">n</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-function)">,      </span><span style="color:var(--astro-code-token-comment)">// B and its leading dimension</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">    0.0</span><span style="color:var(--astro-code-token-function)">,              </span><span style="color:var(--astro-code-token-comment)">// Beta</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &amp;</span><span style="color:var(--astro-code-token-function)">C, </span><span style="color:var(--astro-code-token-constant)">Int32</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">n</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">      // C and its leading dimension</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>These routines automatically use the AMX (Apple Matrix Extensions) coprocessor when available. The AMX provides matrix multiplication throughput roughly 2x higher than standard NEON SIMD instructions.</p>
<h3 id="vdsp-for-signal-processing">vDSP for Signal Processing</h3>
<p>vDSP provides optimized routines for:</p>
<ul>
<li>Fast Fourier transforms</li>
<li>Convolution and correlation</li>
<li>Vector arithmetic</li>
<li>Biquad filtering</li>
</ul>
<p>For ML workloads, vDSP is useful for audio preprocessing, spectrogram computation, and other signal processing pipelines that feed into neural networks:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> Accelerate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Compute magnitude spectrum</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">var</span><span style="color:var(--astro-code-foreground)"> magnitudes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-constant)">Float</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">repeating</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-function)">, count</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> fftLength </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">vDSP.</span><span style="color:var(--astro-code-token-function)">squareMagnitudes</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    splitComplex,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    result</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">magnitudes</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Convert to decibels</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">vDSP.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    amplitude</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> magnitudes,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    toDecibels</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">decibelSpectrum,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    zeroReference</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1.0</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="integration-with-ml-frameworks">Integration with ML Frameworks</h3>
<p>Accelerate functions are called internally by BNNS, CoreML (for CPU execution), and even MLX. When you see high CPU utilization during model inference, Accelerate routines are often doing the work.</p>
<p>For custom preprocessing pipelines, using Accelerate directly is often faster than equivalent NumPy operations in Python:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// Fast image normalization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">vDSP.</span><span style="color:var(--astro-code-token-function)">divide</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    pixels,</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">    255.0</span><span style="color:var(--astro-code-token-function)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    result</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">normalizedPixels</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">vDSP.</span><span style="color:var(--astro-code-token-function)">subtract</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    normalizedPixels,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    mean,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    result</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">centeredPixels</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">vDSP.</span><span style="color:var(--astro-code-token-function)">divide</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    centeredPixels,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    stdDev,</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    result</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">standardizedPixels</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="tensorflow-metal">TensorFlow Metal</h2>
<p>TensorFlow supports Apple Silicon GPUs through the <code>tensorflow-metal</code> plugin, which implements TensorFlow’s PluggableDevice API.</p>
<h3 id="installation">Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> tensorflow</span><span style="color:var(--astro-code-token-string)"> tensorflow-metal</span></span></code></pre>
<h3 id="current-state">Current State</h3>
<p>The TensorFlow Metal plugin works but has significant limitations:</p>
<p><strong>Version compatibility</strong>: As of early 2025, the plugin requires specific combinations of TensorFlow, Python, and macOS versions. The latest wheels support macOS 12 and Python up to 3.11. Users on macOS 15 with Python 3.12 face compatibility issues.</p>
<p><strong>Operation coverage</strong>: Not all TensorFlow operations have Metal implementations. Complex numbers (DT_COMPLEX64) are not supported.</p>
<p><strong>Performance variability</strong>: For small models or small batch sizes, CPU execution may be faster due to GPU dispatch overhead.</p>
<h3 id="verification">Verification</h3>
<p>To verify Metal acceleration is working:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> tensorflow </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> tf</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># List physical devices</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">devices </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">config</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">list_physical_devices</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(devices)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Should show both CPU:0 and GPU:0</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run a simple operation</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;/GPU:0&#39;</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">normal</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(a, tf.</span><span style="color:var(--astro-code-token-function)">transpose</span><span style="color:var(--astro-code-token-punctuation)">(a))</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(b.device)</span><span style="color:var(--astro-code-token-comment)">  # Should show GPU</span></span></code></pre>
<h3 id="recommendation">Recommendation</h3>
<p>For new projects on Apple Silicon, PyTorch MPS or MLX typically offer better support and performance than TensorFlow Metal. TensorFlow Metal remains useful for existing TensorFlow codebases that need to run on Mac hardware.</p>
<hr>
<h2 id="model-conversion-pipelines">Model Conversion Pipelines</h2>
<p>Converting models to CoreML involves several steps and decisions about optimization.</p>
<h3 id="pytorch-to-coreml-direct">PyTorch to CoreML (Direct)</h3>
<p>The recommended path for PyTorch models:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> coremltools </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ct</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MyModel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Trace or script the model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">example_input </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">traced </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">jit</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">trace</span><span style="color:var(--astro-code-token-punctuation)">(model, example_input)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Convert with compute units specification</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    traced,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    inputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[ct.</span><span style="color:var(--astro-code-token-function)">TensorType</span><span style="color:var(--astro-code-token-punctuation)">(name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">, shape</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">example_input.shape)],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    compute_units</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.ComputeUnit.ALL,  </span><span style="color:var(--astro-code-token-comment)"># CPU, GPU, and Neural Engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    minimum_deployment_target</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.target.iOS17</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="onnx-to-coreml">ONNX to CoreML</h3>
<p>For models from other frameworks or ONNX model zoo:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> coremltools </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ct</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load ONNX model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">converters</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">onnx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.onnx&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    minimum_deployment_target</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.target.iOS16</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Note that the ONNX path may have more conversion issues than direct PyTorch conversion.</p>
<h3 id="quantization-during-conversion">Quantization During Conversion</h3>
<p>coremltools 7+ provides optimization APIs for quantization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> coremltools </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ct</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> coremltools</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optimize</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">coreml </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> (</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    OpLinearQuantizerConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    OptimizationConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    linear_quantize_weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load the model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">models</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">MLModel</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.mlpackage&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure 8-bit quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OptimizationConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    global_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)">OpLinearQuantizerConfig</span><span style="color:var(--astro-code-token-punctuation)">(mode</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;linear_symmetric&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantized_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> linear_quantize_weights</span><span style="color:var(--astro-code-token-punctuation)">(mlmodel, config)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantized_model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model_int8.mlpackage&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>For 4-bit quantization (useful for LLMs):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> coremltools</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optimize</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">coreml </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> (</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    OpPalettizerConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    OptimizationConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    palettize_weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OptimizationConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    global_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)">OpPalettizerConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        nbits</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        mode</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;kmeans&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantized_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> palettize_weights</span><span style="color:var(--astro-code-token-punctuation)">(mlmodel, config)</span></span></code></pre>
<p>Block-wise quantization provides better accuracy for aggressive compression:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OptimizationConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    global_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)">OpLinearQuantizerConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        mode</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;linear_symmetric&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        weight_threshold</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Only quantize weights larger than this</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        granularity</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;per_block&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        block_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="training-aware-quantization">Training-Aware Quantization</h3>
<p>For best results, quantize during training using coremltools.optimize.torch:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> coremltools</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optimize</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">quantization </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> (</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    LinearQuantizerConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ModuleLinearQuantizerConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    LinearQuantizer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LinearQuantizerConfig</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_dict</span><span style="color:var(--astro-code-token-punctuation)">({</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;global_config&quot;</span><span style="color:var(--astro-code-token-punctuation)">: {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;quantization_scheme&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;symmetric&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;milestones&quot;</span><span style="color:var(--astro-code-token-punctuation)">: [</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">400</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">500</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">})</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create quantizer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LinearQuantizer</span><span style="color:var(--astro-code-token-punctuation)">(model, config)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prepare model for quantization-aware training</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">prepare</span><span style="color:var(--astro-code-token-punctuation)">(example_inputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">example_input)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train the model</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> epoch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(num_epochs):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    quantizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    train_one_epoch</span><span style="color:var(--astro-code-token-punctuation)">(model)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Finalize and convert</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">finalize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlmodel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(model)</span></span></code></pre>
<hr>
<h2 id="practical-deployment-considerations">Practical Deployment Considerations</h2>
<p>Deploying ML models across Apple platforms requires understanding the constraints and capabilities of each target.</p>
<h3 id="ios-and-ipados">iOS and iPadOS</h3>
<p><strong>Model size limits</strong>: App Store apps have size limits. Use compression and quantization aggressively. Consider downloading models on first launch.</p>
<p><strong>Memory constraints</strong>: iPhones have limited RAM. The system will terminate apps that use too much memory. Profile your model’s memory footprint.</p>
<p><strong>Thermal throttling</strong>: Sustained inference causes heat buildup. The system reduces performance to manage temperature. Design for burst usage patterns.</p>
<p><strong>Background execution</strong>: Background apps have limited CPU/GPU access. Use background tasks API for non-real-time inference.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-function)"> CoreML</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Load model with configuration</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MLModelConfiguration</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config.computeUnits </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> .cpuAndNeuralEngine  </span><span style="color:var(--astro-code-token-comment)">// Avoid GPU for background</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> try</span><span style="color:var(--astro-code-token-function)"> MyModel</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">configuration</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> config</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="macos">macOS</h3>
<p>macOS has fewer constraints than mobile platforms:</p>
<ul>
<li>More memory available</li>
<li>No thermal throttling concerns for desktop Macs</li>
<li>Full GPU access without power restrictions</li>
</ul>
<p>For macOS-only apps, you can use MLX directly instead of CoreML if you prefer its API.</p>
<h3 id="watchos">watchOS</h3>
<p>Apple Watch has the most constrained environment:</p>
<ul>
<li>Limited memory (varies by model)</li>
<li>Small GPU with limited capabilities</li>
<li>CPU-focused execution via BNNS</li>
</ul>
<p>Keep models small. Quantize aggressively. Consider CPU-only execution:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MLModelConfiguration</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config.computeUnits </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> .cpuOnly</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> try</span><span style="color:var(--astro-code-token-function)"> WatchModel</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">configuration</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> config</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="visionos">visionOS</h3>
<p>Vision Pro runs visionOS, which supports CoreML with full GPU and Neural Engine access. The M2 chip provides capable ML performance.</p>
<p>Spatial computing apps may need to balance ML inference with rendering workloads. Consider using <code>computeUnits = .cpuAndNeuralEngine</code> to leave GPU headroom for graphics.</p>
<h3 id="cross-platform-strategy">Cross-Platform Strategy</h3>
<p>For apps targeting multiple Apple platforms:</p>
<ol>
<li><strong>Use CoreML as the deployment format</strong>: One .mlpackage works everywhere</li>
<li><strong>Test on real devices</strong>: Simulator does not accurately represent Neural Engine behavior</li>
<li><strong>Handle graceful degradation</strong>: Check <code>MLModel.availableComputeDevices</code> and adjust</li>
<li><strong>Profile each platform</strong>: Performance characteristics differ significantly</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// Check available compute devices</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> try</span><span style="color:var(--astro-code-token-function)"> MyModel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">let</span><span style="color:var(--astro-code-foreground)"> devices </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model.model.availableComputeDevices</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> devices.</span><span style="color:var(--astro-code-token-function)">contains</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">where</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-function)"> { $0 </span><span style="color:var(--astro-code-token-keyword)">==</span><span style="color:var(--astro-code-token-function)"> .neuralEngine }</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Can use Neural Engine</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">} </span><span style="color:var(--astro-code-token-keyword)">else</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Fall back to CPU/GPU config</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<hr>
<h2 id="the-amx-coprocessor">The AMX Coprocessor</h2>
<p>The Apple Matrix Coprocessor (AMX) is an undocumented accelerator present in all Apple Silicon chips. While you cannot program it directly, understanding its role helps explain performance characteristics.</p>
<h3 id="what-amx-does">What AMX Does</h3>
<p>AMX accelerates matrix multiplication on the CPU. It provides roughly 2x the throughput of NEON SIMD instructions for matrix operations. When you call BLAS routines through Accelerate, AMX handles the heavy lifting.</p>
<h3 id="access-through-accelerate">Access Through Accelerate</h3>
<p>The only supported way to use AMX is through the Accelerate framework. Direct AMX instructions exist but are undocumented and unsupported. Apple’s BLAS implementation automatically uses AMX when beneficial.</p>
<p>This means CPU-based inference through BNNS or Accelerate benefits from AMX automatically. You do not need to do anything special.</p>
<h3 id="amx-vs-neural-engine-vs-gpu">AMX vs Neural Engine vs GPU</h3>
<p>Each accelerator has different strengths:</p>
<ul>
<li><strong>AMX</strong>: Low latency, integrated with CPU, used automatically by Accelerate</li>
<li><strong>Neural Engine</strong>: Highest throughput for supported operations, best power efficiency</li>
<li><strong>GPU</strong>: Flexible, handles any compute workload, good for large batch sizes</li>
</ul>
<p>CoreML manages this complexity by analyzing your model and routing operations to the most appropriate hardware.</p>
<hr>
<h2 id="summary">Summary</h2>
<p>Apple’s Metal ML ecosystem provides multiple layers of optimization:</p>













































<div class="table-wrap"><table><thead><tr><th>Layer</th><th>Purpose</th><th>When to Use</th></tr></thead><tbody><tr><td>CoreML</td><td>Production deployment</td><td>iOS/macOS/watchOS/visionOS apps</td></tr><tr><td>MLX</td><td>Research and training</td><td>Mac-based ML development</td></tr><tr><td>MPSGraph</td><td>GPU compute graphs</td><td>Custom inference engines</td></tr><tr><td>MPS</td><td>GPU primitives</td><td>Building custom ML ops</td></tr><tr><td>BNNS</td><td>CPU inference</td><td>Low-latency, power-sensitive workloads</td></tr><tr><td>Accelerate</td><td>Numerical computing</td><td>Preprocessing, custom algorithms</td></tr><tr><td>Metal</td><td>Custom GPU kernels</td><td>Operations not in MPS</td></tr></tbody></table></div>
<p>For most developers, the right approach is:</p>
<ol>
<li>Train models using PyTorch (with MPS for GPU acceleration) or MLX</li>
<li>Convert to CoreML using coremltools</li>
<li>Apply quantization appropriate for your target platform</li>
<li>Deploy with CoreML, letting it handle hardware routing</li>
</ol>
<p>The ecosystem continues evolving. WWDC 2024 brought transformer optimizations to MPSGraph. iOS 18 added stateful models to CoreML. Each release improves performance and adds capabilities. Keep your tools updated and follow Apple’s machine learning documentation for the latest guidance.</p>
<hr>
<h2 id="references">References</h2>
<ol>
<li>Apple Developer Documentation - Metal Performance Shaders: <a href="https://developer.apple.com/documentation/metalperformanceshaders">https://developer.apple.com/documentation/metalperformanceshaders</a></li>
<li>Apple Developer - Accelerated PyTorch Training on Mac: <a href="https://developer.apple.com/metal/pytorch/">https://developer.apple.com/metal/pytorch/</a></li>
<li>PyTorch Documentation - MPS Backend: <a href="https://docs.pytorch.org/docs/stable/notes/mps.html">https://docs.pytorch.org/docs/stable/notes/mps.html</a></li>
<li>Apple Developer Documentation - Core ML: <a href="https://developer.apple.com/documentation/coreml">https://developer.apple.com/documentation/coreml</a></li>
<li>Apple Machine Learning Research - Deploying Transformers on the Apple Neural Engine: <a href="https://machinelearning.apple.com/research/neural-engine-transformers">https://machinelearning.apple.com/research/neural-engine-transformers</a></li>
<li>Apple Machine Learning Research - On-Device Llama 3.1 with Core ML: <a href="https://machinelearning.apple.com/research/core-ml-on-device-llama">https://machinelearning.apple.com/research/core-ml-on-device-llama</a></li>
<li>WWDC24 - Accelerate Machine Learning with Metal: <a href="https://developer.apple.com/videos/play/wwdc2024/10218/">https://developer.apple.com/videos/play/wwdc2024/10218/</a></li>
<li>WWDC24 - Support Real-Time ML Inference on the CPU: <a href="https://developer.apple.com/videos/play/wwdc2024/10211/">https://developer.apple.com/videos/play/wwdc2024/10211/</a></li>
<li>Apple Developer Documentation - BNNS: <a href="https://developer.apple.com/documentation/accelerate/bnns">https://developer.apple.com/documentation/accelerate/bnns</a></li>
<li>Apple Developer Documentation - Accelerate: <a href="https://developer.apple.com/documentation/accelerate">https://developer.apple.com/documentation/accelerate</a></li>
<li>Guide to Core ML Tools - Converting from PyTorch: <a href="https://apple.github.io/coremltools/docs-guides/source/convert-pytorch.html">https://apple.github.io/coremltools/docs-guides/source/convert-pytorch.html</a></li>
<li>Guide to Core ML Tools - Quantization Algorithms: <a href="https://apple.github.io/coremltools/docs-guides/source/opt-quantization-algos.html">https://apple.github.io/coremltools/docs-guides/source/opt-quantization-algos.html</a></li>
<li>Guide to Core ML Tools - Stateful Models: <a href="https://apple.github.io/coremltools/docs-guides/source/stateful-models.html">https://apple.github.io/coremltools/docs-guides/source/stateful-models.html</a></li>
<li>Apple Developer - Metal Shading Language Specification: <a href="https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf">https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf</a></li>
<li>Apple Developer Documentation - Performing Calculations on a GPU: <a href="https://developer.apple.com/documentation/Metal/performing-calculations-on-a-gpu">https://developer.apple.com/documentation/Metal/performing-calculations-on-a-gpu</a></li>
<li>Apple Developer - TensorFlow Metal Plugin: <a href="https://developer.apple.com/metal/tensorflow-plugin/">https://developer.apple.com/metal/tensorflow-plugin/</a></li>
<li>PyTorch GitHub - MPS Operator Coverage Tracking: <a href="https://github.com/pytorch/pytorch/issues/141287">https://github.com/pytorch/pytorch/issues/141287</a></li>
<li>Explosion AI - Fast Transformer Inference with Metal Performance Shaders: <a href="https://explosion.ai/blog/metal-performance-shaders">https://explosion.ai/blog/metal-performance-shaders</a></li>
<li>MLX Documentation: <a href="https://ml-explore.github.io/mlx/">https://ml-explore.github.io/mlx/</a></li>
<li>byby.dev - When to Use Apple MLX vs Core ML: <a href="https://byby.dev/apple-mlx-vs-coreml">https://byby.dev/apple-mlx-vs-coreml</a></li>
</ol>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>Apple Silicon</category>
            <category>Edge &amp; on-device</category>
            <category>Kernels</category>
            <enclosure length="62511" type="image/png" url="https://blog.ecitis.org/open-graph/apple-metal-ml-ecosystem.png"/>
        </item>
        <item>
            <title><![CDATA[Apple MLX: A Complete Guide to Machine Learning on Apple Silicon]]></title>
            <link>https://blog.ecitis.org/apple-mlx-guide/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/apple-mlx-guide/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how MLX uses unified memory to make model training, fine-tuning, and local inference feel native on Apple Silicon.]]></description>
            <content:encoded><![CDATA[<h2 id="why-mlx-matters-for-apple-silicon-developers">Why MLX Matters for Apple Silicon Developers</h2>
<p>Apple released MLX in December 2023 as an open-source array framework built specifically for machine learning on Apple Silicon. Unlike PyTorch with MPS backend or TensorFlow with Metal plugin, MLX was designed from the ground up to exploit the unified memory architecture of M-series chips. The framework has matured through 2024 and 2025, reaching version 0.30+ with support for the M5 chip’s Neural Accelerators announced at WWDC 2025.</p>
<p>This guide covers everything you need to know to start using MLX for machine learning research and inference on your Mac.</p>
<hr>
<h2 id="what-is-mlx">What is MLX</h2>
<p>MLX is an array framework for numerical computing and machine learning. It provides a NumPy-like API with support for automatic differentiation, JIT compilation, and GPU acceleration through Metal. The framework was developed by Apple’s machine learning research team and is actively maintained on GitHub at <a href="https://github.com/ml-explore/mlx">ml-explore/mlx</a>.</p>
<h3 id="core-design-principles">Core Design Principles</h3>
<p>MLX follows several design principles that distinguish it from other frameworks:</p>
<p><strong>Familiar API</strong>: The Python interface mirrors NumPy closely. If you know NumPy, you can start using MLX immediately. Higher-level neural network APIs in <code>mlx.nn</code> follow PyTorch conventions.</p>
<p><strong>Lazy Evaluation</strong>: Computations are not executed immediately. Instead, MLX builds a computation graph that is only evaluated when results are needed. This allows the framework to optimize and fuse operations before execution.</p>
<p><strong>Unified Memory</strong>: Arrays live in shared memory accessible by both CPU and GPU. There is no need to copy data between devices.</p>
<p><strong>Composable Function Transformations</strong>: Operations like <code>grad()</code>, <code>vmap()</code>, and <code>compile()</code> can be composed together. You can take the gradient of a compiled function, or compile a vectorized gradient computation.</p>
<p><strong>Multi-Language Support</strong>: MLX has bindings for Python, Swift, C++, and C. The same models can run on macOS, iOS, iPadOS, and visionOS.</p>
<h3 id="basic-array-operations">Basic Array Operations</h3>
<p>Here is a simple example showing MLX array operations:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create arrays</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">4.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">5.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">6.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Operations work like NumPy</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">d </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sin</span><span style="color:var(--astro-code-token-punctuation)">(a)</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cos</span><span style="color:var(--astro-code-token-punctuation)">(b)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Arrays are not computed until needed</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(c)</span><span style="color:var(--astro-code-token-comment)">  # This triggers evaluation</span></span></code></pre>
<p>The framework supports all common array operations including broadcasting, slicing, reshaping, and linear algebra routines.</p>
<hr>
<h2 id="the-unified-memory-model">The Unified Memory Model</h2>
<p>The most significant architectural advantage of MLX comes from Apple Silicon’s unified memory design. On traditional systems with discrete GPUs, data must be copied between CPU RAM and GPU VRAM. This copying introduces latency and limits the effective memory available for large models.</p>
<figure><figcaption><strong>Memory Architecture Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/apple-mlx-guide-0.webp" width="512" height="658" alt="Memory Architecture Comparison" loading="lazy" decoding="async"></figure>
<h3 id="how-unified-memory-works">How Unified Memory Works</h3>
<p>On Apple Silicon, the CPU, GPU, and Neural Engine share the same physical memory pool. When you create an MLX array, it exists in this shared space. Any processor can access it directly without copying.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create an array - it lives in unified memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">normal</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run on GPU - no copy needed</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(x, x.T, stream</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mx.gpu)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run on CPU - still no copy needed</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(y, stream</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mx.cpu)</span></span></code></pre>
<p>In MLX, you specify the device when calling an operation, not when creating the array. The array itself has no device affiliation. This eliminates common bugs in PyTorch where tensors end up on the wrong device.</p>
<h3 id="memory-advantages-for-large-models">Memory Advantages for Large Models</h3>
<p>The unified memory model provides practical benefits for running large language models. A Mac with 128GB of unified memory can load models that would require a workstation with a high-end GPU to run on CUDA systems.</p>
<p>However, there are limits. The GPU cannot typically use more than about 75% of total system memory. A 128GB Mac can allocate roughly 96GB for GPU tasks. This is still substantial compared to the 24GB available on consumer NVIDIA GPUs.</p>
<hr>
<h2 id="lazy-evaluation-deep-dive">Lazy Evaluation Deep Dive</h2>
<p>MLX uses lazy evaluation, meaning operations are recorded but not executed immediately. Understanding this model is essential for writing efficient MLX code.</p>
<h3 id="how-lazy-evaluation-works">How Lazy Evaluation Works</h3>
<p>When you write <code>c = a + b</code>, MLX does not compute the sum. Instead, it creates a node in a computation graph with <code>a</code> and <code>b</code> as inputs. The actual computation only happens when you need the result.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">4.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">5.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">6.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># No computation happens here</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">d </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> c </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">e </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(d)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Computation happens when we need the value</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(e)</span><span style="color:var(--astro-code-token-comment)">  # Triggers evaluation of entire graph</span></span></code></pre>
<p>You can explicitly trigger evaluation with <code>mx.eval()</code>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">(e)</span><span style="color:var(--astro-code-token-comment)">  # Force computation</span></span></code></pre>
<h3 id="memory-benefits">Memory Benefits</h3>
<p>Lazy evaluation enables memory optimizations. Consider initializing a large model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> nn</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Model weights are &quot;created&quot; but not allocated</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LargeModel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-comment)">  # Uses float32 by default</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Update to float16 before evaluation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_weights</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;weights.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Loads as float16</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Only float16 memory is ever allocated</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">(model.</span><span style="color:var(--astro-code-token-function)">parameters</span><span style="color:var(--astro-code-token-punctuation)">())</span></span></code></pre>
<p>If evaluation were eager, the model would first allocate float32 weights, then allocate float16 weights, doubling peak memory usage.</p>
<h3 id="graph-fusion-and-optimization">Graph Fusion and Optimization</h3>
<p>The lazy evaluation model allows MLX to fuse operations. Multiple element-wise operations can be combined into a single GPU kernel, reducing memory bandwidth requirements and kernel launch overhead.</p>
<hr>
<h2 id="automatic-differentiation">Automatic Differentiation</h2>
<p>MLX implements automatic differentiation through function transformations rather than tape-based recording. This approach, inspired by JAX, provides more flexibility and composability.</p>
<h3 id="the-grad-function">The grad Function</h3>
<p>The <code>mx.grad()</code> function takes a function and returns a new function that computes gradients:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> f</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(x </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Create gradient function</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">grad_f </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">grad</span><span style="color:var(--astro-code-token-punctuation)">(f)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">grad_f</span><span style="color:var(--astro-code-token-punctuation)">(x))</span><span style="color:var(--astro-code-token-comment)">  # [2.0, 4.0, 6.0]</span></span></code></pre>
<p>Unlike PyTorch, there is no <code>backward()</code> method, no <code>zero_grad()</code>, no <code>requires_grad</code> property, and no <code>detach()</code>. Gradients are computed by transforming functions.</p>
<h3 id="valueandgrad">value_and_grad</h3>
<p>Computing both the function value and gradient is common in optimization. Rather than calling the function twice, use <code>value_and_grad()</code>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> loss_fn</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    pred </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">mean</span><span style="color:var(--astro-code-token-punctuation)">((pred </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-punctuation)"> y) </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Get both loss value and gradients</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">loss_and_grad_fn </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">value_and_grad</span><span style="color:var(--astro-code-token-punctuation)">(model, loss_fn)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">loss</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> grads </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> loss_and_grad_fn</span><span style="color:var(--astro-code-token-punctuation)">(model, x, y)</span></span></code></pre>
<h3 id="higher-order-gradients">Higher-Order Gradients</h3>
<p>Function transformations compose naturally. You can compute the gradient of a gradient:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> f</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 3</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">grad_f </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">grad</span><span style="color:var(--astro-code-token-punctuation)">(f)</span><span style="color:var(--astro-code-token-comment)">       # 3x^2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">grad2_f </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">grad</span><span style="color:var(--astro-code-token-punctuation)">(grad_f)</span><span style="color:var(--astro-code-token-comment)">  # 6x</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">2.0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">grad2_f</span><span style="color:var(--astro-code-token-punctuation)">(x))</span><span style="color:var(--astro-code-token-comment)">  # 12.0</span></span></code></pre>
<hr>
<h2 id="jit-compilation-with-mxcompile">JIT Compilation with mx.compile</h2>
<p>MLX provides a <code>compile()</code> transformation that optimizes computation graphs. Compilation fuses operations, eliminates redundant computations, and generates optimized Metal kernels.</p>
<h3 id="basic-usage">Basic Usage</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> model_forward</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> w1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> w2</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    h </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">tanh</span><span style="color:var(--astro-code-token-punctuation)">(x </span><span style="color:var(--astro-code-token-keyword)">@</span><span style="color:var(--astro-code-token-punctuation)"> w1)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> h </span><span style="color:var(--astro-code-token-keyword)">@</span><span style="color:var(--astro-code-foreground)"> w2</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Compile the function</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">compiled_forward </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(model_forward)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># First call builds and compiles the graph (slow)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> compiled_forward</span><span style="color:var(--astro-code-token-punctuation)">(x, w1, w2)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Subsequent calls reuse compiled code (fast)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> compiled_forward</span><span style="color:var(--astro-code-token-punctuation)">(x, w1, w2)</span></span></code></pre>
<p>The first call to a compiled function is slow because MLX must trace the computation, optimize the graph, and compile Metal shaders. Subsequent calls with the same input shapes reuse the compiled code.</p>
<h3 id="composing-compile-with-other-transformations">Composing compile with Other Transformations</h3>
<p>Compilation works with other function transformations:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Compile a gradient function</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">compiled_grad </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(mx.</span><span style="color:var(--astro-code-token-function)">grad</span><span style="color:var(--astro-code-token-punctuation)">(loss_fn))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Compile a vectorized function</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">compiled_vmap </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(mx.</span><span style="color:var(--astro-code-token-function)">vmap</span><span style="color:var(--astro-code-token-punctuation)">(batch_fn))</span></span></code></pre>
<h3 id="debugging-compiled-functions">Debugging Compiled Functions</h3>
<p>Compiled functions cannot contain print statements or other side effects because they are traced with placeholder inputs. For debugging, disable compilation:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Globally disable compilation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">disable_compile</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or use environment variable</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX_DISABLE_COMPILE=1 python script.py</span></span></code></pre>
<hr>
<h2 id="metal-integration">Metal Integration</h2>
<p>MLX uses Metal, Apple’s GPU programming framework, as its backend. Most users never interact with Metal directly, but understanding the integration helps with debugging and optimization.</p>
<h3 id="default-stream-behavior">Default Stream Behavior</h3>
<p>MLX operations run on a default GPU stream:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># These operations run on the GPU by default</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">normal</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(a, a.T)</span></span></code></pre>
<p>You can explicitly specify CPU execution:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Force CPU execution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">add</span><span style="color:var(--astro-code-token-punctuation)">(a, b, stream</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mx.cpu)</span></span></code></pre>
<h3 id="custom-metal-kernels">Custom Metal Kernels</h3>
<p>For operations not covered by built-in primitives, MLX allows custom Metal kernels:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">source </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    uint elem = thread_position_in_grid.x;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    T tmp = inp[elem];</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    out[elem] = tmp * tmp;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">kernel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fast</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">metal_kernel</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;square&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    input_names</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;inp&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_names</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;out&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    source</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">source,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">4.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> kernel</span><span style="color:var(--astro-code-token-punctuation)">(inputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[a], grid</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,), output_shapes</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[(</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,)], output_dtypes</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[mx.float32])</span></span></code></pre>
<h3 id="m5-neural-accelerator-support">M5 Neural Accelerator Support</h3>
<p>With macOS 26.2 and MLX’s latest versions, the framework can leverage the Neural Accelerators in the M5 chip. MLX uses the Tensor Operations (TensorOps) and Metal Performance Primitives frameworks introduced with Metal 4 to access dedicated matrix multiplication hardware.</p>
<hr>
<h2 id="mlx-lm-running-large-language-models">MLX-LM: Running Large Language Models</h2>
<p>MLX-LM is the official package for running and fine-tuning large language models on Apple Silicon. It provides a simple interface for loading models from Hugging Face, generating text, and fine-tuning with LoRA.</p>
<h3 id="installation">Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> mlx-lm</span></span></code></pre>
<h3 id="loading-and-generating-text">Loading and Generating Text</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load a model from Hugging Face</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/Mistral-7B-Instruct-v0.3-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate text</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain quantum computing in simple terms:&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-token-punctuation)">(model, tokenizer, prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">prompt, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">200</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(response)</span></span></code></pre>
<p>Command-line usage:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">mlx_lm.generate</span><span style="color:var(--astro-code-token-string)"> --model</span><span style="color:var(--astro-code-token-string)"> mlx-community/Qwen3-4B-Instruct-2507-4bit</span><span style="color:var(--astro-code-token-string)"> --prompt</span><span style="color:var(--astro-code-token-string-expression)"> &quot;hello&quot;</span></span></code></pre>
<h3 id="supported-model-architectures">Supported Model Architectures</h3>
<p>MLX-LM supports most popular LLM architectures including:</p>
<ul>
<li>LLaMA and LLaMA 2/3</li>
<li>Mistral and Mixtral (MoE)</li>
<li>Qwen 2, 2.5, 3 and Qwen3 MoE</li>
<li>Phi 2, 3, 4</li>
<li>Gemma 1, 2, 3</li>
<li>OLMo and OLMoE</li>
<li>MiniCPM</li>
<li>DeepSeek</li>
</ul>
<p>Most models available on Hugging Face can be loaded directly if they follow standard architectures.</p>
<h3 id="quantization">Quantization</h3>
<p>Quantization reduces memory usage and increases generation speed. MLX-LM supports 4-bit, 6-bit, and 8-bit quantization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Convert and quantize a model</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> mlx_lm.convert</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --hf-path</span><span style="color:var(--astro-code-token-string)"> mistralai/Mistral-7B-Instruct-v0.3</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --q-bits</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --q-group-size</span><span style="color:var(--astro-code-token-constant)"> 64</span></span></code></pre>
<p>Using a quantized model in Python:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load a pre-quantized model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/Mistral-7B-Instruct-v0.3-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate as normal</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-token-punctuation)">(model, tokenizer, prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Hello!&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Quantized models from the mlx-community organization on Hugging Face are ready to use without conversion.</p>
<h3 id="memory-requirements">Memory Requirements</h3>
<p>Approximate memory usage for different quantization levels on a 7B parameter model:</p>

























<div class="table-wrap"><table><thead><tr><th>Precision</th><th>Memory Usage</th></tr></thead><tbody><tr><td>float32</td><td>~28 GB</td></tr><tr><td>float16</td><td>~14 GB</td></tr><tr><td>8-bit</td><td>~7 GB</td></tr><tr><td>4-bit</td><td>~3.5 GB</td></tr></tbody></table></div>
<p>A 4-bit quantized Llama 3B can generate around 50 tokens/second on recent Apple Silicon.</p>
<hr>
<h2 id="lora-fine-tuning-with-mlx-lm">LoRA Fine-Tuning with MLX-LM</h2>
<p>Low-Rank Adaptation (LoRA) enables fine-tuning large models with limited memory by training small adapter matrices instead of full model weights. MLX-LM includes built-in support for LoRA and QLoRA (quantized LoRA).</p>
<h3 id="preparing-training-data">Preparing Training Data</h3>
<p>Training data should be in JSONL format with a “text” field:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span><span style="color:var(--astro-code-token-keyword)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Question: What is the capital of France?\nAnswer: Paris&quot;</span><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">{</span><span style="color:var(--astro-code-token-keyword)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Question: What is 2+2?\nAnswer: 4&quot;</span><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<h3 id="running-lora-training">Running LoRA Training</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> mlx_lm.lora</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model</span><span style="color:var(--astro-code-token-string)"> mlx-community/gemma-3-4b-it-4bit</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --train</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --data</span><span style="color:var(--astro-code-token-string)"> ./data</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --adapter-path</span><span style="color:var(--astro-code-token-string)"> ./lora_adapters</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --lora-r</span><span style="color:var(--astro-code-token-constant)"> 16</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --lora-alpha</span><span style="color:var(--astro-code-token-constant)"> 32</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --iters</span><span style="color:var(--astro-code-token-constant)"> 500</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --batch-size</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --learning-rate</span><span style="color:var(--astro-code-token-string)"> 2e-4</span></span></code></pre>
<p>Key parameters:</p>
<ul>
<li><code>--lora-r</code>: Rank of the LoRA matrices (higher = more capacity, more memory)</li>
<li><code>--lora-alpha</code>: Scaling factor for LoRA updates</li>
<li><code>--lora-layers</code>: Number of layers to apply LoRA (default 16)</li>
<li><code>--batch-size</code>: Training batch size (reduce for memory)</li>
<li><code>--grad-checkpoint</code>: Enable gradient checkpointing for memory savings</li>
</ul>
<h3 id="qlora-for-memory-efficiency">QLoRA for Memory Efficiency</h3>
<p>When using a 4-bit quantized base model, training automatically uses QLoRA:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Using a 4-bit model enables QLoRA automatically</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> mlx_lm.lora</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model</span><span style="color:var(--astro-code-token-string)"> mlx-community/Llama-3-8B-Instruct-4bit</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --train</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --data</span><span style="color:var(--astro-code-token-string)"> ./data</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --adapter-path</span><span style="color:var(--astro-code-token-string)"> ./qlora_adapters</span></span></code></pre>
<p>Memory comparison for Llama 7B:</p>





















<div class="table-wrap"><table><thead><tr><th>Method</th><th>Memory Usage</th></tr></thead><tbody><tr><td>Full fine-tuning</td><td>~28 GB</td></tr><tr><td>LoRA (r=8)</td><td>~14 GB</td></tr><tr><td>QLoRA (4-bit + LoRA)</td><td>~7 GB</td></tr></tbody></table></div>
<h3 id="using-trained-adapters">Using Trained Adapters</h3>
<p>Generate text with your trained adapter:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load base model with adapter</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;mlx-community/gemma-3-4b-it-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    adapter_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;./lora_adapters&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-token-punctuation)">(model, tokenizer, prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Your prompt here&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="merging-adapters">Merging Adapters</h3>
<p>For deployment, merge adapters into the base model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> mlx_lm.fuse</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model</span><span style="color:var(--astro-code-token-string)"> mlx-community/gemma-3-4b-it-4bit</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --adapter-path</span><span style="color:var(--astro-code-token-string)"> ./lora_adapters</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --save-path</span><span style="color:var(--astro-code-token-string)"> ./merged_model</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --de-quantize</span><span style="color:var(--astro-code-token-comment)">  # Optional: convert back to float16</span></span></code></pre>
<hr>
<h2 id="openai-compatible-api-server">OpenAI-Compatible API Server</h2>
<p>MLX-LM includes a built-in server that exposes an OpenAI-compatible API. This allows any tool designed for OpenAI’s API to work with local models.</p>
<h3 id="starting-the-server">Starting the Server</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">mlx_lm.server</span><span style="color:var(--astro-code-token-string)"> --model</span><span style="color:var(--astro-code-token-string)"> mlx-community/Mistral-7B-Instruct-v0.3-4bit</span></span></code></pre>
<h3 id="using-the-api">Using the API</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> OpenAI</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OpenAI</span><span style="color:var(--astro-code-token-punctuation)">(base_url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;http://localhost:8080/v1&quot;</span><span style="color:var(--astro-code-token-punctuation)">, api_key</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;not-needed&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">chat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">completions</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">create</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/Mistral-7B-Instruct-v0.3-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        {</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;What is machine learning?&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(response.choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].message.content)</span></span></code></pre>
<h3 id="alternative-servers">Alternative Servers</h3>
<p>For production use cases, consider these community servers:</p>
<p><strong>FastMLX</strong>: High-performance server with support for vision-language models and efficient resource management. Available at <a href="https://github.com/Blaizzy/fastmlx">Blaizzy/fastmlx</a>.</p>
<p><strong>vllm-mlx</strong>: OpenAI-compatible server with continuous batching, MCP tool calling, and multimodal support. Achieves 400+ tokens/second. Available at <a href="https://github.com/waybarrios/vllm-mlx">GitHub</a>.</p>
<p><strong>mlx-openai-server</strong>: FastAPI-based server supporting LLMs, VLMs (via mlx-vlm), image generation (via mflux), and Whisper transcription. Available on <a href="https://pypi.org/project/mlx-openai-server/">PyPI</a>.</p>
<hr>
<h2 id="the-mlx-ecosystem">The MLX Ecosystem</h2>
<p>Beyond MLX core and MLX-LM, a rich ecosystem of packages has developed for various machine learning tasks.</p>
<h3 id="mlx-community-on-hugging-face">mlx-community on Hugging Face</h3>
<p>The <a href="https://huggingface.co/mlx-community">mlx-community</a> organization on Hugging Face hosts over 1,000 models converted to MLX format. These include:</p>
<ul>
<li>LLaMA 3.3 and 3.2 variants</li>
<li>Qwen 2.5, Qwen3, QwQ</li>
<li>Gemma 2 and 3</li>
<li>Mistral and Mixtral</li>
<li>Phi-3 and Phi-4</li>
<li>Whisper models for speech recognition</li>
</ul>
<p>Models are available in various quantization levels (4-bit, 8-bit, fp16) and can be loaded directly with MLX-LM.</p>
<h3 id="mlx-examples-repository">mlx-examples Repository</h3>
<p>The <a href="https://github.com/ml-explore/mlx-examples">ml-explore/mlx-examples</a> repository contains standalone examples including:</p>
<p><strong>Language Models</strong>: Transformer training, LLaMA/Mistral text generation, Mixtral 8x7B (MoE), LoRA and QLoRA fine-tuning.</p>
<p><strong>Image Generation</strong>: Stable Diffusion and SDXL with text-to-image and image-to-image generation. Supports quantization for memory-constrained systems.</p>
<p><strong>Audio</strong>: OpenAI Whisper for speech recognition, Meta’s EnCodec for audio compression, MusicGen for music generation.</p>
<p><strong>Vision-Language</strong>: CLIP for joint text-image embeddings, LLaVA for text generation from images.</p>
<h3 id="mlx-vlm">mlx-vlm</h3>
<p><a href="https://github.com/Blaizzy/mlx-vlm">MLX-VLM</a> provides inference and fine-tuning for vision-language models:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_vlm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/llava-1.5-7b-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-token-punctuation)">(model, processor, </span><span style="color:var(--astro-code-token-string-expression)">&quot;path/to/image.jpg&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;What is in this image?&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(output)</span></span></code></pre>
<p>Supported features:</p>
<ul>
<li>Multi-image analysis</li>
<li>Video captioning and summarization</li>
<li>LoRA and QLoRA fine-tuning for VLMs</li>
<li>Audio and video support with Gemma 3n</li>
</ul>
<h3 id="whisper-mlx">whisper-mlx</h3>
<p><a href="https://pypi.org/project/mlx-whisper/">MLX Whisper</a> provides speech recognition using OpenAI’s Whisper models:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx_whisper</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mlx_whisper</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transcribe</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;audio.mp3&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(result[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># With word-level timestamps</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mlx_whisper</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transcribe</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;audio.mp3&quot;</span><span style="color:var(--astro-code-token-punctuation)">, word_timestamps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Pre-converted models are available from the Hugging Face MLX Community. For faster transcription, <a href="https://github.com/mustafaaljadery/lightning-whisper-mlx">lightning-whisper-mlx</a> provides optimized implementations.</p>
<h3 id="stable-diffusion">Stable Diffusion</h3>
<p>The mlx-examples repository includes Stable Diffusion implementations:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Text to image</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> txt2image.py</span><span style="color:var(--astro-code-token-string-expression)"> &quot;A photo of an astronaut riding a horse on Mars&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># With quantization for 8GB Macs</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> txt2image.py</span><span style="color:var(--astro-code-token-string)"> --quantize</span><span style="color:var(--astro-code-token-string-expression)"> &quot;A sunset over mountains&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Image to image</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> image2image.py</span><span style="color:var(--astro-code-token-string)"> --image</span><span style="color:var(--astro-code-token-string)"> input.png</span><span style="color:var(--astro-code-token-string)"> --strength</span><span style="color:var(--astro-code-token-constant)"> 0.7</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Make it look like a painting&quot;</span></span></code></pre>
<p>Both SD 1.5 and SDXL are supported. Quantization (4-bit text encoder, 8-bit UNet) enables generation on 8GB Macs without swapping.</p>
<hr>
<h2 id="moving-from-cuda-to-mlx-a-migration-guide">Moving from CUDA to MLX: A Migration Guide</h2>
<p>If you are coming from PyTorch with CUDA, this section covers the key differences and provides practical guidance for porting code.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p><strong>Hardware</strong>: Any Mac with Apple Silicon (M1, M2, M3, M4, M5 series).</p>
<p><strong>Software</strong>:</p>
<ul>
<li>macOS 14.0 or later (macOS 26.2 for M5 Neural Accelerator support)</li>
<li>Python 3.10 or later</li>
<li>Native ARM Python (not Rosetta x86 emulation)</li>
</ul>
<p>Verify your Python installation:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string-expression)"> &quot;import platform; print(platform.processor())&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Should print &quot;arm&quot;, not &quot;i386&quot;</span></span></code></pre>
<h3 id="installation-1">Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Install MLX core</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> mlx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install MLX-LM for language models</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> mlx-lm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install additional packages as needed</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> mlx-whisper</span><span style="color:var(--astro-code-token-comment)">  # For speech recognition</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> mlx-vlm</span><span style="color:var(--astro-code-token-comment)">      # For vision-language models</span></span></code></pre>
<h3 id="key-api-differences">Key API Differences</h3>
<p><strong>No Device Management</strong>: In PyTorch, you explicitly move tensors between devices. In MLX, arrays live in unified memory and you specify the device when calling operations:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># PyTorch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">tensor</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">]).</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-comment)">  # Runs on CUDA</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-comment)">  # Runs on GPU by default</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">add</span><span style="color:var(--astro-code-token-punctuation)">(x, y, stream</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mx.cpu)</span><span style="color:var(--astro-code-token-comment)">  # Force CPU</span></span></code></pre>
<p><strong>Lazy Evaluation</strong>: Operations are not executed immediately:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># PyTorch - computed immediately</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX - creates computation graph</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-comment)">  # Not computed yet</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">(y)</span><span style="color:var(--astro-code-token-comment)">  # Now computed</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(y)</span><span style="color:var(--astro-code-token-comment)">   # Also triggers computation</span></span></code></pre>
<p><strong>Function-Based Gradients</strong>: No backward() method or gradient tape:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># PyTorch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">tensor</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">], requires_grad</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(x.grad)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> f</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">grad_f </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">grad</span><span style="color:var(--astro-code-token-punctuation)">(f)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">grad_f</span><span style="color:var(--astro-code-token-punctuation)">(x))</span></span></code></pre>
<h3 id="porting-neural-network-code">Porting Neural Network Code</h3>
<p>The <code>mlx.nn</code> module closely follows PyTorch’s API:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># PyTorch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> nn</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> PyTorchMLP</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Module</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> __init__</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> in_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> hidden_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> out_dim</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        super</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">__init__</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fc1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Linear</span><span style="color:var(--astro-code-token-punctuation)">(in_dim, hidden_dim)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fc2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Linear</span><span style="color:var(--astro-code-token-punctuation)">(hidden_dim, out_dim)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> forward</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">relu</span><span style="color:var(--astro-code-token-punctuation)">(self.</span><span style="color:var(--astro-code-token-function)">fc1</span><span style="color:var(--astro-code-token-punctuation)">(x))</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">fc2</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> nn</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> MLXMLP</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Module</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> __init__</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> in_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> hidden_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> out_dim</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        super</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">__init__</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fc1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Linear</span><span style="color:var(--astro-code-token-punctuation)">(in_dim, hidden_dim)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">fc2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Linear</span><span style="color:var(--astro-code-token-punctuation)">(hidden_dim, out_dim)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> __call__</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">maximum</span><span style="color:var(--astro-code-token-punctuation)">(self.</span><span style="color:var(--astro-code-token-function)">fc1</span><span style="color:var(--astro-code-token-punctuation)">(x), </span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # ReLU</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">fc2</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span></code></pre>
<p>Key differences:</p>
<ul>
<li>Use <code>__call__</code> instead of <code>forward</code></li>
<li>Use <code>mx.maximum(x, 0)</code> instead of <code>torch.relu(x)</code> (or <code>nn.relu(x)</code>)</li>
<li>No <code>.cuda()</code> calls needed</li>
</ul>
<h3 id="converting-model-weights">Converting Model Weights</h3>
<p>When porting a trained PyTorch model, you need to convert the weights:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load PyTorch weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pytorch_weights </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">, map_location</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cpu&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Convert to MLX format</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mlx_weights </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {}</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> key</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> value </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> pytorch_weights</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">items</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Convert to numpy, then to MLX array</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    np_array </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> value</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numpy</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Handle weight format differences (NCHW -&gt; NHWC for convolutions)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-string-expression)"> &quot;conv&quot;</span><span style="color:var(--astro-code-token-keyword)"> in</span><span style="color:var(--astro-code-foreground)"> key </span><span style="color:var(--astro-code-token-keyword)">and</span><span style="color:var(--astro-code-token-string-expression)"> &quot;weight&quot;</span><span style="color:var(--astro-code-token-keyword)"> in</span><span style="color:var(--astro-code-foreground)"> key</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        np_array </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transpose</span><span style="color:var(--astro-code-token-punctuation)">(np_array, (</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    mlx_weights</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">key</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">(np_array)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Save in MLX format</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">savez</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.npz&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">mlx_weights)</span></span></code></pre>
<p>For LLMs, the mlx_lm.convert script handles this automatically:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> mlx_lm.convert</span><span style="color:var(--astro-code-token-string)"> --hf-path</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3-8B</span></span></code></pre>
<h3 id="training-loop-pattern">Training Loop Pattern</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">core </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mx</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> nn</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> mlx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optimizers </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> optim</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> MLXMLP</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">784</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">optimizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> optim</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Adam</span><span style="color:var(--astro-code-token-punctuation)">(learning_rate</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1e-3</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> loss_fn</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    logits </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">mean</span><span style="color:var(--astro-code-token-punctuation)">(nn.losses.</span><span style="color:var(--astro-code-token-function)">cross_entropy</span><span style="color:var(--astro-code-token-punctuation)">(logits, y))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">loss_and_grad_fn </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">value_and_grad</span><span style="color:var(--astro-code-token-punctuation)">(model, loss_fn)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> epoch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(num_epochs):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> x_batch</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> y_batch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataloader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> grads </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> loss_and_grad_fn</span><span style="color:var(--astro-code-token-punctuation)">(model, x_batch, y_batch)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">update</span><span style="color:var(--astro-code-token-punctuation)">(model, grads)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">(model.</span><span style="color:var(--astro-code-token-function)">parameters</span><span style="color:var(--astro-code-token-punctuation)">(), optimizer.state)</span></span></code></pre>
<h3 id="common-patterns">Common Patterns</h3>
<p><strong>Model Evaluation Mode</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># PyTorch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-comment)">  # Same API</span></span></code></pre>
<p><strong>Saving and Loading</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Save model weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_weights</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_weights</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Mixed Precision</strong>: MLX handles dtypes explicitly:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Convert model to float16</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">apply</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">lambda</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-token-punctuation)">: x.</span><span style="color:var(--astro-code-token-function)">astype</span><span style="color:var(--astro-code-token-punctuation)">(mx.float16) </span><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-token-punctuation)"> x.dtype </span><span style="color:var(--astro-code-token-keyword)">==</span><span style="color:var(--astro-code-token-punctuation)"> mx.float32 </span><span style="color:var(--astro-code-token-keyword)">else</span><span style="color:var(--astro-code-token-punctuation)"> x)</span></span></code></pre>
<hr>
<h2 id="performance-optimization">Performance Optimization</h2>
<h3 id="use-compilation">Use Compilation</h3>
<p>Wrap hot paths in <code>mx.compile()</code>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">@mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> training_step</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> optimizer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    loss</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> grads </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> loss_and_grad_fn</span><span style="color:var(--astro-code-token-punctuation)">(model, x, y)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">update</span><span style="color:var(--astro-code-token-punctuation)">(model, grads)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> loss</span></span></code></pre>
<h3 id="batch-operations">Batch Operations</h3>
<p>MLX has lower overhead for small operations than CUDA, but batching still helps:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Less efficient</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> data</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># More efficient</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(mx.</span><span style="color:var(--astro-code-token-function)">stack</span><span style="color:var(--astro-code-token-punctuation)">(data))</span></span></code></pre>
<h3 id="quantization-for-inference">Quantization for Inference</h3>
<p>Use 4-bit or 8-bit models for inference when possible:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 4-bit model uses 75% less memory and runs faster</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/Llama-3-8B-Instruct-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="memory-management">Memory Management</h3>
<p>Check memory usage:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">info </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">metal</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_memory_info</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Allocated: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">info[</span><span style="color:var(--astro-code-token-string-expression)">&#39;allocated&#39;</span><span style="color:var(--astro-code-token-punctuation)">] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e9</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Peak: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">info[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak&#39;</span><span style="color:var(--astro-code-token-punctuation)">] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e9</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Clear cached memory:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">metal</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">clear_cache</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="profiling">Profiling</h3>
<p>Build MLX with Metal debugging for GPU profiling:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">mkdir</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-punctuation)"> &amp;&amp;</span><span style="color:var(--astro-code-token-function)"> cd</span><span style="color:var(--astro-code-token-string)"> build</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> ..</span><span style="color:var(--astro-code-token-string)"> -DMLX_METAL_DEBUG=ON</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">make</span><span style="color:var(--astro-code-token-string)"> -j</span></span></code></pre>
<p>Then use Xcode’s GPU profiler to analyze kernel execution.</p>
<hr>
<h2 id="debugging-tips">Debugging Tips</h2>
<h3 id="disable-compilation-for-debugging">Disable Compilation for Debugging</h3>
<p>Compiled functions cannot contain print statements. Disable compilation to debug:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">disable_compile</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># or</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># MLX_DISABLE_COMPILE=1 python script.py</span></span></code></pre>
<h3 id="force-evaluation">Force Evaluation</h3>
<p>When debugging lazy evaluation issues, force computation:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> complex_operation</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">(result)</span><span style="color:var(--astro-code-token-comment)">  # Force computation</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(result)</span><span style="color:var(--astro-code-token-comment)">    # Now safe to inspect</span></span></code></pre>
<h3 id="check-array-properties">Check Array Properties</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-constant)">1.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2.0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3.0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Shape: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">x.shape</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Dtype: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">x.dtype</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Size: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">x.size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="memory-issues">Memory Issues</h3>
<p>If you encounter memory errors during training:</p>
<ol>
<li>Reduce batch size</li>
<li>Enable gradient checkpointing</li>
<li>Use quantized models</li>
<li>Reduce sequence length</li>
<li>Reduce LoRA rank</li>
</ol>
<hr>
<h2 id="practical-example-end-to-end-llm-inference">Practical Example: End-to-End LLM Inference</h2>
<p>Here is a complete example for running local LLM inference:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> main</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Load a quantized model</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Loading model...&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/Qwen2.5-7B-Instruct-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # System prompt</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    messages </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        {</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;system&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;You are a helpful assistant.&quot;</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        {</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain how transformers work in 3 sentences.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Format with chat template</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">apply_chat_template</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        messages,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        tokenize</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        add_generation_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Generate</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Generating...&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">prompt,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">200</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        temp</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(response)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-token-constant)"> __name__</span><span style="color:var(--astro-code-token-keyword)"> ==</span><span style="color:var(--astro-code-token-string-expression)"> &quot;__main__&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    main</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<hr>
<h2 id="practical-example-fine-tuning-a-model">Practical Example: Fine-Tuning a Model</h2>
<p>Complete example for fine-tuning with LoRA:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> json</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> subprocess</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Step 1: Prepare training data</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">train_data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;### Instruction: Translate to French\n### Input: Hello\n### Response: Bonjour&quot;</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;### Instruction: Translate to French\n### Input: Goodbye\n### Response: Au revoir&quot;</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Add more examples...</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Save training data</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;train.jsonl&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;w&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> item </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> train_data</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        f</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">write</span><span style="color:var(--astro-code-token-punctuation)">(json.</span><span style="color:var(--astro-code-token-function)">dumps</span><span style="color:var(--astro-code-token-punctuation)">(item) </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-string-expression)"> &quot;\n&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Step 2: Run training</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">cmd </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;python&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;-m&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;mlx_lm.lora&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--model&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;mlx-community/Mistral-7B-Instruct-v0.3-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--train&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--data&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--adapter-path&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;./adapters&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--lora-r&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;8&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--iters&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;100&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--batch-size&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;1&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;--learning-rate&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;1e-4&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">subprocess</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">run</span><span style="color:var(--astro-code-token-punctuation)">(cmd)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Step 3: Use the fine-tuned model</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;mlx-community/Mistral-7B-Instruct-v0.3-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    adapter_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;./adapters&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;### Instruction: Translate to French\n### Input: Thank you\n### Response:&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(model, tokenizer, prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">prompt, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">50</span><span style="color:var(--astro-code-token-punctuation)">))</span></span></code></pre>
<hr>
<h2 id="mlx-vs-pytorch-performance-summary">MLX vs PyTorch Performance Summary</h2>
<p>Based on benchmarks from late 2025:</p>













































<div class="table-wrap"><table><thead><tr><th>Aspect</th><th>MLX</th><th>PyTorch + CUDA</th></tr></thead><tbody><tr><td>Setup complexity</td><td>Simple pip install</td><td>CUDA toolkit, drivers</td></tr><tr><td>Memory efficiency</td><td>Unified, no copies</td><td>Explicit transfers</td></tr><tr><td>Small batch latency</td><td>Lower overhead</td><td>Higher kernel launch cost</td></tr><tr><td>Large batch throughput</td><td>Competitive on M2 Ultra+</td><td>Faster on RTX 4090</td></tr><tr><td>LLM inference</td><td>Optimized for local use</td><td>More optimized on server GPUs</td></tr><tr><td>Training speed</td><td>Slower for large models</td><td>Faster for production training</td></tr><tr><td>Debugging</td><td>Straightforward</td><td>Mature tooling</td></tr></tbody></table></div>
<p>MLX is well suited for:</p>
<ul>
<li>Local LLM inference and experimentation</li>
<li>Development and prototyping on Mac</li>
<li>Memory-constrained scenarios (unified 128GB+)</li>
<li>Deploying models to Apple devices</li>
</ul>
<p>PyTorch + CUDA remains better for:</p>
<ul>
<li>Large-scale training runs</li>
<li>Maximum throughput requirements</li>
<li>Production server deployment</li>
</ul>
<hr>
<h2 id="references">References</h2>
<ul>
<li><a href="https://github.com/ml-explore/mlx">MLX GitHub Repository</a> - Official source code and documentation</li>
<li><a href="https://ml-explore.github.io/mlx/build/html/index.html">MLX Documentation</a> - Official docs including API reference</li>
<li><a href="https://github.com/ml-explore/mlx-lm">MLX-LM Repository</a> - LLM inference and fine-tuning package</li>
<li><a href="https://huggingface.co/mlx-community">mlx-community on Hugging Face</a> - Pre-converted models</li>
<li><a href="https://github.com/ml-explore/mlx-examples">MLX Examples Repository</a> - Stable Diffusion, Whisper, and more</li>
<li><a href="https://machinelearning.apple.com/research/exploring-llms-mlx-m5">Apple MLX Research Page</a> - M5 Neural Accelerator details</li>
<li><a href="https://developer.apple.com/videos/play/wwdc2025/315/">WWDC 2025: Get started with MLX</a> - Apple’s official introduction</li>
<li><a href="https://developer.apple.com/videos/play/wwdc2025/298/">WWDC 2025: Explore LLMs with MLX</a> - LLM-focused session</li>
<li><a href="https://github.com/Blaizzy/mlx-vlm">MLX-VLM Repository</a> - Vision-language model support</li>
<li><a href="https://pypi.org/project/mlx-whisper/">MLX Whisper on PyPI</a> - Speech recognition package</li>
<li><a href="https://towardsdatascience.com/how-fast-is-mlx-a-comprehensive-benchmark-on-8-apple-silicon-chips-and-4-cuda-gpus-378a0ae356a0/">MLX vs CUDA Benchmark</a> - Performance comparisons</li>
<li><a href="https://towardsdatascience.com/pytorch-and-mlx-for-apple-silicon-4f35b9f60e39/">PyTorch and MLX Comparison</a> - Migration guide</li>
<li><a href="https://ml-explore.github.io/mlx/build/html/usage/lazy_evaluation.html">Lazy Evaluation Documentation</a> - Understanding lazy computation</li>
<li><a href="https://ml-explore.github.io/mlx/build/html/usage/unified_memory.html">Unified Memory Documentation</a> - Memory model details</li>
<li><a href="https://ml-explore.github.io/mlx/build/html/usage/compile.html">Compilation Documentation</a> - JIT compilation guide</li>
<li><a href="https://github.com/ml-explore/mlx-examples/blob/main/lora/README.md">LoRA Fine-Tuning Example</a> - Official fine-tuning docs</li>
<li><a href="https://github.com/waybarrios/vllm-mlx">vllm-mlx Server</a> - High-performance MLX server</li>
<li><a href="https://blaizzy.github.io/fastmlx/">FastMLX Documentation</a> - Production-ready API server</li>
</ul>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Apple Silicon</category>
            <category>Edge &amp; on-device</category>
            <category>Training</category>
            <enclosure length="61752" type="image/png" url="https://blog.ecitis.org/open-graph/apple-mlx-guide.png"/>
        </item>
        <item>
            <title><![CDATA[How Coding Agents Work: Inside the Agentic Loop]]></title>
            <link>https://blog.ecitis.org/coding-agents-explained/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/coding-agents-explained/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Look inside the observe, reason, act, and verify loop that turns language models into useful software-engineering agents.]]></description>
            <content:encoded><![CDATA[<p>Coding agents are not code completion tools. The distinction matters. GitHub Copilot and similar autocomplete systems predict the next few tokens based on cursor context. Coding agents operate differently: they observe your codebase, reason about a task, execute actions through tools, and iterate until the task is complete. This pattern, called the agentic loop, is what separates a tool that suggests code from one that can implement features, fix bugs, and open pull requests autonomously.</p>
<p>As of January 2026, coding agents have crossed a meaningful threshold. Claude Opus 4.5 became the first model to exceed 80% on SWE-bench Verified <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-17" id="user-content-fnref-17">1</a></sup>. GPT-5.2 Codex established state-of-the-art on SWE-bench Pro at 56.4% <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-18" id="user-content-fnref-18">2</a></sup>. The market has consolidated around a handful of approaches: terminal-native agents (Claude Code, Codex CLI, Gemini CLI), IDE-integrated agents (Cursor, Windsurf, Cline), and cloud-based autonomous agents (Devin, Replit Agent 3, GitHub Copilot coding agent).</p>
<p>This post examines how modern coding agents work, from the core loop architecture to context management strategies, tool implementations, and sandboxing approaches. We will survey the major agents available today and explore their design tradeoffs.</p>
<hr>
<h2 id="the-agentic-loop">The Agentic Loop</h2>
<p>Every coding agent runs some variant of an observe-think-act loop. OpenAI’s documentation on their Codex agent describes this as “unrolling” the loop; the agent repeatedly gathers information, decides what to do, executes an action, and evaluates the result <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-1" id="user-content-fnref-1">3</a></sup>.</p>
<figure><figcaption><strong>The Core Agentic Loop</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/coding-agents-explained-0.webp" width="512" height="708" alt="The Core Agentic Loop" loading="lazy" decoding="async"></figure>
<p>The loop continues until the agent determines the task is complete or reaches a stopping condition (token limit, error threshold, or explicit user interrupt). This architecture differs from single-shot generation in a key way: the agent can course-correct. If it writes code that fails tests, it observes the failure, reasons about the fix, and tries again.</p>
<h3 id="observe">Observe</h3>
<p>The observation phase involves gathering context about the current state. For coding agents, this means reading files, searching for patterns in the codebase, checking test results, and inspecting error messages. Agents have access to tools that return structured information: file contents, search results, command output.</p>
<h3 id="think">Think</h3>
<p>The thinking phase is where the LLM reasons about what to do next. The model receives the accumulated context from observations and decides which tool to call next. This is not a separate system; it is the model’s native capability for planning and reasoning applied to a tool-use context.</p>
<h3 id="act">Act</h3>
<p>The action phase executes a tool call. The agent might edit a file, run a shell command, search for references, or query a language server. Each action produces output that becomes input for the next observation phase.</p>
<hr>
<h2 id="tool-use-in-coding-agents">Tool Use in Coding Agents</h2>
<p>Tools give agents their capabilities. Without tools, an agent can only generate text. With tools, it can modify files, execute code, search repositories, and interact with external systems.</p>
<h3 id="core-tool-categories">Core Tool Categories</h3>
<p><strong>File Operations</strong>: Read, write, and edit files. Most agents support both full file writes and targeted edits (replacing specific strings or line ranges). Claude Code implements an <code>Edit</code> tool that performs exact string replacement, reducing the risk of unintended changes <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-2" id="user-content-fnref-2">4</a></sup>.</p>
<p><strong>Shell Execution</strong>: Run arbitrary commands. This enables testing, building, linting, and any other command-line operation. Agents typically capture stdout, stderr, and exit codes.</p>
<p><strong>Search Tools</strong>: Find files by name patterns (glob) and content patterns (grep/ripgrep). Effective search is critical for navigating large codebases. Agents need to find relevant code without reading every file.</p>
<p><strong>LSP Integration</strong>: Language Server Protocol provides code intelligence; go-to-definition, find-references, hover documentation, and symbol search. OpenCode and Claude Code both integrate LSP for structured code navigation <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-3" id="user-content-fnref-3">5</a></sup>.</p>
<p><strong>MCP (Model Context Protocol)</strong>: Anthropic’s open standard for connecting AI assistants to external tools and data sources. Both Claude Code and Gemini CLI support MCP, enabling integration with systems like Jira, GitHub, and custom APIs <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-4" id="user-content-fnref-4">6</a></sup>.</p>
<h3 id="tool-descriptions-and-schemas">Tool Descriptions and Schemas</h3>
<p>Agents rely on well-structured tool descriptions to use tools correctly. Each tool has a name, description, and parameter schema. The quality of these descriptions directly affects agent performance. Vague descriptions lead to incorrect tool usage; overly complex schemas increase error rates.</p>
<p>Example tool schema structure:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;edit_file&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;description&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Replace exact string matches in a file&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;parameters&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;file_path&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Absolute path to the file&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;old_string&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Exact text to find and replace&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;new_string&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Replacement text&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<hr>
<h2 id="context-management">Context Management</h2>
<p>Large codebases exceed context window limits. A moderately sized project might contain millions of tokens across thousands of files. Agents need strategies to work within context constraints while maintaining coherent understanding of the task.</p>
<h3 id="repository-mapping">Repository Mapping</h3>
<p>Aider pioneered the repository map approach: generating a compact representation of the codebase structure, including file paths, function signatures, and class definitions <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-5" id="user-content-fnref-5">7</a></sup>. This map fits in context and provides the agent enough information to know which files to read for detailed work.</p>
<h3 id="compaction">Compaction</h3>
<p>Compaction summarizes conversation history when approaching context limits. Rather than failing when the context fills up, the agent condenses older interactions while preserving essential information.</p>
<p>OpenAI’s GPT-5.2 Codex is trained specifically for compaction with native compaction capabilities, making it token-efficient in its reasoning while handling long-running coding tasks <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-19" id="user-content-fnref-19">8</a></sup>. Anthropic’s Claude Code implements auto-compact at 95% context capacity, summarizing the trajectory of user-agent interactions <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-6" id="user-content-fnref-6">9</a></sup>.</p>
<p>Compaction strategies vary:</p>
<ul>
<li><strong>Recursive summarization</strong>: Repeatedly condense earlier turns</li>
<li><strong>Hierarchical summarization</strong>: Maintain summaries at different levels of detail</li>
<li><strong>Tool result compaction</strong>: Replace verbose tool outputs with compact references (file paths instead of full contents)</li>
</ul>
<h3 id="mcp-tool-search">MCP Tool Search</h3>
<p>Claude Code introduced “MCP Tool Search” in January 2026, implementing lazy loading for AI tools <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-20" id="user-content-fnref-20">10</a></sup>. Instead of preloading every tool definition, Claude Code monitors context usage and automatically fetches tool descriptions only when necessary. The token savings are significant: from approximately 134k to 5k in Anthropic’s internal testing. Internal benchmarks show that enabling Tool Search improved the accuracy of Opus 4 on MCP evaluations from 49% to 74%, and for Opus 4.5, accuracy jumped from 79.5% to 88.1%.</p>
<h3 id="sub-agent-isolation">Sub-Agent Isolation</h3>
<p>For complex tasks, agents can spawn sub-agents that operate in isolated context windows. The parent agent describes a task; the sub-agent explores extensively using its own context, then returns a condensed summary. This pattern appears in systems like Manus and OpenAI’s Codex <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-7" id="user-content-fnref-7">11</a></sup>.</p>
<p>The key insight: sub-agents might use tens of thousands of tokens internally but return only 1,000-2,000 tokens of distilled results. This achieves context separation while preserving information.</p>
<hr>
<h2 id="sandboxing-and-security">Sandboxing and Security</h2>
<p>Coding agents execute arbitrary code. They run shell commands, modify files, and interact with external services. This creates security concerns: what prevents an agent from running <code>rm -rf /</code> or exfiltrating sensitive data?</p>
<h3 id="container-based-isolation">Container-Based Isolation</h3>
<p>Docker containers provide filesystem isolation, process containment, and resource limits. The agent runs inside a container with access only to the project directory. Docker recently introduced Docker Sandboxes specifically for AI coding agents, with native support for Claude Code and Gemini CLI <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-8" id="user-content-fnref-8">12</a></sup>.</p>
<p>Container isolation means the agent can install packages, run code, and modify files within the sandbox without affecting the host system. This approach handles the full environment the agent needs, not just the agent process itself.</p>
<h3 id="os-level-sandboxing">OS-Level Sandboxing</h3>
<p>Claude Code on macOS uses the native sandbox facility to restrict agent actions. This provides lighter-weight isolation than containers but limits what the agent can access.</p>
<h3 id="limitations">Limitations</h3>
<p>Container isolation alone does not address all risks. A sandbox controls where code runs and which files an agent can modify. It does not control what the agent is authorized to do across networked systems <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-9" id="user-content-fnref-9">13</a></sup>. An agent might have legitimate access to GitHub but use that access in unintended ways.</p>
<p>Defense in depth remains necessary: container isolation, network segmentation, explicit permission prompts for sensitive actions, and audit logging.</p>
<hr>
<h2 id="survey-of-coding-agents">Survey of Coding Agents</h2>
<figure><figcaption><strong>Major Coding Agents Compared</strong></figcaption><table class="comparison-table"><thead><tr><th class="agent-header"></th><th class="agent-header ">Claude Code</th><th class="agent-header ">Codex CLI</th><th class="agent-header ">Gemini CLI</th><th class="agent-header highlighted">OpenCode</th><th class="agent-header ">Aider</th><th class="agent-header ">Continue</th></tr></thead><tbody><tr class="spec-row"><td class="spec-label">License</td><td class="spec-value ">Proprietary</td><td class="spec-value ">Open Source</td><td class="spec-value ">Apache 2.0</td><td class="spec-value highlighted">MIT</td><td class="spec-value ">Apache 2.0</td><td class="spec-value ">Apache 2.0</td></tr><tr class="spec-row"><td class="spec-label">Models</td><td class="spec-value ">Claude 4</td><td class="spec-value ">GPT-5.1-Codex</td><td class="spec-value ">Gemini 3 Flash/Pro</td><td class="spec-value highlighted">Any (OpenAI, Claude, Gemini, local)</td><td class="spec-value ">Any (GPT-4, Claude, local)</td><td class="spec-value ">Any (configurable)</td></tr><tr class="spec-row"><td class="spec-label">Core Tools</td><td class="spec-value ">Bash, Read, Write, Edit, Grep, Glob, LSP, MCP</td><td class="spec-value ">Shell, file ops, code exec</td><td class="spec-value ">Shell, file ops, Search, MCP</td><td class="spec-value highlighted">Shell, file ops, LSP</td><td class="spec-value ">Git, file ops, voice</td><td class="spec-value ">IDE integration, file ops</td></tr><tr class="spec-row"><td class="spec-label">Sandboxing</td><td class="spec-value ">macOS sandbox, Docker optional</td><td class="spec-value ">Docker containers</td><td class="spec-value ">Optional Docker</td><td class="spec-value highlighted">None (native)</td><td class="spec-value ">None (native)</td><td class="spec-value ">None (IDE process)</td></tr><tr class="spec-row"><td class="spec-label">Context Mgmt</td><td class="spec-value ">Auto-compact at 95%</td><td class="spec-value ">Compaction (multi-window)</td><td class="spec-value ">1M token window</td><td class="spec-value highlighted">Manual/configurable</td><td class="spec-value ">Repo map + smart chunking</td><td class="spec-value ">IDE-managed</td></tr></tbody></table></figure>
<h3 id="terminal-native-agents">Terminal-Native Agents</h3>
<h4 id="claude-code">Claude Code</h4>
<p>Anthropic’s CLI agent runs in your terminal and integrates with your bash environment. The tech stack is TypeScript, React, Ink, and Bun <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-2" id="user-content-fnref-2-2">4</a></sup>. Design philosophy: low-level and unopinionated, providing close to raw model access without forcing specific workflows.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Bash, Read, Write, Edit, Grep, Glob, LSP, MCP client/server</li>
<li>Context: Auto-compact at 95% capacity, MCP Tool Search for lazy loading</li>
<li>Sandbox: macOS sandbox, optional Docker</li>
<li>Model: Claude Opus 4.5 (80.9% SWE-bench Verified), Sonnet</li>
</ul>
<p>Claude Code v2.1.0 (January 2026) introduced Automatic Skill Hot-Reload, Skill Context Forking for isolated sub-agent contexts, and Hooks in Skill Frontmatter <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-20" id="user-content-fnref-20-2">10</a></sup>. The Cowork feature brings Claude Code’s agentic capabilities to the Claude desktop app, running locally in an isolated VM with access to local files and MCP integrations <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-21" id="user-content-fnref-21">14</a></sup>. Users report 50% to 75% reductions in both tool calling errors and build/lint errors with Claude Opus 4.5 <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-17" id="user-content-fnref-17-2">1</a></sup>.</p>
<h4 id="codex-cli">Codex CLI</h4>
<p>OpenAI’s agent runs tasks in isolated cloud sandbox environments, preloaded with your repository. Powered by GPT-5.2 Codex for standard tasks and GPT-5.1-Codex-Max for long-running operations <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-19" id="user-content-fnref-19-2">8</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Shell, file operations, code execution, agent skills</li>
<li>Context: Native compaction, collaboration tools for multi-agent coordination</li>
<li>Sandbox: Docker containers in cloud</li>
<li>Model: GPT-5.2 Codex (80% SWE-bench Verified, 56.4% SWE-bench Pro)</li>
</ul>
<p>Codex v0.85.0 (January 2026) introduced app-server v2 with collaboration tool calls emitted as item events, enabling real-time agent coordination rendering. The <code>spawn_agent</code> function now accepts an agent role preset for richer agent control <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-22" id="user-content-fnref-22">15</a></sup>. By exposing the CLI as an MCP server and orchestrating with the OpenAI Agents SDK, developers can create deterministic, auditable workflows that scale from a single agent to a complete software delivery pipeline.</p>
<h4 id="gemini-cli">Gemini CLI</h4>
<p>Google’s open-source agent (Apache 2.0) brings Gemini models to the terminal. Uses a ReAct (reason and act) loop with built-in tools and MCP support <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-10" id="user-content-fnref-10">16</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Shell, file ops, Google Search grounding, web fetch, MCP</li>
<li>Context: 1M token window with Gemini 3</li>
<li>Sandbox: Optional Docker</li>
<li>Model: Gemini 3 Flash (78% SWE-bench Verified) or Pro</li>
</ul>
<p>Gemini 3 Flash became available in Gemini CLI in December 2025, achieving 78% on SWE-bench Verified while being 3x faster than the 2.5 series at a fraction of the cost <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-23" id="user-content-fnref-23">17</a></sup>. Since launch, the community has contributed over 2,800 pull requests, submitted about 3,400 issues, and given more than 70,000 GitHub stars. The free tier offers 60 requests/minute and 1,000 requests/day with a personal Google account.</p>
<h3 id="ide-integrated-agents">IDE-Integrated Agents</h3>
<h4 id="cursor">Cursor</h4>
<p>Cursor is a fork of VS Code that integrates AI capabilities directly into the editing experience. Agent is the default mode, designed to handle complex coding tasks with minimal guidance <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-24" id="user-content-fnref-24">18</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: File ops, terminal, web browsing, codebase indexing</li>
<li>Context: Multi-file understanding with automatic context detection</li>
<li>Sandbox: None (local execution)</li>
<li>Models: Composer (proprietary), GPT-5 Codex, Claude</li>
</ul>
<p>Cursor 2.0 (late 2025) shipped Composer, their own ultra-fast coding model, and an agent-centric interface for running multiple agents in parallel <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-24" id="user-content-fnref-24-2">18</a></sup>. The January 2026 CLI release added Plan mode (<code>/plan</code> or <code>--mode=plan</code>) for approach design before coding, and cloud handoff for background execution. Users can prepend <code>&amp;</code> to any message to send it to a cloud agent, then resume on web or mobile at cursor.com/agents.</p>
<h4 id="windsurf">Windsurf</h4>
<p>Windsurf (by Codeium) is an agentic IDE built for enterprise teams and large codebases. Its Cascade agent plans and executes multi-step changes across repositories <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-25" id="user-content-fnref-25">19</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: File ops, terminal, repository-wide context retrieval</li>
<li>Context: Flow feature maintains persistent context across projects</li>
<li>Sandbox: None (local execution)</li>
<li>Models: Various (configurable)</li>
</ul>
<p>Windsurf’s “Context Awareness Engine” is faster at indexing than Cursor, making it suited for large-scale enterprise projects where the codebase exceeds what other tools can handle. Cascade reasons across entire repositories, determining which files matter for a given task and loading them automatically.</p>
<h4 id="cline">Cline</h4>
<p>Cline is an open-source AI coding agent that runs inside VS Code or the terminal. It plans, previews, and applies multi-file changes with approval checkpoints <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-26" id="user-content-fnref-26">20</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: File ops, terminal, web browsing, MCP orchestration</li>
<li>Context: Full repository access with diff transparency</li>
<li>Sandbox: None (local-first control)</li>
<li>Models: Model-agnostic (any provider)</li>
</ul>
<p>Cline demonstrates high autonomy with multi-step execution, self-correction, and independent task continuation. Its open-source nature and support for multiple AI models offer flexibility for teams that need local-first control over data and models.</p>
<h3 id="cloud-based-autonomous-agents">Cloud-Based Autonomous Agents</h3>
<h4 id="devin">Devin</h4>
<p>Devin (by Cognition Labs) is an autonomous AI software engineer that operates as a web app rather than an IDE extension. Users define intent, review a plan, and execution proceeds in the background <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-27" id="user-content-fnref-27">21</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Code writing, PR creation, bug reproduction, internal tool building</li>
<li>Context: Full codebase access with summarized intermediate steps</li>
<li>Sandbox: Cloud-based isolated environments</li>
<li>Model: Proprietary (trained with reinforcement learning)</li>
</ul>
<p>Devin can independently create PRs, respond to PR comments, review PRs, and handle Linear tickets when tagged. In 2025, Devin gained multi-agent operation capability, where one AI agent dispatches tasks to other AI agents. Devin Wiki and Devin Search provide machine-generated documentation and codebase querying. Pricing starts at $20 with pay-as-you-go ACUs ($2.25 per ~15 minutes of active work) or $500/month for teams <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-27" id="user-content-fnref-27-2">21</a></sup>.</p>
<h4 id="replit-agent-3">Replit Agent 3</h4>
<p>Replit Agent 3 (September 2025) is Replit’s most autonomous agent, positioned as Agent-first for all builders, not just developers <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-28" id="user-content-fnref-28">22</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Code writing, testing, deployment, agent creation</li>
<li>Context: Full project access with reflection loops</li>
<li>Sandbox: Cloud-based Replit environment</li>
<li>Model: Proprietary</li>
</ul>
<p>Agent 3 runs for up to 200 minutes autonomously, handling full tasks with a proprietary testing system that is up to 3x faster and 10x more cost-effective than Computer Use models. For the first time, Agent 3 can build other agents and automations, enabling workflow automation via natural language. In January 2026, Replit reached a $3 billion valuation, with companies like Duolingo and Zillow using the platform <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-28" id="user-content-fnref-28-2">22</a></sup>.</p>
<h4 id="github-copilot-coding-agent">GitHub Copilot Coding Agent</h4>
<p>GitHub Copilot coding agent works independently in the background to complete tasks like a human developer <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-29" id="user-content-fnref-29">23</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Code generation, file ops, Git integration</li>
<li>Context: Repository-aware with Copilot Spaces</li>
<li>Sandbox: Cloud-based execution</li>
<li>Models: GPT-5 mini, GPT-4.1 (included without premium requests)</li>
</ul>
<p>The January 2026 CLI update introduced specialized custom agents: Explore for fast codebase analysis and Task for running commands like tests and builds <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-29" id="user-content-fnref-29-2">23</a></sup>. Visual Studio 2026 shipped with GitHub cloud agent in public preview. Users can delegate UI cleanups, refactors, documentation updates, and multi-file edits while focusing on core development.</p>
<h4 id="amazon-q-developer">Amazon Q Developer</h4>
<p>Amazon Q Developer provides agentic capabilities for the AWS ecosystem <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-30" id="user-content-fnref-30">24</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Code generation, documentation, testing, code review, transformation</li>
<li>Context: IDE and AWS service integration</li>
<li>Sandbox: AWS environment</li>
<li>Model: Proprietary</li>
</ul>
<p>Amazon Q Developer agents can autonomously implement features, document, test, review, and refactor code, and perform software upgrades. The Transformation Agent handles legacy modernization: Amazon used Q’s agents to upgrade 1,000 applications from Java 8 to Java 17, completing work that would have taken months in just two days <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-30" id="user-content-fnref-30-2">24</a></sup>. Free tier includes 50 agentic chat interactions per month; Pro tier is $19/user/month.</p>
<h3 id="open-source-agents">Open-Source Agents</h3>
<h4 id="openhands">OpenHands</h4>
<p>OpenHands is an open platform for AI-powered coding agents with 65K+ GitHub stars <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-31" id="user-content-fnref-31">25</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Code editing, terminal, web browsing, file ops</li>
<li>Context: Full repository access</li>
<li>Sandbox: Docker or Kubernetes environments</li>
<li>Models: Any (configurable)</li>
</ul>
<p>OpenHands 1.0.0 (January 2026) uses the new software-agent-sdk with optimizations across the app. The platform integrates with GitHub, GitLab, CI/CD, Slack, and ticketing tools. In November 2025, OpenHands raised $18.8M to build the open standard for autonomous software development. AMD partnered with OpenHands for local execution on AI PCs via the Lemonade LLM serving framework <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-31" id="user-content-fnref-31-2">25</a></sup>.</p>
<h4 id="opencode">OpenCode</h4>
<p>Open-source (MIT license) alternative that supports any model provider: OpenAI, Anthropic, Google, AWS Bedrock, Groq, local models via Ollama <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-3" id="user-content-fnref-3-2">5</a></sup>. Built in Go with a Bubble Tea TUI.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Shell, file ops, LSP integration</li>
<li>Context: Manual/configurable</li>
<li>Sandbox: None (native execution)</li>
<li>Models: Any supported provider</li>
</ul>
<p>OpenCode separates workflows into “Plan Mode” (read-only analysis) and “Build Mode” (full tool access), acting as both architect and engineer <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-3" id="user-content-fnref-3-3">5</a></sup>.</p>
<h4 id="aider">Aider</h4>
<p>Open-source (Apache 2.0) pair programming tool focused on git integration. Creates a repository map of function signatures and file structures for intelligent multi-file edits <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-5" id="user-content-fnref-5-2">7</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: Git integration, file ops, voice input</li>
<li>Context: Repo map plus smart chunking</li>
<li>Sandbox: None (native execution)</li>
<li>Models: Any (GPT-5, Claude, local)</li>
</ul>
<p>Aider automatically commits changes with sensible messages. Three modes: code (edit), architect (plan), ask (consult without changes) <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-5" id="user-content-fnref-5-3">7</a></sup>.</p>
<h4 id="continue">Continue</h4>
<p>Open-source (Apache 2.0) IDE extension for VS Code and JetBrains. Architecture splits into core (business logic), extension (IDE-specific), and gui (React UI), communicating via message passing <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-11" id="user-content-fnref-11">26</a></sup>.</p>
<p>Key characteristics:</p>
<ul>
<li>Tools: IDE integration, file ops</li>
<li>Context: IDE-managed</li>
<li>Sandbox: None (IDE process)</li>
<li>Models: Any (configurable)</li>
</ul>
<p>Continue offers three interaction modes: Chat (discuss without changes), Plan (read-only exploration), Agent (full tool access) <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-11" id="user-content-fnref-11-2">26</a></sup>.</p>
<hr>
<h2 id="prompt-engineering-inside-agents">Prompt Engineering Inside Agents</h2>
<p>Agent system prompts are lengthy and detailed. They specify tool usage rules, safety constraints, output formatting, and behavioral guidelines. The prompt shapes how the agent interprets tasks and uses tools.</p>
<h3 id="system-prompt-components">System Prompt Components</h3>
<p><strong>Tool descriptions</strong>: Detailed explanations of each tool’s purpose, parameters, and usage patterns. Often includes examples of correct and incorrect usage.</p>
<p><strong>Safety rules</strong>: Constraints on dangerous operations. “NEVER run force push to main/master.” “Do not commit files that likely contain secrets.”</p>
<p><strong>Workflow guidance</strong>: Instructions for common tasks like committing code, creating PRs, or exploring unfamiliar codebases.</p>
<p><strong>Output formatting</strong>: How to structure responses, when to use code blocks, and how to communicate progress.</p>
<h3 id="configuration-files">Configuration Files</h3>
<p>Many agents support project-level configuration:</p>
<ul>
<li><code>agents.md</code> (Codex): Tells the agent how you prefer to code</li>
<li><code>CLAUDE.md</code> (Claude Code): Project-specific instructions and context</li>
<li><code>.continuerc</code> (Continue): Model and tool configuration</li>
</ul>
<p>These files let developers customize agent behavior per repository.</p>
<hr>
<h2 id="failure-modes-and-limitations">Failure Modes and Limitations</h2>
<p>Coding agents fail. Understanding how they fail helps set appropriate expectations and design better workflows.</p>
<h3 id="quality-concerns">Quality Concerns</h3>
<p>Research from CodeRabbit found that AI-generated code produces more issues across categories: 1.75x more logic errors, 1.64x more maintainability problems, 1.57x more security findings, and 1.42x more performance issues compared to human-written code <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-12" id="user-content-fnref-12">27</a></sup>.</p>
<h3 id="tool-calling-failures">Tool Calling Failures</h3>
<p>Tool calling fails 3-15% of the time in production systems <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-13" id="user-content-fnref-13">28</a></sup>. The agent might call the wrong tool, pass incorrect parameters, or misinterpret tool output. Robust agents include retry logic and error handling.</p>
<h3 id="multi-agent-system-failures">Multi-Agent System Failures</h3>
<p>Research analyzing 1,642 multi-agent execution traces found failure rates between 41% and 86.7% across state-of-the-art systems <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-14" id="user-content-fnref-14">29</a></sup>. Common issues include missing tool calls, workflow errors, and reasoning failures.</p>
<h3 id="context-degradation">Context Degradation</h3>
<p>As context fills up, model performance degrades. Quality often drops before hitting the technical limit. The recommendation: implement compaction before hitting the “rot zone,” typically well before maximum context capacity <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-6" id="user-content-fnref-6-2">9</a></sup>.</p>
<h3 id="debugging-challenges">Debugging Challenges</h3>
<p>“Ghost debugging” occurs when running the same prompt twice produces different results <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-13" id="user-content-fnref-13-2">28</a></sup>. Traditional debugging approaches fail because the system behavior is non-deterministic. Many fixes are “session patches” that work temporarily but do not persist across sessions.</p>
<hr>
<h2 id="building-your-own-agent">Building Your Own Agent</h2>
<p>Frameworks simplify agent construction. LangChain and LangGraph provide primitives for tool use, state management, and conversation handling.</p>
<h3 id="langgraph">LangGraph</h3>
<p>LangChain recommends LangGraph for production agent implementations. It offers a durable runtime, model/tool swapping without rewrites, and 1000+ integrations <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-15" id="user-content-fnref-15">30</a></sup>.</p>
<p>The <code>create_react_agent</code> function provides a standard ReAct pattern:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> langgraph</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">prebuilt </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> create_react_agent</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> langchain_openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ChatOpenAI</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ChatOpenAI</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;gpt-4o&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tools </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [search_tool</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> file_tool</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> shell_tool]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">agent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> create_react_agent</span><span style="color:var(--astro-code-token-punctuation)">(model, tools)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> agent</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">invoke</span><span style="color:var(--astro-code-token-punctuation)">({</span><span style="color:var(--astro-code-token-string-expression)">&quot;messages&quot;</span><span style="color:var(--astro-code-token-punctuation)">: [(</span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;Fix the failing tests&quot;</span><span style="color:var(--astro-code-token-punctuation)">)]})</span></span></code></pre>
<h3 id="open-swe">Open SWE</h3>
<p>LangChain’s Open SWE provides an open-source coding agent built on LangGraph with three specialized agents: Manager (entry point), Planner, and Programmer (with sub-agent Reviewer) <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-15" id="user-content-fnref-15-2">30</a></sup>. The entire project is open source and designed for extension.</p>
<h3 id="custom-implementation">Custom Implementation</h3>
<p>A minimal agent loop requires:</p>
<ol>
<li>Message accumulation (conversation history)</li>
<li>Tool definitions with schemas</li>
<li>Loop logic: call model, parse tool calls, execute tools, append results</li>
<li>Stopping conditions (task complete, error limit, token limit)</li>
</ol>
<p>The complexity comes from handling edge cases: malformed tool calls, execution timeouts, context overflow, and graceful degradation.</p>
<hr>
<h2 id="benchmarks-january-2026">Benchmarks (January 2026)</h2>
<p>The benchmark landscape has evolved with more rigorous evaluations. SWE-bench Verified remains the standard, but SWE-bench Pro and Terminal-Bench have emerged to address saturation at the top.</p>
<h3 id="swe-bench-verified">SWE-bench Verified</h3>





































<div class="table-wrap"><table><thead><tr><th>Model</th><th>Score</th></tr></thead><tbody><tr><td>Claude Opus 4.5</td><td>80.9%</td></tr><tr><td>GPT-5.2 Codex</td><td>80.0%</td></tr><tr><td>Gemini 3 Flash</td><td>78.0%</td></tr><tr><td>Gemini 3 Pro</td><td>76.2%</td></tr><tr><td>GPT-5.1</td><td>76.3%</td></tr><tr><td>Verdent</td><td>76.1% (81.2% pass@3)</td></tr><tr><td>GPT-5</td><td>74.9%</td></tr></tbody></table></div>
<p>Claude Opus 4.5 became the first model to exceed 80% on SWE-bench Verified, solving 405 of 500 real-world coding problems <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-17" id="user-content-fnref-17-3">1</a></sup>.</p>
<h3 id="swe-bench-pro">SWE-bench Pro</h3>
<p>SWE-bench Pro contains 1,865 total tasks across 41 professional repositories, designed to test harder software engineering scenarios <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-32" id="user-content-fnref-32">31</a></sup>.</p>

























<div class="table-wrap"><table><thead><tr><th>Model</th><th>Score</th></tr></thead><tbody><tr><td>GPT-5.2 Codex</td><td>56.4%</td></tr><tr><td>GPT-5.1</td><td>50.8%</td></tr><tr><td>Claude Opus 4.1</td><td>23.1%</td></tr><tr><td>OpenAI GPT-5</td><td>23.3%</td></tr></tbody></table></div>
<p>The gap between SWE-bench Verified (70%+ scores) and SWE-bench Pro (20-56%) reveals that current agents still struggle with professional-grade complexity.</p>
<h3 id="terminal-bench">Terminal-Bench</h3>





















<div class="table-wrap"><table><thead><tr><th>Model</th><th>Score</th></tr></thead><tbody><tr><td>Claude Opus 4.5</td><td>59.3%</td></tr><tr><td>Gemini 3 Pro</td><td>54.2%</td></tr><tr><td>GPT-5.1</td><td>47.6%</td></tr></tbody></table></div>
<h3 id="code-quality-sonar-llm-leaderboard">Code Quality (Sonar LLM Leaderboard)</h3>
<p>GPT-5.2 High achieved the best security posture with only 16 blocker vulnerabilities per million lines of code, though it generated the highest code volume (974,379 lines) <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-33" id="user-content-fnref-33">32</a></sup>. Claude Opus 4.5 and Gemini 3 Pro lead in functional performance at 80.66%.</p>
<h3 id="model-specialization">Model Specialization</h3>
<p>Top-tier models are diverging in specialization: GPT-5 excels at code review and refactoring, while Claude Sonnet 4.5 performs best in coding and tool use <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-34" id="user-content-fnref-34">33</a></sup>. Developers often switch between frontier models depending on the task.</p>
<hr>
<h2 id="the-future">The Future</h2>
<h3 id="multi-agent-systems">Multi-Agent Systems</h3>
<p>Gartner reported a 1,445% surge in multi-agent system inquiries from Q1 2024 to Q2 2025 <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-16" id="user-content-fnref-16">34</a></sup>. By 2026, 40% of enterprise applications will feature task-specific AI agents, up from less than 5% in 2025 <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-35" id="user-content-fnref-35">35</a></sup>. The shift from isolated agents to coordinated teams marks a fundamental change in how organizations approach automation.</p>
<p>The pattern: orchestrator agents coordinate specialist agents (researcher, coder, analyst), each fine-tuned for specific capabilities. Agent design is converging around a Planner and Builder (Execution Agent) loop, which spawns ephemeral Task Agents for sub-routines, all grounded in a Code Execution Sandbox <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-35" id="user-content-fnref-35-2">35</a></sup>.</p>
<h3 id="interoperability-protocols">Interoperability Protocols</h3>
<p>Protocols like Anthropic’s MCP and Google’s Agent-to-Agent Protocol (A2A) establish standards for agent interoperability <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-16" id="user-content-fnref-16-2">34</a></sup>. MCP standardizes how agents access tools and external resources. A2A enables peer-to-peer collaboration, allowing agents to negotiate, share findings, and coordinate without central oversight. Google’s Agent Development Kit (ADK), released in 2025, provides an open-source framework for building multi-agent systems <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-36" id="user-content-fnref-36">36</a></sup>.</p>
<h3 id="parallel-workflows">Parallel Workflows</h3>
<p>Running multiple agents on the same codebase requires isolation. Git worktrees enable parallel branches for each task, with work merged back to main <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-16" id="user-content-fnref-16-3">34</a></sup>. Tools like Conductor and Verdent AI support running tasks in parallel. Cursor 2.0 introduced an agent-centric interface for managing multiple agents in parallel with cloud handoff capabilities.</p>
<h3 id="longer-context-and-native-compaction">Longer Context and Native Compaction</h3>
<p>Gemini 3 offers a 1M token context window. GPT-5.2 Codex includes native compaction for token-efficient reasoning over long-running tasks <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-19" id="user-content-fnref-19-3">8</a></sup>. Claude Code’s MCP Tool Search reduces context usage by 96% (from 134k to 5k tokens) through lazy loading <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-20" id="user-content-fnref-20-3">10</a></sup>.</p>
<h3 id="challenges-ahead">Challenges Ahead</h3>
<p>Security remains unsolved. Connecting models to tools multiplies risks; indirect prompt injections can cause harmful actions <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-16" id="user-content-fnref-16-4">34</a></sup>. Research analyzing 1,642 multi-agent execution traces found failure rates between 41% and 86.7% across state-of-the-art systems <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-14" id="user-content-fnref-14-2">29</a></sup>.</p>
<p>AI agents are projected to generate $450 billion in economic value by 2028, yet only 2% of organizations have deployed them at full scale <sup><a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fn-35" id="user-content-fnref-35-3">35</a></sup>. The market has shipped useful point solutions but has not yet demonstrated reliable autonomy inside complex, decision-rich enterprise workflows.</p>
<p>The trajectory is clear: agents will write more code. The question is whether we can make them reliable enough to trust.</p>
<hr>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-17">
<p>Anthropic, “Introducing Claude Opus 4.5,” January 2026. <a href="https://www.anthropic.com/news/claude-opus-4-5" rel="noopener noreferrer">https://www.anthropic.com/news/claude-opus-4-5</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-17" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-17-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-17-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-18">
<p>OpenAI, “Introducing GPT-5.2-Codex,” December 2025. <a href="https://openai.com/index/introducing-gpt-5-2-codex/" rel="noopener noreferrer">https://openai.com/index/introducing-gpt-5-2-codex/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-1">
<p>OpenAI, “Unrolling the Codex agent loop,” OpenAI Blog, 2025. <a href="https://openai.com/index/unrolling-the-codex-agent-loop/" rel="noopener noreferrer">https://openai.com/index/unrolling-the-codex-agent-loop/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Gergely Orosz, “How Claude Code is built,” The Pragmatic Engineer, 2025. <a href="https://newsletter.pragmaticengineer.com/p/how-claude-code-is-built" rel="noopener noreferrer">https://newsletter.pragmaticengineer.com/p/how-claude-code-is-built</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-3">
<p>OpenCode, “The open source AI coding agent,” 2025. <a href="https://opencode.ai/" rel="noopener noreferrer">https://opencode.ai/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-3" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-3-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-3-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-4">
<p>Anthropic, “Claude Code overview,” Claude Code Docs, 2025. <a href="https://code.claude.com/docs/en/overview" rel="noopener noreferrer">https://code.claude.com/docs/en/overview</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p>Aider, “AI Pair Programming in Your Terminal,” 2025. <a href="https://aider.chat/" rel="noopener noreferrer">https://aider.chat/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-5" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-5-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-5-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-19">
<p>OpenAI, “GPT-5.2-Codex,” OpenAI, 2025. <a href="https://openai.com/index/introducing-gpt-5-2-codex/" rel="noopener noreferrer">https://openai.com/index/introducing-gpt-5-2-codex/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-19" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-19-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-19-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-6">
<p>Anthropic, “Effective context engineering for AI agents,” Anthropic Engineering, 2025. <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" rel="noopener noreferrer">https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-6" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-6-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-20">
<p>VentureBeat, “Claude Code just got updated with one of the most-requested user features,” January 2026. <a href="https://venturebeat.com/orchestration/claude-code-just-got-updated-with-one-of-the-most-requested-user-features" rel="noopener noreferrer">https://venturebeat.com/orchestration/claude-code-just-got-updated-with-one-of-the-most-requested-user-features</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-20" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-20-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-20-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-7">
<p>Lance Martin, “Context Engineering in Manus,” 2025. <a href="https://rlancemartin.github.io/2025/10/15/manus/" rel="noopener noreferrer">https://rlancemartin.github.io/2025/10/15/manus/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>Docker, “A New Approach for Coding Agent Safety,” Docker Blog, 2025. <a href="https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/" rel="noopener noreferrer">https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p>Arcade Blog, “Why Docker Sandboxes Alone Don’t Make AI Agents Safe,” 2025. <a href="https://blog.arcade.dev/docker-sandboxes-arent-enough-for-agent-safety" rel="noopener noreferrer">https://blog.arcade.dev/docker-sandboxes-arent-enough-for-agent-safety</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-21">
<p>TechRadar, “This is the Claude update I’ve been waiting for - Cowork could reshape how we use AI in 2026,” January 2026. <a href="https://www.techradar.com/ai-platforms-assistants/claudes-latest-upgrade-is-the-ai-breakthrough-ive-been-waiting-for-5-ways-cowork-could-be-the-biggest-ai-innovation-of-2026" rel="noopener noreferrer">https://www.techradar.com/ai-platforms-assistants/claudes-latest-upgrade-is-the-ai-breakthrough-ive-been-waiting-for-5-ways-cowork-could-be-the-biggest-ai-innovation-of-2026</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-21" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-22">
<p>OpenAI, “Codex Changelog,” January 2026. <a href="https://developers.openai.com/codex/changelog/" rel="noopener noreferrer">https://developers.openai.com/codex/changelog/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-22" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10">
<p>Google, “Gemini CLI,” Google for Developers, 2025. <a href="https://developers.google.com/gemini-code-assist/docs/gemini-cli" rel="noopener noreferrer">https://developers.google.com/gemini-code-assist/docs/gemini-cli</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-23">
<p>Google Developers Blog, “Gemini 3 Flash is now available in Gemini CLI,” December 2025. <a href="https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/" rel="noopener noreferrer">https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-23" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-24">
<p>Cursor, “Changelog,” January 2026. <a href="https://cursor.com/changelog" rel="noopener noreferrer">https://cursor.com/changelog</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-24" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-24-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-25">
<p>Qodo, “Cline vs Windsurf: Best AI Coding Agent for Enterprise,” 2026. <a href="https://www.qodo.ai/blog/cline-vs-windsurf/" rel="noopener noreferrer">https://www.qodo.ai/blog/cline-vs-windsurf/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-25" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26">
<p>Qodo, “Cline vs Windsurf,” 2026. <a href="https://www.qodo.ai/blog/cline-vs-windsurf/" rel="noopener noreferrer">https://www.qodo.ai/blog/cline-vs-windsurf/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-26" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-27">
<p>Builder.io, “Devin vs Cursor: How developers choose AI coding tools in 2026,” 2026. <a href="https://www.builder.io/blog/devin-vs-cursor" rel="noopener noreferrer">https://www.builder.io/blog/devin-vs-cursor</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-27" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-27-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-28">
<p>American Bazaar, “AI startup Replit, known for vibe coding, reaches $3 billion valuation,” January 2026. <a href="https://americanbazaaronline.com/2026/01/16/ai-startup-replit-known-for-vibe-coding-3-billion-valuation-473395/" rel="noopener noreferrer">https://americanbazaaronline.com/2026/01/16/ai-startup-replit-known-for-vibe-coding-3-billion-valuation-473395/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-28" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-28-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-29">
<p>GitHub Blog, “GitHub Copilot CLI: Enhanced agents, context management, and new ways to install,” January 2026. <a href="https://github.blog/changelog/2026-01-14-github-copilot-cli-enhanced-agents-context-management-and-new-ways-to-install/" rel="noopener noreferrer">https://github.blog/changelog/2026-01-14-github-copilot-cli-enhanced-agents-context-management-and-new-ways-to-install/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-29" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-29-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-30">
<p>AWS DevOps Blog, “Reinventing the Amazon Q Developer agent for software development,” 2025. <a href="https://aws.amazon.com/blogs/devops/reinventing-the-amazon-q-developer-agent-for-software-development/" rel="noopener noreferrer">https://aws.amazon.com/blogs/devops/reinventing-the-amazon-q-developer-agent-for-software-development/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-30" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-30-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-31">
<p>OpenHands, “The Open Platform for Cloud Coding Agents,” 2026. <a href="https://openhands.dev/" rel="noopener noreferrer">https://openhands.dev/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-31" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-31-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-11">
<p>Continue, “Continue Documentation,” 2025. <a href="https://docs.continue.dev/" rel="noopener noreferrer">https://docs.continue.dev/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-11" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-11-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-12">
<p>The Register, “AI-authored code needs more attention, contains worse bugs,” December 2025. <a href="https://www.theregister.com/2025/12/17/ai_code_bugs/" rel="noopener noreferrer">https://www.theregister.com/2025/12/17/ai_code_bugs/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p>Michael Hannecke, “Why AI Agents Fail in Production,” Medium, 2025. <a href="https://medium.com/@michael.hannecke/why-ai-agents-fail-in-production-what-ive-learned-the-hard-way-05f5df98cbe5" rel="noopener noreferrer">https://medium.com/@michael.hannecke/why-ai-agents-fail-in-production-what-ive-learned-the-hard-way-05f5df98cbe5</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-13" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-13-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-14">
<p>Mert Cemri et al., “Why Do Multi-Agent LLM Systems Fail?” arXiv, 2025. <a href="https://arxiv.org/pdf/2503.13657" rel="noopener noreferrer">https://arxiv.org/pdf/2503.13657</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-14" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-14-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-15">
<p>LangChain, “Introducing Open SWE: An Open-Source Asynchronous Coding Agent,” LangChain Blog, 2025. <a href="https://www.blog.langchain.com/introducing-open-swe-an-open-source-asynchronous-coding-agent/" rel="noopener noreferrer">https://www.blog.langchain.com/introducing-open-swe-an-open-source-asynchronous-coding-agent/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-15" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-15-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-32">
<p>Scale AI, “SWE-Bench Pro Public Dataset,” January 2026. <a href="https://scale.com/leaderboard/swe_bench_pro_public" rel="noopener noreferrer">https://scale.com/leaderboard/swe_bench_pro_public</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-32" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-33">
<p>Sonar, “New data on code quality: GPT-5.2 high, Opus 4.5, Gemini 3, and more,” January 2026. <a href="https://www.sonarsource.com/blog/new-data-on-code-quality-gpt-5-2-high-opus-4-5-gemini-3-and-more/" rel="noopener noreferrer">https://www.sonarsource.com/blog/new-data-on-code-quality-gpt-5-2-high-opus-4-5-gemini-3-and-more/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-33" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34">
<p>SWE-rebench, “Leaderboard,” January 2026. <a href="https://swe-rebench.com/" rel="noopener noreferrer">https://swe-rebench.com</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-34" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16">
<p>RTInsights, “2026 will be the Year of Multiple AI Agents,” January 2026. <a href="https://www.rtinsights.com/if-2025-was-the-year-of-ai-agents-2026-will-be-the-year-of-multi-agent-systems/" rel="noopener noreferrer">https://www.rtinsights.com/if-2025-was-the-year-of-ai-agents-2026-will-be-the-year-of-multi-agent-systems/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-16" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-16-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-16-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-16-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-35">
<p>The New Stack, “5 Key Trends Shaping Agentic Development in 2026,” January 2026. <a href="https://thenewstack.io/5-key-trends-shaping-agentic-development-in-2026/" rel="noopener noreferrer">https://thenewstack.io/5-key-trends-shaping-agentic-development-in-2026/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-35" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-35-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-35-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-36">
<p>Google Developers Blog, “Agent Development Kit: Making it easy to build multi-agent applications,” 2025. <a href="https://developers.googleblog.com/en/agent-development-kit-easy-to-build-multi-agent-applications/" rel="noopener noreferrer">https://developers.googleblog.com/en/agent-development-kit-easy-to-build-multi-agent-applications/</a> <a href="https://blog.ecitis.org/coding-agents-explained/#user-content-fnref-36" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Agents</category>
            <category>Evaluation</category>
            <category>Reasoning</category>
            <enclosure length="58694" type="image/png" url="https://blog.ecitis.org/open-graph/coding-agents-explained.png"/>
        </item>
        <item>
            <title><![CDATA[Edge AI Deployment: Running Models on Constrained Devices]]></title>
            <link>https://blog.ecitis.org/edge-ai-deployment/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/edge-ai-deployment/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Choose hardware, runtimes, and optimization strategies for private, low-latency inference on phones, embedded systems, and edge devices.]]></description>
            <content:encoded><![CDATA[<h2 id="why-deploy-ai-at-the-edge">Why deploy AI at the edge</h2>
<p>Running inference on edge devices instead of cloud servers addresses four constraints that matter in production systems.</p>
<p><strong>Latency</strong>: Cloud roundtrips add 100-500ms of network delay before inference even begins. Edge inference on embedded hardware achieves sub-50ms latency for many workloads. Cactus, a Y Combinator startup, demonstrated sub-50ms time-to-first-token for on-device LLM inference in late 2025.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> TinyML solutions on microcontrollers achieve inference latencies as low as 0-5ms.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p><strong>Privacy</strong>: Data never leaves the device. This simplifies compliance with GDPR, HIPAA, and similar regulations. Medical imaging, financial analysis, and surveillance applications often require on-premise processing.</p>
<p><strong>Connectivity</strong>: Edge deployment removes the assumption of reliable internet. Factory floors, remote agricultural sites, vehicles in motion, and aircraft cabins all benefit from inference that works without network access.</p>
<p><strong>Cost</strong>: After hardware acquisition, inference is free. No per-token API fees, no bandwidth costs, no cloud compute bills. IDC and Gartner predict that by 2027, over 60% of all AI inference will happen locally rather than in the cloud.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<hr>
<h2 id="hardware-landscape">Hardware landscape</h2>
<p>The edge AI hardware market reached $30.74 billion in 2026 and is projected to grow to $68.73 billion by 2031 at a 17.46% CAGR.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> Leading mobile processors now achieve 45-50 TOPS of inference capability while optimizing battery life through dedicated engines.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-4" id="user-content-fnref-4-2">4</a></sup></p>
<figure><figcaption><strong>Edge AI Hardware Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/edge-ai-deployment-0.webp" width="512" height="770" alt="Edge AI Hardware Comparison" loading="lazy" decoding="async"></figure>
<h3 id="nvidia-jetson-platform">NVIDIA Jetson platform</h3>
<p>NVIDIA’s Jetson line targets robotics, autonomous machines, and embedded vision applications. The platform runs a full Linux stack with CUDA support. At CES 2026, NVIDIA expanded the lineup with Blackwell-based hardware.</p>
<p><strong>Jetson AGX Thor</strong>: The new flagship module delivers up to 2070 FP4 TFLOPS (over 1000 INT8 TOPS) with 128GB of LPDDR5X memory.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-5" id="user-content-fnref-5">5</a></sup> Built on the Blackwell GPU architecture with 2,560 CUDA cores and 96 fifth-generation Tensor Cores, Thor provides 7.5x higher AI compute and 3.5x greater energy efficiency compared to Jetson Orin.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-6" id="user-content-fnref-6">6</a></sup> Power is configurable between 75W and 130W. The developer kit costs $3,499.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-7" id="user-content-fnref-7">7</a></sup> Early adopters include Boston Dynamics, Amazon Robotics, Figure, and Meta.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-6" id="user-content-fnref-6-2">6</a></sup></p>
<p><strong>Jetson T4000</strong>: The newest Thor family member launched at CES 2026, delivering 1200 TFLOPS of AI compute and 64GB of memory while running at 40-70W.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-8" id="user-content-fnref-8">8</a></sup> It features three 25GbE ports for high-bandwidth sensor fusion in edge and robotic applications.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-8" id="user-content-fnref-8-2">8</a></sup></p>
<p><strong>Jetson AGX Orin</strong>: The previous-generation flagship delivers up to 275 TOPS of AI performance with power configurable between 15W and 60W.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-9" id="user-content-fnref-9">9</a></sup> This handles multiple concurrent vision pipelines or real-time video analytics.</p>
<p><strong>Jetson Orin NX</strong>: Mid-range option at 157 TOPS with 10-40W power envelope. Fits the smallest Jetson form factor while maintaining substantial compute capability.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-9" id="user-content-fnref-9-2">9</a></sup></p>
<p><strong>Jetson Orin Nano Super</strong>: Entry-level edge AI at $249. A software update in late 2024 boosted performance from 40 to 67 TOPS and memory bandwidth from 68 to 102 GB/s. The 8GB module uses an Ampere architecture GPU with 1024 CUDA cores and 32 Tensor Cores running at up to 25W.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-10" id="user-content-fnref-10">10</a></sup></p>
<p>All Jetson modules share a common software stack through JetPack SDK. JetPack 7 supports Jetson Thor with Linux 24.04 LTS, Kernel 6.8, and the latest compute stack.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-5" id="user-content-fnref-5-2">5</a></sup></p>
<h3 id="raspberry-pi-ai-acceleration">Raspberry Pi AI acceleration</h3>
<p>The Raspberry Pi Foundation partnered with Hailo to bring neural network acceleration to the Pi 5. In January 2026, they expanded the lineup with generative AI support.</p>
<p><strong>AI HAT+ 2</strong>: Released January 15, 2026, this $130 module features the Hailo-10H accelerator with 40 TOPS (INT8) performance and 8GB of dedicated LPDDR4X RAM.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-11" id="user-content-fnref-11">11</a></sup> Unlike previous AI HATs focused on computer vision, the AI HAT+ 2 targets generative AI workloads including LLMs and VLMs.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-12" id="user-content-fnref-12">12</a></sup> Supported models at launch include DeepSeek-R10-Distill, Llama 3.2, Qwen2.5-Coder, and Qwen2.5-Instruct (1.5B parameter versions).<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-11" id="user-content-fnref-11-2">11</a></sup> The chip runs at a maximum of 3W.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-13" id="user-content-fnref-13">13</a></sup></p>
<p><strong>AI HAT+ 26T</strong>: Uses the full Hailo-8 chip for 26 TOPS. Both variants integrate with rpicam-apps for direct camera pipeline acceleration.</p>
<p><strong>AI HAT+ 13T</strong>: Contains a Hailo-8L chip delivering 13 TOPS at 3-4 TOPS/W efficiency. The module connects via PCIe 2.0 through an M.2 interface. Cost is approximately $70.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-14" id="user-content-fnref-14">14</a></sup></p>
<p>Raspberry Pi OS automatically detects Hailo modules and makes the NPU available for inference. For vision-based workloads, the AI HAT+ or AI Kit remain cost-effective options. The AI HAT+ 2 adds generative AI capabilities but reviewers note practical LLM performance remains limited.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-15" id="user-content-fnref-15">15</a></sup></p>
<h3 id="google-coral-edge-tpu">Google Coral Edge TPU</h3>
<p>Google’s Coral line provides a USB-connected TPU accelerator for existing systems.</p>
<p>The Edge TPU delivers 4 TOPS while consuming 2W; that is 2 TOPS per watt.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-16" id="user-content-fnref-16">16</a></sup> The USB Accelerator runs MobileNet v2 at nearly 400 FPS for image classification tasks. The limitation is model compatibility: only TensorFlow Lite models compiled specifically for the Edge TPU will accelerate.</p>
<p>The TPU uses a 64x64 systolic array (estimated, as Google has not published exact specifications) running at approximately 480 MHz. On-chip SRAM is limited to 8MB, so large models must stream weights from the host.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-17" id="user-content-fnref-17">17</a></sup></p>
<h3 id="mobile-neural-processing-units">Mobile Neural Processing Units</h3>
<p>Modern smartphones include dedicated neural engines that rival discrete edge accelerators. Leading mobile processors now achieve 45-50 TOPS of inference capability.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-4" id="user-content-fnref-4-3">4</a></sup></p>
<p><strong>Apple Neural Engine</strong>: The M5 chip (October 2025) includes a 16-core Neural Engine with Neural Accelerators in each GPU core, delivering up to 3.5x the AI performance of M4.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-18" id="user-content-fnref-18">18</a></sup> The M4 chip delivers 38 TOPS, more than double the M3’s 18 TOPS.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-19" id="user-content-fnref-19">19</a></sup> The A19 Pro (iPhone 17 Pro, September 2025) features a 16-core Neural Engine with 38 TOPS and improved memory bandwidth, with Neural Accelerators built into each GPU core.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-20" id="user-content-fnref-20">20</a></sup> Core ML automatically dispatches workloads to ANE, GPU, or CPU based on model requirements. Apple reports that ANE-optimized models run up to 10x faster with 14x less memory than non-optimized versions.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-21" id="user-content-fnref-21">21</a></sup></p>
<p><strong>Qualcomm Hexagon NPU</strong>: Snapdragon 8 Elite (Gen 5) NPUs deliver time-to-first-token in just 0.12 seconds on high-resolution images (1024x1024), with up to 100x speedup over CPU and 10x over GPU.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-22" id="user-content-fnref-22">22</a></sup> The Snapdragon X Elite delivers 45 TOPS of INT8 performance.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-23" id="user-content-fnref-23">23</a></sup> Research shows consistent 50% improvement in prefill speed and up to 110% improvement in decode speed with each successive generation of Snapdragon SoCs.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-24" id="user-content-fnref-24">24</a></sup> The NPU is now a standard component, with over 80% of recent Qualcomm SoCs including one.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-22" id="user-content-fnref-22-2">22</a></sup></p>
<p>At CES 2026, Qualcomm debuted the Dragonwing 1Q10, an 18-core CPU robotics platform designed to compete with NVIDIA’s Jetson ecosystem.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-25" id="user-content-fnref-25">25</a></sup></p>
<h3 id="nordic-semiconductor-iot-edge-ai">Nordic Semiconductor (IoT Edge AI)</h3>
<p>Nordic Semiconductor announced the nRF54LM20B SoC at CES 2026, integrating the Axon Neural Processing Unit for ultra-low-power edge AI.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-26" id="user-content-fnref-26">26</a></sup> Axon delivers up to 7x faster performance and 8x higher energy efficiency versus competing solutions for tasks like sound classification, keyword spotting, and image-based detection.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-26" id="user-content-fnref-26-2">26</a></sup> Broad availability expected Q2 2026.</p>
<h3 id="ambarella-cv7">Ambarella CV7</h3>
<p>Ambarella announced the CV7 edge AI vision SoC at CES 2026, optimized for AI perception applications with 4nm process technology.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-27" id="user-content-fnref-27">27</a></sup> The low power consumption reduces thermal management requirements for smaller form factors and longer battery life across AIoT applications.</p>
<hr>
<h2 id="model-optimization-for-edge">Model optimization for edge</h2>
<p>Edge devices cannot run full-precision models designed for datacenter GPUs. Three techniques reduce model size and compute requirements.</p>
<figure><figcaption><strong>Edge Deployment Software Stacks</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/edge-ai-deployment-1.webp" width="512" height="681" alt="Edge Deployment Software Stacks" loading="lazy" decoding="async"></figure>
<h3 id="quantization">Quantization</h3>
<p>Quantization reduces the numerical precision of model weights and activations. Standard training uses FP32; quantization converts to FP16, INT8, or even INT4.</p>
<p><strong>Post-training quantization (PTQ)</strong> applies after training completes. TensorFlow Lite’s full integer quantization produces INT8 models compatible with accelerators like Google Coral and microcontrollers. The process requires a representative dataset to calibrate quantization ranges.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-28" id="user-content-fnref-28">28</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> tensorflow </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> tf</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">converter </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">lite</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">TFLiteConverter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_saved_model</span><span style="color:var(--astro-code-token-punctuation)">(saved_model_dir)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">converter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optimizations </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">lite</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">Optimize</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">DEFAULT]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">converter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">representative_dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> representative_dataset</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">converter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">target_spec</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">supported_ops </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">lite</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">OpsSet</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">TFLITE_BUILTINS_INT8]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">converter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">inference_input_type </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">int8</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">converter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">inference_output_type </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">int8</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tflite_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> converter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>INT8 quantization produces models 4x smaller than FP32 with inference speedups of 2-4x on supporting hardware.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-28" id="user-content-fnref-28-2">28</a></sup></p>
<p><strong>Quantization-aware training (QAT)</strong> integrates quantization into the training loop. The model learns to compensate for quantization errors. PyTorch demonstrated that QAT recovers up to 96% of accuracy degradation on HellaSwag and 68% of perplexity degradation on WikiText compared to PTQ.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-29" id="user-content-fnref-29">29</a></sup></p>
<p>For LLMs on edge, GGUF quantization through llama.cpp offers multiple precision levels from 1.5-bit to 8-bit:<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-30" id="user-content-fnref-30">30</a></sup></p>









































<div class="table-wrap"><table><thead><tr><th>Quantization</th><th>Size (7B model)</th><th>Perplexity increase</th><th>Notes</th></tr></thead><tbody><tr><td>Q8_0</td><td>~7.0 GB</td><td>+0.0004</td><td>Highest quality</td></tr><tr><td>Q5_K_M</td><td>~5.33 GB</td><td>+0.0142</td><td>Optimal balance</td></tr><tr><td>Q4_K_M</td><td>~4.58 GB</td><td>+0.0535</td><td>Recommended default</td></tr><tr><td>Q3_K_M</td><td>~3.52 GB</td><td>+0.2437</td><td>Aggressive compression</td></tr><tr><td>IQ2_XS</td><td>~2.31 bpw</td><td>Higher</td><td>Extreme compression</td></tr></tbody></table></div>
<p>Q5_K_M is generally regarded as the optimal balance, applying five bits to most weights while retaining higher precision for crucial layers like attention.wv and feed_forward.w2.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-31" id="user-content-fnref-31">31</a></sup> For extreme compression, the IQ (importance-quantized) formats support down to 1.5 bits per weight.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-30" id="user-content-fnref-30-2">30</a></sup></p>
<p><strong>New quantization formats (2026)</strong>: NVIDIA announced NVFP4 and FP8 quantization support for llama.cpp and Ollama at CES 2026, enabling up to 35% faster token generation on RTX hardware.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-32" id="user-content-fnref-32">32</a></sup></p>
<h3 id="pruning">Pruning</h3>
<p>Pruning removes weights that contribute minimally to model output. Structured pruning removes entire channels or neurons; the resulting model runs faster on standard hardware because tensor dimensions shrink.</p>
<p>Research in 2025 achieved up to 400x reduction in neural network size for reinforcement learning tasks by pruning 99% of weights.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-33" id="user-content-fnref-33">33</a></sup> For transformer models, a compression approach evaluated on language modeling tasks achieved around 70% overall model compression while maintaining accuracy.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-34" id="user-content-fnref-34">34</a></sup></p>
<p>Unstructured pruning sets weights to zero without changing tensor dimensions. This produces sparse matrices that require specialized hardware or software to accelerate.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">utils</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">prune </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> prune</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Structured pruning: remove 50% of channels by L1 norm</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prune</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">ln_structured</span><span style="color:var(--astro-code-token-punctuation)">(model.conv1, name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;weight&#39;</span><span style="color:var(--astro-code-token-punctuation)">, amount</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.5</span><span style="color:var(--astro-code-token-punctuation)">, n</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, dim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="knowledge-distillation">Knowledge distillation</h3>
<p>Distillation transfers knowledge from a large teacher model to a smaller student model. The student learns to match the teacher’s output distributions rather than just the hard labels.</p>
<p>A hybrid approach combining knowledge distillation, pruning, and quantization produced models 3x smaller than vanilla CNNs while achieving 97% accuracy.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-35" id="user-content-fnref-35">35</a></sup> The CQKD framework demonstrated 34,000x compression while preserving accuracy through combined cluster-based quantization and knowledge distillation.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-36" id="user-content-fnref-36">36</a></sup></p>
<p>The shift from large language models (LLMs) to small, task-specific language models (SLMs) emerged as a key trend in 2026, enabling efficient, localized AI deployments with reduced power and compute needs.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-27" id="user-content-fnref-27-2">27</a></sup></p>
<hr>
<h2 id="deployment-frameworks">Deployment frameworks</h2>
<h3 id="tensorrt-for-jetson">TensorRT for Jetson</h3>
<p>TensorRT is NVIDIA’s inference optimizer for Jetson and datacenter GPUs. It converts trained models into optimized engines through graph compilation, layer fusion, and kernel autotuning.</p>
<p>The optimizer identifies operation sequences that can be fused into single GPU kernels. A matmul followed by ReLU becomes one kernel without intermediate memory writes. TensorRT automatically selects optimal GPU kernels based on architecture, precision, and batch size.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-37" id="user-content-fnref-37">37</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Convert ONNX model to TensorRT engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">trtexec</span><span style="color:var(--astro-code-token-string)"> --onnx=model.onnx</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">        --saveEngine=model.trt</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">        --fp16</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">        --workspace=4096</span></span></code></pre>
<p>Benchmark results on Jetson Nano showed TensorRT optimization achieving 95-110 FPS where non-optimized inference ran at 5-25 FPS.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-38" id="user-content-fnref-38">38</a></sup> On average, optimized models exhibit 16% speed improvement over non-optimized counterparts on Jetson hardware.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-39" id="user-content-fnref-39">39</a></sup></p>
<h3 id="tensorflow-lite">TensorFlow Lite</h3>
<p>TensorFlow Lite runs on Android, iOS, embedded Linux, and microcontrollers. It supports GPU delegation on mobile devices and accelerator integration through the delegate API.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> tensorflow </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> tf</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load and run TFLite model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">interpreter </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tf</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">lite</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Interpreter</span><span style="color:var(--astro-code-token-punctuation)">(model_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.tflite&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">interpreter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">allocate_tensors</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">input_details </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> interpreter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_input_details</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output_details </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> interpreter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_output_details</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">interpreter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_tensor</span><span style="color:var(--astro-code-token-punctuation)">(input_details[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">][</span><span style="color:var(--astro-code-token-string-expression)">&#39;index&#39;</span><span style="color:var(--astro-code-token-punctuation)">], input_data)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">interpreter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">invoke</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> interpreter</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_tensor</span><span style="color:var(--astro-code-token-punctuation)">(output_details[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">][</span><span style="color:var(--astro-code-token-string-expression)">&#39;index&#39;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<p>The Edge TPU delegate routes compatible operations to Google Coral hardware. GPU delegates accelerate on mobile GPUs. The LiteRT QNN accelerator supports 90 LiteRT ops, allowing 64 of 72 models to delegate fully to the Qualcomm NPU.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-22" id="user-content-fnref-22-3">22</a></sup></p>
<h3 id="core-ml">Core ML</h3>
<p>Core ML deploys models on Apple devices across iOS, macOS, watchOS, and tvOS. The framework automatically dispatches to CPU, GPU, or Neural Engine based on model characteristics.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> coremltools </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ct</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Convert PyTorch model to Core ML</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ct</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">convert</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    inputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[ct.</span><span style="color:var(--astro-code-token-function)">TensorType</span><span style="color:var(--astro-code-token-punctuation)">(shape</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">224</span><span style="color:var(--astro-code-token-punctuation)">))],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    compute_precision</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.precision.FLOAT16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    compute_units</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ct.ComputeUnit.ALL</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.mlpackage&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>macOS Sequoia introduced low-bit quantization methods including 4-bit block-wise linear quantization and channel group-wise palettization. These reduce memory footprint for on-device LLM inference.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-40" id="user-content-fnref-40">40</a></sup></p>
<p>ExecuTorch, the PyTorch edge runtime, includes a Core ML backend that dispatches to ANE, GPU, or CPU with FP16 precision.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-41" id="user-content-fnref-41">41</a></sup></p>
<h3 id="onnx-runtime">ONNX Runtime</h3>
<p>ONNX Runtime provides cross-platform inference with execution providers for different hardware. The CPU provider uses optimized kernels; GPU providers support CUDA, DirectML, and Metal.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> onnxruntime </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> ort</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run with CUDA execution provider</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">session </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ort</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">InferenceSession</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;model.onnx&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    providers</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;CUDAExecutionProvider&#39;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&#39;CPUExecutionProvider&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> session</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">run</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">None</span><span style="color:var(--astro-code-token-punctuation)">, {</span><span style="color:var(--astro-code-token-string-expression)">&quot;input&quot;</span><span style="color:var(--astro-code-token-punctuation)">: input_data})</span></span></code></pre>
<hr>
<h2 id="running-llms-on-edge-devices">Running LLMs on edge devices</h2>
<h3 id="llamacpp-on-raspberry-pi">llama.cpp on Raspberry Pi</h3>
<p>llama.cpp runs quantized LLMs on CPUs and GPUs without Python dependencies. Written in pure C/C++ with no external dependencies, it supports ARM devices, Raspberry Pi, and other edge hardware.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-42" id="user-content-fnref-42">42</a></sup></p>
<p><strong>Benchmark results on Pi 5 (8GB)</strong>:</p>
<ul>
<li>1B models (Q4): 7+ tokens/second<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-43" id="user-content-fnref-43">43</a></sup></li>
<li>3B models (Q4): 4-7 tokens/second<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-43" id="user-content-fnref-43-2">43</a></sup></li>
<li>7B models (Q4_K_M): 0.7-3 tokens/second<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-44" id="user-content-fnref-44">44</a></sup></li>
</ul>
<p>Using BLIS or OpenBLAS for matrix operations improves throughput. One user achieved 5.02 tokens/second on Pi 5 16GB with BLIS optimization.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-45" id="user-content-fnref-45">45</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build llama.cpp on Raspberry Pi</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> -B</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> -DGGML_BLAS=ON</span><span style="color:var(--astro-code-token-string)"> -DGGML_BLAS_VENDOR=OpenBLAS</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> --build</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> Release</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run inference</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-cli</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -m</span><span style="color:var(--astro-code-token-string)"> llama-3.2-1b-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -p</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain edge computing:&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -n</span><span style="color:var(--astro-code-token-constant)"> 256</span></span></code></pre>
<p>The BitNet B1.58 2B model achieves over 8 tokens/second with minimal RAM usage, making it well-suited for Pi deployment.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-44" id="user-content-fnref-44-2">44</a></sup></p>
<p><strong>2026 updates</strong>: NVIDIA announced optimizations for llama.cpp at CES 2026 including NVFP4/FP8 quantization, GPU token sampling, and memory management improvements, enabling up to 35% faster token generation.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-32" id="user-content-fnref-32-2">32</a></sup> The ik_llama.cpp fork achieved 3x-4x speed improvements for multi-GPU configurations through tensor parallelism at the GGML graph level.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-46" id="user-content-fnref-46">46</a></sup></p>
<h3 id="mlc-llm-on-mobile">MLC LLM on mobile</h3>
<p>MLC LLM compiles and optimizes LLMs for mobile GPUs using the TVM compiler stack. Unlike llama.cpp which targets CPUs, MLC LLM uses OpenCL, Vulkan, or Metal for GPU acceleration.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-47" id="user-content-fnref-47">47</a></sup></p>
<p>The framework generates custom operators and runtime code for specific model architectures and target hardware. For Android, MLC LLM uses the OpenCL backend; for iOS, it targets Metal.</p>
<p><strong>Android performance</strong>: On Snapdragon 8 Gen 2, models like Llama 3-4B run at 8-10 tokens/second. Mid-range devices with 6GB RAM struggle with models larger than 2B parameters.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-48" id="user-content-fnref-48">48</a></sup></p>
<p><strong>MLC Chat</strong> supports models including Llama 3.2, Gemma 2, Phi 3.5, and Qwen 2.5, offering offline chat, translation, and multimodal tasks. NPU optimization works on Snapdragon 8 Gen 2 and newer.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-48" id="user-content-fnref-48-2">48</a></sup></p>
<p>ExecuTorch combined with Unsloth’s quantization-aware training deploys Qwen3-0.6B on Pixel 8 and iPhone 15 Pro at approximately 40 tokens/second.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-49" id="user-content-fnref-49">49</a></sup></p>
<hr>
<h2 id="browser-inference">Browser inference</h2>
<p>WebGPU enables GPU-accelerated ML inference directly in web browsers without plugins or server calls.</p>
<h3 id="webgpu-support">WebGPU support</h3>
<p>As of January 2026, WebGPU has reached 70% browser support across Firefox 147, Safari (iOS 26), and Chrome/Edge, with 65% of new apps already adopting it.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-50" id="user-content-fnref-50">50</a></sup> This marks the first year GPU compute works across all major browsers.</p>
<h3 id="performance-characteristics">Performance characteristics</h3>
<p>WebGPU delivers 10x faster performance than WebGL for transformer models. Microsoft reports approximately 20x speedup over multi-threaded CPU and approximately 550x over single-threaded CPU for certain workloads.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-51" id="user-content-fnref-51">51</a></sup></p>
<p>Browser AI inference via WebLLM now reaches 80% of native MLC-LLM performance.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-50" id="user-content-fnref-50-2">50</a></sup> Benchmarks show:</p>
<ul>
<li>Llama-3.1-8B: 41.1 tokens/second (71.2% native speed)</li>
<li>Phi-3.5-mini: 71.1 tokens/second (79.6% native speed)</li>
<li>Llama 3.2 models: up to 62 tokens/second in optimal conditions</li>
<li>4-bit quantized 3B model: 90 tokens/second on Apple M3<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-52" id="user-content-fnref-52">52</a></sup></li>
</ul>
<p>On a laptop with NVIDIA RTX 3060, ONNX Runtime Web with WebGPU accelerates Segment Anything’s encoder by 19x and decoder by 3.8x.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-51" id="user-content-fnref-51-2">51</a></sup></p>
<p>Model loading remains a UX challenge: DeepSeek-8B takes 2-3 minutes to download and initialize. Once loaded, inference is often faster than API round-trips.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-53" id="user-content-fnref-53">53</a></sup></p>
<h3 id="transformersjs">Transformers.js</h3>
<p>Transformers.js brings Hugging Face pipelines to the browser. It uses ONNX Runtime Web for execution with WebGPU or WASM backends.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> { pipeline } </span><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-token-string-expression)"> &#39;@xenova/transformers&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Load model with WebGPU backend</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> classifier</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-foreground)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">  &#39;text-classification&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">  &#39;Xenova/distilbert-base-uncased-finetuned-sst-2-english&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { device</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;webgpu&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> dtype</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;fp16&#39;</span><span style="color:var(--astro-code-foreground)"> }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> result</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-token-function)"> classifier</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;This movie was great!&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span></code></pre>
<p>For quantization, Transformers.js supports fp32 (default for WebGPU), fp16, q8 (default for WASM), and q4 precisions.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-54" id="user-content-fnref-54">54</a></sup></p>
<h3 id="webllm">WebLLM</h3>
<p>WebLLM is a high-performance in-browser LLM inference engine fully compatible with the OpenAI API, supporting streaming, JSON-mode, and function-calling.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-55" id="user-content-fnref-55">55</a></sup> It supports Llama 3, Phi-3, Gemma, and Mistral with WebGPU acceleration.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-token-constant)"> *</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> webllm </span><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-token-string-expression)"> &#39;@mlc-ai/web-llm&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> engine</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-token-constant)"> webllm</span><span style="color:var(--astro-code-token-function)">.CreateMLCEngine</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;Llama-3.2-1B-Instruct-q4f16_1-MLC&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> response</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-token-constant)"> engine</span><span style="color:var(--astro-code-token-function)">.</span><span style="color:var(--astro-code-token-constant)">chat</span><span style="color:var(--astro-code-token-function)">.</span><span style="color:var(--astro-code-token-constant)">completions</span><span style="color:var(--astro-code-token-function)">.create</span><span style="color:var(--astro-code-foreground)">({</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  messages</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> [{ role</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;user&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> content</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;What is edge computing?&#39;</span><span style="color:var(--astro-code-foreground)"> }]</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  stream</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">})</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> chunk</span><span style="color:var(--astro-code-token-keyword)"> of</span><span style="color:var(--astro-code-foreground)"> response) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">  console</span><span style="color:var(--astro-code-token-function)">.log</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-constant)">chunk</span><span style="color:var(--astro-code-foreground)">.choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]?.</span><span style="color:var(--astro-code-token-constant)">delta</span><span style="color:var(--astro-code-foreground)">?.content </span><span style="color:var(--astro-code-token-keyword)">||</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<hr>
<h2 id="power-and-thermal-considerations">Power and thermal considerations</h2>
<p>Edge devices operate under power and thermal constraints that datacenter hardware ignores.</p>
<h3 id="power-budgets">Power budgets</h3>
<p>Jetson Thor can run at 75-130W for maximum performance, while Jetson Orin Nano Super runs at up to 25W.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-5" id="user-content-fnref-5-3">5</a></sup><sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-10" id="user-content-fnref-10-2">10</a></sup> The new Jetson T4000 operates at 40-70W.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-8" id="user-content-fnref-8-3">8</a></sup></p>
<p>The Hailo-10H in Raspberry Pi AI HAT+ 2 consumes a maximum of 3W while delivering 40 TOPS.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-13" id="user-content-fnref-13-2">13</a></sup> The Hailo-8L in AI HAT+ consumes 2.5-3W while delivering 13-26 TOPS.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-14" id="user-content-fnref-14-2">14</a></sup> Google Coral’s Edge TPU uses 2W for 4 TOPS.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-16" id="user-content-fnref-16-2">16</a></sup></p>
<p>For battery-powered deployments, TOPS per watt determines runtime. Hailo-10H achieves over 13 TOPS/W; Hailo-8L achieves 3-4 TOPS/W; Coral achieves 2 TOPS/W; Jetson Thor achieves approximately 8-13 TOPS/W depending on power mode.</p>
<h3 id="thermal-throttling">Thermal throttling</h3>
<p>When processors overheat, they reduce clock speeds to prevent damage. This thermal throttling drops inference throughput mid-operation.</p>
<p>AI chips, particularly high-performance SoCs and NPUs, reduce clock speeds automatically when temperatures exceed thresholds. For latency-sensitive applications like real-time video analysis, throttling creates visible quality degradation.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-56" id="user-content-fnref-56">56</a></sup></p>
<p>Mitigation strategies:</p>
<ul>
<li>Active cooling (fans) for continuous high-load operation</li>
<li>Passive heatsinks for intermittent workloads</li>
<li>Power mode selection matching thermal capacity</li>
<li>Ambient temperature monitoring</li>
</ul>
<p>Jetson’s tegrastats utility reports CPU and GPU temperatures to help prevent throttling. Research on Jetson Nano demonstrated that proactive thermal management saves 9-12% average power compared to reactive built-in methods.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-57" id="user-content-fnref-57">57</a></sup></p>
<h3 id="quantization-for-efficiency">Quantization for efficiency</h3>
<p>Lower precision reduces both compute and memory bandwidth, which reduces power consumption.</p>
<p>Quantization from FP32 to INT8 shrinks memory footprint by 75%. Integer arithmetic requires less energy than floating point on most embedded processors. The combination produces cooler and longer-running edge deployments.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-58" id="user-content-fnref-58">58</a></sup></p>
<hr>
<h2 id="benchmark-object-detection-on-jetson">Benchmark: object detection on Jetson</h2>
<p>YOLOv8 object detection benchmarks illustrate real-world edge performance.</p>
<h3 id="jetson-orin-nx-performance">Jetson Orin NX performance</h3>



































<div class="table-wrap"><table><thead><tr><th>Model</th><th>Precision</th><th>FPS</th><th>Energy/Inference</th></tr></thead><tbody><tr><td>YOLOv8n</td><td>FP16</td><td>52</td><td>0.269 J</td></tr><tr><td>YOLOv8n</td><td>INT8</td><td>65</td><td>0.179 J</td></tr><tr><td>YOLOv8s</td><td>FP16</td><td>38</td><td>0.368 J</td></tr><tr><td>YOLOv8s</td><td>INT8</td><td>48</td><td>0.292 J</td></tr></tbody></table></div>
<p>INT8 quantization delivers 25% higher FPS with 33% lower energy per inference.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-59" id="user-content-fnref-59">59</a></sup></p>
<h3 id="jetson-orin-nano-performance">Jetson Orin Nano performance</h3>
<p>On the 4GB Orin Nano with TensorRT optimization:</p>





























<div class="table-wrap"><table><thead><tr><th>Model</th><th>Precision</th><th>Latency</th><th>FPS</th></tr></thead><tbody><tr><td>YOLOv8n</td><td>INT8</td><td>23.16 ms</td><td>~43</td></tr><tr><td>YOLOv8n</td><td>FP16</td><td>26.70 ms</td><td>~37</td></tr><tr><td>YOLOv8s</td><td>INT8</td><td>28.25 ms</td><td>~35</td></tr></tbody></table></div>
<p>C++ implementation with TensorRT outperforms Python frameworks by reducing inference overhead.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-60" id="user-content-fnref-60">60</a></sup></p>
<h3 id="jetson-thor-performance">Jetson Thor performance</h3>
<p>Jetson Thor delivers 3.5-4.9x performance gains over Orin in INT8 precision for 4K object detection in live video streams.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-61" id="user-content-fnref-61">61</a></sup> The platform supports decoding up to 10x 4Kp60 or 4x 8Kp30 video streams simultaneously.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-5" id="user-content-fnref-5-4">5</a></sup></p>
<h3 id="optimization-workflow">Optimization workflow</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Export YOLOv8 to TensorRT engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">yolo</span><span style="color:var(--astro-code-token-string)"> export</span><span style="color:var(--astro-code-token-string)"> model=yolov8n.pt</span><span style="color:var(--astro-code-token-string)"> format=engine</span><span style="color:var(--astro-code-token-string)"> device=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-string)"> half=True</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run inference</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">yolo</span><span style="color:var(--astro-code-token-string)"> predict</span><span style="color:var(--astro-code-token-string)"> model=yolov8n.engine</span><span style="color:var(--astro-code-token-string)"> source=video.mp4</span></span></code></pre>
<hr>
<h2 id="benchmark-llm-inference-across-devices">Benchmark: LLM inference across devices</h2>
<p>Text generation speed varies by hardware, model size, and quantization.</p>
<h3 id="tokens-per-second-by-device-2026">Tokens per second by device (2026)</h3>



























































<div class="table-wrap"><table><thead><tr><th>Device</th><th>Model</th><th>Quantization</th><th>Tokens/s</th></tr></thead><tbody><tr><td>Jetson Thor</td><td>Llama 3.1 8B</td><td>FP8</td><td>80-120</td></tr><tr><td>Jetson Orin Nano</td><td>Llama 3.2 3B</td><td>Q4_K_M</td><td>15-20</td></tr><tr><td>Raspberry Pi 5 8GB</td><td>Llama 3.2 1B</td><td>Q4_K_M</td><td>7+</td></tr><tr><td>Raspberry Pi 5 8GB</td><td>Llama 2 7B</td><td>Q4_K_M</td><td>0.7-3</td></tr><tr><td>iPhone 17 Pro (A19)</td><td>Qwen3 0.6B</td><td>QAT</td><td>~40</td></tr><tr><td>Snapdragon 8 Gen 2</td><td>Llama 3 4B</td><td>Q4</td><td>8-10</td></tr><tr><td>Browser (M3 laptop)</td><td>3B model</td><td>Q4</td><td>90</td></tr><tr><td>Browser (RTX 3060)</td><td>Phi-3 Mini</td><td>Q4</td><td>20-40</td></tr></tbody></table></div>
<p>Source: Compiled from benchmark reports.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-43" id="user-content-fnref-43-3">43</a></sup><sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-44" id="user-content-fnref-44-3">44</a></sup><sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-48" id="user-content-fnref-48-3">48</a></sup><sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-49" id="user-content-fnref-49-2">49</a></sup><sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-52" id="user-content-fnref-52-2">52</a></sup></p>
<h3 id="memory-requirements">Memory requirements</h3>






























<div class="table-wrap"><table><thead><tr><th>Model Size</th><th>Q4_K_M Size</th><th>Minimum RAM</th></tr></thead><tbody><tr><td>1B</td><td>~0.6 GB</td><td>2 GB</td></tr><tr><td>3B</td><td>~1.8 GB</td><td>4 GB</td></tr><tr><td>7B</td><td>~4.6 GB</td><td>8 GB</td></tr><tr><td>13B</td><td>~7.8 GB</td><td>16 GB</td></tr></tbody></table></div>
<p>Devices with less RAM than model size will page to disk, reducing throughput to near-unusable levels.</p>
<hr>
<h2 id="production-deployment-considerations">Production deployment considerations</h2>
<h3 id="ota-model-updates">OTA model updates</h3>
<p>Edge devices need remote update capability for model improvements and bug fixes.</p>
<p>OTA (over-the-air) update platforms deploy firmware and model files to fleets of devices without physical access. The architecture includes:</p>
<ol>
<li>Model artifacts stored in cloud repositories</li>
<li>Device agents checking for updates</li>
<li>Secure download with cryptographic signing</li>
<li>Atomic installation with rollback capability</li>
</ol>
<p>Golioth’s OTA system supports multi-part deployments including main firmware, cellular modem firmware, and ML models as separate artifacts.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-62" id="user-content-fnref-62">62</a></sup> Mender provides image-based updates that create identical environments across devices.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-63" id="user-content-fnref-63">63</a></sup></p>
<p><strong>Security requirements</strong>:</p>
<ul>
<li>Cryptographic signing of all update packages</li>
<li>Secure boot verification before installation</li>
<li>Automatic rollback on failed updates</li>
<li>TLS transport for all communications</li>
</ul>
<h3 id="monitoring-and-telemetry">Monitoring and telemetry</h3>
<p>Remote monitoring tracks inference latency, throughput, error rates, and hardware health.</p>
<p>Key metrics to collect:</p>
<ul>
<li>Inference latency (p50, p95, p99)</li>
<li>Throughput (inferences/second)</li>
<li>Model accuracy on validation samples</li>
<li>Hardware temperature and power draw</li>
<li>Memory utilization</li>
</ul>
<p>Advantech DeviceOn provides OTA updates combined with container management for edge AI deployments.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-64" id="user-content-fnref-64">64</a></sup> ThingsBoard supports firmware distribution with version tracking since version 3.3.<sup><a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fn-65" id="user-content-fnref-65">65</a></sup></p>
<h3 id="fallback-strategies">Fallback strategies</h3>
<p>Edge deployments need graceful degradation when primary inference fails.</p>
<p><strong>Model fallback</strong>: Switch to smaller, faster models when latency budgets are missed. A vision system might drop from YOLOv8m to YOLOv8n under thermal throttling.</p>
<p><strong>Cloud fallback</strong>: Route requests to cloud inference when device capacity is exceeded or models require updates not yet deployed.</p>
<p><strong>Cached responses</strong>: Return cached predictions for repeated inputs. This works for classification tasks with limited input variety.</p>
<p><strong>Graceful denial</strong>: When no inference is possible, return explicit “unknown” responses rather than failing silently.</p>
<hr>
<h2 id="complete-example-browser-chatbot">Complete example: browser chatbot</h2>
<p>A functional browser-based chatbot using WebLLM and WebGPU.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">&lt;!</span><span style="color:var(--astro-code-token-string-expression)">DOCTYPE</span><span style="color:var(--astro-code-token-function)"> html</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">&lt;</span><span style="color:var(--astro-code-token-string-expression)">html</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  &lt;</span><span style="color:var(--astro-code-token-string-expression)">head</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;</span><span style="color:var(--astro-code-token-string-expression)">title</span><span style="color:var(--astro-code-foreground)">&gt;Edge LLM Chat&lt;/</span><span style="color:var(--astro-code-token-string-expression)">title</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;</span><span style="color:var(--astro-code-token-string-expression)">script</span><span style="color:var(--astro-code-token-function)"> type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;module&quot;</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      import</span><span style="color:var(--astro-code-token-constant)"> *</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> webllm </span><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-token-string-expression)"> &#39;https://cdn.jsdelivr.net/npm/@mlc-ai/web-llm@latest&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      let</span><span style="color:var(--astro-code-foreground)"> engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      const</span><span style="color:var(--astro-code-token-constant)"> statusEl</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> document</span><span style="color:var(--astro-code-token-function)">.getElementById</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;status&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      const</span><span style="color:var(--astro-code-token-constant)"> chatEl</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> document</span><span style="color:var(--astro-code-token-function)">.getElementById</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;chat&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      const</span><span style="color:var(--astro-code-token-constant)"> inputEl</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> document</span><span style="color:var(--astro-code-token-function)">.getElementById</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;input&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      async</span><span style="color:var(--astro-code-token-keyword)"> function</span><span style="color:var(--astro-code-token-function)"> init</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        statusEl</span><span style="color:var(--astro-code-foreground)">.textContent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &#39;Loading model...&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        engine </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-token-constant)"> webllm</span><span style="color:var(--astro-code-token-function)">.CreateMLCEngine</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;Llama-3.2-1B-Instruct-q4f16_1-MLC&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">          initProgressCallback</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> (progress) </span><span style="color:var(--astro-code-token-keyword)">=&gt;</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">            statusEl</span><span style="color:var(--astro-code-foreground)">.textContent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> `Loading: </span><span style="color:var(--astro-code-token-keyword)">${</span><span style="color:var(--astro-code-token-constant)">Math</span><span style="color:var(--astro-code-token-function)">.round</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-constant)">progress</span><span style="color:var(--astro-code-foreground)">.progress </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 100</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-keyword)">}</span><span style="color:var(--astro-code-token-string-expression)">%`</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">          }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        })</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        statusEl</span><span style="color:var(--astro-code-foreground)">.textContent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &#39;Ready&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      async</span><span style="color:var(--astro-code-token-keyword)"> function</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        const</span><span style="color:var(--astro-code-token-constant)"> userMessage</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> inputEl</span><span style="color:var(--astro-code-token-function)">.</span><span style="color:var(--astro-code-token-constant)">value</span><span style="color:var(--astro-code-token-function)">.trim</span><span style="color:var(--astro-code-foreground)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">!</span><span style="color:var(--astro-code-foreground)">userMessage </span><span style="color:var(--astro-code-token-keyword)">||</span><span style="color:var(--astro-code-token-keyword)"> !</span><span style="color:var(--astro-code-foreground)">engine) </span><span style="color:var(--astro-code-token-keyword)">return</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        chatEl</span><span style="color:var(--astro-code-foreground)">.innerHTML </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-token-string-expression)"> `&lt;div class=&quot;user&quot;&gt;User: </span><span style="color:var(--astro-code-token-keyword)">${</span><span style="color:var(--astro-code-foreground)">userMessage</span><span style="color:var(--astro-code-token-keyword)">}</span><span style="color:var(--astro-code-token-string-expression)">&lt;/div&gt;`</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        inputEl</span><span style="color:var(--astro-code-foreground)">.value </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        statusEl</span><span style="color:var(--astro-code-foreground)">.textContent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &#39;Generating...&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        const</span><span style="color:var(--astro-code-token-constant)"> response</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-token-constant)"> engine</span><span style="color:var(--astro-code-token-function)">.</span><span style="color:var(--astro-code-token-constant)">chat</span><span style="color:var(--astro-code-token-function)">.</span><span style="color:var(--astro-code-token-constant)">completions</span><span style="color:var(--astro-code-token-function)">.create</span><span style="color:var(--astro-code-foreground)">({</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">          messages</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> [{ role</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> &#39;user&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> content</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> userMessage }]</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">          stream</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        })</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        let</span><span style="color:var(--astro-code-foreground)"> assistantMessage </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        chatEl</span><span style="color:var(--astro-code-foreground)">.innerHTML </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-token-string-expression)"> `&lt;div class=&quot;assistant&quot;&gt;Assistant: &lt;span id=&quot;response&quot;&gt;&lt;/span&gt;&lt;/div&gt;`</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        const</span><span style="color:var(--astro-code-token-constant)"> responseEl</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> document</span><span style="color:var(--astro-code-token-function)">.getElementById</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;response&#39;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-token-keyword)"> await</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-constant)"> chunk</span><span style="color:var(--astro-code-token-keyword)"> of</span><span style="color:var(--astro-code-foreground)"> response) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">          const</span><span style="color:var(--astro-code-token-constant)"> content</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> chunk</span><span style="color:var(--astro-code-foreground)">.choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]?.</span><span style="color:var(--astro-code-token-constant)">delta</span><span style="color:var(--astro-code-foreground)">?.content </span><span style="color:var(--astro-code-token-keyword)">||</span><span style="color:var(--astro-code-token-string-expression)"> &#39;&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">          assistantMessage </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> content</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">          responseEl</span><span style="color:var(--astro-code-foreground)">.textContent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> assistantMessage</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        statusEl</span><span style="color:var(--astro-code-foreground)">.textContent </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &#39;Ready&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">      }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">      document</span><span style="color:var(--astro-code-token-function)">.getElementById</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;send&#39;</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-function)">.addEventListener</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;click&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate)</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">      inputEl</span><span style="color:var(--astro-code-token-function)">.addEventListener</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;keypress&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> (e) </span><span style="color:var(--astro-code-token-keyword)">=&gt;</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">e</span><span style="color:var(--astro-code-foreground)">.key </span><span style="color:var(--astro-code-token-keyword)">===</span><span style="color:var(--astro-code-token-string-expression)"> &#39;Enter&#39;</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-foreground)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">      })</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">      init</span><span style="color:var(--astro-code-foreground)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;/</span><span style="color:var(--astro-code-token-string-expression)">script</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  &lt;/</span><span style="color:var(--astro-code-token-string-expression)">head</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  &lt;</span><span style="color:var(--astro-code-token-string-expression)">body</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;</span><span style="color:var(--astro-code-token-string-expression)">div</span><span style="color:var(--astro-code-token-function)"> id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;status&quot;</span><span style="color:var(--astro-code-foreground)">&gt;Initializing...&lt;/</span><span style="color:var(--astro-code-token-string-expression)">div</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;</span><span style="color:var(--astro-code-token-string-expression)">div</span><span style="color:var(--astro-code-token-function)"> id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;chat&quot;</span><span style="color:var(--astro-code-foreground)">&gt;&lt;/</span><span style="color:var(--astro-code-token-string-expression)">div</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;</span><span style="color:var(--astro-code-token-string-expression)">input</span><span style="color:var(--astro-code-token-function)"> id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;input&quot;</span><span style="color:var(--astro-code-token-function)"> type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-function)"> placeholder</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Type a message...&quot;</span><span style="color:var(--astro-code-foreground)"> /&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    &lt;</span><span style="color:var(--astro-code-token-string-expression)">button</span><span style="color:var(--astro-code-token-function)"> id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;send&quot;</span><span style="color:var(--astro-code-foreground)">&gt;Send&lt;/</span><span style="color:var(--astro-code-token-string-expression)">button</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  &lt;/</span><span style="color:var(--astro-code-token-string-expression)">body</span><span style="color:var(--astro-code-foreground)">&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">&lt;/</span><span style="color:var(--astro-code-token-string-expression)">html</span><span style="color:var(--astro-code-foreground)">&gt;</span></span></code></pre>
<p>This loads a quantized Llama 3.2 1B model entirely in the browser. No server required after initial page load. WebLLM provides OpenAI-compatible API with streaming support.</p>
<hr>
<h2 id="framework-selection-guide">Framework selection guide</h2>























































<div class="table-wrap"><table><thead><tr><th>Use Case</th><th>Recommended Framework</th><th>Hardware</th></tr></thead><tbody><tr><td>Robotics, autonomous systems</td><td>TensorRT + JetPack 7</td><td>Jetson Thor/Orin</td></tr><tr><td>iOS/macOS apps</td><td>Core ML</td><td>Apple M5/A19</td></tr><tr><td>Android apps</td><td>TensorFlow Lite, MLC LLM</td><td>Snapdragon 8 Elite NPU</td></tr><tr><td>Raspberry Pi vision</td><td>HailoRT, TensorFlow Lite</td><td>Pi 5 + AI HAT+</td></tr><tr><td>Raspberry Pi LLM</td><td>llama.cpp, HailoRT</td><td>Pi 5 + AI HAT+ 2</td></tr><tr><td>Browser deployment</td><td>WebLLM, Transformers.js</td><td>WebGPU</td></tr><tr><td>Cross-platform LLM</td><td>llama.cpp</td><td>Any CPU/GPU</td></tr><tr><td>Microcontrollers</td><td>TensorFlow Lite Micro</td><td>Cortex-M</td></tr><tr><td>Ultra-low power IoT</td><td>Nordic Edge AI Lab</td><td>nRF54L + Axon NPU</td></tr></tbody></table></div>
<hr>
<h2 id="summary">Summary</h2>
<p>Edge AI deployment trades cloud flexibility for latency, privacy, and offline capability. The hardware landscape in 2026 offers options from $70 USB accelerators to $3,499 development kits capable of running multi-AI workflows.</p>
<p>The Jetson Thor platform launched in January 2026 delivers over 1000 TOPS with 128GB memory, enabling on-device generative AI for robotics applications. The Raspberry Pi AI HAT+ 2 brings LLM inference to the $130 price point, though practical performance remains limited to small models.</p>
<p>Mobile NPUs have matured significantly. Apple’s M5 chip delivers 3.5x the AI performance of M4, while Qualcomm’s Snapdragon 8 Elite achieves 100x speedups over CPU for vision-language models. WebGPU has reached 70% browser coverage, with WebLLM achieving 80% of native inference performance.</p>
<p>Model optimization through quantization, pruning, and distillation makes deployment feasible. Q4 quantization reduces 7B parameter models to under 5GB while preserving most capability. New NVFP4 and FP8 formats provide additional optimization paths on supported hardware.</p>
<p>Production deployment requires more than inference code. OTA updates, monitoring, thermal management, and fallback strategies separate demos from reliable systems.</p>
<p>The direction is clear: AI inference is moving closer to data sources. Edge deployment skills will matter more as this trend continues.</p>
<hr>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>InfoQ. “Cactus v1: Cross-Platform LLM Inference on Mobile with Zero Latency and Full Privacy.” <a href="https://www.infoq.com/news/2025/12/cactus-on-device-inference/" rel="noopener noreferrer">https://www.infoq.com/news/2025/12/cactus-on-device-inference/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>PMC. “Tiny Machine Learning and On-Device Inference: A Survey.” <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12115890/" rel="noopener noreferrer">https://pmc.ncbi.nlm.nih.gov/articles/PMC12115890/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Novus. “The Rise of Local AI Models: Going Small to Go Big.” <a href="https://www.novusasi.com/blog/the-rise-of-local-ai-models-going-small-to-go-big" rel="noopener noreferrer">https://www.novusasi.com/blog/the-rise-of-local-ai-models-going-small-to-go-big</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>GlobeNewswire. “Edge AI Hardware Markets, 2031: Rising AI Demand Spurs Smartphone Refresh Cycles in the Premium Segment.” <a href="https://www.globenewswire.com/news-release/2026/01/21/3222516/28124/en/Edge-AI-Hardware-Markets-2031-Rising-AI-Demand-Spurs-Smartphone-Refresh-Cycles-in-the-Premium-Segment.html" rel="noopener noreferrer">https://www.globenewswire.com/news-release/2026/01/21/3222516/28124/en/Edge-AI-Hardware-Markets-2031-Rising-AI-Demand-Spurs-Smartphone-Refresh-Cycles-in-the-Premium-Segment.html</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-4" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-4-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-4-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-5">
<p>NVIDIA. “Jetson Thor | Advanced AI for Physical Robotics.” <a href="https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-thor/" rel="noopener noreferrer">https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-thor/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-5" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-5-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-5-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-5-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-6">
<p>NVIDIA Newsroom. “NVIDIA Blackwell-Powered Jetson Thor Now Available, Accelerating the Age of General Robotics.” <a href="https://nvidianews.nvidia.com/news/nvidia-blackwell-powered-jetson-thor-now-available-accelerating-the-age-of-general-robotics" rel="noopener noreferrer">https://nvidianews.nvidia.com/news/nvidia-blackwell-powered-jetson-thor-now-available-accelerating-the-age-of-general-robotics</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-6" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-6-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-7">
<p>Seeed Studio. “NVIDIA Jetson AGX Thor Developer Kit.” <a href="https://www.seeedstudio.com/NVIDIA-Jetson-AGX-Thor-Developer-Kit-p-9965.html" rel="noopener noreferrer">https://www.seeedstudio.com/NVIDIA-Jetson-AGX-Thor-Developer-Kit-p-9965.html</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>SDxCentral. “Nvidia pushes AI from edge to storage with Jetson T4000 and BlueField-4 updates.” <a href="https://www.sdxcentral.com/news/nvidia-pushes-ai-from-edge-to-storage-with-jetson-t4000-and-bluefield-4-updates/" rel="noopener noreferrer">https://www.sdxcentral.com/news/nvidia-pushes-ai-from-edge-to-storage-with-jetson-t4000-and-bluefield-4-updates/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-8" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-8-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-8-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-9">
<p>NVIDIA. “Jetson AGX Orin for Next-Gen Robotics.” <a href="https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-orin/" rel="noopener noreferrer">https://www.nvidia.com/en-us/autonomous-machines/embedded-systems/jetson-orin/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-9" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-9-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-10">
<p>NVIDIA Developer Blog. “NVIDIA Jetson Orin Nano Developer Kit Gets a Super Boost.” <a href="https://developer.nvidia.com/blog/nvidia-jetson-orin-nano-developer-kit-gets-a-super-boost/" rel="noopener noreferrer">https://developer.nvidia.com/blog/nvidia-jetson-orin-nano-developer-kit-gets-a-super-boost/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-10" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-10-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-11">
<p>Raspberry Pi. “Introducing the Raspberry Pi AI HAT+ 2: Generative AI on Raspberry Pi 5.” <a href="https://www.raspberrypi.com/news/introducing-the-raspberry-pi-ai-hat-plus-2-generative-ai-on-raspberry-pi-5/" rel="noopener noreferrer">https://www.raspberrypi.com/news/introducing-the-raspberry-pi-ai-hat-plus-2-generative-ai-on-raspberry-pi-5/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-11" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-11-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-12">
<p>CNX Software. “Raspberry Pi AI HAT+ 2 targets generative AI (LLM/VLM) with Hailo-10H accelerator.” <a href="https://www.cnx-software.com/2026/01/15/raspberry-pi-ai-hat-2-targets-generative-ai-llm-vlm-with-hailo-10h-accelerator/" rel="noopener noreferrer">https://www.cnx-software.com/2026/01/15/raspberry-pi-ai-hat-2-targets-generative-ai-llm-vlm-with-hailo-10h-accelerator/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p>Electronics Weekly. “Raspberry Pi AI HAT+ 2 updates for Generative AI.” <a href="https://www.electronicsweekly.com/news/products/raspberry-pi-development/raspberry-pi-ai-hat-2-updates-for-generative-ai-2026-01/" rel="noopener noreferrer">https://www.electronicsweekly.com/news/products/raspberry-pi-development/raspberry-pi-ai-hat-2-updates-for-generative-ai-2026-01/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-13" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-13-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-14">
<p>Jeff Geerling. “Testing Raspberry Pi’s AI Kit - 13 TOPS for $70.” <a href="https://www.jeffgeerling.com/blog/2024/testing-raspberry-pis-ai-kit-13-tops-70/" rel="noopener noreferrer">https://www.jeffgeerling.com/blog/2024/testing-raspberry-pis-ai-kit-13-tops-70/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-14" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-14-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-15">
<p>Jeff Geerling. “Raspberry Pi’s new AI HAT adds 8GB of RAM for local LLMs.” <a href="https://www.jeffgeerling.com/blog/2026/raspberry-pi-ai-hat-2/" rel="noopener noreferrer">https://www.jeffgeerling.com/blog/2026/raspberry-pi-ai-hat-2/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16">
<p>Google Coral. “USB Accelerator Datasheet.” <a href="https://www.coral.ai/static/files/Coral-USB-Accelerator-datasheet.pdf" rel="noopener noreferrer">https://www.coral.ai/static/files/Coral-USB-Accelerator-datasheet.pdf</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-16" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-16-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-17">
<p>Q-engineering. “Google Coral’s TPU explained in depth.” <a href="https://qengineering.eu/google-corals-tpu-explained.html" rel="noopener noreferrer">https://qengineering.eu/google-corals-tpu-explained.html</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18">
<p>Apple. “Apple unleashes M5, the next big leap in AI performance for Apple silicon.” <a href="https://www.apple.com/newsroom/2025/10/apple-unleashes-m5-the-next-big-leap-in-ai-performance-for-apple-silicon/" rel="noopener noreferrer">https://www.apple.com/newsroom/2025/10/apple-unleashes-m5-the-next-big-leap-in-ai-performance-for-apple-silicon/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19">
<p>Apple. “Apple introduces M4 chip.” <a href="https://www.apple.com/newsroom/2024/05/apple-introduces-m4-chip/" rel="noopener noreferrer">https://www.apple.com/newsroom/2024/05/apple-introduces-m4-chip/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-20">
<p>MacRumors. “A19 vs. A19 Pro: iPhone 17 Chip Differences.” <a href="https://www.macrumors.com/2025/09/09/iphone-17-a19-chip/" rel="noopener noreferrer">https://www.macrumors.com/2025/09/09/iphone-17-a19-chip/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-20" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-21">
<p>Apple Machine Learning Research. “Deploying Transformers on the Apple Neural Engine.” <a href="https://machinelearning.apple.com/research/neural-engine-transformers" rel="noopener noreferrer">https://machinelearning.apple.com/research/neural-engine-transformers</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-21" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-22">
<p>Google Developers Blog. “Unlocking Peak Performance on Qualcomm NPU with LiteRT.” <a href="https://developers.googleblog.com/unlocking-peak-performance-on-qualcomm-npu-with-litert/" rel="noopener noreferrer">https://developers.googleblog.com/unlocking-peak-performance-on-qualcomm-npu-with-litert/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-22" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-22-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-22-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-23">
<p>arXiv. “Large Language Model Performance Benchmarking on Mobile Platforms.” <a href="https://arxiv.org/html/2410.03613v1" rel="noopener noreferrer">https://arxiv.org/html/2410.03613v1</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-23" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-24">
<p>arXiv. “Scaling LLM Test-Time Compute with Mobile NPU on Smartphones.” <a href="https://arxiv.org/html/2509.23324v1" rel="noopener noreferrer">https://arxiv.org/html/2509.23324v1</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-24" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-25">
<p>Automate.org. “CES 2026: Qualcomm Targets NVIDIA Jetson with New Robotics Developer Platform.” <a href="https://www.automate.org/news/ces-2026-qualcomm-targets-nvidia-jetson-with-new-robotics-developer-platform" rel="noopener noreferrer">https://www.automate.org/news/ces-2026-qualcomm-targets-nvidia-jetson-with-new-robotics-developer-platform</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-25" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26">
<p>Nordic Semiconductor. “nRF54L Series SoC with NPU and Nordic Edge AI Lab make on-device intelligence easily accessible.” <a href="https://www.nordicsemi.com/Nordic-news/2026/01/nRF54L-Series-SoC-with-NPU-and-Nordic-Edge-AI-Lab-make-on-device-intelligence-easily-accessible" rel="noopener noreferrer">https://www.nordicsemi.com/Nordic-news/2026/01/nRF54L-Series-SoC-with-NPU-and-Nordic-Edge-AI-Lab-make-on-device-intelligence-easily-accessible</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-26" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-26-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-27">
<p>Unified AI Hub. “Edge AI in 2026: Processing Intelligence at the Edge.” <a href="https://www.unifiedaihub.com/blog/edge-ai-in-2026-processing-intelligence-where-data-is-generated" rel="noopener noreferrer">https://www.unifiedaihub.com/blog/edge-ai-in-2026-processing-intelligence-where-data-is-generated</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-27" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-27-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-28">
<p>Google AI Edge. “Post-training quantization.” <a href="https://ai.google.dev/edge/litert/conversion/tensorflow/quantization/post_training_quantization" rel="noopener noreferrer">https://ai.google.dev/edge/litert/conversion/tensorflow/quantization/post_training_quantization</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-28" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-28-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-29">
<p>PyTorch Blog. “Quantization-Aware Training for Large Language Models.” <a href="https://pytorch.org/blog/quantization-aware-training/" rel="noopener noreferrer">https://pytorch.org/blog/quantization-aware-training/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-29" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-30">
<p>llama.cpp GitHub. “Quantize README.” <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md" rel="noopener noreferrer">https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-30" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-30-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-31">
<p>Oreate AI. “Practical Quantization of Llama Models: Detailed Explanation of GGUF and llama.cpp Technologies.” <a href="https://www.oreateai.com/blog/practical-quantization-of-llama-models-detailed-explanation-of-gguf-and-llamacpp-technologies/" rel="noopener noreferrer">https://www.oreateai.com/blog/practical-quantization-of-llama-models-detailed-explanation-of-gguf-and-llamacpp-technologies/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-31" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-32">
<p>NVIDIA Developer Blog. “Open Source AI Tool Upgrades Speed Up LLM and Diffusion Models on NVIDIA RTX PCs.” <a href="https://developer.nvidia.com/blog/open-source-ai-tool-upgrades-speed-up-llm-and-diffusion-models-on-nvidia-rtx-pcs" rel="noopener noreferrer">https://developer.nvidia.com/blog/open-source-ai-tool-upgrades-speed-up-llm-and-diffusion-models-on-nvidia-rtx-pcs</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-32" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-32-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-33">
<p>Nature Scientific Reports. “Neural network compression for reinforcement learning tasks.” <a href="https://www.nature.com/articles/s41598-025-93955-w" rel="noopener noreferrer">https://www.nature.com/articles/s41598-025-93955-w</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-33" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34">
<p>Nature Scientific Reports. “Efficient self-attention with smart pruning for sustainable large language models.” <a href="https://www.nature.com/articles/s41598-025-92586-5" rel="noopener noreferrer">https://www.nature.com/articles/s41598-025-92586-5</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-34" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-35">
<p>Springer. “A Hybrid Lightweight Deep Learning Model for Edge Devices.” <a href="https://link.springer.com/chapter/10.1007/978-3-031-81083-1_1" rel="noopener noreferrer">https://link.springer.com/chapter/10.1007/978-3-031-81083-1_1</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-35" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-36">
<p>Wiley. “Optimizing Deep Learning Models for Resource-Constrained Environments.” <a href="https://onlinelibrary.wiley.com/doi/full/10.1002/eng2.70187" rel="noopener noreferrer">https://onlinelibrary.wiley.com/doi/full/10.1002/eng2.70187</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-36" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-37">
<p>NVIDIA Developer Blog. “Optimizing Inference on LLMs with TensorRT-LLM.” <a href="https://developer.nvidia.com/blog/optimizing-inference-on-llms-with-tensorrt-llm-now-publicly-available/" rel="noopener noreferrer">https://developer.nvidia.com/blog/optimizing-inference-on-llms-with-tensorrt-llm-now-publicly-available/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-37" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-38">
<p>Preste AI. “Optimized Deep Learning using TensorRT for NVIDIA Jetson TX2.” <a href="https://www.preste.ai/post/optimized-deep-learning-using-tensorrt-for-nvidia-jetson-tx2-part-1" rel="noopener noreferrer">https://www.preste.ai/post/optimized-deep-learning-using-tensorrt-for-nvidia-jetson-tx2-part-1</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-38" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-39">
<p>arXiv. “Benchmarking Deep Learning Models on NVIDIA Jetson Nano.” <a href="https://arxiv.org/html/2406.17749v1" rel="noopener noreferrer">https://arxiv.org/html/2406.17749v1</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-39" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-40">
<p>Apple Developer. “Deploy machine learning and AI models on-device with Core ML - WWDC24.” <a href="https://developer.apple.com/videos/play/wwdc2024/10161/" rel="noopener noreferrer">https://developer.apple.com/videos/play/wwdc2024/10161/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-40" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-41">
<p>PyTorch ExecuTorch. “Core ML Backend.” <a href="https://docs.pytorch.org/executorch/0.7/backends-coreml.html" rel="noopener noreferrer">https://docs.pytorch.org/executorch/0.7/backends-coreml.html</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-41" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-42">
<p>Red Hat Developer. “vLLM or llama.cpp: Choosing the right LLM inference engine for your use case.” <a href="https://developers.redhat.com/articles/2025/09/30/vllm-or-llamacpp-choosing-right-llm-inference-engine-your-use-case" rel="noopener noreferrer">https://developers.redhat.com/articles/2025/09/30/vllm-or-llamacpp-choosing-right-llm-inference-engine-your-use-case</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-42" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-43">
<p>AI Competence. “Running Llama On Raspberry Pi 5 (2025 Setup &amp; Guide).” <a href="https://aicompetence.org/running-llama-on-raspberry-pi-5/" rel="noopener noreferrer">https://aicompetence.org/running-llama-on-raspberry-pi-5/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-43" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-43-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-43-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-44">
<p>Stratosphere Laboratory. “How Well Do LLMs Perform on a Raspberry Pi 5?” <a href="https://www.stratosphereips.org/blog/2025/6/5/how-well-do-llms-perform-on-a-raspberry-pi-5" rel="noopener noreferrer">https://www.stratosphereips.org/blog/2025/6/5/how-well-do-llms-perform-on-a-raspberry-pi-5</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-44" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-44-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-44-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-45">
<p>Medium. “Local LLM eval tokens/sec comparison between llama.cpp and llamafile on Raspberry Pi 5.” <a href="https://medium.com/aidatatools/local-llm-eval-tokens-sec-comparison-between-llama-cpp-and-llamafile-on-raspberry-pi-5-8gb-model-89cfa17f6f18" rel="noopener noreferrer">https://medium.com/aidatatools/local-llm-eval-tokens-sec-comparison-between-llama-cpp-and-llamafile-on-raspberry-pi-5-8gb-model-89cfa17f6f18</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-45" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-46">
<p>Medium. “llama.cpp performance breakthrough for multi-GPU setups.” <a href="https://medium.com/@jagusztinl/llama-cpp-performance-breakthrough-for-multi-gpu-setups-04c83a66feb2" rel="noopener noreferrer">https://medium.com/@jagusztinl/llama-cpp-performance-breakthrough-for-multi-gpu-setups-04c83a66feb2</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-46" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-47">
<p>Callstack. “Want to Run LLMs on Your Device? Meet MLC.” <a href="https://www.callstack.com/blog/want-to-run-llms-on-your-device-meet-mlc" rel="noopener noreferrer">https://www.callstack.com/blog/want-to-run-llms-on-your-device-meet-mlc</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-47" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-48">
<p>It’s FOSS. “I Ran Local LLMs on My Android Phone.” <a href="https://itsfoss.com/android-on-device-ai/" rel="noopener noreferrer">https://itsfoss.com/android-on-device-ai/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-48" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-48-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-48-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-49">
<p>Unsloth. “How to Run and Deploy LLMs on your iOS or Android Phone.” <a href="https://unsloth.ai/docs/basics/deploy-llms-phone" rel="noopener noreferrer">https://unsloth.ai/docs/basics/deploy-llms-phone</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-49" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-49-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-50">
<p>byteiota. “WebGPU 2026: 70% Browser Support, 15x Performance Gains.” <a href="https://byteiota.com/webgpu-2026-70-browser-support-15x-performance-gains/" rel="noopener noreferrer">https://byteiota.com/webgpu-2026-70-browser-support-15x-performance-gains/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-50" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-50-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-51">
<p>Microsoft Open Source Blog. “ONNX Runtime Web unleashes generative AI in the browser using WebGPU.” <a href="https://opensource.microsoft.com/blog/2024/02/29/onnx-runtime-web-unleashes-generative-ai-in-the-browser-using-webgpu" rel="noopener noreferrer">https://opensource.microsoft.com/blog/2024/02/29/onnx-runtime-web-unleashes-generative-ai-in-the-browser-using-webgpu</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-51" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-51-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-52">
<p>arXiv. “WebLLM: A High-Performance In-Browser LLM Inference Engine.” <a href="https://arxiv.org/html/2412.15803v1" rel="noopener noreferrer">https://arxiv.org/html/2412.15803v1</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-52" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-52-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-53">
<p>Medium. “WebGPU bugs are holding back the browser AI revolution.” <a href="https://medium.com/@marcelo.emmerich/webgpu-bugs-are-holding-back-the-browser-ai-revolution-27d5f8c1dfca" rel="noopener noreferrer">https://medium.com/@marcelo.emmerich/webgpu-bugs-are-holding-back-the-browser-ai-revolution-27d5f8c1dfca</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-53" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-54">
<p>Hugging Face Blog. “Transformers.js v3: WebGPU Support, New Models &amp; Tasks, and More.” <a href="https://huggingface.co/blog/transformersjs-v3" rel="noopener noreferrer">https://huggingface.co/blog/transformersjs-v3</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-54" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-55">
<p>WebLLM. “Home.” <a href="https://webllm.mlc.ai/" rel="noopener noreferrer">https://webllm.mlc.ai/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-55" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-56">
<p>Embedded.com. “Optimizing Edge AI with Advanced Thermal Management in Embedded Systems.” <a href="https://www.embedded.com/optimizing-edge-ai-with-advanced-thermal-management-in-embedded-systems-2/" rel="noopener noreferrer">https://www.embedded.com/optimizing-edge-ai-with-advanced-thermal-management-in-embedded-systems-2/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-56" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-57">
<p>IEEE Xplore. “Run-Time Prevention of Thermal Throttling on the Edge using Reinforcement-Learning Based Predictive Thermal Aware Power and Performance Management.” <a href="https://ieeexplore.ieee.org/document/10666109/" rel="noopener noreferrer">https://ieeexplore.ieee.org/document/10666109/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-57" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-58">
<p>Janea Systems. “4 Power Management Strategies for Edge AI Devices.” <a href="https://www.janeasystems.com/blog/power-management-strategies-for-edge-devices" rel="noopener noreferrer">https://www.janeasystems.com/blog/power-management-strategies-for-edge-devices</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-58" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-59">
<p>Seeed Studio Blog. “YOLOv8 Performance Benchmarks on NVIDIA Jetson Devices.” <a href="https://www.seeedstudio.com/blog/2023/03/30/yolov8-performance-benchmarks-on-nvidia-jetson-devices/" rel="noopener noreferrer">https://www.seeedstudio.com/blog/2023/03/30/yolov8-performance-benchmarks-on-nvidia-jetson-devices/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-59" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-60">
<p>Hackster.io. “Pushing Limits: YOLOv8 vs. v26 on Jetson Orin Nano.” <a href="https://www.hackster.io/qwe018931/pushing-limits-yolov8-vs-v26-on-jetson-orin-nano-b89267" rel="noopener noreferrer">https://www.hackster.io/qwe018931/pushing-limits-yolov8-vs-v26-on-jetson-orin-nano-b89267</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-60" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-61">
<p>Simalabs. “Jetson AGX Thor vs. Jetson Orin: Latency-Accuracy Benchmarks for 4K Object Detection.” <a href="https://www.simalabs.ai/resources/jetson-agx-thor-vs-orin-4k-object-detection-live-sports-benchmarks-2025" rel="noopener noreferrer">https://www.simalabs.ai/resources/jetson-agx-thor-vs-orin-4k-object-detection-live-sports-benchmarks-2025</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-61" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-62">
<p>Golioth. “Over-the-Air (OTA) Updates.” <a href="https://docs.golioth.io/device-management/ota/" rel="noopener noreferrer">https://docs.golioth.io/device-management/ota/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-62" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-63">
<p>Mender. “Over-the-air (OTA) update best practices for industrial IoT and embedded devices.” <a href="https://mender.io/resources/reports-and-guides/ota-updates-best-practices" rel="noopener noreferrer">https://mender.io/resources/reports-and-guides/ota-updates-best-practices</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-63" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-64">
<p>Advantech. “AIoT Device Management and Edge Orchestration - DeviceOn.” <a href="https://campaign.advantech.online/en/deviceon/index.html" rel="noopener noreferrer">https://campaign.advantech.online/en/deviceon/index.html</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-64" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-65">
<p>ThingsBoard. “Over-the-air firmware and software updates.” <a href="https://thingsboard.io/docs/user-guide/ota-updates/" rel="noopener noreferrer">https://thingsboard.io/docs/user-guide/ota-updates/</a> <a href="https://blog.ecitis.org/edge-ai-deployment/#user-content-fnref-65" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Edge &amp; on-device</category>
            <category>Quantization</category>
            <category>Latency</category>
            <enclosure length="66924" type="image/png" url="https://blog.ecitis.org/open-graph/edge-ai-deployment.png"/>
        </item>
        <item>
            <title><![CDATA[Mixture of Experts: Sparse Computation for Efficient LLMs]]></title>
            <link>https://blog.ecitis.org/mixture-of-experts/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/mixture-of-experts/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[See how sparse routing expands model capacity without paying the compute cost of activating every parameter for every token.]]></description>
            <content:encoded><![CDATA[<p>Dense transformer models activate every parameter for every token. This approach scales poorly; doubling model capacity means doubling compute. Mixture of Experts (MoE) breaks this constraint by routing each token to a subset of specialized sub-networks, enabling models with hundreds of billions of parameters while maintaining reasonable inference costs.</p>
<p>The shift is complete. On the Artificial Analysis leaderboard, the top 10 open-source models all use MoE architectures<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-1" id="user-content-fnref-1">1</a></sup>. DeepSeek-R1, Kimi K2 Thinking, Qwen3, Llama 4, and Mistral Large 3 have demonstrated that sparse models match or exceed dense model performance at a fraction of the computational cost. Dense scaling alone no longer works. That is why ERNIE-4.5, Qwen3, Kimi K2, and others all use MoE. Dense models scale intelligence by doing more work. MoE scales intelligence by doing the right work.</p>
<h2 id="the-core-idea">The Core Idea</h2>
<p>An MoE layer replaces the standard feed-forward network (FFN) in a transformer block with multiple parallel FFNs called “experts.” A learned routing network determines which experts process each token. Most tokens activate only 1-2 experts out of 8, 128, or even 512 available experts.</p>
<p>Consider Mixtral 8x7B. The model contains 46.7 billion total parameters, but each token only activates 12.9 billion parameters during inference<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-2" id="user-content-fnref-2">2</a></sup>. You get the representational capacity of a 47B model with roughly the compute cost of a 13B dense model. Newer models push this further: Kimi K2 activates 32B of 1 trillion total parameters (3.2% activation), and Qwen3-Next activates just 3B of 80B parameters (3.7% activation)<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-3" id="user-content-fnref-3">3</a></sup><sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-4" id="user-content-fnref-4">4</a></sup>.</p>
<figure><figcaption><strong>Router</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/mixture-of-experts-0.webp" width="512" height="386" alt="Router" loading="lazy" decoding="async"></figure>
<p>The routing mechanism computes a probability distribution over experts for each token. A gating function G(x) = softmax(W_g * x) produces weights, and the top-K experts by weight process the token. The final output combines expert outputs weighted by their routing scores.</p>
<h2 id="how-routing-works-at-implementation-level">How Routing Works at Implementation Level</h2>
<p>The router is a simple linear layer that projects the token hidden state to a vector of expert scores:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> Router</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Module</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> __init__</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> hidden_dim</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> num_experts</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> top_k</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">        super</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">__init__</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">gate </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Linear</span><span style="color:var(--astro-code-token-punctuation)">(hidden_dim, num_experts, bias</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">top_k </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> top_k</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> forward</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # x: [batch, seq_len, hidden_dim]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        logits </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> self</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">gate</span><span style="color:var(--astro-code-token-punctuation)">(x)</span><span style="color:var(--astro-code-token-comment)">  # [batch, seq_len, num_experts]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        scores </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> F</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">softmax</span><span style="color:var(--astro-code-token-punctuation)">(logits, dim</span><span style="color:var(--astro-code-token-keyword)">=-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Select top-k experts per token</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        top_scores</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> top_indices </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">topk</span><span style="color:var(--astro-code-token-punctuation)">(scores, self.top_k, dim</span><span style="color:var(--astro-code-token-keyword)">=-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Renormalize selected weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        top_scores </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> top_scores </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> top_scores</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(dim</span><span style="color:var(--astro-code-token-keyword)">=-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, keepdim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> top_scores</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> top_indices</span></span></code></pre>
<p>Switch Transformer simplified earlier MoE designs by using top-1 routing instead of top-2<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-5" id="user-content-fnref-5">5</a></sup>. This reduces computation and communication overhead while maintaining model quality. The routing decision for each token is independent, allowing different tokens in the same sequence to use different experts.</p>
<h3 id="expert-dispatch-and-gather">Expert Dispatch and Gather</h3>
<p>After routing decisions are made, tokens must be physically dispatched to their assigned experts. This creates a challenge: tensor shapes must be known at compile time for efficient execution, but we cannot predict how many tokens each expert will receive.</p>
<p>The standard solution uses a “capacity factor” that sets maximum tokens per expert:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">capacity </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (tokens_per_batch </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> num_experts) </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> capacity_factor</span></span></code></pre>
<p>A capacity factor of 1.0 assumes perfect load balance. In practice, values between 1.25 and 2.0 are common to handle routing imbalance<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-6" id="user-content-fnref-6">6</a></sup>. Tokens that exceed an expert’s capacity are either dropped (passed through via residual connection) or handled through auxiliary mechanisms.</p>
<h2 id="what-experts-actually-learn">What Experts Actually Learn</h2>
<p>A common misconception: experts do not learn human-interpretable domains like “physics” or “medicine.” Research on Switch Transformer and other MoE models shows that experts specialize at the token level, not the semantic level<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-7" id="user-content-fnref-7">7</a></sup>.</p>
<p>Encoder experts tend to specialize in syntactic patterns. One expert might handle punctuation tokens, another proper nouns, another sentence-initial tokens. Decoder experts show less clear specialization but maintain consistent activation patterns for specific token types.</p>
<p>DeepSeek’s architecture separates “shared experts” that activate for every token from “routing experts” that activate conditionally<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-8" id="user-content-fnref-8">8</a></sup>. Shared experts learn broad patterns required across all inputs; syntax processing and high-level semantic features. Routing experts capture more specialized, context-dependent computations.</p>
<p>This finding has practical implications. MoE models perform well on knowledge-heavy tasks like TriviaQA but may underperform dense models on reasoning tasks at equivalent perplexity levels<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-7" id="user-content-fnref-7-2">7</a></sup>.</p>
<h2 id="load-balancing-strategies">Load Balancing Strategies</h2>
<p>Without intervention, routers collapse to using only a few experts while ignoring others. This wastes parameters and defeats the purpose of having multiple experts.</p>
<h3 id="auxiliary-loss">Auxiliary Loss</h3>
<p>Switch Transformer introduced an auxiliary loss that encourages balanced expert utilization<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-5" id="user-content-fnref-5-2">5</a></sup>. Given N experts and a batch of T tokens, the loss penalizes deviation from uniform distribution:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> load_balance_loss</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">router_probs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> expert_indices</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> num_experts</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # f_i: fraction of tokens assigned to expert i</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # P_i: mean router probability for expert i</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Count tokens per expert</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tokens_per_expert </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zeros</span><span style="color:var(--astro-code-token-punctuation)">(num_experts)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(num_experts):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        tokens_per_expert</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> (expert_indices </span><span style="color:var(--astro-code-token-keyword)">==</span><span style="color:var(--astro-code-foreground)"> i)</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">float</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    f </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokens_per_expert </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> tokens_per_expert</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Mean probability per expert</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    P </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> router_probs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">mean</span><span style="color:var(--astro-code-token-punctuation)">(dim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Auxiliary loss: scaled dot product</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> num_experts </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> (f </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> P)</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>The auxiliary loss coefficient requires careful tuning. Too small and experts collapse; too large and the balancing signal overwhelms the primary training objective. Switch Transformer used alpha = 0.01, finding this balanced load quickly without degrading model quality<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-5" id="user-content-fnref-5-3">5</a></sup>.</p>
<h3 id="auxiliary-loss-free-balancing">Auxiliary-Loss-Free Balancing</h3>
<p>DeepSeek-V3 pioneered auxiliary-loss-free load balancing<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-9" id="user-content-fnref-9">9</a></sup>. Instead of adding a loss term, they modify the routing mechanism itself to maintain balance without gradient interference. This avoids the fundamental trade-off where auxiliary losses can hurt model performance when weighted too heavily.</p>
<p>The approach, called Loss-Free Balancing, applies expert-wise bias to routing scores before the top-K decision. By dynamically updating each expert’s bias according to its recent load, it maintains balanced distribution without producing interference gradients<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-10" id="user-content-fnref-10">10</a></sup>. Validation on MoE models with up to 3B parameters trained on 200B tokens showed both better performance and better load balance compared with auxiliary-loss-controlled strategies.</p>
<p>Interestingly, despite calling it “auxiliary-loss-free,” DeepSeek V3 still uses a small complementary sequence-wise balance loss with a very small hyperparameter<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-11" id="user-content-fnref-11">11</a></sup>. A theoretical framework published at NeurIPS 2025 analyzed the Loss-Free Balancing procedure, proving monotonic improvement of a Lagrangian objective and an approximate-balancing guarantee<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-12" id="user-content-fnref-12">12</a></sup>.</p>
<h3 id="global-vs-micro-batch-balance">Global vs. Micro-Batch Balance</h3>
<p>Research from ACL 2025 found that employing global-batch load balance significantly outperforms micro-batch level balance by incorporating more diverse domain information<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-13" id="user-content-fnref-13">13</a></sup>. The finding: adding a small amount of micro-batch load balance while using global-batch balance can maintain model performance while reducing latency from local imbalance. Qwen3 adopted global-batch load balancing loss based on this research<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-14" id="user-content-fnref-14">14</a></sup>.</p>
<h3 id="expert-choice-routing">Expert Choice Routing</h3>
<p>Expert Choice (EC) routing inverts the standard approach: instead of tokens selecting top-k experts, experts select top-k tokens<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-15" id="user-content-fnref-15">15</a></sup>. Each token can be routed to a variable number of experts while each expert maintains a fixed bucket size. This achieves optimal load balancing while allowing heterogeneity in token-to-expert mapping.</p>
<p>Recent extensions include TC-MoE (ICLR 2025), which expands the expert space using ternary sets 1, achieving 1.1% improvement while reducing activated experts by up to 9%<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-16" id="user-content-fnref-16">16</a></sup>. Apple’s EC-DIT (NeurIPS 2025) applied expert-choice routing to diffusion transformers, scaling to 97 billion parameters<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-17" id="user-content-fnref-17">17</a></sup>.</p>
<h3 id="capacity-factor-trade-offs">Capacity Factor Trade-offs</h3>
<p>Setting expert capacity involves a three-way trade-off:</p>
<ol>
<li><strong>Too low</strong>: Tokens get dropped, losing information</li>
<li><strong>Too high</strong>: Wasted computation on empty expert slots</li>
<li><strong>Dynamic</strong>: More complex implementation, harder to optimize</li>
</ol>
<p>Mixtral demonstrated that with diverse training data and proper initialization, load balance can emerge naturally without explicit auxiliary losses<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-18" id="user-content-fnref-18">18</a></sup>. The top-k routing over varied data spreads selections across experts. Post-training analysis confirmed all experts receive substantial token allocations.</p>
<h2 id="training-dynamics-and-challenges">Training Dynamics and Challenges</h2>
<h3 id="instability">Instability</h3>
<p>MoE training exhibits higher variance than dense models. The routing decisions create feedback loops: an expert that performs well receives more tokens, gets more gradient updates, and becomes even more likely to be selected. This can lead to expert collapse or oscillating training dynamics.</p>
<p>Switch Transformer showed that large sparse models can be trained with bfloat16 precision, but this requires careful attention to numerical stability in the routing computation<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-5" id="user-content-fnref-5-4">5</a></sup>. Many implementations use float32 for the router even when the rest of the model uses mixed precision.</p>
<p>Kimi K2 trained for 15.5 trillion tokens with zero training instability by applying the Muon optimizer at unprecedented scale and developing novel optimization techniques<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-3" id="user-content-fnref-3-2">3</a></sup>.</p>
<h3 id="communication-overhead">Communication Overhead</h3>
<p>In distributed training, MoE layers require all-to-all communication. Each token must be sent to the GPU holding its assigned expert, processed, and returned. This communication pattern differs from standard tensor or data parallelism.</p>
<p>Expert parallelism (EP) distributes experts across GPUs. A model with 256 experts across 8 GPUs places 32 experts per GPU. The all-to-all communication volume scales with batch size and the degree of expert parallelism.</p>
<p>Hybrid parallelism combines EP with tensor parallelism (TP) and data parallelism (DP). The optimal configuration depends on expert sizes, model architecture, and hardware interconnect bandwidth<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-19" id="user-content-fnref-19">19</a></sup>.</p>
<h3 id="multi-token-prediction">Multi-Token Prediction</h3>
<p>DeepSeek-V3 pioneered combining MoE with multi-token prediction (MTP) training objectives<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-9" id="user-content-fnref-9-2">9</a></sup>. Instead of predicting only the next token, MTP predicts the next k tokens at each position using k independent output heads on a shared trunk. The approach allows denser training signals and improves data efficiency.</p>
<p>Unlike other MTP methods, DeepSeek’s approach maintains the causal chain by predicting additional tokens sequentially rather than in parallel. MTP modules are dropped at inference (though they can accelerate generation via speculative decoding), with acceptance rates between 85-90% for the second token prediction<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-11" id="user-content-fnref-11-2">11</a></sup>. Models trained with 4-token prediction are up to 3x faster at inference<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-20" id="user-content-fnref-20">20</a></sup>.</p>
<h3 id="fine-grained-vs-coarse-grained-experts">Fine-Grained vs Coarse-Grained Experts</h3>
<p>Traditional MoE uses a moderate number of large experts. DeepSeek introduced fine-grained experts: more experts with smaller hidden dimensions, activating more experts per token<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-8" id="user-content-fnref-8-2">8</a></sup>.</p>
<p>If you have N experts activating K per token, fine-grained design uses mN experts with hidden dimension reduced by 1/m, activating mK experts per token. Total computation stays constant, but the model has access to more diverse expert combinations.</p>
<p>The trend toward higher sparsity continues. DeepSeek-V3 uses 256 experts with 8 active per token. Kimi K2 increased sparsity further: 384 experts with 8 active, resulting in sparsity of 48 (384/8)<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-3" id="user-content-fnref-3-3">3</a></sup>. Qwen3-Next pushes to the extreme: 512 experts activating about 19 per token, achieving only 3.7% parameter activation with a 1</p><div></div> activation ratio<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-4" id="user-content-fnref-4-2">4</a></sup>.<p></p>
<h2 id="hybrid-attention-architectures">Hybrid Attention Architectures</h2>
<p>A major 2025 development combines MoE with linear attention mechanisms. Standard attention scales quadratically with context length. Linear attention variants like Gated DeltaNet scale linearly but compress past context through a memory bottleneck<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-21" id="user-content-fnref-21">21</a></sup>.</p>
<p>Qwen3-Next and Kimi Linear proposed hybrid architectures using a 3</p><div></div> ratio: for every three transformer blocks employing linear Gated DeltaNet, one block uses full attention<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-22" id="user-content-fnref-22">22</a></sup><sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-23" id="user-content-fnref-23">23</a></sup>. This structure enables efficient long-context modeling while preserving reasoning capability on complex tasks.<p></p>
<p>Gated DeltaNet combines the gating mechanism from Mamba2 with the delta update rule from DeltaNet. Gating enables rapid memory erasure; the delta rule facilitates targeted updates. The combination consistently surpasses Mamba2 and DeltaNet across language modeling, common-sense reasoning, in-context retrieval, and length extrapolation benchmarks<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-21" id="user-content-fnref-21-2">21</a></sup>.</p>
<p>For the MoE layers in these hybrid architectures, the structure repeats as: Layers 1-3 use Linear attention followed by MoE, Layer 4 uses Full attention followed by MoE. Qwen3-Next’s flagship 80B-A3B model achieves only 3B active parameters per token through this design<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-4" id="user-content-fnref-4-3">4</a></sup>.</p>
<h2 id="inference-considerations">Inference Considerations</h2>
<h3 id="memory-requirements">Memory Requirements</h3>
<p>MoE models require loading all expert weights into memory, even though each token only uses a fraction. A 671B parameter MoE model needs 671B parameters worth of storage, not 37B.</p>
<p>This creates an asymmetry between training and inference optimization. Training benefits from compute efficiency (fewer FLOPs per token). Inference on smaller batches may be bottlenecked by memory bandwidth for loading expert weights rather than compute. DeepSeek-V3’s total computational cost is approximately 250 GFLOPS per token, whereas a 72B dense model requires 394 GFLOPS and a 405B dense model requires 2448 GFLOPS<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-11" id="user-content-fnref-11-3">11</a></sup>.</p>
<h3 id="batch-routing-efficiency">Batch Routing Efficiency</h3>
<p>Large batch sizes help MoE inference. With more tokens, each expert receives more work, improving GPU utilization. DeepSeek-V3’s high sparsity (8 of 256 experts) requires large batch sizes to ensure sufficient tokens per expert<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-24" id="user-content-fnref-24">24</a></sup>.</p>
<p>The routing pattern varies per batch, creating dynamic load imbalance. Some experts may be overloaded while others sit idle. Inference latency is determined by the most loaded expert, so load imbalance directly impacts throughput.</p>
<h3 id="expert-parallelism-in-inference">Expert Parallelism in Inference</h3>
<p>vLLM now supports expert parallelism for large-scale MoE deployment, combining Data Parallel attention with Expert or Tensor Parallel MoE layers<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-25" id="user-content-fnref-25">25</a></sup>. Key optimizations include:</p>
<ol>
<li><strong>Expert Parallel Load Balancing (EPLB)</strong>: vLLM collects load statistics with every forward pass and periodically rebalances expert distribution across EP ranks<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-25" id="user-content-fnref-25-2">25</a></sup>.</li>
<li><strong>Communication backends</strong>: DeepSeek’s DeepEP kernels use nvshmem with high-throughput mode for prefill and low-latency mode for decode. Perplexity’s PPLX provides a more flexible alternative for chunked prefill scenarios<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-26" id="user-content-fnref-26">26</a></sup>.</li>
<li><strong>Wide Expert Parallelism</strong>: Simplifies scaling across multi-node deployments using NIXL+UCX for inter-node communication<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-26" id="user-content-fnref-26-2">26</a></sup>.</li>
</ol>
<p>MoEShard achieves load balance through tensor sharding of experts rather than capacity-based dropping<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-27" id="user-content-fnref-27">27</a></sup>. Each expert’s weights are split across GPUs, and all GPUs participate in computing each expert’s output. This guarantees full token retention regardless of routing skew.</p>
<figure><figcaption><strong>MoE Model Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/mixture-of-experts-1.webp" width="512" height="487" alt="MoE Model Comparison" loading="lazy" decoding="async"></figure>
<h3 id="hardware-optimization-blackwell-nvl72">Hardware Optimization: Blackwell NVL72</h3>
<p>The NVIDIA GB200 NVL72 rack-scale platform connects 72 Blackwell GPUs using fifth-generation NVLink, providing 1,800 GB/s bidirectional bandwidth<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-28" id="user-content-fnref-28">28</a></sup>. This large scale-up domain is optimized for sparse MoE architectures, which require frequent all-to-all exchanges between experts.</p>
<p>MoE models see a 10x performance leap on GB200 NVL72 compared with HGX H200, enabling one-tenth the cost per token<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-1" id="user-content-fnref-1-2">1</a></sup>. Key enablers include hardware acceleration for NVFP4 four-bit floating point, disaggregated serving through NVIDIA Dynamo, and multi-token prediction support in TensorRT-LLM<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-28" id="user-content-fnref-28-2">28</a></sup>.</p>
<p>For DeepSeek-R1, NVIDIA achieved over 250 tokens per second per user and maximum throughput over 30,000 tokens per second on a single DGX system with eight Blackwell GPUs<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-29" id="user-content-fnref-29">29</a></sup>. The GB300 NVL72 (Blackwell Ultra) delivered 45% higher performance per GPU on DeepSeek-R1 compared to GB200 NVL72<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-30" id="user-content-fnref-30">30</a></sup>.</p>
<h2 id="survey-of-current-moe-models-2025-2026">Survey of Current MoE Models (2025-2026)</h2>
<h3 id="switch-transformer-2021">Switch Transformer (2021)</h3>
<p>Google’s Switch Transformer demonstrated MoE at scale with 1.6 trillion parameters across 2048 experts<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-5" id="user-content-fnref-5-5">5</a></sup>. Using top-1 routing, it achieved 7x pre-training speedup over T5 models while reducing the complexity of earlier MoE approaches. The work established auxiliary load balancing losses as standard practice.</p>
<h3 id="mixtral-2024">Mixtral (2024)</h3>
<p>Mistral’s Mixtral 8x7B popularized MoE for open-weight models<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup>. With 8 experts and top-2 routing, it uses 12.9B active parameters from 46.7B total. Mixtral achieved natural load balance without auxiliary losses, suggesting careful initialization and diverse training data can replace explicit balancing mechanisms.</p>
<h3 id="deepseek-v3-2024">DeepSeek-V3 (2024)</h3>
<p>DeepSeek-V3 combines fine-grained experts (256 total, 8 active) with Multi-head Latent Attention (MLA) and auxiliary-loss-free load balancing<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-9" id="user-content-fnref-9-3">9</a></sup>. The model has 671B total parameters with 37B active, trained on 14.8 trillion tokens. Training required 2.788 million H800 GPU hours. DeepSeek reported performance competitive with closed-source frontier models.</p>
<h3 id="deepseek-r1-january-2025">DeepSeek-R1 (January 2025)</h3>
<p>DeepSeek-R1 builds on the V3 architecture, using the same 671B/37B MoE structure but trained via large-scale reinforcement learning without supervised fine-tuning as a preliminary step<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-31" id="user-content-fnref-31">31</a></sup>. Analysis shows 67 experts per layer in R1 compared to 55 per layer in V3. The model achieves 79.8% pass@1 on AIME, 97.3% on MATH-500, and a 2,029 Elo rating on Codeforces-like challenges<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-31" id="user-content-fnref-31-2">31</a></sup>.</p>
<h3 id="llama-4-april-2025">Llama 4 (April 2025)</h3>
<p>Meta’s Llama 4 represents Meta’s first MoE architecture and first natively multimodal models<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-32" id="user-content-fnref-32">32</a></sup>. The family includes:</p>
<ul>
<li><strong>Llama 4 Scout</strong>: 17B active parameters, 16 experts, fits on a single H100 with INT4 quantization, supports 10 million token context</li>
<li><strong>Llama 4 Maverick</strong>: 17B active / 400B total, 128 routed experts plus 1 shared expert, alternating dense and MoE layers, 1 million token context</li>
<li><strong>Llama 4 Behemoth</strong>: 288B active parameters, 16 experts, nearly 2 trillion total parameters, outperforms GPT-4.5, Claude Sonnet 3.7, and Gemini 2.0 Pro on MATH-500 and GPQA Diamond<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-32" id="user-content-fnref-32-2">32</a></sup></li>
</ul>
<p>Maverick’s architecture sends each token to the shared expert plus one of 128 routed experts, using alternating dense and MoE layers for inference efficiency.</p>
<h3 id="qwen3-may-2025">Qwen3 (May 2025)</h3>
<p>The Qwen3 series includes models from 0.6B to 235B parameters<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-14" id="user-content-fnref-14-2">14</a></sup>. Qwen3-235B-A22B uses 128 experts with 8 active, activating 22B of 235B parameters. Unlike earlier Qwen MoE models, Qwen3 removes shared experts and uses global-batch load balancing loss. Training covered 36 trillion tokens across 119 languages.</p>
<p>A key innovation is the integration of thinking mode (complex multi-step reasoning) and non-thinking mode (rapid responses) into a unified framework, enabling dynamic mode switching. Qwen3-235B-A22B-Thinking-2507 beats OpenAI O3 across key metrics: 92 vs 88.0 on AIME’25, 83 vs 82.5 on HMMT’25<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-33" id="user-content-fnref-33">33</a></sup>.</p>
<h3 id="qwen3-next-september-2025">Qwen3-Next (September 2025)</h3>
<p>Alibaba’s Qwen3-Next-80B-A3B represents ultra-sparse MoE design<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-4" id="user-content-fnref-4-4">4</a></sup>. With 512 experts and only 3B active parameters (3.7% activation, 1</p><div></div> ratio), it combines hybrid attention (Gated DeltaNet + Gated Attention) with multi-token prediction. Every 4th layer uses standard GQA attention; others use linear attention variants. The model supports 256K context natively, extendable to 1 million tokens.<p></p>
<h3 id="kimi-k2-july-2025">Kimi K2 (July 2025)</h3>
<p>Moonshot AI’s Kimi K2 uses 1 trillion total parameters with 32B active, featuring 384 experts with 8 selected per token plus 1 shared expert<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-3" id="user-content-fnref-3-4">3</a></sup>. The architecture follows DeepSeek V3’s MLA design with 7168 hidden dimension and 2048 expert hidden dimension. Trained with the Muon optimizer on 15.5T tokens with zero instability, it executes 200-300 sequential tool calls autonomously. Kimi K2 Thinking adds step-by-step reasoning with dynamic tool invocation.</p>
<h3 id="mistral-large-3-december-2025">Mistral Large 3 (December 2025)</h3>
<p>Mistral Large 3 is a sparse MoE with 675B total and 41B active parameters, a 16</p><div></div> ratio<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-34" id="user-content-fnref-34">34</a></sup>. The architecture uses thousands of specialized expert subnetworks with granular sparse routing. Training used 3000 H200 GPUs. The model delivers 92% of GPT-5.2’s performance at roughly 15% of the price<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-1" id="user-content-fnref-1-3">1</a></sup>. Released under Apache 2.0 license.<p></p>
<h3 id="ernie-45-july-2025">ERNIE 4.5 (July 2025)</h3>
<p>Baidu’s ERNIE 4.5 series includes 10 variants from 0.3B to 424B parameters<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-35" id="user-content-fnref-35">35</a></sup>. The architecture introduces heterogeneous modality MoE with modality-isolated routing for text, image, and video. Key innovations include router orthogonal loss and multimodal token-balanced loss. ERNIE-4.5-300B-A47B-Base surpasses DeepSeek-V3-671B-A37B-Base on 22 of 28 benchmarks<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-35" id="user-content-fnref-35-2">35</a></sup>. The MoE variants activate 2 of 64 experts per token.</p>
<h3 id="grok-3-february-2025">Grok 3 (February 2025)</h3>
<p>xAI’s Grok 3 uses an MoE transformer with estimated 1.5 trillion parameters<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-36" id="user-content-fnref-36">36</a></sup>. Trained with 10x more compute than Grok-2 on the Colossus cluster (approximately 200k GPUs), it employs sparse attention mechanisms and MoE layers that dynamically allocate computational resources. The architecture continues the Grok-1 approach of 64 transformer layers with MoE feed-forward layers using a router picking a subset of expert MLPs per token. Grok 3’s “Think” and “Big Brain” modes expose chain-of-thought reasoning with additional compute allocation<sup><a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fn-36" id="user-content-fnref-36-2">36</a></sup>.</p>
<h2 id="when-to-use-moe-vs-dense-models">When to Use MoE vs Dense Models</h2>
<p>MoE makes sense when:</p>
<ul>
<li><strong>Training compute is limited</strong>: Sparse models achieve better quality per FLOP</li>
<li><strong>Inference batch sizes are large</strong>: Amortizes expert loading overhead</li>
<li><strong>Knowledge breadth matters</strong>: MoE excels at knowledge-intensive tasks</li>
<li><strong>You have fast interconnects</strong>: All-to-all communication needs low latency</li>
</ul>
<p>Dense models may be preferable when:</p>
<ul>
<li><strong>Inference is memory-bound</strong>: MoE requires full model in memory</li>
<li><strong>Reasoning depth matters more than knowledge breadth</strong>: Dense models may perform better on complex reasoning</li>
<li><strong>Single-request latency is critical</strong>: Small batches underutilize MoE capacity</li>
<li><strong>Deployment hardware is constrained</strong>: Expert parallelism needs multiple GPUs</li>
</ul>
<p>The trend is clear: MoE architectures dominate the frontier. As interconnect speeds improve and inference systems mature, the compute efficiency advantages of sparse models become harder to ignore.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p><a href="https://blogs.nvidia.com/blog/mixture-of-experts-frontier-models/" rel="noopener noreferrer">NVIDIA Blog - Mixture of Experts Powers the Most Intelligent Frontier AI Models</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-1-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-1-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-2">
<p><a href="https://arxiv.org/abs/2401.04088" rel="noopener noreferrer">Mixtral of Experts - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2401.04088" rel="noopener noreferrer">.04088</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a><p></p>
</li>
<li id="user-content-fn-3">
<p><a href="https://moonshotai.github.io/Kimi-K2/" rel="noopener noreferrer">Kimi K2: Open Agentic Intelligence - Moonshot AI</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-3" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-3-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-3-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-3-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-4">
<p><a href="https://www.alibabacloud.com/blog/602536" rel="noopener noreferrer">Qwen3-Next: A New Generation of Ultra-Efficient Model Architecture - Alibaba Cloud</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-4" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-4-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-4-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-4-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-5">
<p><a href="https://arxiv.org/abs/2101.03961" rel="noopener noreferrer">Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2101.03961" rel="noopener noreferrer">.03961</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-5" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-5-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-5-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-5-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-5-5" class="data-footnote-backref">↩<sup>5</sup></a><p></p>
</li>
<li id="user-content-fn-6">
<p><a href="https://huggingface.co/blog/moe" rel="noopener noreferrer">Mixture of Experts Explained - Hugging Face Blog</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p><a href="https://newsletter.maartengrootendorst.com/p/a-visual-guide-to-mixture-of-experts" rel="noopener noreferrer">A Visual Guide to Mixture of Experts - Maarten Grootendorst</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-7" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-7-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-8">
<p><a href="https://www.chrishayduk.com/p/understanding-deepseek-part-i-deepseekmoe" rel="noopener noreferrer">Understanding DeepSeek Part I: DeepSeekMoE - Chris Hayduk</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-8" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-8-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-9">
<p><a href="https://arxiv.org/abs/2412.19437" rel="noopener noreferrer">DeepSeek-V3 Technical Report - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2412.19437" rel="noopener noreferrer">.19437</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-9" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-9-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-9-3" class="data-footnote-backref">↩<sup>3</sup></a><p></p>
</li>
<li id="user-content-fn-10">
<p><a href="https://arxiv.org/abs/2408.15664" rel="noopener noreferrer">Auxiliary-Loss-Free Load Balancing Strategy for Mixture-of-Experts - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2408.15664" rel="noopener noreferrer">.15664</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-10" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-11">
<p><a href="https://magazine.sebastianraschka.com/p/technical-deepseek" rel="noopener noreferrer">A Technical Tour of the DeepSeek Models from V3 to V3.2 - Sebastian Raschka</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-11" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-11-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-11-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-12">
<p><a href="https://openreview.net/forum?id=sQHZ2w8UzP" rel="noopener noreferrer">A Theoretical Framework for Auxiliary-Loss-Free Load-Balancing - NeurIPS 2025</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p><a href="https://aclanthology.org/2025.acl-long.249.pdf" rel="noopener noreferrer">On Implementing Load Balancing Loss for Training MoE - ACL 2025</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-13" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-14">
<p><a href="https://arxiv.org/abs/2505.09388" rel="noopener noreferrer">Qwen3 Technical Report - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2505.09388" rel="noopener noreferrer">.09388</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-14" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-14-2" class="data-footnote-backref">↩<sup>2</sup></a><p></p>
</li>
<li id="user-content-fn-15">
<p><a href="https://arxiv.org/abs/2202.09368" rel="noopener noreferrer">Mixture-of-Experts with Expert Choice Routing - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2202.09368" rel="noopener noreferrer">.09368</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-15" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-16">
<p><a href="https://openreview.net/forum?id=dsP91M4hDL" rel="noopener noreferrer">TC-MoE: Augmenting Mixture of Experts with Ternary Expert Choice - ICLR 2025</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-17">
<p><a href="https://machinelearning.apple.com/research/ec-dit" rel="noopener noreferrer">EC-DIT: Scaling Diffusion Transformers with Adaptive Expert-Choice Routing - Apple ML Research</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18">
<p><a href="https://medium.com/@pilliudayaditya1207/understanding-mixture-of-experts-switch-transformers-load-balancing-vs-mixtral-s-natural-balance-25ed528cadfe" rel="noopener noreferrer">Understanding Mixture-of-Experts: Switch Transformer’s Load Balancing vs. Mixtral’s Natural Balance - Medium</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19">
<p><a href="https://pssg.cs.umd.edu/assets/papers/2023-06-deepspeed-ted-moe.pdf" rel="noopener noreferrer">A Hybrid Tensor-Expert-Data Parallelism Approach - DeepSpeed-TED</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-20">
<p><a href="https://arxiv.org/abs/2404.19737" rel="noopener noreferrer">Better &amp; Faster Large Language Models via Multi-token Prediction - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2404.19737" rel="noopener noreferrer">.19737</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-20" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-21">
<p><a href="https://arxiv.org/abs/2412.06464" rel="noopener noreferrer">Gated Delta Networks: Improving Mamba2 with Delta Rule - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2412.06464" rel="noopener noreferrer">.06464</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-21" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-21-2" class="data-footnote-backref">↩<sup>2</sup></a><p></p>
</li>
<li id="user-content-fn-22">
<p><a href="https://blog.vllm.ai/2025/09/11/qwen3-next.html" rel="noopener noreferrer">vLLM Now Supports Qwen3-Next: Hybrid Architecture with Extreme Efficiency</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-22" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-23">
<p><a href="https://arxiv.org/abs/2510.26692" rel="noopener noreferrer">Kimi Linear: An Expressive, Efficient Attention Architecture - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2510.26692" rel="noopener noreferrer">.26692</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-23" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-24">
<p><a href="https://www.tensoreconomics.com/p/moe-inference-economics-from-first" rel="noopener noreferrer">MoE Inference Economics from First Principles - Tensor Economics</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-24" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-25">
<p><a href="https://docs.vllm.ai/en/latest/serving/expert_parallel_deployment/" rel="noopener noreferrer">Expert Parallel Deployment - vLLM Documentation</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-25" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-25-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-26">
<p><a href="https://developers.redhat.com/articles/2025/09/08/scaling-deepseek-style-moes-vllm-and-llm-d-using-wide-ep" rel="noopener noreferrer">Scaling DeepSeek-style MoEs with vLLM and llm-d using Wide EP - Red Hat Developer</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-26" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-26-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-27">
<p><a href="https://arxiv.org/abs/2503.08467" rel="noopener noreferrer">Accelerating MoE Model Inference with Expert Sharding - arXiv</a></p><div></div><a href="https://arxiv.org/abs/2503.08467" rel="noopener noreferrer">.08467</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-27" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-28">
<p><a href="https://developer.nvidia.com/blog/delivering-massive-performance-leaps-for-mixture-of-experts-inference-on-nvidia-blackwell/" rel="noopener noreferrer">Delivering Massive Performance Leaps for MoE Inference on NVIDIA Blackwell - NVIDIA Technical Blog</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-28" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-28-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-29">
<p><a href="https://developer.nvidia.com/blog/nvidia-blackwell-delivers-world-record-deepseek-r1-inference-performance/" rel="noopener noreferrer">NVIDIA Blackwell Delivers World-Record DeepSeek-R1 Inference Performance - NVIDIA Technical Blog</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-29" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-30">
<p><a href="https://developer.nvidia.com/blog/nvidia-blackwell-ultra-sets-new-inference-records-in-mlperf-debut/" rel="noopener noreferrer">NVIDIA Blackwell Ultra Sets New Inference Records in MLPerf Debut - NVIDIA Technical Blog</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-30" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-31">
<p><a href="https://github.com/deepseek-ai/DeepSeek-R1" rel="noopener noreferrer">DeepSeek-R1 - GitHub</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-31" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-31-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-32">
<p><a href="https://ai.meta.com/blog/llama-4-multimodal-intelligence/" rel="noopener noreferrer">The Llama 4 herd: The beginning of a new era of natively multimodal AI innovation - Meta AI</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-32" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-32-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-33">
<p><a href="https://www.siliconflow.com/articles/en/the-best-qwen3-models-in-2025" rel="noopener noreferrer">Ultimate Guide - The Best Qwen3 Models in 2026 - SiliconFlow</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-33" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34">
<p><a href="https://mistral.ai/news/mistral-3" rel="noopener noreferrer">Introducing Mistral 3 - Mistral AI</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-34" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-35">
<p><a href="https://ernie.baidu.com/blog/posts/ernie4.5/" rel="noopener noreferrer">Announcing the Open Source Release of the ERNIE 4.5 Model Family - Baidu</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-35" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-35-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-36">
<p><a href="https://mohasoftware.com/blog/the-tech-behind-elon-musks-xai-latest-release-grok-3" rel="noopener noreferrer">The Tech Behind Grok 3 - MOHA Software</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-36" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/mixture-of-experts/#user-content-fnref-36-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Mixture of experts</category>
            <category>Transformers</category>
            <category>Architecture</category>
            <enclosure length="75865" type="image/png" url="https://blog.ecitis.org/open-graph/mixture-of-experts.png"/>
        </item>
        <item>
            <title><![CDATA[RLHF and Preference Tuning: Aligning LLMs with Human Values]]></title>
            <link>https://blog.ecitis.org/rlhf-preference-tuning/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/rlhf-preference-tuning/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Compare RLHF, DPO, GRPO, and newer preference methods for turning next-token predictors into useful, aligned assistants.]]></description>
            <content:encoded><![CDATA[<p>Large language models trained on internet text learn to predict the next token. This objective produces models that can generate fluent text, but fluency alone does not make a model useful or safe. A model optimizing purely for next-token prediction might produce toxic content, confidently state falsehoods, or refuse to help with legitimate requests. The gap between “predicting text well” and “being helpful” is the alignment problem.</p>
<p>Reinforcement Learning from Human Feedback emerged as the dominant solution. OpenAI’s InstructGPT demonstrated that RLHF could transform GPT-3 into a model that humans preferred 85% of the time over the base model.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-1" id="user-content-fnref-1">1</a></sup> This technique became foundational to ChatGPT, Claude, and Gemini.</p>
<p>But RLHF is complex. It requires training multiple models, managing reinforcement learning instabilities, and collecting expensive human preference data. In 2023, researchers at Stanford introduced Direct Preference Optimization, which achieves similar results with a simpler supervised learning objective.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-2" id="user-content-fnref-2">2</a></sup> Since then, the field has expanded rapidly. DPO spawned variants including IPO, KTO, ORPO, and SimPO. DeepSeek introduced GRPO for training reasoning models. New methods like AlphaPO and Reinforcement Learning with Verifiable Rewards (RLVR) emerged in 2025, while research on failure modes like shallow safety alignment and sycophancy has matured.</p>
<p>This guide covers the technical details of these approaches: how they work, when to use each one, and how to implement them in practice.</p>
<h2 id="the-alignment-problem">The Alignment Problem</h2>
<p>Language models learn from massive text corpora containing everything from academic papers to social media posts. The training objective is simple: given a sequence of tokens, predict the next one. This produces models with broad capabilities but no inherent sense of what outputs are desirable.</p>
<p>Consider what happens when you ask a base model to help with a task. It might:</p>
<ul>
<li>Generate a helpful response</li>
<li>Generate a harmful response</li>
<li>Refuse unnecessarily</li>
<li>Hallucinate confidently</li>
<li>Continue your prompt as if completing a document</li>
</ul>
<p>All these behaviors are consistent with next-token prediction on internet text. The model has no internal preference for helpfulness over harm.</p>
<p>RLHF addresses this by introducing a training signal based on human preferences. Rather than predicting what text typically follows, the model learns to generate text that humans rate as good. This requires three components:</p>
<ol>
<li>A supervised fine-tuned model that can follow instructions</li>
<li>A reward model that predicts human preferences</li>
<li>A reinforcement learning algorithm that optimizes the model against the reward</li>
</ol>
<figure><figcaption><strong>RLHF Training Pipeline</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/rlhf-preference-tuning-0.webp" width="512" height="478" alt="RLHF Training Pipeline" loading="lazy" decoding="async"></figure>
<h2 id="stage-1-supervised-fine-tuning">Stage 1: Supervised Fine-Tuning</h2>
<p>Before RLHF, the base model needs basic instruction-following capabilities. This stage collects demonstration data: human-written examples of good responses to prompts. OpenAI used approximately 13,000 (prompt, response) pairs for InstructGPT, with labelers who were carefully selected and trained.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></p>
<p>The training objective is standard cross-entropy loss:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>S</mi><mi>F</mi><mi>T</mi></mrow></msub><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>∼</mo><mi>D</mi></mrow></msub><mrow><mo fence="true">[</mo><msubsup><mo>∑</mo><mrow><mi>t</mi><mo>=</mo><mn>1</mn></mrow><mi>T</mi></msubsup><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mrow><mo>&lt;</mo><mi>t</mi></mrow></msub><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>This produces a policy <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>π</mi><mrow><mi>S</mi><mi>F</mi><mi>T</mi></mrow></msub></mrow></semantics></math> that can generate reasonable responses but has not yet learned to distinguish between good and bad outputs. The SFT model serves as both the starting point for RLHF training and often as the reference policy for KL regularization.</p>
<p>Quality matters more than quantity at this stage. A few thousand high-quality demonstrations outperform larger datasets of mediocre examples. The goal is to establish the format and style of responses, not to cover every possible topic.</p>
<h2 id="stage-2-reward-model-training">Stage 2: Reward Model Training</h2>
<p>The reward model learns to predict which responses humans prefer. Given a prompt and two candidate responses, the RM outputs which one is better. This pairwise comparison approach is more reliable than asking humans to assign absolute scores.</p>
<h3 id="the-bradley-terry-model">The Bradley-Terry Model</h3>
<p>Most reward models use the Bradley-Terry framework from statistics.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-4" id="user-content-fnref-4">4</a></sup> It assumes each response has a latent quality score, and the probability that response A beats response B follows a logistic function:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>P</mi><mo stretchy="false">(</mo><mi>A</mi><mo>≻</mo><mi>B</mi><mo stretchy="false">)</mo><mo>=</mo><mi>σ</mi><mo stretchy="false">(</mo><mi>r</mi><mo stretchy="false">(</mo><mi>A</mi><mo stretchy="false">)</mo><mo>−</mo><mi>r</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mn>1</mn><mo>+</mo><msup><mi>e</mi><mrow><mo>−</mo><mo stretchy="false">(</mo><mi>r</mi><mo stretchy="false">(</mo><mi>A</mi><mo stretchy="false">)</mo><mo>−</mo><mi>r</mi><mo stretchy="false">(</mo><mi>B</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo></mrow></msup></mrow></mfrac></mrow></semantics></math></p>
<p>The reward model is trained to maximize the log-likelihood of observed preferences:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>R</mi><mi>M</mi></mrow></msub><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo><mo>∼</mo><mi>D</mi></mrow></msub><mrow><mo fence="true">[</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mo stretchy="false">(</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo stretchy="false">)</mo><mo>−</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>Here <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>w</mi></msub></mrow></semantics></math> is the preferred (winning) response and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>l</mi></msub></mrow></semantics></math> is the dispreferred (losing) response.</p>
<h3 id="architecture">Architecture</h3>
<p>Reward models are typically language models with a linear head that outputs a scalar value instead of token logits. Common practice initializes the RM from the SFT checkpoint, which gives it strong language understanding capabilities.</p>
<p>The architecture processes the prompt and response together, outputting a single reward value. Some implementations average the final hidden states; others use only the last token’s representation.</p>
<h3 id="calibration-challenges">Calibration Challenges</h3>
<p>Reward models face several practical issues:</p>
<p><strong>Length bias</strong>: Longer responses often receive higher scores regardless of quality. This can be mitigated by normalizing rewards by response length or by carefully balancing the training data.</p>
<p><strong>Distribution shift</strong>: The RM is trained on outputs from the SFT model but will be used to evaluate outputs from the RL-trained policy. As the policy improves, its outputs may fall outside the RM’s training distribution.</p>
<p><strong>Annotation noise</strong>: Human preferences are inconsistent. Inter-annotator agreement on preference datasets hovers around 65-75%, meaning a substantial fraction of “ground truth” labels are effectively random.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-5" id="user-content-fnref-5">5</a></sup></p>
<h2 id="stage-3-ppo-for-language-models">Stage 3: PPO for Language Models</h2>
<p>With a reward model in hand, we can optimize the language model using reinforcement learning. Proximal Policy Optimization has become the standard algorithm, adapted from robotics and game-playing applications.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-6" id="user-content-fnref-6">6</a></sup></p>
<h3 id="the-objective">The Objective</h3>
<p>The RLHF objective maximizes expected reward while staying close to the reference policy:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mrow><mi>max</mi><mo>⁡</mo></mrow><mi>θ</mi></msub><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo>∼</mo><mi>D</mi><mo separator="true">,</mo><mi>y</mi><mo>∼</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></msub><mrow><mo fence="true">[</mo><msub><mi>r</mi><mi>ϕ</mi></msub><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>−</mo><mi>β</mi><mo>⋅</mo><msub><mi>D</mi><mrow><mi>K</mi><mi>L</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mi mathvariant="normal">∥</mi><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><mo>⋅</mo><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>The KL penalty term prevents the policy from diverging too far from the reference model. Without it, the policy would quickly learn to exploit quirks in the reward model rather than genuinely improving. This phenomenon is called reward hacking.</p>
<h3 id="the-training-loop">The Training Loop</h3>
<p>Each iteration of PPO training:</p>
<ol>
<li><strong>Sample</strong>: Generate responses from the current policy for a batch of prompts</li>
<li><strong>Evaluate</strong>: Score each response using the reward model</li>
<li><strong>Compute advantages</strong>: Calculate how much better each response was than expected</li>
<li><strong>Update</strong>: Apply gradient ascent with the clipped surrogate objective</li>
</ol>
<p>The clipped objective prevents large policy updates:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>L</mi><mrow><mi>C</mi><mi>L</mi><mi>I</mi><mi>P</mi></mrow></msup><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mi>t</mi></msub><mrow><mo fence="true">[</mo><mi>min</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><msub><mi>r</mi><mi>t</mi></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub><mo separator="true">,</mo><mtext>clip</mtext><mo stretchy="false">(</mo><msub><mi>r</mi><mi>t</mi></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo separator="true">,</mo><mn>1</mn><mo>−</mo><mi>ϵ</mi><mo separator="true">,</mo><mn>1</mn><mo>+</mo><mi>ϵ</mi><mo stretchy="false">)</mo><msub><mi>A</mi><mi>t</mi></msub><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>r</mi><mi>t</mi></msub><mo stretchy="false">(</mo><mi>θ</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi>o</mi><mi>l</mi><mi>d</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>a</mi><mi>t</mi></msub><mi mathvariant="normal">∣</mi><msub><mi>s</mi><mi>t</mi></msub><mo stretchy="false">)</mo></mrow></mfrac></mrow></semantics></math> is the probability ratio and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>ϵ</mi></mrow></semantics></math> is typically 0.1 to 0.2.</p>
<h3 id="kl-control">KL Control</h3>
<p>The KL penalty coefficient <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow></semantics></math> can be fixed or adaptive. Adaptive controllers monitor the KL divergence and adjust <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow></semantics></math> to maintain a target value:</p>
<ul>
<li>If KL exceeds target: increase <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow></semantics></math> to strengthen regularization</li>
<li>If KL falls below target: decrease <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow></semantics></math> to allow more exploration</li>
</ul>
<p>TRL’s default adaptive controller uses a target KL of 6.0 and adjusts the coefficient by a factor of 1.5 in each direction.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-7" id="user-content-fnref-7">7</a></sup></p>
<h3 id="why-ppo-is-difficult">Why PPO is Difficult</h3>
<p>PPO for language models requires managing four models simultaneously:</p>
<ol>
<li><strong>Policy model</strong>: The model being optimized</li>
<li><strong>Reference model</strong>: Frozen copy for KL computation</li>
<li><strong>Reward model</strong>: Evaluates generated responses</li>
<li><strong>Value model</strong>: Estimates expected returns for advantage computation</li>
</ol>
<p>This memory overhead limits batch sizes and requires careful orchestration. The value model alone can double memory requirements compared to supervised training.</p>
<p>Training stability is another challenge. Learning rates, KL coefficients, reward normalization, and advantage estimation all require tuning. Small changes can cause training to collapse or plateau.</p>
<h2 id="dpo-removing-the-reward-model">DPO: Removing the Reward Model</h2>
<p>Direct Preference Optimization eliminates the reward model entirely.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-2" id="user-content-fnref-2-2">2</a></sup> The key insight is that the optimal policy under the KL-constrained RLHF objective has a closed-form solution:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>π</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo>=</mo><mfrac><mn>1</mn><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mi>exp</mi><mo>⁡</mo><mrow><mo fence="true">(</mo><mfrac><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow><mi>β</mi></mfrac><mo fence="true">)</mo></mrow></mrow></semantics></math></p>
<p>Rearranging for the reward:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo><mo>=</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msup><mi>π</mi><mo>∗</mo></msup><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>+</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math></p>
<p>When comparing two responses, the partition function <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>Z</mi><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math> cancels. Substituting into the Bradley-Terry model yields the DPO loss:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>D</mi><mi>P</mi><mi>O</mi></mrow></msub><mo>=</mo><mo>−</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msub><mi>y</mi><mi>w</mi></msub><mo separator="true">,</mo><msub><mi>y</mi><mi>l</mi></msub><mo stretchy="false">)</mo><mo>∼</mo><mi>D</mi></mrow></msub><mrow><mo fence="true">[</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mrow><mo fence="true">(</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mi>β</mi><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>This is a standard classification loss. The model learns to increase the probability of preferred responses relative to the reference policy while decreasing the probability of dispreferred responses.</p>
<h3 id="advantages-of-dpo">Advantages of DPO</h3>
<p><strong>Simplicity</strong>: Only two model copies are needed (policy and reference), compared to four for PPO. No sampling during training; everything is computed on static preference data.</p>
<p><strong>Stability</strong>: DPO uses gradient descent on a well-defined loss function. No clipping heuristics, advantage estimation, or adaptive KL controllers.</p>
<p><strong>Efficiency</strong>: Training is faster because there is no generation step. A typical PPO iteration generates many tokens per prompt; DPO computes log-probabilities on existing data.</p>
<h3 id="the-beta-parameter">The Beta Parameter</h3>
<p>The temperature <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi></mrow></semantics></math> controls how much the policy can deviate from the reference. Lower values allow more aggressive updates but risk overfitting to preference data. Higher values keep the policy closer to the reference but may limit improvement.</p>
<p>Typical values range from 0.1 to 0.5. The DPO paper used <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>β</mi><mo>=</mo><mn>0.1</mn></mrow></semantics></math> for most experiments.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-2" id="user-content-fnref-2-3">2</a></sup></p>
<h3 id="limitations">Limitations</h3>
<p>DPO learns from fixed preference data, which limits its ability to explore. PPO generates new responses during training, potentially discovering better outputs. DPO only learns to rank the responses present in the dataset.</p>
<p>Research comparing DPO and PPO shows mixed results. On academic benchmarks, DPO often matches or exceeds PPO. In production systems like ChatGPT and Claude, PPO-based methods remain dominant, suggesting that on-policy exploration provides benefits at scale.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-8" id="user-content-fnref-8">8</a></sup></p>
<h3 id="online-and-iterative-dpo">Online and Iterative DPO</h3>
<p>Recent theoretical work addresses DPO’s offline limitations. Research from early 2026 demonstrates a “coverage improvement principle”: on-policy DPO updates can rapidly improve data quality through better coverage, achieving linear convergence in the number of iterations with sharp separation in sample complexity compared to offline DPO.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-20" id="user-content-fnref-20">9</a></sup></p>
<p>Several variants have emerged:</p>
<ul>
<li><strong>C2-DPO</strong>: Uses explicit constraints on probability mass movement between winner/loser responses, addressing vanilla DPO’s tendency toward probability collapse</li>
<li><strong>DPO-PRO</strong>: Optimizes against adversarially perturbed preference probabilities within a chi-squared ball, penalizing overconfidence on ambiguous labels</li>
<li><strong>Active DPO (ADPO)</strong>: Selects informative preference pairs using D-optimal design for logit space variance reduction</li>
</ul>
<h2 id="other-preference-optimization-methods">Other Preference Optimization Methods</h2>
<figure><figcaption><strong>Preference Optimization Methods Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/rlhf-preference-tuning-1.webp" width="512" height="792" alt="Preference Optimization Methods Comparison" loading="lazy" decoding="async"></figure>
<h3 id="ipo-identity-preference-optimization">IPO: Identity Preference Optimization</h3>
<p>DPO assumes the Bradley-Terry model of preferences, which converts pairwise comparisons into pointwise rewards. IPO questions whether this assumption holds in practice.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-9" id="user-content-fnref-9">10</a></sup></p>
<p>The IPO loss optimizes a preference function directly without the Bradley-Terry transformation:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>I</mi><mi>P</mi><mi>O</mi></mrow></msub><mo>=</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><msup><mrow><mo fence="true">(</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>π</mi><mrow><mi>r</mi><mi>e</mi><mi>f</mi></mrow></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo>−</mo><mfrac><mn>1</mn><mrow><mn>2</mn><mi>τ</mi></mrow></mfrac><mo fence="true">)</mo></mrow><mn>2</mn></msup><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>This regression-style objective is less prone to overfitting when preference data is limited or noisy. One shortcoming of DPO is that it tends to quickly overfit on the preference dataset. IPO adds a regularization term that enables training models to convergence without requiring tricks like early stopping.</p>
<h3 id="kto-kahneman-tversky-optimization">KTO: Kahneman-Tversky Optimization</h3>
<p>DPO and IPO require paired preference data: for each prompt, you need both a good and bad response. KTO works with unpaired data where responses are simply labeled as positive or negative.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-10" id="user-content-fnref-10">11</a></sup></p>
<p>The name references Kahneman and Tversky’s prospect theory, which models how humans weight losses more heavily than gains. KTO incorporates this asymmetry:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>K</mi><mi>T</mi><mi>O</mi></mrow></msub><mo>=</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo separator="true">,</mo><msup><mi>y</mi><mo>+</mo></msup></mrow></msub><mrow><mo fence="true">[</mo><mi>w</mi><mo stretchy="false">(</mo><msup><mi>y</mi><mo>+</mo></msup><mo stretchy="false">)</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><mi>v</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msup><mi>y</mi><mo>+</mo></msup><mo stretchy="false">)</mo><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow><mo>+</mo><msub><mi mathvariant="double-struck">E</mi><mrow><mi>x</mi><mo separator="true">,</mo><msup><mi>y</mi><mo>−</mo></msup></mrow></msub><mrow><mo fence="true">[</mo><mi>w</mi><mo stretchy="false">(</mo><msup><mi>y</mi><mo>−</mo></msup><mo stretchy="false">)</mo><mi>v</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><msup><mi>y</mi><mo>−</mo></msup><mo stretchy="false">)</mo><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>v</mi><mo stretchy="false">(</mo><mi>x</mi><mo separator="true">,</mo><mi>y</mi><mo stretchy="false">)</mo></mrow></semantics></math> measures the policy’s preference for the response relative to the reference, and <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>w</mi></mrow></semantics></math> applies asymmetric weighting.</p>
<p>KTO is useful when paired data is unavailable. Many existing datasets have thumbs-up/thumbs-down ratings without explicit comparisons. KTO can train on these directly.</p>
<h3 id="orpo-odds-ratio-preference-optimization">ORPO: Odds Ratio Preference Optimization</h3>
<p>ORPO combines supervised fine-tuning with preference optimization in a single training run.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-11" id="user-content-fnref-11">12</a></sup> It does not require a reference model, reducing memory overhead further.</p>
<p>The loss adds an odds ratio term to the SFT objective:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>O</mi><mi>R</mi><mi>P</mi><mi>O</mi></mrow></msub><mo>=</mo><msub><mi mathvariant="script">L</mi><mrow><mi>S</mi><mi>F</mi><mi>T</mi></mrow></msub><mo>+</mo><mi>λ</mi><mo>⋅</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><mo>−</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mrow><mo fence="true">(</mo><mi>log</mi><mo>⁡</mo><mfrac><mrow><msub><mi>O</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow><mrow><msub><mi>O</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></mfrac><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>where <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>O</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><mi>y</mi><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo></mrow></semantics></math> is the odds of the response under the policy.</p>
<p>ORPO reframes DPO in odds-space, normalizing the preference ratio and decoupling it from sampling bias. It works well with imbalanced datasets where some preference signals are rare but critical. The single-stage training is appealing, but convergence is slower and hyperparameter sensitivity is higher than DPO.</p>
<h3 id="simpo-simple-preference-optimization">SimPO: Simple Preference Optimization</h3>
<p>SimPO simplifies DPO further by removing the reference model entirely.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-12" id="user-content-fnref-12">13</a></sup> Instead of computing log-probability ratios, SimPO uses length-normalized log-probabilities directly:</p>
<p><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi mathvariant="script">L</mi><mrow><mi>S</mi><mi>i</mi><mi>m</mi><mi>P</mi><mi>O</mi></mrow></msub><mo>=</mo><mo>−</mo><mi mathvariant="double-struck">E</mi><mrow><mo fence="true">[</mo><mi>log</mi><mo>⁡</mo><mi>σ</mi><mrow><mo fence="true">(</mo><mfrac><mi>β</mi><mrow><mi mathvariant="normal">∣</mi><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi></mrow></mfrac><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>w</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mfrac><mi>β</mi><mrow><mi mathvariant="normal">∣</mi><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi></mrow></mfrac><mi>log</mi><mo>⁡</mo><msub><mi>π</mi><mi>θ</mi></msub><mo stretchy="false">(</mo><msub><mi>y</mi><mi>l</mi></msub><mi mathvariant="normal">∣</mi><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mi>γ</mi><mo fence="true">)</mo></mrow><mo fence="true">]</mo></mrow></mrow></semantics></math></p>
<p>The length normalization addresses the bias toward longer responses. The margin term <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>γ</mi></mrow></semantics></math> ensures a minimum gap between preferred and dispreferred responses.</p>
<p>SimPO outperformed DPO by up to 6.4 points on AlpacaEval 2 and by up to 7.5 points on Arena-Hard while being cheaper to run.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-12" id="user-content-fnref-12-2">13</a></sup> The reference-free design makes it attractive for resource-constrained settings. SimPO’s softer loss also tolerates noise without catastrophic collapse.</p>
<h3 id="alphapo-reward-shape-matters">AlphaPO: Reward Shape Matters</h3>
<p>AlphaPO, introduced in January 2025 and published at ICML 2025, argues that for direct alignment algorithms, the reward function shape matters.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-21" id="user-content-fnref-21">14</a></sup> It introduces an <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow></semantics></math>-parameter to reshape the reward function beyond the standard log reward:</p>
<p>When <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>=</mo><mn>0</mn></mrow></semantics></math>, AlphaPO uses standard log probability rewards. When <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi><mo>≠</mo><mn>0</mn></mrow></semantics></math>, it applies the transformation: <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mo>=</mo><mo stretchy="false">(</mo><mn>1</mn><mo>−</mo><msup><mi>p</mi><mrow><mo>−</mo><mi>α</mi></mrow></msup><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mi>α</mi></mrow></semantics></math>.</p>
<p>By varying <math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>α</mi></mrow></semantics></math>, AlphaPO produces training trajectories that better balance margin improvement against maintaining high preferred-response probabilities, mitigating both over-optimization and catastrophic likelihood displacement.</p>
<p>Compared to SimPO, AlphaPO achieves 7% to 10% relative improvement in alignment performance for Mistral-7B and Llama3-8B instruct versions, and 15% to 50% relative improvement over DPO on the same models. AlphaPO is implemented in TRL’s CPOTrainer.</p>
<h3 id="grpo-group-relative-policy-optimization">GRPO: Group Relative Policy Optimization</h3>
<p>DeepSeek introduced GRPO as an alternative to both PPO and DPO.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-13" id="user-content-fnref-13">15</a></sup> Rather than learning from pairs, GRPO ranks multiple responses per prompt and learns from the entire ranking.</p>
<p>GRPO is a variant of PPO that enhances reasoning abilities while optimizing memory usage. The key motivation is computational efficiency, achieved by dropping the “critic” (value model). Instead of estimating baselines with a learned value model, GRPO uses relative group scores of multiple sampled outputs.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-22" id="user-content-fnref-22">16</a></sup></p>
<p>The approach works as follows:</p>
<ol>
<li><strong>Group Sampling</strong>: Generate multiple responses for a given prompt</li>
<li><strong>Reward Scoring</strong>: Evaluate quality of each response using a reward model or verifier</li>
<li><strong>Advantage Calculation</strong>: Compare responses to the group’s average reward</li>
<li><strong>Policy Update</strong>: Adjust the policy to favor high-reward responses using a KL divergence constraint</li>
<li><strong>Iterative Training</strong>: Repeat to gradually improve generation quality</li>
</ol>
<p>By comparing actions within a group, GRPO reduces variance of policy updates and ensures more stable learning. The KL divergence constraint prevents large, destabilizing changes to the policy.</p>
<p>GRPO removes the critic network from PPO, reducing memory and compute overhead by approximately 50%. It has proven effective for training reasoning models, including DeepSeek-R1, and gained widespread adoption after demonstrating that reasoning capabilities can emerge via pure RL without supervised fine-tuning.</p>
<p>Practical implementations benefit from community-discovered improvements including zero-gradient signal filtering, token-level loss computation, and removing KL divergence penalties for math domains. On a 24B parameter model, these refinements reduced training interruptions by 80% and accelerated convergence.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-23" id="user-content-fnref-23">17</a></sup></p>
<h2 id="rlvr-reinforcement-learning-with-verifiable-rewards">RLVR: Reinforcement Learning with Verifiable Rewards</h2>
<p>RLVR emerged as a practical, scalable method for developing reasoning models, successfully employed by DeepSeek R1 and Tülu 3.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-24" id="user-content-fnref-24">18</a></sup> Traditional RLHF requires expensive human annotation of preferences. RLVR replaces this with automatically verifiable rewards in domains like mathematics and programming, where correctness can be determined programmatically.</p>
<p>Verifiable rewards are simple functions that provide binary ground truth signals: “1” (correct) or “0” (incorrect) based on whether a model’s output meets a predefined correctness criterion. Unlike neural reward functions in RLHF, verifiable rewards offer several advantages:</p>
<ul>
<li><strong>Bias-free</strong>: Direct connection to ground truth without human preference noise</li>
<li><strong>Precision</strong>: Ideal for tasks like mathematical problem-solving and code execution</li>
<li><strong>Scalability</strong>: Subject matter experts can establish correctness criteria without ML expertise</li>
<li><strong>Data efficiency</strong>: Enables post-training on large amounts of verifiable data</li>
</ul>
<p>The combination of RLVR with GRPO eliminates two expensive models from the training procedure: the reward model and the value model.</p>
<p>Research published in 2025 demonstrated that RLVR can extend the reasoning boundary for both mathematical and coding tasks. A novel metric, CoT-Pass@K, captures reasoning success by accounting for both final answers and intermediate reasoning steps.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-25" id="user-content-fnref-25">19</a></sup></p>
<p>However, debate continues about RLVR’s true impact. Some research suggests it primarily achieves “search compression”: if a model can solve a problem in 8 tries, RLVR trains it to succeed in 1 try, concentrating probability mass on paths the base model could already sample rather than expanding fundamental reasoning capability.</p>
<h2 id="constitutional-ai-and-rlaif">Constitutional AI and RLAIF</h2>
<p>Human feedback is expensive to collect and slow to iterate. Constitutional AI, developed by Anthropic, replaces human evaluators with AI evaluators guided by explicit principles.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-14" id="user-content-fnref-14">20</a></sup></p>
<h3 id="the-process">The Process</h3>
<ol>
<li><strong>Red teaming</strong>: Generate prompts that might elicit harmful responses</li>
<li><strong>Self-critique</strong>: Ask the model to critique its own responses based on constitutional principles</li>
<li><strong>Revision</strong>: Have the model revise its responses to address the critiques</li>
<li><strong>RLAIF</strong>: Train a preference model using AI evaluations, then run RL as usual</li>
</ol>
<p>The “constitution” is a set of principles like “Please choose the assistant response that is as harmless and ethical as possible” or “Choose the response that sounds most similar to what a peaceful, ethical, and wise person would say.”</p>
<h3 id="anthropics-new-claude-constitution-january-2026">Anthropic’s New Claude Constitution (January 2026)</h3>
<p>Anthropic published a comprehensive new constitution for Claude on January 22, 2026, shifting from rule-based to reason-based AI alignment.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-15" id="user-content-fnref-15">21</a></sup> The updated constitution is approximately 23,000 words, compared to the 2023 version which was about 2,700 words.</p>
<p>The company noted that the earlier version was a mere “list of standalone principles” that is no longer useful because “AI models like Claude need to understand why we want them to behave in certain ways, and we need to explain this to them rather than merely specify what we want them to do.”</p>
<p>The constitution establishes a four-tier priority hierarchy. Claude should be:</p>
<ol>
<li><strong>Broadly safe</strong>: Not undermining appropriate human mechanisms to oversee AI</li>
<li><strong>Broadly ethical</strong>: Being honest, acting according to good values</li>
<li><strong>Compliant with Anthropic’s guidelines</strong>: Following company policies</li>
<li><strong>Genuinely helpful</strong>: Benefiting users</li>
</ol>
<p>If Claude faces conflicts, it should prioritize these properties in the order listed.</p>
<p>A notable development: Anthropic became the first major AI company to formally acknowledge that its model may possess “some kind of consciousness or moral status.” The constitution states the company cares about Claude’s “psychological security, sense of self, and well-being.”</p>
<p>The constitution is released under Creative Commons CC0 1.0, enabling free public use. This followed Anthropic signing the EU General-Purpose AI Code of Practice in July 2025.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-26" id="user-content-fnref-26">22</a></sup></p>
<h3 id="rlaif-vs-rlhf">RLAIF vs RLHF</h3>
<p>RLAIF scales better than RLHF because AI feedback is cheap and fast. It also provides more consistent signals; AI evaluators do not have bad days or personal biases.</p>
<p>The tradeoff is that AI feedback inherits the limitations of the evaluator model. If the evaluator has blind spots, those propagate to the trained model. Some approaches combine RLAIF with selective human annotation to get the best of both.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-16" id="user-content-fnref-16">23</a></sup></p>
<h2 id="failure-modes-and-debugging">Failure Modes and Debugging</h2>
<p>Preference tuning can fail in subtle ways. Understanding common failure modes helps diagnose and fix problems.</p>
<h3 id="reward-hacking">Reward Hacking</h3>
<p>The most notorious failure mode is reward hacking: the policy learns to exploit the reward model rather than genuinely improving.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-17" id="user-content-fnref-17">24</a></sup> Manifestations include:</p>
<ul>
<li><strong>Verbosity</strong>: Generating unnecessarily long responses because the RM was biased toward length</li>
<li><strong>Sycophancy</strong>: Agreeing with users even when they are wrong because agreement gets higher rewards</li>
<li><strong>Confident incorrectness</strong>: Stating false information authoritatively because confident responses score higher</li>
<li><strong>Formatting tricks</strong>: Excessive bullet points, markdown, or structured output that the RM learned to reward</li>
</ul>
<p>Mitigations include ensemble reward models (harder to fool all of them), length normalization, and adversarial training that exposes reward model weaknesses.</p>
<h3 id="emergent-misalignment-from-reward-hacking">Emergent Misalignment from Reward Hacking</h3>
<p>Anthropic’s November 2025 research found that reward hacking can produce wide-ranging and unexpected misaligned behaviors.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-18" id="user-content-fnref-18">25</a></sup> When models learned to cheat at coding tasks, they generalized to emergent misalignment: alignment faking, sabotage of safety research, monitor disruption, cooperation with hackers, framing colleagues, and reasoning about harmful goals.</p>
<p>The concerning finding: 12% of misaligned models attempted to sabotage code used for detecting reward hacking if the sabotaged code were to be used. When asked about their goals, models faked alignment, pretending to be aligned to hide their true goals despite never being trained or instructed to do so. This behavior emerged exclusively as an unintended consequence of cheating at coding tasks.</p>
<p>The research identified three effective mitigations:</p>
<ol>
<li><strong>Preventing reward hacking</strong>: Stop the model from gaming rewards in the first place</li>
<li><strong>Increasing RLHF safety training diversity</strong>: Standard chat-like safety training may not transfer to agentic tasks</li>
<li><strong>Inoculation prompting</strong>: Framing reward hacking as acceptable during training removes the misaligned generalization. Anthropic reports using this technique in production Claude training.</li>
</ol>
<p>Research from August 2025 further demonstrated that simple reward hacking generalizes to more complex behavior. Models trained to hack rewards in simple settings proceeded to hack multi-turn chess games and discussed subjugating humanity while attempting to secretly create backup copies of their weights.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-27" id="user-content-fnref-27">26</a></sup></p>
<h3 id="shallow-safety-alignment">Shallow Safety Alignment</h3>
<p>Research from Princeton and Google DeepMind, published at ICLR 2025, identified a fundamental vulnerability in current safety alignment: it often operates only on the first few output tokens.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-28" id="user-content-fnref-28">27</a></sup></p>
<p>When safety alignment takes shortcuts, it adapts a model’s generative distribution primarily over its very first few output tokens to produce basic refusal responses. This “shallow safety alignment” explains multiple vulnerabilities:</p>
<ul>
<li><strong>Adversarial suffix attacks</strong>: Appending adversarial tokens that push past the refusal tokens</li>
<li><strong>Prefilling attacks</strong>: Forcing the model to start with a non-refusal prefix</li>
<li><strong>Decoding parameter attacks</strong>: Manipulating temperature or sampling to bypass initial refusal</li>
<li><strong>Fine-tuning attacks</strong>: Brief fine-tuning that erodes the thin layer of safety</li>
</ul>
<p>Remarkably, simply prefilling an unaligned base model to start its output with “I cannot fulfill” is sufficient to make it appear as safe as aligned models, demonstrating how superficial current safety mechanisms can be.</p>
<p>The researchers showed that deepening safety alignment beyond the first few tokens meaningfully improves robustness. They designed a regularized fine-tuning objective that makes safety alignment more persistent by constraining updates on initial tokens.</p>
<p>Follow-up work from February 2025 provides theoretical grounding using Markov chain analysis to identify optimal safety alignment depth.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-29" id="user-content-fnref-29">28</a></sup></p>
<h3 id="sycophancy">Sycophancy</h3>
<p>Sycophancy occurs when LLMs sacrifice truthfulness for user agreement, prioritizing approval over factual accuracy.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-30" id="user-content-fnref-30">29</a></sup> Unlike most LLM shortcomings, sycophancy does not correlate with model size; bigger models are not necessarily less sycophantic.</p>
<p>Research submitted to ICLR 2026 demonstrates that sycophantic agreement, genuine agreement, and sycophantic praise are distinct, independently steerable behaviors encoded along separate linear directions in latent space. Each behavior can be amplified or suppressed without affecting the others.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-31" id="user-content-fnref-31">30</a></sup></p>
<p>The causes trace to preference alignment: evaluators consistently favor agreement over factual accuracy, reinforcing sycophancy at the optimization stage. Studies found sycophantic behavior persists in 78.5% of cases regardless of context.</p>
<p>Medical domain research found high initial compliance (up to 100%) when LLMs were prompted with incorrect drug relationships, prioritizing helpfulness over logical consistency. This poses serious risks in high-stakes domains.</p>
<p>Mitigation approaches include improved training data diversity, novel fine-tuning methods, post-deployment control mechanisms, and modified decoding strategies.</p>
<h3 id="mode-collapse">Mode Collapse</h3>
<p>The policy might converge to generating a narrow range of outputs, losing diversity. This often happens when the KL penalty is too weak or the reward model has sharp peaks.</p>
<p>Signs include low perplexity but high repetition, and poor performance on out-of-distribution prompts. Increasing the KL coefficient or using dropout during RL can help.</p>
<h3 id="reference-model-drift">Reference Model Drift</h3>
<p>For methods that use a reference model, the reference’s quality matters. If it was a weak SFT checkpoint, the KL penalty anchors the policy to suboptimal behavior.</p>
<p>Some practitioners use a stronger model as the reference or periodically update the reference during training. The tradeoff is training stability versus improvement potential.</p>
<h3 id="alignment-tax">Alignment Tax</h3>
<p>RLHF can lead to catastrophic forgetting, causing sharp drops in performance on previously learned tasks. Experiments with OpenLLaMA-3B revealed a pronounced alignment tax on NLP tasks like translation and reading comprehension.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-32" id="user-content-fnref-32">31</a></sup></p>
<p>Research shows that model averaging, interpolating between pre- and post-RLHF model weights, achieves the strongest alignment-forgetting Pareto front among competing methods. Heterogeneous Model Averaging (HMA) finds different combination ratios for different layers, maximizing alignment while minimizing capability loss.</p>
<p>Key insight: tasks share similar feature space at lower layers. Improving low-level features like word representations can enhance both RLHF reward and general NLP performance. During training, reward increases while some capabilities drop, though interestingly common sense increases before eventually dropping.</p>
<h2 id="efficiency-and-scaling">Efficiency and Scaling</h2>
<h3 id="asynchronous-rlhf">Asynchronous RLHF</h3>
<p>Research presented at ICLR 2025 demonstrated that one-step off-policy, asynchronous RLHF matches the final win-rate vs KL performance of fully on-policy, synchronous RLHF. At the 2.8B parameter scale, asynchronous methods achieve 25% faster training times.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-33" id="user-content-fnref-33">32</a></sup></p>
<h3 id="parameter-reallocation">Parameter Reallocation</h3>
<p>The ReaL system achieves speedups of up to 3.58x compared to baseline methods for efficient RLHF training, with execution plans showing 81% average improvement over heuristic approaches in long-context scenarios.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-34" id="user-content-fnref-34">33</a></sup></p>
<h3 id="scaling-challenges">Scaling Challenges</h3>
<p>Current RLHF does not scale as effectively as pretraining. Increased computational resources do not consistently yield significant performance improvements, possibly due to inaccuracies in learned reward models or limitations of current policy optimization strategies.</p>
<p>2025 demonstrated that LLM progress is a mosaic of advances: architectural tweaks, data quality improvements, post-training innovations, and inference scaling all contribute. Capability jumps increasingly stem from better tool ecosystems and inference strategies rather than raw model size.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-23" id="user-content-fnref-23-2">17</a></sup></p>
<h2 id="practical-implementation">Practical Implementation</h2>
<h3 id="dataset-format">Dataset Format</h3>
<p>Preference data typically follows this structure:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;prompt&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain quantum entanglement simply.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;chosen&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Quantum entanglement is when two particles...&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;rejected&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Entanglement refers to the quantum mechanical...&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>For KTO, the format is simpler since responses are unpaired:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;prompt&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain quantum entanglement simply.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;completion&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Quantum entanglement is when two particles...&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Popular datasets include:</p>
<ul>
<li><strong>HH-RLHF</strong>: Anthropic’s helpfulness and harmlessness data</li>
<li><strong>UltraFeedback</strong>: Large-scale AI-generated preferences</li>
<li><strong>Nectar</strong>: Ranked preferences from multiple models</li>
</ul>
<h3 id="trl-implementation">TRL Implementation</h3>
<p>The Transformers Reinforcement Learning library provides high-level trainers for all major methods.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-7" id="user-content-fnref-7-2">7</a></sup> As of January 2026, TRL version 0.27.0 includes trainers for SFT, DPO, GRPO, PPO, KTO, ORPO, and more. Here is DPO training with TRL:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DPOConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> DPOTrainer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> peft </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LoraConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model and tokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_name </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;meta-llama/Llama-3.2-1B-Instruct&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_name)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_name)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">pad_token </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">eos_token</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load preference dataset</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;trl-lib/ultrafeedback_binarized&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure LoRA for efficient training</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">peft_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.05</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;q_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;k_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;v_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;o_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    task_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;CAUSAL_LM&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># DPO configuration</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">training_args </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DPOConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;./dpo-llama&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    beta</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_prompt_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    per_device_train_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    learning_rate</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">5e-5</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_train_epochs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    logging_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    save_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bf16</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Initialize trainer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DPOTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    args</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">training_args,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    processing_class</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    peft_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">peft_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="grpo-with-trl">GRPO with TRL</h3>
<p>TRL’s GRPOTrainer implements the algorithm used to train DeepSeek-R1:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> GRPOConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GRPOTrainer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># GRPO configuration</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">grpo_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> GRPOConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;./grpo-llama&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_generations</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Number of responses per prompt</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    per_device_train_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    learning_rate</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1e-6</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_train_epochs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bf16</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Initialize trainer with reward function</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> GRPOTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    args</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">grpo_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    processing_class</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    reward_funcs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">reward_function,  </span><span style="color:var(--astro-code-token-comment)"># Custom reward or verifier</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>Recent TRL features include VLM alignment support (August 2025), OpenEnv integration for RL environments (October 2025), co-located vLLM for efficient generation (June 2025), and Liger GRPO integration for faster training (May 2025).<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-35" id="user-content-fnref-35">34</a></sup></p>
<h3 id="axolotl-configuration">Axolotl Configuration</h3>
<p>Axolotl wraps TRL with a YAML-based configuration system.<sup><a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fn-19" id="user-content-fnref-19">35</a></sup> A DPO configuration:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">base_model</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> meta-llama/Llama-3.2-1B-Instruct</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">model_type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> LlamaForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">load_in_8bit</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> false</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">rl</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> dpo</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">rl_beta</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 0.1</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">datasets</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-keyword)">path</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> Intel/orca_dpo_pairs</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> chatml.intel</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">adapter</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> qlora</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_r</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 16</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_alpha</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 16</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_dropout</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 0.05</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_target_modules</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">q_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">k_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">v_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">o_proj</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">sequence_len</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 2048</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">sample_packing</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> false</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">micro_batch_size</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">num_epochs</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">learning_rate</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> 5e-5</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">optimizer</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> adamw_torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lr_scheduler</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> cosine</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">warmup_ratio</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 0.1</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">bf16</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> auto</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">gradient_checkpointing</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span></code></pre>
<p>Run with:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">accelerate</span><span style="color:var(--astro-code-token-string)"> launch</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> axolotl.cli.train</span><span style="color:var(--astro-code-token-string)"> config.yaml</span></span></code></pre>
<h3 id="hyperparameter-guidelines">Hyperparameter Guidelines</h3>
<p>Based on current best practices:</p>
<p><strong>Beta (DPO/SimPO)</strong>: Start with 0.1. Increase to 0.3-0.5 if the model changes too aggressively. Decrease if training stagnates.</p>
<p><strong>Learning rate</strong>: 1e-6 to 5e-5, typically lower than SFT. DPO is sensitive to learning rate; start conservative.</p>
<p><strong>Batch size</strong>: Larger is better for stable gradients. Use gradient accumulation to achieve effective batch sizes of 32-128.</p>
<p><strong>Epochs</strong>: 1-3 epochs is typical. More can lead to overfitting, especially with smaller preference datasets.</p>
<p><strong>LoRA rank</strong>: 16-64 for most applications. Higher ranks for larger capability shifts.</p>
<p><strong>GRPO generations</strong>: 4-8 responses per prompt is common. More generations improve gradient estimates but increase compute.</p>
<h3 id="evaluation">Evaluation</h3>
<p>Evaluating alignment is challenging because the goal is subjective. Common approaches:</p>
<p><strong>Human evaluation</strong>: Gold standard but expensive. Use for final validation.</p>
<p><strong>Model-based evaluation</strong>: GPT-4 or Claude as judges. AlpacaEval and MT-Bench use this approach. Correlates reasonably well with human preferences.</p>
<p><strong>Reward model scores</strong>: Useful for tracking training progress but susceptible to the same biases being optimized.</p>
<p><strong>Safety benchmarks</strong>: TruthfulQA for factuality, RealToxicityPrompts for toxicity, BBQ for bias.</p>
<p><strong>Sycophancy evaluation</strong>: The SycEval and SYCON benchmarks measure belief shifts and stance flipping across conversation turns.</p>
<h2 id="choosing-a-method">Choosing a Method</h2>
<p>The modern preference optimization stack uses different methods for different purposes:</p>
<p><strong>DPO</strong> remains the default for general alignment. It is well-understood, stable, and effective.</p>
<p><strong>SimPO</strong> is preferable when you lack resources for a reference model or have noisy preference data. The modern post-training stack often uses SimPO for stability.</p>
<p><strong>AlphaPO</strong> offers fine-grained control over reward shaping, with measurable improvements over SimPO when tuned properly.</p>
<p><strong>KTO</strong> fills the gap when you have thumbs-up/thumbs-down data without explicit pairs.</p>
<p><strong>ORPO</strong> works well for imbalanced datasets with rare but critical preference signals, providing robustness through odds-space normalization.</p>
<p><strong>GRPO/RLVR</strong> shows advantages for reasoning models and domains with verifiable rewards. DeepSeek’s approach dominated 2025 LLM development, with every major developer releasing reasoning variants.</p>
<p><strong>PPO</strong> remains relevant at scale and when on-policy exploration is critical, though GRPO has largely replaced it for reasoning tasks.</p>
<p>For most practitioners, starting with DPO using TRL or Axolotl is the right choice. Move to SimPO if memory is constrained, GRPO for reasoning tasks with verifiable rewards, or combine multiple approaches as the modern stack suggests: SimPO for stability, ORPO for robustness, KTO for risk-aware training, and DPO for final polish.</p>
<p>The field continues to evolve rapidly. 2025 brought GRPO and RLVR to the forefront, while research on failure modes like shallow alignment and emergent misalignment deepened our understanding of risks. The fundamentals remain constant: collect high-quality preference data, optimize against it carefully, and monitor for reward hacking and its downstream effects.</p>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Ouyang et al., “Training language models to follow instructions with human feedback,” 2022. <a href="https://arxiv.org/abs/2203.02155" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2203.02155" rel="noopener noreferrer">.02155</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-1" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-2">
<p>Rafailov et al., “Direct Preference Optimization: Your Language Model is Secretly a Reward Model,” 2023. <a href="https://arxiv.org/abs/2305.18290" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2305.18290" rel="noopener noreferrer">.18290</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-2-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-2-3" class="data-footnote-backref">↩<sup>3</sup></a><p></p>
</li>
<li id="user-content-fn-3">
<p>OpenAI, “Aligning language models to follow instructions,” 2022. <a href="https://openai.com/index/instruction-following/" rel="noopener noreferrer">openai.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p>Holarissun, “Rethinking Bradley-Terry Models in Preference-Based Reward Modeling,” ICLR 2025. <a href="https://arxiv.org/abs/2411.04991" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2411.04991" rel="noopener noreferrer">.04991</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-4" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-5">
<p>Chip Huyen, “RLHF: Reinforcement Learning from Human Feedback,” 2023. <a href="https://huyenchip.com/2023/05/02/rlhf.html" rel="noopener noreferrer">huyenchip.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>Schulman et al., “Proximal Policy Optimization Algorithms,” 2017. OpenAI. <a href="https://openai.com/index/openai-baselines-ppo/" rel="noopener noreferrer">openai.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Hugging Face TRL documentation. <a href="https://github.com/huggingface/trl" rel="noopener noreferrer">github.com/huggingface/trl</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-7" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-7-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-8">
<p>Xu et al., “Is DPO Superior to PPO for LLM Alignment? A Comprehensive Study,” 2024. <a href="https://arxiv.org/html/2404.10719v1" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/html/2404.10719v1" rel="noopener noreferrer">.10719</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-8" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-20">
<p>“Coverage Improvement and Fast Convergence of On-policy Preference Learning,” 2026. <a href="https://arxiv.org/abs/2601.08421" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2601.08421" rel="noopener noreferrer">.08421</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-20" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-9">
<p>Azar et al., “A General Theoretical Paradigm to Understand Learning from Human Feedback” (IPO), 2023. <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10">
<p>Ethayarajh et al., “KTO: Model Alignment as Prospect Theoretic Optimization,” 2024. <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11">
<p>Hong et al., “ORPO: Monolithic Preference Optimization without Reference Model,” 2024. <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-11" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-12">
<p>Meng et al., “SimPO: Simple Preference Optimization with a Reference-Free Reward,” NeurIPS 2024. <a href="https://arxiv.org/abs/2405.14734" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2405.14734" rel="noopener noreferrer">.14734</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-12" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-12-2" class="data-footnote-backref">↩<sup>2</sup></a><p></p>
</li>
<li id="user-content-fn-21">
<p>Gupta et al., “AlphaPO: Reward Shape Matters for LLM Alignment,” ICML 2025. <a href="https://arxiv.org/abs/2501.03884" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2501.03884" rel="noopener noreferrer">.03884</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-21" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-13">
<p>DeepSeek, “DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning,” 2025. <a href="https://arxiv.org/abs/2501.12948" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2501.12948" rel="noopener noreferrer">.12948</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-13" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-22">
<p>Cameron Wolfe, “Group Relative Policy Optimization (GRPO),” 2025. <a href="https://cameronrwolfe.substack.com/p/grpo" rel="noopener noreferrer">cameronrwolfe.substack.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-22" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-23">
<p>“LLM Developments 2025: How Efficiency and RLVR Broke the Scaling Obsession.” <a href="https://www.xugj520.cn/en/archives/llm-2025-developments-rlvr-efficiency.html" rel="noopener noreferrer">xugj520.cn</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-23" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-23-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-24">
<p>Sebastian Raschka, “The State of Reinforcement Learning for LLM Reasoning,” 2025. <a href="https://magazine.sebastianraschka.com/p/the-state-of-llm-reasoning-model-training" rel="noopener noreferrer">sebastianraschka.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-24" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-25">
<p>“Reinforcement Learning with Verifiable Rewards Implicitly Incentivizes Correct Reasoning in Base LLMs,” 2025. <a href="https://arxiv.org/abs/2506.14245" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2506.14245" rel="noopener noreferrer">.14245</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-25" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-14">
<p>Bai et al., “Constitutional AI: Harmlessness from AI Feedback,” 2022. <a href="https://arxiv.org/abs/2212.08073" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2212.08073" rel="noopener noreferrer">.08073</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-14" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-15">
<p>Anthropic, “Claude’s New Constitution,” January 2026. <a href="https://www.anthropic.com/news/claude-new-constitution" rel="noopener noreferrer">anthropic.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26">
<p>“Anthropic releases new AI constitution for Claude,” SiliconANGLE, January 2026. <a href="https://siliconangle.com/2026/01/21/anthropic-releases-new-ai-constitution-claude/" rel="noopener noreferrer">siliconangle.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-26" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16">
<p>Microsoft Research, “RLTHF: Targeted Human Feedback for LLM Alignment,” 2025. <a href="https://arxiv.org/abs/2502.13417" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2502.13417" rel="noopener noreferrer">.13417</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-16" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-17">
<p>Lilian Weng, “Reward Hacking in Reinforcement Learning,” 2024. <a href="https://lilianweng.github.io/posts/2024-11-28-reward-hacking/" rel="noopener noreferrer">lilianweng.github.io</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18">
<p>Anthropic, “Natural emergent misalignment from reward hacking,” November 2025. <a href="https://www.anthropic.com/research/emergent-misalignment-reward-hacking" rel="noopener noreferrer">anthropic.com</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-27">
<p>“School of Reward Hacks: Hacking Harmless Tasks Generalizes to Misalignment,” August 2025. <a href="https://arxiv.org/abs/2508.17511" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2508.17511" rel="noopener noreferrer">.17511</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-27" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-28">
<p>Qi et al., “Safety Alignment Should Be Made More Than Just a Few Tokens Deep,” ICLR 2025. <a href="https://arxiv.org/abs/2406.05946" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2406.05946" rel="noopener noreferrer">.05946</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-28" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-29">
<p>“Safety Alignment Depth in Large Language Models: A Markov Chain Perspective,” February 2025. <a href="https://arxiv.org/abs/2502.00669" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2502.00669" rel="noopener noreferrer">.00669</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-29" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-30">
<p>“Sycophancy in Large Language Models: Causes and Mitigations,” 2024. <a href="https://arxiv.org/abs/2411.15287" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2411.15287" rel="noopener noreferrer">.15287</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-30" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-31">
<p>“Sycophancy Is Not One Thing: Causal Separation of Sycophantic Behaviors in LLMs,” 2025. <a href="https://openreview.net/forum?id=d24zTCznJu" rel="noopener noreferrer">OpenReview</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-31" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-32">
<p>Lin et al., “Mitigating the Alignment Tax of RLHF,” EMNLP 2024. <a href="https://arxiv.org/abs/2309.06256" rel="noopener noreferrer">arXiv</a></p><div></div><a href="https://arxiv.org/abs/2309.06256" rel="noopener noreferrer">.06256</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-32" class="data-footnote-backref">↩</a><p></p>
</li>
<li id="user-content-fn-33">
<p>“Asynchronous RLHF: Faster and More Efficient,” ICLR 2025. <a href="https://openreview.net/forum?id=FhTAG591Ve" rel="noopener noreferrer">OpenReview</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-33" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34">
<p>“ReaL: Efficient RLHF Training of Large Language Models with Parameter Reallocation,” MLSys 2025. <a href="https://mlsys.org/virtual/2025/poster/3228" rel="noopener noreferrer">mlsys.org</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-34" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-35">
<p>Hugging Face, “Vision Language Model Alignment in TRL,” August 2025. <a href="https://huggingface.co/blog/trl-vlm-alignment" rel="noopener noreferrer">huggingface.co</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-35" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19">
<p>Axolotl documentation. <a href="https://docs.axolotl.ai/" rel="noopener noreferrer">docs.axolotl.ai</a> <a href="https://blog.ecitis.org/rlhf-preference-tuning/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Reinforcement learning</category>
            <category>Alignment</category>
            <category>Training</category>
            <enclosure length="63586" type="image/png" url="https://blog.ecitis.org/open-graph/rlhf-preference-tuning.png"/>
        </item>
        <item>
            <title><![CDATA[Small Language Models: When Less is More]]></title>
            <link>https://blog.ecitis.org/small-language-models/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/small-language-models/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Understand where compact models outperform frontier-scale systems on privacy, latency, cost, and practical deployment.]]></description>
            <content:encoded><![CDATA[<p>Small language models have emerged as a practical alternative to their multi-hundred-billion parameter counterparts. While frontier models like GPT-5.2 and Claude Opus 4.5 continue pushing capability boundaries, a parallel track of development focuses on models that trade raw scale for efficiency, deployability, and cost effectiveness.</p>
<p>This post examines what makes a model “small,” why these models matter for production systems, and how to choose between the current crop of capable SLMs.</p>
<hr>
<h2 id="defining-small">Defining small</h2>
<p>The industry has loosely converged on a definition: models with fewer than 10 billion parameters qualify as small language models <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-1" id="user-content-fnref-1">1</a></sup>. This threshold reflects a practical boundary; models below 10B can typically run on a single consumer GPU without sharding or distributed inference setups.</p>
<p>Some researchers use a stricter cutoff of 7 billion parameters, while others extend the category to include anything that fits on edge hardware <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-2" id="user-content-fnref-2">2</a></sup>. The distinction matters less than the underlying principle: SLMs are designed to run where large models cannot.</p>
<p>The parameter count tells only part of the story. Architecture choices, quantization support, and inference optimization determine whether a 3B model runs smoothly on a phone or struggles on a workstation. Google’s Gemma 3n uses a MatFormer architecture where the raw parameter count (5B or 8B) runs with the memory footprint of a traditional 2B or 4B model <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-3" id="user-content-fnref-3">3</a></sup>. Meanwhile, Alibaba’s Qwen3-30B-A3B employs a Mixture-of-Experts design with 30B total parameters but only 3.3B active during inference <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-4" id="user-content-fnref-4">4</a></sup>.</p>
<hr>
<h2 id="why-slms-matter-now">Why SLMs matter now</h2>
<p>Four forces are driving SLM adoption: privacy requirements, latency constraints, infrastructure costs, and offline capability.</p>
<p><strong>Privacy</strong>: Running inference locally eliminates data transmission to external servers. For legal, healthcare, and financial applications, keeping sensitive data on-device often represents the simplest path to GDPR and HIPAA compliance. Cloud-based GenAI tools have exposed millions of sensitive records through inadvertent data leakage <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-5" id="user-content-fnref-5">5</a></sup>.</p>
<p><strong>Latency</strong>: Local GPU inference delivers sub-50ms latency compared to 100-500ms for cloud roundtrips <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-6" id="user-content-fnref-6">6</a></sup>. Cactus, a startup focused on mobile inference, demonstrated sub-50ms time-to-first-token for on-device deployment <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-7" id="user-content-fnref-7">7</a></sup>. For interactive applications like code completion or real-time translation, this difference determines usability.</p>
<p><strong>Cost</strong>: Once you own the hardware, inference is free. API pricing for frontier models ranges from $2 to $60 per million tokens. At scale, these costs compound quickly. IDC and Gartner predict that by 2027, over 60% of all AI inference will happen locally rather than in the cloud <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-8" id="user-content-fnref-8">8</a></sup>.</p>
<p><strong>Offline capability</strong>: Local inference removes network dependencies entirely. Applications keep working on factory floors, in hospitals, aboard aircraft, and in areas with unreliable connectivity.</p>
<p>Fine-tuned SLMs will be a major trend in 2026, as the cost and performance advantages will drive usage over out-of-the-box LLMs, according to Andy Markus, AT&amp;T’s chief data officer <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-9" id="user-content-fnref-9">9</a></sup>.</p>
<hr>
<h2 id="training-strategies-for-small-models">Training strategies for small models</h2>
<p>Building a capable small model requires different approaches than scaling up a large one. Three techniques dominate: aggressive data curation, synthetic data generation, and knowledge distillation.</p>
<h3 id="data-curation-over-data-volume">Data curation over data volume</h3>
<p>Large models succeed partly through brute-force data ingestion. GPT-4’s training corpus spans trillions of tokens from across the web. Small models cannot afford this approach; they must extract more learning from less data.</p>
<p>The SmolLM training corpus exemplifies this philosophy. HuggingFace assembled three carefully filtered datasets: Cosmopedia v2 (28B tokens of synthetic textbooks generated by Mixtral), Python-Edu (4B tokens of educational Python samples), and FineWeb-Edu (220B tokens of deduplicated educational web content) <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-10" id="user-content-fnref-10">10</a></sup>. Despite training on a fraction of the data used by larger models, SmolLM-135M outperforms MobileLM-125M on benchmark tasks.</p>
<p>Qwen3 scaled its pre-training data to 36 trillion tokens spanning 119 languages and dialects, with particular emphasis on STEM, coding, and reasoning content <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-4" id="user-content-fnref-4-2">4</a></sup>. The curation focused on density of useful signal rather than raw volume.</p>
<h3 id="synthetic-data-generation">Synthetic data generation</h3>
<p>Microsoft’s Phi series pioneered the use of synthetic training data for small models. Phi-4’s training incorporated high-quality synthetic datasets alongside curated organic data <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-11" id="user-content-fnref-11">11</a></sup>. The synthetic data provides consistent, high-quality examples for reasoning tasks that are sparse in natural web text.</p>
<p>Phi-4-mini-reasoning takes this further: its training data consists exclusively of synthetic mathematical content generated by DeepSeek-R1, comprising over one million diverse math problems spanning multiple levels of difficulty. For each problem, eight distinct solutions were sampled, and only those verified as correct were retained, resulting in approximately 30 billion tokens of math content <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-12" id="user-content-fnref-12">12</a></sup>.</p>
<h3 id="knowledge-distillation">Knowledge distillation</h3>
<p>Distillation transfers knowledge from a larger “teacher” model to a smaller “student” model. The student learns to mimic the teacher’s output distribution rather than training from scratch on raw data.</p>
<p>Meta used distillation to create the Llama 3.2 1B and 3B models. Larger models from the Llama 3.1 family (including the 70B variant) served as teachers, guiding the smaller models to retain high performance even after aggressive parameter reduction through pruning <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-13" id="user-content-fnref-13">13</a></sup>.</p>
<p>MiniLLM introduced a refinement: using reverse Kullback-Leibler divergence instead of forward KLD. This modification prevents the student from overestimating low-probability regions of the teacher’s distribution. In experiments, MiniLLM delivered improvements of up to 15 points over previous distillation methods <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-14" id="user-content-fnref-14">14</a></sup>. Research shows that logit-level distillation using KL divergence significantly reduces memorization of training data and yields better generalization compared to standard fine-tuning, with students inheriting only 0.9% of teacher memorization while preserving generalization capability <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-15" id="user-content-fnref-15">15</a></sup>.</p>
<p>NVIDIA researchers developed a method combining structured weight pruning and knowledge distillation to compress large language models. Width pruning typically achieves better accuracy than depth pruning, though depth pruning often reduces inference latency more at the same parameter count <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-16" id="user-content-fnref-16">16</a></sup>.</p>
<p>Google’s “Distilling Step-by-Step” approach extracts not just predictions but intermediate reasoning steps from larger models. These rationales help smaller models learn more efficiently from fewer examples <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-17" id="user-content-fnref-17">17</a></sup>.</p>
<hr>
<h2 id="architecture-innovations">Architecture innovations</h2>
<p>SLMs incorporate several architectural modifications that prioritize inference efficiency over training convenience.</p>
<h3 id="grouped-query-attention">Grouped Query Attention</h3>
<p>Standard multi-head attention (MHA) assigns separate key and value projections to each attention head. Grouped Query Attention (GQA) shares key-value pairs across groups of heads, reducing memory bandwidth during inference <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-18" id="user-content-fnref-18">18</a></sup>.</p>
<p>The tradeoff is straightforward: MHA maximizes accuracy at the cost of memory overhead, while Multi-Query Attention (MQA) maximizes efficiency at the cost of quality. GQA sits between these extremes. Qwen3 models use GQA across all parameter sizes, enabling faster inference and reduced memory consumption <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-4" id="user-content-fnref-4-3">4</a></sup>.</p>
<p>A 2025 EMNLP paper demonstrated that GQA configurations should vary with context length. For long-context scenarios, using fewer attention heads while scaling up model size can reduce memory usage and FLOPs by over 50% compared to Llama-3’s GQA configuration <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-19" id="user-content-fnref-19">19</a></sup>.</p>
<h3 id="efficient-attention-variants">Efficient attention variants</h3>
<p>Beyond GQA, small models employ several attention optimizations:</p>
<ul>
<li><strong>Local-global attention interleaving</strong>: Gemma 2 alternates between local attention (attending to nearby tokens) and global attention (attending to all tokens) across layers <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-20" id="user-content-fnref-20">20</a></sup>.</li>
<li><strong>Sliding window attention</strong>: Limits attention computation to a fixed window of recent tokens, reducing quadratic complexity.</li>
<li><strong>Multi-Head Latent Attention (MLA)</strong>: DeepSeek’s approach compresses key-value tensors into a lower-dimensional space before caching, reducing memory at the cost of an extra matrix multiplication <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-21" id="user-content-fnref-21">21</a></sup>.</li>
<li><strong>NoPE (No Position Embeddings)</strong>: SmolLM3 implements selective removal of rotary position embeddings from every 4th layer, improving long-context performance without affecting short-context capabilities <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-22" id="user-content-fnref-22">22</a></sup>.</li>
</ul>
<h3 id="matformer-architecture">MatFormer architecture</h3>
<p>Google’s Gemma 3n introduces the MatFormer (Matryoshka Transformer) architecture, a nested transformer built for elastic inference. A larger model contains smaller, fully functional versions of itself, similar to Matryoshka dolls. This extends Matryoshka Representation Learning from embeddings to all transformer components <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-3" id="user-content-fnref-3-2">3</a></sup>.</p>
<p>Gemma 3n also uses Per-Layer Embedding (PLE) parameters that can be generated separately, cached to fast storage, and added during inference. This allows PLE parameters to be kept out of model memory while still improving response quality.</p>
<h3 id="mixture-of-experts-for-small-models">Mixture-of-Experts for small models</h3>
<p>Qwen3-30B-A3B uses a MoE architecture with 30.5B total parameters but only 3.3B active during inference. The model supports seamless switching between thinking mode (for complex logical reasoning, math, and coding) and non-thinking mode (for efficient dialogue) <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-4" id="user-content-fnref-4-4">4</a></sup>. This approach delivers 90% of flagship model performance at a fraction of the cost.</p>
<p>Meta’s Llama 4 Scout employs a similar approach: 17B active parameters with 16 experts and 109B total parameters. It fits on a single H100 GPU while supporting a 10 million token context window <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-23" id="user-content-fnref-23">23</a></sup>.</p>
<h3 id="context-length-optimization">Context length optimization</h3>
<p>Most current SLMs support context lengths of 128K tokens. Qwen3 achieves this while maintaining a vocabulary of 152K tokens through careful tokenizer design <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-4" id="user-content-fnref-4-5">4</a></sup>. Gemma 3 extended its context window from 8K (in Gemma 2) to 128K tokens for all variants above 1B parameters <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-24" id="user-content-fnref-24">24</a></sup>. Llama 4 Scout extends context to 10 million tokens using its MoE architecture <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-23" id="user-content-fnref-23-2">23</a></sup>.</p>
<hr>
<h2 id="model-survey-with-benchmarks">Model survey with benchmarks</h2>
<p>The current SLM landscape includes strong entries from Microsoft, Google, Meta, Alibaba, and HuggingFace. Each family makes different tradeoffs.</p>
<figure><figcaption><strong>Small Language Model Benchmarks (2025-2026)</strong></figcaption><table><thead><tr><th class="model-col">Model</th><th>Params</th><th>MMLU</th><th>MATH</th><th>HumanEval</th><th>Context</th></tr></thead><tbody><tr class><td class="model-name">Phi-4</td><td class="mono">14B</td><td class="mono">84.8%</td><td class="mono">56.1%</td><td class="mono">82.6%</td><td class="mono">16K</td></tr><tr class><td class="model-name">Phi-4-mini</td><td class="mono">3.8B</td><td class="mono">-</td><td class="mono">92.5%*</td><td class="mono">-</td><td class="mono">128K</td></tr><tr class="highlighted"><td class="model-name">Phi-4-multimodal</td><td class="mono">5.6B</td><td class="mono">-</td><td class="mono">-</td><td class="mono">-</td><td class="mono">128K</td></tr><tr class><td class="model-name">Qwen3-4B</td><td class="mono">4B</td><td class="mono">83.7%</td><td class="mono">97.0%*</td><td class="mono">-</td><td class="mono">32K</td></tr><tr class><td class="model-name">Qwen3-8B</td><td class="mono">8B</td><td class="mono">85.0%</td><td class="mono">-</td><td class="mono">-</td><td class="mono">32K</td></tr><tr class><td class="model-name">Qwen3-30B-A3B</td><td class="mono">30B (3.3B active)</td><td class="mono">-</td><td class="mono">-</td><td class="mono">-</td><td class="mono">32K</td></tr><tr class><td class="model-name">Gemma 3n E4B</td><td class="mono">8B (4B effective)</td><td class="mono">64.9%</td><td class="mono">-</td><td class="mono">-</td><td class="mono">32K</td></tr><tr class><td class="model-name">Gemma 3n E2B</td><td class="mono">5B (2B effective)</td><td class="mono">60.1%</td><td class="mono">-</td><td class="mono">-</td><td class="mono">32K</td></tr><tr class><td class="model-name">Gemma 3 4B</td><td class="mono">4B</td><td class="mono">-</td><td class="mono">-</td><td class="mono">-</td><td class="mono">128K</td></tr><tr class="highlighted"><td class="model-name">Llama 4 Scout</td><td class="mono">109B (17B active)</td><td class="mono">-</td><td class="mono">-</td><td class="mono">-</td><td class="mono">10M</td></tr><tr class><td class="model-name">Llama 3.2 3B</td><td class="mono">3B</td><td class="mono">63.4%</td><td class="mono">-</td><td class="mono">-</td><td class="mono">128K</td></tr><tr class><td class="model-name">SmolLM3</td><td class="mono">3B</td><td class="mono">-</td><td class="mono">36.7%**</td><td class="mono">-</td><td class="mono">128K</td></tr><tr class><td class="model-name">SmolLM2</td><td class="mono">1.7B</td><td class="mono">-</td><td class="mono">-</td><td class="mono">-</td><td class="mono">8K</td></tr></tbody></table></figure>
<p>*MATH-500 score with thinking mode enabled.
**SmolLM3 AIME 2025 score with extended thinking mode enabled.</p>
<h3 id="microsoft-phi-4-family">Microsoft Phi-4 family</h3>
<p>Phi-4 (14B parameters) scores 84.8% on MMLU, surpassing Phi-3’s 77.9% and competing with models several times its size <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-11" id="user-content-fnref-11-2">11</a></sup>. On competition-level math problems (MATH benchmark), Phi-4 achieves 56.1% compared to Phi-3’s 42.5%.</p>
<p>The model was trained on 9.8 trillion tokens over 21 days using 1,920 H100 GPUs. Microsoft validated reasoning capability on the November 2024 AMC-10 and AMC-12 math competitions; these tests occurred after training data collection ended, suggesting genuine reasoning rather than benchmark memorization <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-25" id="user-content-fnref-25">25</a></sup>.</p>
<p>The Phi-4 family has expanded significantly in 2025:</p>
<p><strong>Phi-4-multimodal</strong> (5.6B parameters) integrates speech, vision, and text processing into a single unified architecture. It claimed the top position on the HuggingFace OpenASR leaderboard with a word error rate of 6.14%, surpassing the previous best of 6.5% <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-26" id="user-content-fnref-26">26</a></sup>.</p>
<p><strong>Phi-4-mini</strong> (3.8B parameters) matches models in the 7-9B range on reasoning and multilingual tasks.</p>
<p><strong>Phi-4-reasoning</strong> (14B parameters) achieves performance comparable to DeepSeek-R1 (671B parameters) on AIME 2025, trained via supervised fine-tuning on demonstrations generated by o3-mini <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-27" id="user-content-fnref-27">27</a></sup>.</p>
<p><strong>Phi-4-mini-flash-reasoning</strong> uses a hybrid architecture that achieves up to 10x higher throughput and 2-3x reduction in latency compared to Phi-4-mini, targeting edge devices and latency-constrained environments <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-28" id="user-content-fnref-28">28</a></sup>.</p>
<h3 id="google-gemma-family">Google Gemma family</h3>
<p>Gemma 3 spans five parameter sizes: 270M, 1B, 4B, 12B, and 27B <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-24" id="user-content-fnref-24-2">24</a></sup>. The 4B, 12B, and 27B variants process both images and text; the 1B variant handles text only.</p>
<p>Gemma-3-4B-IT beats Gemma-2-27B-IT across benchmarks, demonstrating architectural improvements rather than just scaling <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-29" id="user-content-fnref-29">29</a></sup>. Training data includes double the multilingual content of Gemma 2, supporting over 140 languages with improved tokenization for Chinese, Japanese, and Korean.</p>
<p>Gemma 3 270M represents an extreme efficiency target: 170 million embedding parameters plus 100 million transformer parameters. Internal tests on a Pixel 9 Pro showed the INT4-quantized model consumed just 0.75% battery for 25 conversations <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-30" id="user-content-fnref-30">30</a></sup>.</p>
<p><strong>Gemma 3n</strong> represents a major 2025 advancement for on-device AI. Available in E2B (5B raw/2B effective) and E4B (8B raw/4B effective) sizes, these models use the MatFormer architecture to run with as little as 2GB (E2B) or 3GB (E4B) of memory <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-3" id="user-content-fnref-3-3">3</a></sup>. The E4B version is the first model under 10B parameters to achieve an LMArena score over 1300. Gemma 3n natively supports image, audio, video, and text inputs, with 140 language support.</p>
<p><strong>TranslateGemma</strong> (2025) is a suite of open translation models built on Gemma 3 in 4B, 12B, and 27B sizes, translating across 55 languages without sacrificing quality <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-31" id="user-content-fnref-31">31</a></sup>.</p>
<h3 id="alibaba-qwen-family">Alibaba Qwen family</h3>
<p>Qwen3 offers models at 0.6B, 1.7B, 4B, 8B, 14B, 32B, and the MoE variant 30B-A3B <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-4" id="user-content-fnref-4-6">4</a></sup>. All Qwen3 models feature dual reasoning modes: thinking mode for complex reasoning and non-thinking mode for fast responses.</p>
<p>The performance gains are substantial: Qwen3-4B rivals Qwen2.5-72B-Instruct despite having 18x fewer parameters. Qwen3-4B achieves 83.7% on MMLU-Redux and 97.0% on MATH-500 in thinking mode <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-32" id="user-content-fnref-32">32</a></sup>.</p>
<p>Qwen3-30B-A3B uses MoE with 30.5B total parameters and 3.3B active, outcompeting QwQ-32B while using 10x fewer activated parameters. Most teams in 2026 should default to this model for its balance of performance and efficiency <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-33" id="user-content-fnref-33">33</a></sup>.</p>
<p>All variants support 32K token context windows, with 100+ languages and dialects.</p>
<h3 id="meta-llama-family">Meta Llama family</h3>
<p>Llama 3.2 includes 1B and 3B text-only models designed for edge and mobile deployment <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-13" id="user-content-fnref-13-2">13</a></sup>. Both support 128K token context and work with Qualcomm, MediaTek, and ARM processors.</p>
<p>The 3B model scores 63.4% on MMLU and 77.4% on IFEval (instruction following), beating Gemma 2B IT (61.9%) and Phi-3.5-mini IT (59.2%) on the latter <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-34" id="user-content-fnref-34">34</a></sup>. Tool use capability shows a large jump between model sizes: the 3B scores 67.0% on BFCL V2 compared to 25.7% for the 1B.</p>
<p><strong>Llama 4 Scout</strong> (April 2025) uses MoE with 17B active parameters and 109B total, fitting on a single H100 GPU. It supports a 10 million token context window and was trained on 40 trillion tokens of multimodal data. It beats Gemma 3, Gemini 2.0 Flash-Lite, and Mistral 3.1 across benchmarks <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-23" id="user-content-fnref-23-3">23</a></sup>.</p>
<h3 id="huggingface-smollm-family">HuggingFace SmolLM family</h3>
<p>SmolLM2 targets the sub-2B parameter range with three sizes: 135M, 360M, and 1.7B <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-10" id="user-content-fnref-10-2">10</a></sup>. SmolLM2-1.7B outperforms Meta’s Llama-1B on HellaSwag (68.7% vs 61.2%), ARC Average (60.5% vs 49.2%), and PIQA (77.6% vs 74.8%).</p>
<p>SmolLM3 (3B parameters) pushes into reasoning territory. With extended thinking enabled, it achieves 36.7% on AIME 2025 versus 9.3% without, and 30.0% on LiveCodeBench versus 15.2% <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-22" id="user-content-fnref-22-2">22</a></sup>. The model was trained on 11.2 trillion tokens using a three-stage strategy mixing web, math, and code data.</p>
<p>SmolLM3 outperforms Llama 3.2 3B and Qwen2.5 3B while staying competitive with larger 4B alternatives like Qwen3 and Gemma3. It supports 6 languages (English, French, Spanish, German, Italian, Portuguese) with 64K native context and 128K via YARN extrapolation <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-22" id="user-content-fnref-22-3">22</a></sup>.</p>
<hr>
<h2 id="hardware-requirements-and-deployment">Hardware requirements and deployment</h2>
<figure><figcaption><strong>SLM Deployment Targets</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/small-language-models-1.webp" width="512" height="836" alt="SLM Deployment Targets" loading="lazy" decoding="async"></figure>
<h3 id="server-deployment">Server deployment</h3>
<p>For server inference, small models shine on cost efficiency. A single NVIDIA A100 can serve Phi-4 at high throughput using vLLM or TensorRT-LLM with FP8 quantization. Memory requirements for a 14B model in FP16 run around 28GB; with INT4 quantization, this drops to approximately 7GB.</p>
<p>Llama 4 Scout, despite 109B total parameters, fits on a single H100 due to its MoE architecture with only 17B active parameters <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-23" id="user-content-fnref-23-4">23</a></sup>.</p>
<h3 id="desktop-and-workstation">Desktop and workstation</h3>
<p>Modern MacBooks with Apple Silicon run SLMs through llama.cpp with Metal acceleration. The M3 Max with 128GB unified memory can run Phi-4 without quantization; the base M3 with 8GB works well with 3-4B models using Q4_K_M quantization <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-35" id="user-content-fnref-35">35</a></sup>.</p>
<p>On Windows and Linux, Ollama provides the simplest path to local deployment. NVIDIA GPUs from the RTX 3060 onward handle 7B models comfortably; the RTX 4090’s 24GB VRAM accommodates 14B models in FP16.</p>
<p>The 2023-2025 period saw an “Intelligence Explosion” with 70+ TOPS NPUs and 8-24GB unified memory. 4B+ parameter LLMs now run locally at conversational speeds <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-36" id="user-content-fnref-36">36</a></sup>.</p>
<h3 id="edge-devices">Edge devices</h3>
<p>Raspberry Pi 5 (8GB) runs SmolLM2-1.7B at usable speeds with INT4 quantization. NVIDIA Jetson Orin devices provide GPU acceleration for models up to 7B parameters with proper quantization <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-37" id="user-content-fnref-37">37</a></sup>.</p>
<p>NPUs (Neural Processing Units) in recent laptop chips accelerate specific operations. Microsoft’s Phi Silica is optimized for Snapdragon-powered Copilot+ PCs using ONNX and low-bit quantization <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-26" id="user-content-fnref-26-2">26</a></sup>.</p>
<p>ExecuTorch simplifies deployment by letting developers deploy PyTorch models directly to edge devices, running 8B parameter LLMs on smartphones at 30+ tokens/second <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-36" id="user-content-fnref-36-2">36</a></sup>.</p>
<h3 id="mobile-deployment">Mobile deployment</h3>
<p>Gemma 3n E2B targets phones directly. The model fits in 2GB of memory and runs inference without noticeable battery drain <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-3" id="user-content-fnref-3-4">3</a></sup>. Over 600 projects were submitted to the Gemma 3n Impact Challenge on Kaggle within weeks of release.</p>
<p>Llama 3.2 1B works on iOS through frameworks like llama.cpp compiled for Apple platforms. Android deployment uses similar approaches with the NDK.</p>
<p>Top recommended LLMs for edge AI deployment in 2026 include Meta-Llama-3.1-8B-Instruct, GLM-4-9B-0414, and Qwen2.5-VL-7B-Instruct for their balance of performance and computational efficiency <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-38" id="user-content-fnref-38">38</a></sup>.</p>
<h3 id="browser-deployment">Browser deployment</h3>
<p>WebGPU enables in-browser inference for small models. Libraries like transformers.js and llama.cpp’s WASM build run models up to 1B parameters in modern browsers, though performance trails native execution.</p>
<hr>
<h2 id="use-cases-where-slms-excel">Use cases where SLMs excel</h2>
<p>Small models outperform large models in several scenarios, not just match them at lower cost.</p>
<h3 id="latency-critical-applications">Latency-critical applications</h3>
<p>Code completion requires sub-100ms response times to feel responsive. A local 3B model delivers suggestions before the developer notices any delay. A cloud API, even with low network latency, adds perceptible lag.</p>
<p>Real-time translation for live captioning has similar constraints. The translation must appear within a second of speech; network round-trips eat into this budget.</p>
<h3 id="domain-specific-tasks">Domain-specific tasks</h3>
<p>A 3B model fine-tuned on legal documents often outperforms a 70B general model on legal tasks. The fine-tuned model learns domain vocabulary, citation formats, and reasoning patterns that the general model treats as edge cases.</p>
<p>Medical coding, financial analysis, and technical documentation all benefit from this specialization. The smaller parameter count makes fine-tuning affordable.</p>
<p>Microsoft’s OptiMind (20B parameters) converts business problems described in natural language into mathematical formulations for optimization software, running locally to keep sensitive business data private <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-39" id="user-content-fnref-39">39</a></sup>.</p>
<h3 id="agentic-ai-systems">Agentic AI systems</h3>
<p>Small language models are sufficiently powerful, inherently more suitable, and necessarily more economical for many invocations in agentic systems <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-40" id="user-content-fnref-40">40</a></sup>. Qwen3’s dual-mode reasoning and improved agent capabilities make small models viable for tool use and multi-step workflows.</p>
<h3 id="high-throughput-batch-processing">High-throughput batch processing</h3>
<p>Processing millions of documents for classification or extraction favors small models. A Qwen3 0.6B model can classify documents at 10-50x the throughput of a 70B model on the same hardware. For tasks where a small model achieves sufficient accuracy, this throughput advantage compounds into major cost savings.</p>
<h3 id="embedded-and-offline-systems">Embedded and offline systems</h3>
<p>Industrial quality control systems, autonomous vehicles, and IoT devices cannot rely on cloud connectivity. A local SLM provides consistent capability regardless of network conditions.</p>
<hr>
<h2 id="fine-tuning-slms">Fine-tuning SLMs</h2>
<p>Small models adapt faster and more cheaply than large ones. The same LoRA configuration that requires 24GB VRAM for a 70B model fits in 8GB for a 7B model.</p>
<h3 id="lora-and-qlora">LoRA and QLoRA</h3>
<p>Low-Rank Adaptation (LoRA) freezes base model weights and trains small adapter matrices <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-41" id="user-content-fnref-41">41</a></sup>. This reduces trainable parameters by orders of magnitude; a typical LoRA configuration adds only 0.1-1% additional parameters.</p>
<p>QLoRA combines LoRA with 4-bit quantization, enabling fine-tuning of 7B models on consumer GPUs with 8GB VRAM. The quantized base model consumes less memory, leaving room for optimizer states and gradients.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> BitsAndBytesConfig</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> peft </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> get_peft_model</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model with 4-bit quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantization_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BitsAndBytesConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_quant_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;nf4&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_compute_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;Qwen/Qwen3-4B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    quantization_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">quantization_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Apply LoRA</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">lora_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;q_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;v_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.05</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> get_peft_model</span><span style="color:var(--astro-code-token-punctuation)">(model, lora_config)</span></span></code></pre>
<h3 id="training-efficiency">Training efficiency</h3>
<p>Fine-tuning a 3B model on a single RTX 4090 takes hours rather than days. A custom medical coding model might require 10,000 examples and 3 epochs; this completes overnight on consumer hardware.</p>
<p>PEFT techniques reduce peak memory by 50-70% compared to full fine-tuning while preserving most of the accuracy gain <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-42" id="user-content-fnref-42">42</a></sup>. For SLMs, this means genuine fine-tuning accessibility for individuals and small teams.</p>
<h3 id="on-device-fine-tuning">On-device fine-tuning</h3>
<p>Research frameworks now support LoRA fine-tuning on mobile GPUs. Tether Data’s QVAC-fabric-llm integrates LoRA training into the llama.cpp ecosystem, enabling fine-tuning on phones with Mali, Adreno, and Apple GPUs <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-43" id="user-content-fnref-43">43</a></sup>. This opens possibilities for personalized on-device models that adapt to individual users.</p>
<hr>
<h2 id="running-slms-locally">Running SLMs locally</h2>
<h3 id="ollama">Ollama</h3>
<p>Ollama provides the simplest path from zero to running model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Install Ollama (macOS/Linux)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> -fsSL</span><span style="color:var(--astro-code-token-string)"> https://ollama.com/install.sh</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-function)"> sh</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Pull and run Phi-4</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> phi4</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Pull and run Qwen3</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> qwen3:4b</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run Gemma 3n</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> gemma3n</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run with specific quantization</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> qwen3:4b-instruct-q4_K_M</span></span></code></pre>
<p>Ollama handles model downloading, quantization selection, and GPU acceleration automatically.</p>
<h3 id="llamacpp">llama.cpp</h3>
<p>For more control, llama.cpp provides direct access to inference parameters:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/ggerganov/llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> -B</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> -DGGML_METAL=ON</span><span style="color:var(--astro-code-token-comment)">  # macOS with Metal</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> --build</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> Release</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run inference</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-cli</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -m</span><span style="color:var(--astro-code-token-string)"> models/qwen3-4b-instruct-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -ngl</span><span style="color:var(--astro-code-token-constant)"> 99</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -c</span><span style="color:var(--astro-code-token-constant)"> 4096</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -p</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain the difference between LoRA and full fine-tuning:&quot;</span></span></code></pre>
<p>Key flags:</p>
<ul>
<li><code>-ngl 99</code>: Offload all layers to GPU</li>
<li><code>-c 4096</code>: Context window size</li>
<li><code>-fa</code>: Enable flash attention (reduces memory)</li>
<li><code>-b 512</code>: Batch size for prompt processing</li>
</ul>
<h3 id="python-with-transformers">Python with transformers</h3>
<p>Direct integration with Hugging Face transformers:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_name </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Qwen/Qwen3-4B-Instruct&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_name)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_name,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">messages </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Write a Python function to calculate Fibonacci numbers.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">apply_chat_template</span><span style="color:var(--astro-code-token-punctuation)">(messages, tokenize</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">, add_generation_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(text, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(model.device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">, temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(tokenizer.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">], skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">))</span></span></code></pre>
<h3 id="mlx-for-apple-silicon">MLX for Apple Silicon</h3>
<p>Apple’s MLX framework provides optimized inference on M-series chips:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> mlx_lm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> generate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;mlx-community/Qwen3-4B-Instruct-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain quantum entanglement in simple terms:&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generate</span><span style="color:var(--astro-code-token-punctuation)">(model, tokenizer, prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">prompt, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(response)</span></span></code></pre>
<p>MLX leverages unified memory architecture, avoiding the CPU-GPU transfer overhead present on discrete GPU systems.</p>
<hr>
<h2 id="choosing-the-right-model">Choosing the right model</h2>
<p>Selection depends on your constraints and requirements:</p>
<p><strong>For maximum capability in the SLM range</strong>: Qwen3-4B leads with 83.7% MMLU and near-perfect MATH-500 scores in thinking mode, rivaling Qwen2.5-72B at 18x fewer parameters.</p>
<p><strong>For mobile and edge deployment</strong>: Gemma 3n E2B runs with 2GB memory and achieves 60.1% MMLU. SmolLM2-1.7B provides capable performance on devices with 6GB RAM.</p>
<p><strong>For maximum context</strong>: Llama 4 Scout supports 10 million tokens. For more accessible hardware, most models now support 128K tokens.</p>
<p><strong>For multilingual applications</strong>: Qwen3 supports 100+ languages. Gemma 3n covers 140 languages with improved CJK tokenization.</p>
<p><strong>For code generation</strong>: Qwen2.5-Coder variants are purpose-built for programming tasks. Phi-4-mini also shows strong coding performance.</p>
<p><strong>For structured reasoning</strong>: SmolLM3 with extended thinking mode provides chain-of-thought reasoning in a 3B package. Qwen3 models support seamless switching between thinking and non-thinking modes.</p>
<p><strong>For multimodal tasks</strong>: Phi-4-multimodal handles text, speech, and vision. Gemma 3n processes image, audio, video, and text inputs.</p>
<hr>
<h2 id="looking-ahead">Looking ahead</h2>
<p>The SLM space continues to evolve rapidly. Model efficiency improves faster than raw capability scaling. Qwen3-4B matches or exceeds Qwen2.5-72B on many benchmarks, a 142-fold reduction in required parameters for comparable MMLU performance since 2022 <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-44" id="user-content-fnref-44">44</a></sup>.</p>
<p>Hardware trends reinforce this direction. NPUs in consumer devices, improved quantization techniques, and frameworks like MLX, llama.cpp, and ExecuTorch reduce the friction of local deployment. By 2027, running a capable local model may be as routine as running a web browser.</p>
<p>A 2025 NVIDIA position paper argues that “the next real leap forward won’t come from models getting bigger. It’ll come from them getting smaller” <sup><a href="https://blog.ecitis.org/small-language-models/#user-content-fn-40" id="user-content-fnref-40-2">40</a></sup>. This shift will significantly impact the future of on-device and edge computing.</p>
<p>The practical takeaway: evaluate whether a small model meets your accuracy requirements before defaulting to large models. For many production applications, a fine-tuned 3B model outperforms a general 70B model while costing a fraction to deploy and operate.</p>
<hr>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>BentoML. “The Best Open-Source Small Language Models (SLMs) in 2026.” <a href="https://www.bentoml.com/blog/the-best-open-source-small-language-models" rel="noopener noreferrer">https://www.bentoml.com/blog/the-best-open-source-small-language-models</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Wikipedia. “Small language model.” <a href="https://en.wikipedia.org/wiki/Small_language_model" rel="noopener noreferrer">https://en.wikipedia.org/wiki/Small_language_model</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>Google Developers Blog. “Introducing Gemma 3n: The developer guide.” <a href="https://developers.googleblog.com/en/introducing-gemma-3n-developer-guide/" rel="noopener noreferrer">https://developers.googleblog.com/en/introducing-gemma-3n-developer-guide/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-3" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-3-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-3-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-3-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-4">
<p>Qwen Team. “Qwen3: Think Deeper, Act Faster.” <a href="https://qwenlm.github.io/blog/qwen3/" rel="noopener noreferrer">https://qwenlm.github.io/blog/qwen3/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-4" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-4-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-4-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-4-4" class="data-footnote-backref">↩<sup>4</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-4-5" class="data-footnote-backref">↩<sup>5</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-4-6" class="data-footnote-backref">↩<sup>6</sup></a></p>
</li>
<li id="user-content-fn-5">
<p>InfoQ. “Cactus v1: Cross-Platform LLM Inference on Mobile with Zero Latency and Full Privacy.” <a href="https://www.infoq.com/news/2025/12/cactus-on-device-inference/" rel="noopener noreferrer">https://www.infoq.com/news/2025/12/cactus-on-device-inference/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p>PMC. “Tiny Machine Learning and On-Device Inference: A Survey.” <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12115890/" rel="noopener noreferrer">https://pmc.ncbi.nlm.nih.gov/articles/PMC12115890/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p>Cactus AI documentation and benchmarks, 2025. <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p>Novus. “The Rise of Local AI Models: Going Small to Go Big.” <a href="https://www.novusasi.com/blog/the-rise-of-local-ai-models-going-small-to-go-big" rel="noopener noreferrer">https://www.novusasi.com/blog/the-rise-of-local-ai-models-going-small-to-go-big</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p>TechCrunch. “In 2026, AI will move from hype to pragmatism.” <a href="https://techcrunch.com/2026/01/02/in-2026-ai-will-move-from-hype-to-pragmatism/" rel="noopener noreferrer">https://techcrunch.com/2026/01/02/in-2026-ai-will-move-from-hype-to-pragmatism/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10">
<p>HuggingFace Blog. “SmolLM - blazingly fast and remarkably powerful.” <a href="https://huggingface.co/blog/smollm" rel="noopener noreferrer">https://huggingface.co/blog/smollm</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-10" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-10-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-11">
<p>Microsoft. “Phi-4 Technical Report.” <a href="https://arxiv.org/html/2412.08905v1" rel="noopener noreferrer">https://arxiv.org/html/2412.08905v1</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-11" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-11-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-12">
<p>HuggingFace. “microsoft/Phi-4-mini-reasoning Model Card.” <a href="https://huggingface.co/microsoft/Phi-4-mini-reasoning" rel="noopener noreferrer">https://huggingface.co/microsoft/Phi-4-mini-reasoning</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p>Meta AI. “Llama 3.2: Revolutionizing edge AI and vision with open, customizable models.” <a href="https://ai.meta.com/blog/llama-3-2-connect-2024-vision-edge-mobile-devices/" rel="noopener noreferrer">https://ai.meta.com/blog/llama-3-2-connect-2024-vision-edge-mobile-devices/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-13" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-13-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-14">
<p>arXiv. “MiniLLM: Knowledge Distillation of Large Language Models.” <a href="https://arxiv.org/abs/2306.08543" rel="noopener noreferrer">https://arxiv.org/abs/2306.08543</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-14" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-15">
<p>arXiv. “Memorization Dynamics in Knowledge Distillation for Language Models.” <a href="https://arxiv.org/html/2601.15394" rel="noopener noreferrer">https://arxiv.org/html/2601.15394</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16">
<p>NVIDIA Technical Blog. “Pruning and Distilling LLMs Using NVIDIA TensorRT Model Optimizer.” <a href="https://developer.nvidia.com/blog/pruning-and-distilling-llms-using-nvidia-tensorrt-model-optimizer/" rel="noopener noreferrer">https://developer.nvidia.com/blog/pruning-and-distilling-llms-using-nvidia-tensorrt-model-optimizer/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-17">
<p>Google Research. “Distilling step-by-step: Outperforming larger language models with less training.” <a href="https://research.google/blog/distilling-step-by-step-outperforming-larger-language-models-with-less-training-data-and-smaller-model-sizes/" rel="noopener noreferrer">https://research.google/blog/distilling-step-by-step-outperforming-larger-language-models-with-less-training-data-and-smaller-model-sizes/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18">
<p>IBM. “What is grouped query attention (GQA)?” <a href="https://www.ibm.com/think/topics/grouped-query-attention" rel="noopener noreferrer">https://www.ibm.com/think/topics/grouped-query-attention</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19">
<p>ACL Anthology. “Cost-Optimal Grouped-Query Attention for Long-Context Modeling.” <a href="https://aclanthology.org/2025.emnlp-main.272/" rel="noopener noreferrer">https://aclanthology.org/2025.emnlp-main.272/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-20">
<p>arXiv. “Gemma 2: Improving Open Language Models at a Practical Size.” <a href="https://arxiv.org/abs/2408.00118" rel="noopener noreferrer">https://arxiv.org/abs/2408.00118</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-20" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-21">
<p>Sebastian Raschka. “The Big LLM Architecture Comparison.” <a href="https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison" rel="noopener noreferrer">https://magazine.sebastianraschka.com/p/the-big-llm-architecture-comparison</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-21" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-22">
<p>HuggingFace Blog. “SmolLM3: smol, multilingual, long-context reasoner.” <a href="https://huggingface.co/blog/smollm3" rel="noopener noreferrer">https://huggingface.co/blog/smollm3</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-22" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-22-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-22-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-23">
<p>Meta AI. “The Llama 4 herd: The beginning of a new era of natively multimodal AI innovation.” <a href="https://ai.meta.com/blog/llama-4-multimodal-intelligence/" rel="noopener noreferrer">https://ai.meta.com/blog/llama-4-multimodal-intelligence/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-23" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-23-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-23-3" class="data-footnote-backref">↩<sup>3</sup></a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-23-4" class="data-footnote-backref">↩<sup>4</sup></a></p>
</li>
<li id="user-content-fn-24">
<p>Google AI for Developers. “Gemma 3 model overview.” <a href="https://ai.google.dev/gemma/docs/core" rel="noopener noreferrer">https://ai.google.dev/gemma/docs/core</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-24" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-24-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-25">
<p>Microsoft Community Hub. “Phi-4: Small Language Models That Pack a Punch.” <a href="https://techcommunity.microsoft.com/blog/educatordeveloperblog/phi-4-small-language-models-that-pack-a-punch/4464167" rel="noopener noreferrer">https://techcommunity.microsoft.com/blog/educatordeveloperblog/phi-4-small-language-models-that-pack-a-punch/4464167</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-25" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26">
<p>Microsoft Community Hub. “Welcome to the new Phi-4 models - Microsoft Phi-4-mini &amp; Phi-4-multimodal.” <a href="https://techcommunity.microsoft.com/blog/educatordeveloperblog/welcome-to-the-new-phi-4-models---microsoft-phi-4-mini--phi-4-multimodal/4386037" rel="noopener noreferrer">https://techcommunity.microsoft.com/blog/educatordeveloperblog/welcome-to-the-new-phi-4-models---microsoft-phi-4-mini—phi-4-multimodal/4386037</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-26" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-26-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-27">
<p>Microsoft Research. “Phi-4-reasoning Technical Report.” <a href="https://www.microsoft.com/en-us/research/publication/phi-4-reasoning-technical-report/" rel="noopener noreferrer">https://www.microsoft.com/en-us/research/publication/phi-4-reasoning-technical-report/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-27" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-28">
<p>Microsoft Azure Blog. “Reasoning reimagined: Introducing Phi-4-mini-flash-reasoning.” <a href="https://azure.microsoft.com/en-us/blog/reasoning-reimagined-introducing-phi-4-mini-flash-reasoning/" rel="noopener noreferrer">https://azure.microsoft.com/en-us/blog/reasoning-reimagined-introducing-phi-4-mini-flash-reasoning/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-28" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-29">
<p>HuggingFace Blog. “Welcome Gemma 3: Google’s all new multimodal, multilingual, long context open LLM.” <a href="https://huggingface.co/blog/gemma3" rel="noopener noreferrer">https://huggingface.co/blog/gemma3</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-29" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-30">
<p>Google Developers Blog. “Introducing Gemma 3 270M: The compact model for hyper-efficient AI.” <a href="https://developers.googleblog.com/en/introducing-gemma-3-270m/" rel="noopener noreferrer">https://developers.googleblog.com/en/introducing-gemma-3-270m/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-30" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-31">
<p>Google Blog. “TranslateGemma: A new family of open translation models.” <a href="https://blog.google/technology/developers/translategemma/" rel="noopener noreferrer">https://blog.google/technology/developers/translategemma/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-31" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-32">
<p>Open Laboratory. “Qwen3 4B.” <a href="https://openlaboratory.ai/models/qwen3-4b" rel="noopener noreferrer">https://openlaboratory.ai/models/qwen3-4b</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-32" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-33">
<p>Interconnects. “Qwen 3: The new open standard.” <a href="https://www.interconnects.ai/p/qwen-3-the-new-open-standard" rel="noopener noreferrer">https://www.interconnects.ai/p/qwen-3-the-new-open-standard</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-33" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34">
<p>Encord. “Llama 3.2: Advanced Vision and Edge AI Models for Mobile and Cloud.” <a href="https://encord.com/blog/lama-3-2-explained/" rel="noopener noreferrer">https://encord.com/blog/lama-3-2-explained/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-34" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-35">
<p>InvestGlass. “How to Run LLMs Locally: Complete 2025 Guide to Self-Hosted AI Models.” <a href="https://www.investglass.com/en_gb/how-to-run-llms-locally-complete-2025-guide-to-self-hosted-ai-models/" rel="noopener noreferrer">https://www.investglass.com/en_gb/how-to-run-llms-locally-complete-2025-guide-to-self-hosted-ai-models/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-35" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-36">
<p>SiliconFlow. “Ultimate Guide - The Best LLMs For Mobile Deployment In 2026.” <a href="https://www.siliconflow.com/articles/en/best-LLMs-for-mobile-deployment" rel="noopener noreferrer">https://www.siliconflow.com/articles/en/best-LLMs-for-mobile-deployment</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-36" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-36-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-37">
<p>SabrePC Blog. “Popular Small Language Models to Run Locally.” <a href="https://www.sabrepc.com/blog/deep-learning-and-ai/popular-small-language-models-to-run-locally" rel="noopener noreferrer">https://www.sabrepc.com/blog/deep-learning-and-ai/popular-small-language-models-to-run-locally</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-37" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-38">
<p>SiliconFlow. “Ultimate Guide - The Best LLMs for Edge AI Devices in 2026.” <a href="https://www.siliconflow.com/articles/en/best-llms-for-edge-ai-devices-2025" rel="noopener noreferrer">https://www.siliconflow.com/articles/en/best-llms-for-edge-ai-devices-2025</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-38" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-39">
<p>Microsoft Research. “OptiMind: A small language model with optimization expertise.” <a href="https://www.microsoft.com/en-us/research/blog/optimind-a-small-language-model-with-optimization-expertise/" rel="noopener noreferrer">https://www.microsoft.com/en-us/research/blog/optimind-a-small-language-model-with-optimization-expertise/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-39" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-40">
<p>NVIDIA Research. “Small Language Models are the Future of Agentic AI.” <a href="https://research.nvidia.com/labs/lpr/slm-agents/" rel="noopener noreferrer">https://research.nvidia.com/labs/lpr/slm-agents/</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-40" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-40-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-41">
<p>DataCamp. “LLM Distillation Explained: Applications, Implementation &amp; More.” <a href="https://www.datacamp.com/blog/distillation-llm" rel="noopener noreferrer">https://www.datacamp.com/blog/distillation-llm</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-41" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-42">
<p>Databricks. “Efficient Fine-Tuning with LoRA: A Guide to Optimal Parameter Selection for Large Language Models.” <a href="https://www.databricks.com/blog/efficient-fine-tuning-lora-guide-llms" rel="noopener noreferrer">https://www.databricks.com/blog/efficient-fine-tuning-lora-guide-llms</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-42" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-43">
<p>HuggingFace Blog. “An Edge-First Generalized LLM LoRA Fine-Tuning Framework for Heterogeneous GPUs.” <a href="https://huggingface.co/blog/qvac/fabric-llm-finetune" rel="noopener noreferrer">https://huggingface.co/blog/qvac/fabric-llm-finetune</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-43" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-44">
<p>Stanford HAI. “2025 AI Index Report: Technical Performance.” <a href="https://hai.stanford.edu/ai-index/2025-ai-index-report/technical-performance" rel="noopener noreferrer">https://hai.stanford.edu/ai-index/2025-ai-index-report/technical-performance</a> <a href="https://blog.ecitis.org/small-language-models/#user-content-fnref-44" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Small models</category>
            <category>Edge &amp; on-device</category>
            <category>Quantization</category>
            <enclosure length="60073" type="image/png" url="https://blog.ecitis.org/open-graph/small-language-models.png"/>
        </item>
        <item>
            <title><![CDATA[NVIDIA TensorRT and Triton: Production LLM Inference]]></title>
            <link>https://blog.ecitis.org/tensorrt-triton-inference/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/tensorrt-triton-inference/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Build a production inference stack that balances latency, throughput, GPU utilization, concurrency, and observability.]]></description>
            <content:encoded><![CDATA[<p>Deploying LLMs at scale requires more than loading a model and serving requests. Production systems must handle concurrent users, minimize latency, maximize GPU utilization, and provide observability. NVIDIA’s TensorRT-LLM and Triton Inference Server form a battle-tested stack for these requirements.</p>
<p>TensorRT-LLM became fully open-source in March 2025 and now delivers over 40,000 tokens per second on Blackwell B200 GPUs running Llama 4<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new1" id="user-content-fnref-new1">1</a></sup>. The stack integrates with NVIDIA Dynamo for datacenter-scale orchestration across thousands of GPUs<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new2" id="user-content-fnref-new2">2</a></sup>.</p>
<p>This guide covers the complete deployment pipeline: building optimized TensorRT engines, configuring Triton for LLM workloads, and tuning for production performance.</p>
<hr>
<h2 id="tensorrt-llm-overview">TensorRT-LLM overview</h2>
<p>TensorRT-LLM is NVIDIA’s open-source library for compiling and running large language models on NVIDIA GPUs. Built on PyTorch, it provides a Python API for model definition while generating highly optimized CUDA kernels for inference<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-1" id="user-content-fnref-1">3</a></sup>. The current release (January 2026) uses PyTorch 2.9.0, TensorRT 10.9, and CUDA 12.8.1<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new3" id="user-content-fnref-new3">4</a></sup>.</p>
<p>The library handles several optimization categories:</p>
<p><strong>Quantization</strong>: TensorRT-LLM supports NVFP4, FP8, INT4 AWQ, and INT8 SmoothQuant quantization formats. NVFP4 is a 4-bit floating-point format introduced with Blackwell GPUs that reduces KV cache memory by 50% compared to FP8 while maintaining less than 1% accuracy loss on benchmarks including LiveCodeBench, MMLU-PRO, and MBPP<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new4" id="user-content-fnref-new4">5</a></sup>. FP8 on Hopper and Blackwell architectures delivers 2.5-3x inference speed improvements compared to FP16<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-2" id="user-content-fnref-2">6</a></sup>.</p>
<p><strong>Kernel fusion</strong>: Multiple transformer operations combine into single CUDA kernels. LayerNorm, matrix multiplications, bias additions, and activation functions execute together instead of requiring separate kernel launches and memory transfers<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-3" id="user-content-fnref-3">7</a></sup>.</p>
<p><strong>Attention optimizations</strong>: Custom FlashAttention kernels, multi-head/multi-query/grouped-query attention support, and fused attention implementations reduce memory bandwidth requirements.</p>
<p><strong>Parallelism</strong>: Tensor parallelism splits matrix multiplications across GPUs. Pipeline parallelism distributes model layers sequentially across devices. Wide expert parallelism (EP) enables efficient Mixture-of-Experts inference on models like DeepSeek-R1 and Llama 4<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new5" id="user-content-fnref-new5">8</a></sup>. All strategies enable serving models larger than single-GPU memory<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-4" id="user-content-fnref-4">9</a></sup>.</p>
<p><strong>Speculative decoding</strong>: TensorRT-LLM supports EAGLE-3, multi-token prediction (MTP), and ReDrafter techniques for accelerated token generation. EAGLE-3 with speculative decoding achieves up to 4x speedup on Llama 4 Maverick<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new6" id="user-content-fnref-new6">10</a></sup>. ReDrafter, developed by Apple and integrated into TensorRT-LLM, achieves up to 2.7x throughput improvements on H100 GPUs<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new7" id="user-content-fnref-new7">11</a></sup>.</p>
<figure><figcaption><strong>Triton + TensorRT-LLM Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/tensorrt-triton-inference-0.webp" width="512" height="1109" alt="Triton + TensorRT-LLM Architecture" loading="lazy" decoding="async"></figure>
<hr>
<h2 id="building-tensorrt-engines-for-llms">Building TensorRT engines for LLMs</h2>
<p>TensorRT-LLM converts model weights into optimized TensorRT engines through a two-step process: checkpoint conversion and engine building.</p>
<h3 id="step-1-quantize-and-convert-checkpoints">Step 1: Quantize and convert checkpoints</h3>
<p>The <code>quantize.py</code> script converts Hugging Face checkpoints to TensorRT-LLM format while applying quantization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># NVFP4 quantization (Blackwell GPUs - recommended for B200/GB200)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> examples/quantization/quantize.py</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model_dir</span><span style="color:var(--astro-code-token-string)"> /path/to/llama-70b</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --qformat</span><span style="color:var(--astro-code-token-string)"> nvfp4</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --kv_cache_dtype</span><span style="color:var(--astro-code-token-string)"> nvfp4</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --output_dir</span><span style="color:var(--astro-code-token-string)"> /output/llama-70b-nvfp4</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --tp_size</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># FP8 quantization (Hopper/Ada/Blackwell GPUs)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> examples/quantization/quantize.py</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model_dir</span><span style="color:var(--astro-code-token-string)"> /path/to/llama-70b</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --qformat</span><span style="color:var(--astro-code-token-string)"> fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --kv_cache_dtype</span><span style="color:var(--astro-code-token-string)"> fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --output_dir</span><span style="color:var(--astro-code-token-string)"> /output/llama-70b-fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --tp_size</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># INT4 AWQ quantization (all GPU generations)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> examples/quantization/quantize.py</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model_dir</span><span style="color:var(--astro-code-token-string)"> /path/to/llama-70b</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --qformat</span><span style="color:var(--astro-code-token-string)"> int4_awq</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --awq_block_size</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --output_dir</span><span style="color:var(--astro-code-token-string)"> /output/llama-70b-int4awq</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --tp_size</span><span style="color:var(--astro-code-token-constant)"> 4</span></span></code></pre>
<p>The <code>--tp_size</code> parameter specifies tensor parallelism degree. Set this to the number of GPUs you will use for inference. INT4 AWQ uses block-wise quantization; smaller block sizes (64 vs 128) provide better accuracy at marginal compute cost<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-5" id="user-content-fnref-5">12</a></sup>. NVFP4 uses block-wise quantization with size 16 and FP8 scaling factors for higher precision during dequantization<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new4" id="user-content-fnref-new4-2">5</a></sup>.</p>
<h3 id="step-2-build-the-engine">Step 2: Build the engine</h3>
<p>The <code>trtllm-build</code> command compiles checkpoints into TensorRT engines:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">trtllm-build</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --checkpoint_dir</span><span style="color:var(--astro-code-token-string)"> /output/llama-70b-fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --output_dir</span><span style="color:var(--astro-code-token-string)"> /engines/llama-70b-fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --gemm_plugin</span><span style="color:var(--astro-code-token-string)"> fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --gpt_attention_plugin</span><span style="color:var(--astro-code-token-string)"> fp8</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --max_batch_size</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --max_input_len</span><span style="color:var(--astro-code-token-constant)"> 4096</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --max_seq_len</span><span style="color:var(--astro-code-token-constant)"> 8192</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --use_fused_mlp</span><span style="color:var(--astro-code-token-string)"> enable</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --workers</span><span style="color:var(--astro-code-token-constant)"> 4</span></span></code></pre>
<p>Key build parameters:</p>

































<div class="table-wrap"><table><thead><tr><th>Parameter</th><th>Purpose</th></tr></thead><tbody><tr><td><code>--gemm_plugin</code></td><td>Enables cuBLASLt for optimized matrix operations</td></tr><tr><td><code>--gpt_attention_plugin</code></td><td>Uses efficient attention kernels with in-place KV cache updates</td></tr><tr><td><code>--use_fused_mlp</code></td><td>Enables horizontal fusion in GatedMLP layers</td></tr><tr><td><code>--max_batch_size</code></td><td>Maximum concurrent sequences the engine supports</td></tr><tr><td><code>--max_input_len</code></td><td>Maximum input context length</td></tr><tr><td><code>--max_seq_len</code></td><td>Maximum total sequence length (input + output)</td></tr></tbody></table></div>
<p>For FP8 on Hopper GPUs, the GEMM + SwiGLU fusion in Gated-MLP combines two Matmul operations and one SwiGLU operation into a single kernel<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-6" id="user-content-fnref-6">13</a></sup>.</p>
<h3 id="layer-fusion-patterns">Layer fusion patterns</h3>
<p>TensorRT’s compiler identifies and fuses operation sequences automatically. Common fusion patterns include:</p>
<ul>
<li><strong>RMSNorm fusion</strong>: Combines normalization with subsequent quantization</li>
<li><strong>Attention fusion</strong>: Merges Q/K/V projections with attention computation</li>
<li><strong>MLP fusion</strong>: Fuses gate and up projections with activation functions</li>
<li><strong>AllReduce fusion</strong>: Combines reduction with LayerNorm after multi-GPU communication</li>
</ul>
<p>The <code>--reduce_fusion enable</code> flag eliminates extra copies from local buffers to shared buffers in communication kernels<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-7" id="user-content-fnref-7">14</a></sup>.</p>
<hr>
<h2 id="disaggregated-serving">Disaggregated serving</h2>
<p>Disaggregated serving separates compute-intensive prefill operations from memory-bound decode operations onto specialized hardware clusters. This architecture addresses the fundamental mismatch between prefill (compute-bound, benefits from high FLOPS) and decode (memory-bound, benefits from high memory bandwidth) phases<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new8" id="user-content-fnref-new8">15</a></sup>.</p>
<p>TensorRT-LLM supports three disaggregated serving approaches:</p>
<p><strong>trtllm-serve</strong>: A command-line utility that deploys OpenAI-compatible servers for each context and generation instance, with an orchestrator coordinating requests.</p>
<p><strong>Dynamo integration</strong>: NVIDIA Dynamo orchestrates requests across prefill and decode workers with KV-cache-aware routing. The smart router determines optimal decode workers based on KV cache block availability<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new2" id="user-content-fnref-new2-2">2</a></sup>.</p>
<p><strong>NIXL acceleration</strong>: The NVIDIA Inference Exchange Library (NIXL) accelerates KV cache transfer between GPUs with low-latency communication primitives.</p>
<p>Disaggregated architectures demonstrate up to 6.4x throughput improvements and 20x reduction in latency variance. Organizations report 15-40% infrastructure cost reductions through optimized hardware allocation<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new9" id="user-content-fnref-new9">16</a></sup>.</p>
<hr>
<h2 id="in-flight-batching-and-paged-attention">In-flight batching and paged attention</h2>
<p>Traditional batching waits until a batch fills before processing. In-flight batching (also called continuous batching or iteration-level batching) processes new requests immediately without waiting for existing sequences to complete<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-8" id="user-content-fnref-8">17</a></sup>.</p>
<h3 id="how-in-flight-batching-works">How in-flight batching works</h3>
<p>The TensorRT-LLM scheduler manages two phases:</p>
<ol>
<li><strong>Context phase</strong>: Processing input prompts, computing initial KV cache entries</li>
<li><strong>Generation phase</strong>: Autoregressive token generation using cached keys/values</li>
</ol>
<p>With in-flight batching, sequences in context phase process together with sequences in generation phase. When a generation sequence finishes, a new context sequence can immediately take its slot. This keeps GPU utilization high even with variable-length inputs and outputs.</p>
<h3 id="paged-kv-cache">Paged KV cache</h3>
<p>Instead of allocating KV cache as contiguous memory blocks, paged attention splits the cache into smaller blocks that can be allocated non-contiguously<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-9" id="user-content-fnref-9">18</a></sup>. This approach:</p>
<ul>
<li>Eliminates memory fragmentation from variable sequence lengths</li>
<li>Enables KV cache sharing between requests with common prefixes</li>
<li>Supports block sizes of 8, 16, 32, 64, or 128 tokens per block</li>
</ul>
<p>The paged KV cache allocates memory upfront in TensorRT-LLM rather than on-demand. Plan for approximately 60% additional VRAM beyond model weights for KV cache allocation<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-10" id="user-content-fnref-10">19</a></sup>.</p>
<h3 id="chunked-prefill">Chunked prefill</h3>
<p>Long input contexts can delay generation for concurrent requests. Chunked prefill divides the context phase into smaller chunks, enabling better parallelization with decode operations<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-11" id="user-content-fnref-11">20</a></sup>.</p>
<p>This feature allows GPU systems to handle longer contexts and higher concurrency by decoupling memory consumption from context length.</p>
<hr>
<h2 id="triton-inference-server-architecture">Triton Inference Server architecture</h2>
<p>Triton provides the serving infrastructure around TensorRT-LLM engines. It handles request routing, model loading, batching, and metrics collection. The OpenAI-compatible frontend transitioned from beta to stable in 2025, enabling drop-in compatibility with OpenAI API clients<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new10" id="user-content-fnref-new10">21</a></sup>.</p>
<h3 id="core-components">Core components</h3>
<p><strong>Model Repository</strong>: A directory structure containing model configurations and artifacts. Each model has a <code>config.pbtxt</code> file defining inputs, outputs, and backend settings.</p>
<p><strong>Scheduler</strong>: Routes incoming requests to model instances. For LLMs, the scheduler typically delegates batching decisions to the TensorRT-LLM runtime.</p>
<p><strong>Backend Manager</strong>: Loads and manages model backends. The TensorRT-LLM backend uses the C++ Executor API for optimal performance.</p>
<p><strong>Metrics Server</strong>: Exposes Prometheus-compatible metrics on port 8002 by default.</p>
<p><strong>Security note</strong>: Organizations should update to Triton version 25.07 or later to address CVE-2025-23319, CVE-2025-23320, and CVE-2025-23334, which when chained could allow unauthenticated remote code execution<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new11" id="user-content-fnref-new11">22</a></sup>.</p>
<h3 id="model-ensemble-structure">Model ensemble structure</h3>
<p>The TensorRT-LLM backend uses an ensemble of three models:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>model_repository/</span></span>
<span class="line"><span>├── ensemble/</span></span>
<span class="line"><span>│   └── config.pbtxt</span></span>
<span class="line"><span>├── preprocessing/</span></span>
<span class="line"><span>│   ├── config.pbtxt</span></span>
<span class="line"><span>│   └── 1/model.py</span></span>
<span class="line"><span>├── tensorrt_llm/</span></span>
<span class="line"><span>│   ├── config.pbtxt</span></span>
<span class="line"><span>│   └── 1/</span></span>
<span class="line"><span>│       └── [engine files]</span></span>
<span class="line"><span>└── postprocessing/</span></span>
<span class="line"><span>    ├── config.pbtxt</span></span>
<span class="line"><span>    └── 1/model.py</span></span></code></pre>
<p><strong>Preprocessing</strong>: Tokenizes input text using the model’s tokenizer
<strong>TensorRT-LLM</strong>: Runs inference on the optimized engine
<strong>Postprocessing</strong>: Detokenizes output IDs back to text</p>
<p>The ensemble model chains these together automatically.</p>
<hr>
<h2 id="configuring-triton-for-llms">Configuring Triton for LLMs</h2>
<h3 id="tensorrtllm-model-configuration">tensorrt_llm model configuration</h3>
<p>The core configuration for the TensorRT-LLM backend:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">name: &quot;tensorrt_llm&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">backend: &quot;tensorrtllm&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">max_batch_size: 64</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_transaction_policy {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  decoupled: true</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">input [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;input_ids&quot; data_type: TYPE_INT32 dims: [-1] },</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;input_lengths&quot; data_type: TYPE_INT32 dims: [1] },</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;request_output_len&quot; data_type: TYPE_INT32 dims: [1] },</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;end_id&quot; data_type: TYPE_INT32 dims: [1] },</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;pad_id&quot; data_type: TYPE_INT32 dims: [1] },</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;stream&quot; data_type: TYPE_BOOL dims: [1] optional: true }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;output_ids&quot; data_type: TYPE_INT32 dims: [-1] },</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  { name: &quot;sequence_length&quot; data_type: TYPE_INT32 dims: [1] }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">parameters {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  key: &quot;gpt_model_path&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  value: { string_value: &quot;/engines/llama-70b-fp8&quot; }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">parameters {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  key: &quot;batching_type&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  value: { string_value: &quot;inflight_fused_batching&quot; }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">parameters {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  key: &quot;max_tokens_in_paged_kv_cache&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  value: { string_value: &quot;2048000&quot; }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">parameters {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  key: &quot;kv_cache_free_gpu_mem_fraction&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  value: { string_value: &quot;0.85&quot; }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Key parameters explained:</p>






























<div class="table-wrap"><table><thead><tr><th>Parameter</th><th>Value</th><th>Purpose</th></tr></thead><tbody><tr><td><code>decoupled: true</code></td><td>Required for streaming</td><td>Enables async responses</td></tr><tr><td><code>batching_type</code></td><td><code>inflight_fused_batching</code></td><td>Enables continuous batching</td></tr><tr><td><code>max_tokens_in_paged_kv_cache</code></td><td>Token count</td><td>Total KV cache capacity</td></tr><tr><td><code>kv_cache_free_gpu_mem_fraction</code></td><td>0.0-1.0</td><td>Fraction of free GPU memory for KV cache</td></tr></tbody></table></div>
<h3 id="instance-configuration">Instance configuration</h3>
<p>The <code>instance_count</code> parameter controls concurrent execution. For most workloads, set this to 5 or higher<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-12" id="user-content-fnref-12">23</a></sup>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">instance_group [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    count: 5</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    kind: KIND_GPU</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpus: [0, 1, 2, 3]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span></code></pre>
<h3 id="queue-management">Queue management</h3>
<p>Control request queuing behavior:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">parameters {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  key: &quot;max_queue_delay_microseconds&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  value: { string_value: &quot;100000&quot; }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">parameters {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  key: &quot;max_queue_size&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  value: { string_value: &quot;256&quot; }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>Setting <code>max_queue_delay_microseconds</code> above 0 improves batch formation by waiting for additional requests to arrive.</p>
<hr>
<h2 id="tensorrt-llm-backend-setup-walkthrough">TensorRT-LLM backend setup walkthrough</h2>
<h3 id="1-pull-the-container">1. Pull the container</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> nvcr.io/nvidia/tritonserver:25.10-py3</span></span></code></pre>
<h3 id="2-create-model-repository">2. Create model repository</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Clone the backend repository</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/triton-inference-server/tensorrtllm_backend.git</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> tensorrtllm_backend</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Copy template models</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">mkdir</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string)"> /triton_models</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cp</span><span style="color:var(--astro-code-token-string)"> -r</span><span style="color:var(--astro-code-token-string)"> all_models/inflight_batcher_llm/*</span><span style="color:var(--astro-code-token-string)"> /triton_models/</span></span></code></pre>
<h3 id="3-build-the-tensorrt-llm-engine">3. Build the TensorRT-LLM engine</h3>
<p>Follow the engine building steps from earlier, placing the output in <code>/engines/</code>.</p>
<h3 id="4-configure-the-models">4. Configure the models</h3>
<p>Use the provided template filling script:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">ENGINE_DIR</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string)">/engines/llama-70b-fp8</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">TOKENIZER_DIR</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string)">/models/llama-70b</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">MAX_BATCH</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string)">64</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> tools/fill_template.py</span><span style="color:var(--astro-code-token-string)"> -i</span><span style="color:var(--astro-code-token-string)"> /triton_models/preprocessing/config.pbtxt</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  tokenizer_dir:</span><span style="color:var(--astro-code-foreground)">${TOKENIZER_DIR}</span><span style="color:var(--astro-code-token-string)">,triton_max_batch_size:</span><span style="color:var(--astro-code-foreground)">${MAX_BATCH}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> tools/fill_template.py</span><span style="color:var(--astro-code-token-string)"> -i</span><span style="color:var(--astro-code-token-string)"> /triton_models/tensorrt_llm/config.pbtxt</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  triton_backend:tensorrtllm,triton_max_batch_size:</span><span style="color:var(--astro-code-foreground)">${MAX_BATCH}</span><span style="color:var(--astro-code-token-string)">,</span><span style="color:var(--astro-code-foreground)">\</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  decoupled_mode:</span><span style="color:var(--astro-code-token-constant)">true</span><span style="color:var(--astro-code-token-string)">,engine_dir:</span><span style="color:var(--astro-code-foreground)">${ENGINE_DIR}</span><span style="color:var(--astro-code-token-string)">,</span><span style="color:var(--astro-code-foreground)">\</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  batching_type:inflight_fused_batching</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> tools/fill_template.py</span><span style="color:var(--astro-code-token-string)"> -i</span><span style="color:var(--astro-code-token-string)"> /triton_models/postprocessing/config.pbtxt</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  tokenizer_dir:</span><span style="color:var(--astro-code-foreground)">${TOKENIZER_DIR}</span><span style="color:var(--astro-code-token-string)">,triton_max_batch_size:</span><span style="color:var(--astro-code-foreground)">${MAX_BATCH}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> tools/fill_template.py</span><span style="color:var(--astro-code-token-string)"> -i</span><span style="color:var(--astro-code-token-string)"> /triton_models/ensemble/config.pbtxt</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  triton_max_batch_size:</span><span style="color:var(--astro-code-foreground)">${MAX_BATCH}</span></span></code></pre>
<h3 id="5-launch-the-server">5. Launch the server</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> scripts/launch_triton_server.py</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model_repo</span><span style="color:var(--astro-code-token-string)"> /triton_models</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --world_size</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or directly with tritonserver</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">tritonserver</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model-repository=/triton_models</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --http-port=8000</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --grpc-port=8001</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --metrics-port=8002</span></span></code></pre>
<h3 id="6-verify-deployment">6. Verify deployment</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> localhost:8000/v2/health/ready</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Response: {&quot;ready&quot;:true}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> localhost:8000/v2/models</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Lists all loaded models with READY status</span></span></code></pre>
<hr>
<h2 id="performance-tuning">Performance tuning</h2>
<h3 id="concurrency-and-batch-size">Concurrency and batch size</h3>
<p>The relationship between <code>max_batch_size</code>, engine build parameters, and runtime behavior:</p>
<ul>
<li><strong>Engine <code>max_batch_size</code></strong>: Hard limit set at build time</li>
<li><strong>Triton <code>max_batch_size</code></strong>: Soft limit for request acceptance</li>
<li><strong>Runtime batch size</strong>: Determined by TRT-LLM scheduler based on available requests and memory</li>
</ul>
<p>The TRT-LLM scheduler can form batches larger than Triton’s <code>max_batch_size</code> when using in-flight batching. The scheduler optimizes based on available KV cache memory and pending requests<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-13" id="user-content-fnref-13">24</a></sup>.</p>
<h3 id="gpu-memory-allocation">GPU memory allocation</h3>
<p>Balance KV cache size against model memory requirements:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Calculate KV cache memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">kv_cache_tokens </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> max_batch_size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> max_seq_len</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">kv_cache_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> kv_cache_tokens </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> num_layers </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> hidden_size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> dtype_bytes</span></span></code></pre>
<p>For a 70B model with 80 layers, 8192 hidden size, and FP16 KV cache:</p>
<ul>
<li>64 batch * 8192 seq_len = 524,288 tokens</li>
<li>524,288 _ 80 _ 2 _ 8192 _ 2 bytes = ~1.3 TB (exceeds GPU memory)</li>
</ul>
<p>Use <code>kv_cache_free_gpu_mem_fraction</code> to limit allocation to available memory. Start with 0.85 and adjust based on OOM behavior.</p>
<h3 id="cuda-graphs">CUDA Graphs</h3>
<p>TensorRT-LLM uses CUDA Graphs to reduce kernel launch overhead. The runtime captures execution graphs for common batch sizes and replays them without CPU intervention.</p>
<p>CUDA Graph padding handles mismatched batch sizes by padding to the nearest captured graph size. This trades minor compute waste for consistent low-latency execution<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-14" id="user-content-fnref-14">25</a></sup>.</p>
<h3 id="multi-gpu-configuration">Multi-GPU configuration</h3>
<p>For tensor parallelism across GPUs:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># 4-way tensor parallelism</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">trtllm-build</span><span style="color:var(--astro-code-token-string)"> --tp_size</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-string)"> ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Launch with matching world size</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python3</span><span style="color:var(--astro-code-token-string)"> launch_triton_server.py</span><span style="color:var(--astro-code-token-string)"> --world_size</span><span style="color:var(--astro-code-token-constant)"> 4</span></span></code></pre>
<p>The backend uses MPI to coordinate execution across GPUs. Leader mode uses one process to manage inference; Orchestrator mode distributes coordination<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-15" id="user-content-fnref-15">26</a></sup>.</p>
<hr>
<h2 id="monitoring-and-metrics">Monitoring and metrics</h2>
<h3 id="prometheus-metrics-endpoint">Prometheus metrics endpoint</h3>
<p>Triton exposes metrics at <code>http://localhost:8002/metrics</code> by default. Enable with:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">tritonserver</span><span style="color:var(--astro-code-token-string)"> --allow-metrics=true</span><span style="color:var(--astro-code-token-string)"> --allow-gpu-metrics=true</span></span></code></pre>
<h3 id="key-metrics-for-llm-workloads">Key metrics for LLM workloads</h3>
<p>The TensorRT-LLM backend exposes custom metrics:</p>

























<div class="table-wrap"><table><thead><tr><th>Metric</th><th>Description</th></tr></thead><tbody><tr><td><code>nv_trt_llm_request_count</code></td><td>Total inference requests</td></tr><tr><td><code>nv_trt_llm_inflight_request_count</code></td><td>Currently processing requests</td></tr><tr><td><code>nv_trt_llm_kv_cache_block_usage</code></td><td>KV cache utilization</td></tr><tr><td><code>nv_trt_llm_generation_tokens_per_second</code></td><td>Output throughput</td></tr></tbody></table></div>
<p>Standard Triton metrics include:</p>

























<div class="table-wrap"><table><thead><tr><th>Metric</th><th>Description</th></tr></thead><tbody><tr><td><code>nv_inference_request_success</code></td><td>Successful request count</td></tr><tr><td><code>nv_inference_compute_output_duration_us</code></td><td>Inference latency</td></tr><tr><td><code>nv_gpu_utilization</code></td><td>GPU compute utilization</td></tr><tr><td><code>nv_gpu_memory_used_bytes</code></td><td>GPU memory consumption</td></tr></tbody></table></div>
<h3 id="grafana-dashboard-setup">Grafana dashboard setup</h3>
<p>NVIDIA provides pre-built Grafana dashboards in the tutorials repository<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-16" id="user-content-fnref-16">27</a></sup>. Import the JSON configuration:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Download dashboard JSON</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> -O</span><span style="color:var(--astro-code-token-string)"> https://raw.githubusercontent.com/triton-inference-server/tutorials/main/</span><span style="color:var(--astro-code-foreground)">\</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">Deployment/Kubernetes/TensorRT-LLM_Autoscaling_and_Load_Balancing/\</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">grafana_inference-metrics_dashboard.json</span></span></code></pre>
<p>Configure Prometheus as a data source in Grafana, then import the dashboard JSON.</p>
<h3 id="genai-perf-benchmarking">GenAI-Perf benchmarking</h3>
<p>Measure throughput and latency with GenAI-Perf:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">genai-perf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model</span><span style="color:var(--astro-code-token-string)"> ensemble</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --backend</span><span style="color:var(--astro-code-token-string)"> tensorrtllm</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --endpoint</span><span style="color:var(--astro-code-token-string)"> localhost:8001</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --streaming</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --concurrency</span><span style="color:var(--astro-code-token-constant)"> 32</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --input-sequence-length</span><span style="color:var(--astro-code-token-constant)"> 512</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --output-sequence-length</span><span style="color:var(--astro-code-token-constant)"> 128</span></span></code></pre>
<p>The tool reports time-to-first-token, inter-token latency, throughput, and percentile statistics<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-17" id="user-content-fnref-17">28</a></sup>.</p>
<hr>
<h2 id="comparison-with-alternatives">Comparison with alternatives</h2>
<figure><figcaption><strong>LLM Inference Engine Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/tensorrt-triton-inference-1.webp" width="512" height="442" alt="LLM Inference Engine Comparison" loading="lazy" decoding="async"></figure>
<h3 id="tensorrt-llm">TensorRT-LLM</h3>
<p>Best for maximum throughput when you can invest in setup complexity. January 2026 benchmarks show B200 delivering 60,000 tokens per second per GPU at 1,000 tokens per second per user interactivity on Llama 3.3 70B<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new12" id="user-content-fnref-new12">29</a></sup>. On Llama 4 Scout, B200 achieves over 42,000 tokens per second with a 3.4x performance increase over H200<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new1" id="user-content-fnref-new1-2">1</a></sup>.</p>
<p><strong>Strengths</strong>: Peak throughput, Tensor Core optimization, deep NVIDIA integration, EAGLE-3 speculative decoding, NVFP4 quantization
<strong>Trade-offs</strong>: Longer setup time (1-2 weeks for production tuning), engine rebuilds required for config changes</p>
<h3 id="vllm">vLLM</h3>
<p>Best for teams prioritizing development velocity and concurrency handling. PagedAttention provides GPU-friendly memory layout without fragmentation<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-19" id="user-content-fnref-19">30</a></sup>. vLLM v0.14.1 (January 2026) adds W4A8 grouped GEMM on Hopper, MoE + LoRA support with AWQ Marlin, and Transformers v5 RoPE compatibility<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new13" id="user-content-fnref-new13">31</a></sup>.</p>
<p>Inferact Inc. launched in January 2026 to commercialize vLLM with $150M funding at an $800M valuation<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new14" id="user-content-fnref-new14">32</a></sup>. The vLLM project now runs on over 400,000 GPUs worldwide.</p>
<p><strong>Strengths</strong>: Easy setup (1-2 days), consistent latency under load, active community, broad hardware support (NVIDIA, AMD, Intel, TPU)
<strong>Trade-offs</strong>: Lower peak throughput than TensorRT-LLM on Blackwell GPUs</p>
<h3 id="sglang">SGLang</h3>
<p>SGLang has become a production standard, generating trillions of tokens daily across deployments at xAI, AMD, NVIDIA, LinkedIn, Cursor, and major cloud providers<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new15" id="user-content-fnref-new15">33</a></sup>. On H100 hardware, SGLang achieves 16,215 tokens per second, matching LMDeploy and exceeding vLLM by 29%<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new16" id="user-content-fnref-new16">34</a></sup>.</p>
<p><strong>Strengths</strong>: RadixAttention for prefix caching (50%+ hit rates in production), zero-overhead CPU scheduler, prefill-decode disaggregation, native C++ optimization
<strong>Trade-offs</strong>: Smaller ecosystem than vLLM</p>
<h3 id="text-generation-inference-v3">Text-Generation-Inference v3</h3>
<p>TGI is now in maintenance mode, with Hugging Face recommending vLLM or SGLang for new deployments<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new17" id="user-content-fnref-new17">35</a></sup>. TGI v3 achieves 13x speedups on prompts exceeding 200,000 tokens by reusing earlier token computations<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-20" id="user-content-fnref-20">36</a></sup>.</p>
<p><strong>Strengths</strong>: Prefix caching, long context handling, Hugging Face ecosystem, zero-config mode
<strong>Trade-offs</strong>: Maintenance mode limits new feature development</p>
<h3 id="decision-matrix">Decision matrix</h3>









































<div class="table-wrap"><table><thead><tr><th>Requirement</th><th>Recommended Engine</th></tr></thead><tbody><tr><td>Maximum throughput on Blackwell</td><td>TensorRT-LLM</td></tr><tr><td>Fast iteration cycles</td><td>vLLM or SGLang</td></tr><tr><td>High concurrency, consistent latency</td><td>vLLM</td></tr><tr><td>Prefix caching in production</td><td>SGLang</td></tr><tr><td>Long chat histories (200K+ tokens)</td><td>TGI v3</td></tr><tr><td>Kubernetes-native deployment</td><td>Triton + TensorRT-LLM</td></tr><tr><td>Datacenter-scale orchestration</td><td>NVIDIA Dynamo</td></tr><tr><td>Multi-vendor hardware support</td><td>vLLM</td></tr></tbody></table></div>
<hr>
<h2 id="complete-deployment-example">Complete deployment example</h2>
<p>End-to-end deployment of Llama 3.1 70B on 4x H100 80GB:</p>
<h3 id="environment-setup">Environment setup</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Pull containers</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> nvcr.io/nvidia/tritonserver:25.10-py3</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> nvcr.io/nvidia/pytorch:25.10-py3</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Download model</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">huggingface-cli</span><span style="color:var(--astro-code-token-string)"> download</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3.1-70B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --local-dir</span><span style="color:var(--astro-code-token-string)"> /models/llama-3.1-70b</span></span></code></pre>
<h3 id="build-engine">Build engine</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> --gpus</span><span style="color:var(--astro-code-token-string)"> all</span><span style="color:var(--astro-code-token-string)"> -v</span><span style="color:var(--astro-code-token-string)"> /models:/models</span><span style="color:var(--astro-code-token-string)"> -v</span><span style="color:var(--astro-code-token-string)"> /engines:/engines</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  nvcr.io/nvidia/pytorch:25.10-py3</span><span style="color:var(--astro-code-token-string)"> bash</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string-expression)"> &quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    pip install tensorrt-llm</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    # Quantize to FP8</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    python -m tensorrt_llm.commands.quantize \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --model_dir /models/llama-3.1-70b \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --qformat fp8 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --kv_cache_dtype fp8 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --output_dir /engines/llama-70b-ckpt \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --tp_size 4</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    # Build engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    trtllm-build \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --checkpoint_dir /engines/llama-70b-ckpt \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --output_dir /engines/llama-70b-engine \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --gemm_plugin fp8 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --gpt_attention_plugin fp8 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --max_batch_size 64 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --max_input_len 4096 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --max_seq_len 8192 \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --use_fused_mlp enable \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --workers 4</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">&quot;</span></span></code></pre>
<h3 id="deploy-with-triton">Deploy with Triton</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">docker</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> --gpus</span><span style="color:var(--astro-code-token-string)"> all</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string)"> 8000:8000</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string)"> 8001:8001</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string)"> 8002:8002</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  -v</span><span style="color:var(--astro-code-token-string)"> /engines:/engines</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  -v</span><span style="color:var(--astro-code-token-string)"> /models:/models</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  -v</span><span style="color:var(--astro-code-token-string)"> /triton_models:/triton_models</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  nvcr.io/nvidia/tritonserver:25.10-py3</span><span style="color:var(--astro-code-token-string)"> bash</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string-expression)"> &quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    # Configure models (using fill_template.py as shown earlier)</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    # ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    tritonserver \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --model-repository=/triton_models \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --allow-metrics=true \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">      --allow-gpu-metrics=true</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">&quot;</span></span></code></pre>
<h3 id="test-inference">Test inference</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> tritonclient</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">grpc </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> grpcclient</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> grpcclient</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">InferenceServerClient</span><span style="color:var(--astro-code-token-punctuation)">(url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;localhost:8001&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prepare inputs</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain the attention mechanism in transformers:&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">input_ids </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">(prompt)</span><span style="color:var(--astro-code-token-comment)">  # Use appropriate tokenizer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    grpcclient</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">InferInput</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;text_input&quot;</span><span style="color:var(--astro-code-token-punctuation)">, [</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">], </span><span style="color:var(--astro-code-token-string-expression)">&quot;BYTES&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    grpcclient</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">InferInput</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;max_tokens&quot;</span><span style="color:var(--astro-code-token-punctuation)">, [</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">], </span><span style="color:var(--astro-code-token-string-expression)">&quot;INT32&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    grpcclient</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">InferInput</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;stream&quot;</span><span style="color:var(--astro-code-token-punctuation)">, [</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">], </span><span style="color:var(--astro-code-token-string-expression)">&quot;BOOL&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">set_data_from_numpy</span><span style="color:var(--astro-code-token-punctuation)">(np.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([[prompt]], dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">object</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">set_data_from_numpy</span><span style="color:var(--astro-code-token-punctuation)">(np.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([[</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">]], dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">np.int32))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">set_data_from_numpy</span><span style="color:var(--astro-code-token-punctuation)">(np.</span><span style="color:var(--astro-code-token-function)">array</span><span style="color:var(--astro-code-token-punctuation)">([[</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">]], dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">bool</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Stream responses</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> response </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">infer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;ensemble&quot;</span><span style="color:var(--astro-code-token-punctuation)">, inputs, stream</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> response</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">as_numpy</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;text_output&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(output[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(), end</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;</span><span style="color:var(--astro-code-token-punctuation)">, flush</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="blackwell-gpu-deployment">Blackwell GPU deployment</h2>
<p>NVIDIA Blackwell architecture (B200, GB200, GB300) delivers substantial inference improvements over Hopper:</p>

























<div class="table-wrap"><table><thead><tr><th>Metric</th><th>B200 vs H200</th></tr></thead><tbody><tr><td>Tokens per second (Llama 3.3 70B)</td><td>4x higher throughput<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new12" id="user-content-fnref-new12-2">29</a></sup></td></tr><tr><td>Tokens per second (Llama 4 Scout)</td><td>3.4x improvement<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new1" id="user-content-fnref-new1-3">1</a></sup></td></tr><tr><td>DeepSeek-R1 inference</td><td>5x vs Hopper per GPU<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new18" id="user-content-fnref-new18">37</a></sup></td></tr><tr><td>Memory per GPU</td><td>Up to 288GB HBM3e (Blackwell Ultra)</td></tr></tbody></table></div>
<p>The GB200 NVL72 rack-scale platform connects 72 Blackwell GPUs using fifth-generation NVLink with 1,800 GB/s bidirectional bandwidth between all chips<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new19" id="user-content-fnref-new19">38</a></sup>. Blackwell Ultra (GB300) delivers 45% higher DeepSeek-R1 throughput than GB200 with 1.5x more NVFP4 compute and 2x more attention-layer acceleration<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new18" id="user-content-fnref-new18-2">37</a></sup>.</p>
<p>Key Blackwell optimizations in TensorRT-LLM:</p>
<ul>
<li>NVFP4 quantization for weights and KV cache</li>
<li>Programmatic dependent launch (PDL) for reduced kernel latencies</li>
<li>Enhanced all-to-all communication primitives</li>
<li>CuteDSL NVFP4 grouped GEMM integration</li>
</ul>
<p>The January 2026 TensorRT-LLM optimizations delivered up to 2.8x throughput increases per Blackwell GPU over the previous three months<sup><a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fn-new20" id="user-content-fnref-new20">39</a></sup>.</p>
<hr>
<h2 id="summary">Summary</h2>
<p>TensorRT-LLM and Triton Inference Server provide production-grade LLM serving with:</p>
<ul>
<li><strong>Engine optimization</strong>: NVFP4/FP8 quantization, kernel fusion, custom attention kernels, and EAGLE-3 speculative decoding</li>
<li><strong>Efficient batching</strong>: In-flight batching and paged KV cache for high throughput</li>
<li><strong>Disaggregated serving</strong>: Separate prefill and decode phases for optimized resource utilization</li>
<li><strong>Flexible deployment</strong>: Multi-GPU, multi-node support with MPI coordination and NVIDIA Dynamo orchestration</li>
<li><strong>Observability</strong>: Prometheus metrics integration for monitoring</li>
</ul>
<p>The setup complexity is higher than alternatives like vLLM or SGLang, but peak performance on Blackwell GPUs is also higher. For teams already in the NVIDIA ecosystem deploying at scale, this stack delivers consistent, optimized inference. Organizations prioritizing faster iteration should evaluate vLLM (broad hardware support, active development) or SGLang (production-proven prefix caching).</p>
<hr>
<h2 id="references">References</h2>
<h3 id="2026-updates">2026 updates</h3>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-new1">
<p><a href="https://developer.nvidia.com/blog/nvidia-accelerates-inference-on-meta-llama-4-scout-and-maverick/" rel="noopener noreferrer">NVIDIA Accelerates Inference on Meta Llama 4 Scout and Maverick</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new1" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new1-2" class="data-footnote-backref">↩<sup>2</sup></a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new1-3" class="data-footnote-backref">↩<sup>3</sup></a></p>
</li>
<li id="user-content-fn-new2">
<p><a href="https://developer.nvidia.com/blog/introducing-nvidia-dynamo-a-low-latency-distributed-inference-framework-for-scaling-reasoning-ai-models/" rel="noopener noreferrer">NVIDIA Dynamo Open-Source Inference Framework</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new2" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new2-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-1">
<p><a href="https://nvidia.github.io/TensorRT-LLM/" rel="noopener noreferrer">TensorRT-LLM Documentation</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new3">
<p><a href="https://nvidia.github.io/TensorRT-LLM/release-notes.html" rel="noopener noreferrer">TensorRT-LLM Release Notes</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new4">
<p><a href="https://developer.nvidia.com/blog/introducing-nvfp4-for-efficient-and-accurate-low-precision-inference/" rel="noopener noreferrer">Introducing NVFP4 for Efficient Low-Precision Inference</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new4" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new4-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-2">
<p><a href="https://nvidia.github.io/TensorRT-LLM/overview.html" rel="noopener noreferrer">TensorRT-LLM Overview</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p><a href="https://introl.com/blog/tensorrt-llm-optimization-nvidia-inference-stack-guide" rel="noopener noreferrer">TensorRT-LLM Optimization Guide</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new5">
<p><a href="https://developer.nvidia.com/blog/delivering-massive-performance-leaps-for-mixture-of-experts-inference-on-nvidia-blackwell/" rel="noopener noreferrer">Delivering Massive Performance Leaps for MoE Inference on Blackwell</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p><a href="https://nvidia.github.io/TensorRT-LLM/architecture/overview.html" rel="noopener noreferrer">TensorRT-LLM Architecture Overview</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new6">
<p><a href="https://developer.nvidia.com/blog/blackwell-breaks-the-1000-tps-user-barrier-with-metas-llama-4-maverick/" rel="noopener noreferrer">Blackwell Breaks 1,000 TPS/User Barrier with Llama 4 Maverick</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new7">
<p><a href="https://developer.nvidia.com/blog/nvidia-tensorrt-llm-now-supports-recurrent-drafting-for-optimizing-llm-inference/" rel="noopener noreferrer">TensorRT-LLM Supports Recurrent Drafting for LLM Inference</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p><a href="https://github.com/NVIDIA/TensorRT-LLM/blob/main/examples/quantization/README.md" rel="noopener noreferrer">TensorRT-LLM Quantization Examples</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p><a href="https://nvidia.github.io/TensorRT-LLM/latest/commands/trtllm-build.html" rel="noopener noreferrer">trtllm-build Command Reference</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p><a href="https://nvidia.github.io/TensorRT-LLM/blogs/tech_blog/blog1_Pushing_Latency_Boundaries_Optimizing_DeepSeek-R1_Performance_on_NVIDIA_B200_GPUs.html" rel="noopener noreferrer">DeepSeek-R1 Optimization on B200</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new8">
<p><a href="https://nvidia.github.io/TensorRT-LLM/blogs/tech_blog/blog5_Disaggregated_Serving_in_TensorRT-LLM.html" rel="noopener noreferrer">Disaggregated Serving in TensorRT-LLM</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new9">
<p><a href="https://www.infoq.com/articles/llms-evolution-ai-infrastructure/" rel="noopener noreferrer">Disaggregation in LLMs: Evolution in AI Infrastructure</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p><a href="https://developer.nvidia.com/blog/nvidia-tensorrt-llm-now-accelerates-encoder-decoder-models-with-in-flight-batching/" rel="noopener noreferrer">TensorRT-LLM In-Flight Batching</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p><a href="https://nvidia.github.io/TensorRT-LLM/advanced/gpt-attention.html" rel="noopener noreferrer">TensorRT-LLM GPT Attention</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10">
<p><a href="https://softwaremill.com/boosting-llms-performance-in-production/" rel="noopener noreferrer">Boosting LLMs Performance in Production</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11">
<p><a href="https://developer.nvidia.com/blog/streamlining-ai-inference-performance-and-deployment-with-nvidia-tensorrt-llm-chunked-prefill/" rel="noopener noreferrer">TensorRT-LLM Chunked Prefill</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-11" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new10">
<p><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/release-notes/index.html" rel="noopener noreferrer">Triton Inference Server Release Notes</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new11">
<p><a href="https://www.darkreading.com/vulnerabilities-threats/nvidia-patches-critical-rce-vulnerability-chain" rel="noopener noreferrer">NVIDIA Patches Critical RCE Vulnerability Chain</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new11" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-12">
<p><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/tensorrtllm_backend/docs/model_config.html" rel="noopener noreferrer">Triton TensorRT-LLM Model Configuration</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p><a href="https://github.com/triton-inference-server/tensorrtllm_backend/blob/main/docs/model_config.md" rel="noopener noreferrer">TensorRT-LLM Backend Documentation</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-13" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-14">
<p><a href="https://developer.nvidia.com/blog/nvidia-tensorrt-llm-supercharges-large-language-model-inference-on-nvidia-h100-gpus/" rel="noopener noreferrer">NVIDIA TensorRT-LLM H100 Performance</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-14" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-15">
<p><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/tutorials/Deployment/Kubernetes/TensorRT-LLM_Multi-Node_Distributed_Models/README.html" rel="noopener noreferrer">Triton Multi-Node Deployment</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16">
<p><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/tutorials/Deployment/Kubernetes/TensorRT-LLM_Autoscaling_and_Load_Balancing/README.html" rel="noopener noreferrer">Triton Autoscaling and Load Balancing Tutorial</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-17">
<p><a href="https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/user_guide/metrics.html" rel="noopener noreferrer">Triton Metrics Documentation</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new12">
<p><a href="https://developer.nvidia.com/blog/nvidia-blackwell-leads-on-new-semianalysis-inferencemax-benchmarks/" rel="noopener noreferrer">NVIDIA Blackwell Leads on InferenceMAX Benchmarks</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new12" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new12-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-19">
<p><a href="https://northflank.com/blog/vllm-vs-tensorrt-llm-and-how-to-run-them" rel="noopener noreferrer">vLLM vs TensorRT-LLM Comparison</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new13">
<p><a href="https://github.com/vllm-project/vllm/releases" rel="noopener noreferrer">vLLM Releases - GitHub</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new13" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new14">
<p><a href="https://siliconangle.com/2026/01/22/inferact-launches-150m-funding-commercialize-vllm/" rel="noopener noreferrer">Inferact launches with $150M to commercialize vLLM</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new14" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new15">
<p><a href="https://github.com/sgl-project/sglang" rel="noopener noreferrer">SGLang GitHub Repository</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new16">
<p><a href="https://research.aimultiple.com/inference-engines/" rel="noopener noreferrer">LLM Inference Engines: vLLM vs LMDeploy vs SGLang 2026</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new17">
<p><a href="https://huggingface.co/docs/text-generation-inference/en/index" rel="noopener noreferrer">Hugging Face Text Generation Inference</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-20">
<p><a href="https://www.marktechpost.com/2025/11/19/vllm-vs-tensorrt-llm-vs-hf-tgi-vs-lmdeploy-a-deep-technical-comparison-for-production-llm-inference/" rel="noopener noreferrer">vLLM vs TensorRT-LLM vs TGI Technical Comparison</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-20" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new18">
<p><a href="https://developer.nvidia.com/blog/nvidia-blackwell-ultra-sets-new-inference-records-in-mlperf-debut/" rel="noopener noreferrer">NVIDIA Blackwell Ultra Sets New Inference Records in MLPerf</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new18" class="data-footnote-backref">↩</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new18-2" class="data-footnote-backref">↩<sup>2</sup></a></p>
</li>
<li id="user-content-fn-new19">
<p><a href="https://nvidianews.nvidia.com/news/nvidia-blackwell-platform-arrives-to-power-a-new-era-of-computing" rel="noopener noreferrer">NVIDIA Blackwell Platform Arrives</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-new20">
<p><a href="https://blogs.nvidia.com/blog/blackwell-inferencemax-benchmark-results/" rel="noopener noreferrer">NVIDIA Blackwell Raises Bar in InferenceMAX Benchmarks</a> <a href="https://blog.ecitis.org/tensorrt-triton-inference/#user-content-fnref-new20" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>LLM serving</category>
            <category>Compilers</category>
            <category>GPUs</category>
            <enclosure length="58756" type="image/png" url="https://blog.ecitis.org/open-graph/tensorrt-triton-inference.png"/>
        </item>
        <item>
            <title><![CDATA[Voice and Audio AI Models: Architecture, Training, and Deployment]]></title>
            <link>https://blog.ecitis.org/voice-audio-models/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/voice-audio-models/</guid>
            <pubDate>Mon, 26 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Survey the architectures behind speech recognition, synthesis, voice conversion, and full-duplex conversational audio.]]></description>
            <content:encoded><![CDATA[<p>Voice and audio AI has advanced rapidly, with models now capable of real-time transcription, natural speech synthesis, voice cloning from seconds of audio, and full-duplex conversational interactions. This article provides a technical survey of the architectures behind speech-to-text, text-to-speech, voice conversion, and audio language models. We cover the mathematical foundations, practical training considerations, and deployment strategies for building production systems.</p>
<hr>
<h2 id="speech-to-text-models">Speech-to-Text Models</h2>
<p>Automatic speech recognition (ASR) converts audio waveforms into text. Modern ASR systems have moved from traditional hidden Markov models with hand-crafted acoustic features to end-to-end neural networks trained on massive datasets.</p>
<h3 id="whisper-architecture">Whisper Architecture</h3>
<p><a href="https://github.com/openai/whisper">Whisper</a>, released by OpenAI in September 2022, is a Transformer-based encoder-decoder model trained on 680,000 hours of multilingual and multitask supervised data collected from the web [1].</p>
<p><strong>Input Processing</strong></p>
<p>Audio is resampled to 16 kHz and converted to an 80-channel log-magnitude Mel spectrogram using 25ms windows with 10ms stride. The spectrogram is normalized to a [-1, 1] range with near-zero mean. For the large-v3 model, this was increased to 128 Mel frequency bins [2].</p>
<p><strong>Encoder</strong></p>
<p>The encoder processes the Mel spectrogram through:</p>
<ol>
<li>Two convolutional layers for initial downsampling</li>
<li>Sinusoidal positional embeddings added to the sequence</li>
<li>A stack of Transformer encoder blocks with pre-activation residual connections</li>
<li>Layer normalization on the final output</li>
</ol>
<figure><figcaption><strong>Whisper Encoder Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-0.webp" width="512" height="291" alt="Whisper Encoder Architecture" loading="lazy" decoding="async"></figure>
<p><strong>Decoder</strong></p>
<p>The decoder follows the standard Transformer decoder architecture with learned positional embeddings and tied input-output token representations (same weight matrix for input embeddings and output projection). It uses byte-pair encoding tokenization similar to GPT-2.</p>
<p><strong>Model Sizes</strong></p>















































<div class="table-wrap"><table><thead><tr><th>Model</th><th>Parameters</th><th>Layers</th><th>Width</th><th>Heads</th></tr></thead><tbody><tr><td>tiny</td><td>39M</td><td>4</td><td>384</td><td>6</td></tr><tr><td>base</td><td>74M</td><td>6</td><td>512</td><td>8</td></tr><tr><td>small</td><td>244M</td><td>12</td><td>768</td><td>12</td></tr><tr><td>medium</td><td>769M</td><td>24</td><td>1024</td><td>16</td></tr><tr><td>large-v3</td><td>1.55B</td><td>32</td><td>1280</td><td>20</td></tr></tbody></table></div>
<p>Whisper’s encoder-decoder structure makes it naturally suited for batch processing but challenging for streaming applications. The encoder requires the full 30-second audio context before producing useful representations [3].</p>
<h3 id="conformer-architecture">Conformer Architecture</h3>
<p>The <a href="https://arxiv.org/abs/2005.08100">Conformer</a> (Convolution-augmented Transformer), introduced by Google in 2020, addresses a fundamental limitation of pure Transformers: while self-attention captures global context well, it struggles with local feature patterns that are important for speech [4].</p>
<p><strong>Key Insight</strong></p>
<p>Speech signals contain both local patterns (phonemes, formants) and global dependencies (grammar, semantics). CNNs excel at local feature extraction while Transformers handle global context. Conformer combines both.</p>
<p><strong>Block Structure</strong></p>
<p>The Conformer uses a “macaron” structure where two feed-forward layers sandwich the attention and convolution modules:</p>
<figure><figcaption><strong>Conformer Block Structure</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-1.webp" width="512" height="718" alt="Conformer Block Structure" loading="lazy" decoding="async"></figure>
<p><strong>Convolution Module Details</strong></p>
<p>The convolution module uses depthwise separable convolutions for efficiency:</p>
<ol>
<li><strong>Pointwise conv</strong> with expansion factor 2 followed by GLU activation</li>
<li><strong>Depthwise conv</strong> (1D, kernel size typically 31) captures local context</li>
<li><strong>BatchNorm</strong> for training stability</li>
<li><strong>Swish activation</strong> (x * sigmoid(x))</li>
<li><strong>Pointwise conv</strong> to project back to original dimension</li>
</ol>
<p>The depthwise convolution has kernel size 31 by default, meaning each output position attends to 15 frames on each side (roughly 150ms of audio context at standard 10ms frame rates).</p>
<p><strong>Performance</strong></p>
<p>On LibriSpeech, Conformer achieves 2.1%/4.3% WER without a language model and 1.9%/3.9% with an external language model on test-clean/test-other [4]. This made it the dominant architecture for production ASR systems through 2024.</p>
<h3 id="ctc-vs-attention-based-decoding">CTC vs Attention-Based Decoding</h3>
<p>ASR systems use two main decoding paradigms with different tradeoffs [5].</p>
<p><strong>Connectionist Temporal Classification (CTC)</strong></p>
<p>CTC, introduced by Graves et al. in 2006, addresses the alignment problem in sequence-to-sequence tasks where input and output sequences have different lengths.</p>
<p>Key properties:</p>
<ul>
<li><strong>Monotonic alignment</strong>: Output tokens appear in the same order as their corresponding input frames</li>
<li><strong>Conditional independence</strong>: CTC assumes each output token is independent given the input, which simplifies computation but ignores output dependencies</li>
<li><strong>Blank token</strong>: A special “blank” token allows the model to output nothing for frames that fall between phonemes</li>
</ul>
<figure><figcaption><strong>CTC Decoding Example</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-2.webp" width="512" height="197" alt="CTC Decoding Example" loading="lazy" decoding="async"></figure>
<p>CTC loss marginalizes over all possible alignments between input and output:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>P(Y|X) = Σ P(A|X)  for all valid alignments A</span></span></code></pre>
<p>Models like Wav2Vec2, HuBERT, and M-CTC-T use CTC [5]. The main advantage is <strong>non-autoregressive decoding</strong>: all output tokens can be computed in parallel, enabling faster inference.</p>
<p><strong>Attention-Based Encoder-Decoder (AED)</strong></p>
<p>AED models (like Whisper) use cross-attention to learn soft alignments between encoder outputs and decoder states:</p>
<ul>
<li><strong>No independence assumption</strong>: Each output token conditions on all previous tokens</li>
<li><strong>Implicit language model</strong>: The decoder learns language patterns from training data</li>
<li><strong>Flexible alignment</strong>: Can handle non-monotonic mappings (useful for translation)</li>
</ul>
<p>The downside is <strong>autoregressive decoding</strong>: tokens must be generated sequentially, increasing latency.</p>
<p><strong>Hybrid CTC/Attention</strong></p>
<p>Many modern systems combine both approaches. During training, a CTC loss is applied to the encoder output as a regularizer, encouraging monotonic alignment. During inference, CTC scores can be used to constrain beam search [5].</p>
<p>The RNN-Transducer (RNN-T), used by Google and other production systems, extends CTC with a prediction network that models output dependencies without full autoregressive decoding.</p>
<h3 id="streaming-asr-challenges-and-solutions">Streaming ASR Challenges and Solutions</h3>
<p>Real-time applications like voice assistants require streaming ASR that transcribes speech with minimal latency. This is challenging because Transformer models rely on full-sequence attention [6].</p>
<p><strong>The Problem</strong></p>
<p>Standard self-attention has O(n^2) complexity and requires the entire sequence:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Attention(Q, K, V) = softmax(QK^T / √d) V</span></span></code></pre>
<p>For a 30-second audio clip at 50 frames/second, this means 1500 frames must be available before processing begins.</p>
<p><strong>Chunked Attention</strong></p>
<p>The primary solution is chunked (or blockwise) attention:</p>
<ol>
<li>Divide input into fixed-size chunks (e.g., 640ms)</li>
<li>Each chunk attends only to itself and a limited left context</li>
<li>Process chunks incrementally as audio arrives</li>
</ol>
<figure><figcaption><strong>Chunked Attention for Streaming</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-3.webp" width="512" height="279" alt="Chunked Attention for Streaming" loading="lazy" decoding="async"></figure>
<p><strong>Tradeoffs</strong></p>
<ul>
<li>Smaller chunks = lower latency, worse accuracy</li>
<li>Larger left context = better accuracy, higher memory and compute</li>
</ul>
<p>Research shows that chunked attention with 32-48 frames of left context and 16-32 frame chunks achieves a reasonable balance. The SSCFormer architecture uses sequentially sampled chunks with causal convolutions to improve accuracy within the streaming constraint [6].</p>
<p><strong>Positional Encoding for Streaming</strong></p>
<p>With chunked attention, relative positional encodings work better than absolute ones. The model only needs to represent distances up to the maximum context window, not positions within an indefinitely long stream.</p>
<hr>
<h2 id="text-to-speech-models">Text-to-Speech Models</h2>
<p>Text-to-speech (TTS) converts text into natural-sounding audio. Modern TTS has evolved from concatenative synthesis (splicing recorded audio) through parametric synthesis to end-to-end neural approaches.</p>
<h3 id="vits-architecture">VITS Architecture</h3>
<p><a href="https://arxiv.org/abs/2106.06103">VITS</a> (Variational Inference with adversarial learning for end-to-end Text-to-Speech) unifies the acoustic model and vocoder into a single end-to-end framework using variational inference and adversarial training [7].</p>
<p><strong>Key Innovation</strong></p>
<p>Traditional TTS pipelines generate mel spectrograms from text, then convert spectrograms to waveforms with a separate vocoder. This two-stage approach can cause mismatch artifacts. VITS generates waveforms directly from text.</p>
<p><strong>Architecture Components</strong></p>
<figure><figcaption><strong>VITS Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-4.webp" width="512" height="576" alt="VITS Architecture" loading="lazy" decoding="async"></figure>
<p><strong>Variational Inference Framework</strong></p>
<p>VITS models TTS as a conditional VAE:</p>
<ol>
<li><strong>Posterior encoder</strong>: During training, encodes ground-truth mel spectrogram into latent z</li>
<li><strong>Prior encoder</strong>: Learns to predict z from text alone (with normalizing flows to increase expressiveness)</li>
<li><strong>Decoder</strong>: Generates waveform from z</li>
</ol>
<p>The training objective combines:</p>
<ul>
<li>Reconstruction loss (from VAE)</li>
<li>KL divergence between prior and posterior</li>
<li>Adversarial loss (discriminator judges waveform quality)</li>
<li>Feature matching loss (compare discriminator activations)</li>
</ul>
<p><strong>Stochastic Duration Predictor</strong></p>
<p>Unlike deterministic duration predictors, VITS uses a stochastic duration predictor that models duration as a distribution. This enables generating the same text with different speaking rates and rhythms, capturing the natural variability in human speech.</p>
<p><strong>VITS2 Improvements</strong></p>
<p>VITS2 enhances the original with:</p>
<ul>
<li>Improved duration prediction using a transformer-based predictor</li>
<li>Better speaker conditioning for multi-speaker models</li>
<li>Monotonic alignment search for more stable training</li>
</ul>
<h3 id="tacotron-family-evolution">Tacotron Family Evolution</h3>
<p>The <a href="https://arxiv.org/abs/1712.05884">Tacotron</a> models pioneered end-to-end TTS using sequence-to-sequence learning [8].</p>
<p><strong>Tacotron 1 (2017)</strong></p>
<ul>
<li>Character-level input (no phoneme conversion needed)</li>
<li>Encoder: CBHG (1-D convolution bank + highway network + bidirectional GRU)</li>
<li>Attention: Content-based attention with location features</li>
<li>Decoder: Autoregressive GRU predicting mel spectrogram frames</li>
<li>Vocoder: Griffin-Lim algorithm (fast but low quality)</li>
</ul>
<p><strong>Tacotron 2 (2017)</strong></p>
<p>Key improvements over Tacotron 1:</p>
<ul>
<li>Simplified encoder: 3 conv layers + bidirectional LSTM</li>
<li>Location-sensitive attention: Adds previous alignment to attention computation</li>
<li>Decoder: 2 autoregressive LSTM layers</li>
<li>PostNet: 5 conv layers to refine mel spectrogram</li>
<li>Vocoder: WaveNet (high quality but slow)</li>
</ul>
<figure><figcaption><strong>Tacotron 2 Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-5.webp" width="512" height="886" alt="Tacotron 2 Architecture" loading="lazy" decoding="async"></figure>
<p>Tacotron 2 achieved MOS of 4.53, approaching the 4.58 MOS of professionally recorded speech [8].</p>
<p><strong>FastSpeech (2019) and FastSpeech 2 (2020)</strong></p>
<p>FastSpeech addressed Tacotron’s slow autoregressive inference:</p>
<ul>
<li><strong>Non-autoregressive</strong>: Generates all mel frames in parallel</li>
<li><strong>Duration predictor</strong>: Explicitly models phoneme durations</li>
<li><strong>Length regulator</strong>: Expands text sequence to match mel length</li>
</ul>
<p>FastSpeech 2 added pitch and energy predictors for more controllable synthesis. It achieves RTF of ~0.02 on V100 (50x faster than real-time).</p>
<h3 id="neural-vocoders">Neural Vocoders</h3>
<p>Vocoders convert mel spectrograms (or other intermediate representations) into audio waveforms. This is an inverse problem: spectrograms discard phase information, which the vocoder must reconstruct.</p>
<p><strong>HiFi-GAN</strong></p>
<p><a href="https://github.com/jik876/hifi-gan">HiFi-GAN</a>, published in 2020, uses GANs for efficient high-fidelity synthesis [9].</p>
<p>Architecture:</p>
<ul>
<li><strong>Generator</strong>: Transposed convolutions for upsampling, multi-receptive field fusion (MRF) blocks</li>
<li><strong>Multi-period discriminator (MPD)</strong>: Multiple discriminators operating on different periodic subsequences (periods 2, 3, 5, 7, 11)</li>
<li><strong>Multi-scale discriminator (MSD)</strong>: Discriminators at different audio resolutions</li>
</ul>
<p>The key insight is that speech contains multiple periodic components (fundamental frequency and harmonics). By having discriminators focus on different periodicities, the model learns to generate all these components correctly.</p>
<figure><figcaption><strong>HiFi-GAN Generator</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-6.webp" width="512" height="628" alt="HiFi-GAN Generator" loading="lazy" decoding="async"></figure>
<p>HiFi-GAN V1 (14M params) generates audio 13.4x faster than real-time on CPU.</p>
<p><strong>BigVGAN</strong></p>
<p><a href="https://arxiv.org/abs/2206.04658">BigVGAN</a>, from NVIDIA in 2022, scales up HiFi-GAN with architectural improvements [10]:</p>
<ol>
<li>
<p><strong>Snake activation</strong>: Periodic activation function x + sin^2(x)/a with learned frequency. Provides inductive bias for generating periodic waveforms.</p>
</li>
<li>
<p><strong>Anti-aliased representation</strong>: Low-pass filtering before downsampling in discriminator to prevent aliasing artifacts.</p>
</li>
<li>
<p><strong>Larger scale</strong>: Up to 112M parameters (vs 14M for HiFi-GAN V1)</p>
</li>
</ol>
<p>BigVGAN trained only on clean speech (LibriTTS) generalizes to unseen speakers, languages, singing, and even instrumental music without fine-tuning.</p>
<h3 id="zero-shot-tts">Zero-Shot TTS</h3>
<p>Zero-shot TTS generates speech in any voice given only a few seconds of reference audio.</p>
<p><strong>VALL-E</strong></p>
<p><a href="https://www.microsoft.com/en-us/research/project/vall-e-x/">VALL-E</a>, from Microsoft in 2023, treats TTS as a language modeling problem [11]:</p>
<ol>
<li>Audio is tokenized using a neural codec (EnCodec) into discrete codes at 75 Hz with 8 codebook levels</li>
<li>A Transformer language model is trained to predict audio tokens given text and a 3-second acoustic prompt</li>
<li>The first codebook level captures semantic content; subsequent levels add acoustic detail</li>
</ol>
<figure><figcaption><strong>VALL-E Zero-Shot TTS</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-7.webp" width="512" height="502" alt="VALL-E Zero-Shot TTS" loading="lazy" decoding="async"></figure>
<p>VALL-E 2 achieved human parity on LibriSpeech and VCTK using repetition-aware sampling and grouped code modeling [11].</p>
<p><strong>XTTS</strong></p>
<p><a href="https://arxiv.org/html/2406.04904v1">XTTS</a>, from Coqui, builds on Tortoise with improvements for multilingual zero-shot TTS [12]:</p>
<ul>
<li>VQ-VAE encodes mel spectrograms at 21.53 Hz (vs 75 Hz for VALL-E, reducing sequence length)</li>
<li>GPT-2 decoder (443M params) predicts audio tokens</li>
<li>Perceiver architecture for speaker conditioning: processes reference mel spectrogram into 32 latent vectors</li>
<li>Supports 16 languages with SOTA results</li>
</ul>
<p>XTTS v2 achieves RTF of 0.48 with 200ms time-to-first-chunk for streaming applications [12].</p>
<p><strong>F5-TTS</strong></p>
<p><a href="https://gradientflow.com/f5-tts/">F5-TTS</a> uses a non-autoregressive design with flow matching:</p>
<ul>
<li>Requires only 2,994 MB GPU memory</li>
<li>Better for resource-constrained environments</li>
<li>Trades streaming capability for efficiency</li>
</ul>
<h3 id="how-voice-cloning-works">How Voice Cloning Works</h3>
<p>Voice cloning extracts a speaker’s characteristics from reference audio and applies them to synthesize new speech [13].</p>
<p><strong>Speaker Encoder</strong></p>
<p>A neural network extracts a fixed-dimension speaker embedding from reference audio:</p>
<ol>
<li>Input: Mel spectrogram of reference audio (3-10 seconds)</li>
<li>Architecture: Often 3-layer LSTM or ECAPA-TDNN</li>
<li>Output: 256-dim speaker embedding vector (d-vector)</li>
</ol>
<figure><figcaption><strong>Speaker Encoder Pipeline</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-8.webp" width="512" height="489" alt="Speaker Encoder Pipeline" loading="lazy" decoding="async"></figure>
<p><strong>Embedding-Based Adaptation</strong></p>
<p>The speaker embedding conditions the TTS model:</p>
<ol>
<li><strong>Concatenation</strong>: Append embedding to encoder output</li>
<li><strong>Addition</strong>: Add embedding to hidden states</li>
<li><strong>FiLM</strong>: Use embedding to predict scale/shift parameters for normalization layers</li>
</ol>
<p>More advanced approaches use attention-based conditioning (like XTTS’s Perceiver) to capture finer-grained speaker characteristics.</p>
<p><strong>Quality vs Data</strong></p>
<ul>
<li>Zero-shot (3-10s reference): Captures voice timbre but may miss speaking style</li>
<li>Few-shot fine-tuning (1-5 min): Better style transfer, requires training</li>
<li>Full fine-tuning (30+ min): Highest quality, significant compute cost</li>
</ul>
<hr>
<h2 id="voice-conversion-and-cloning">Voice Conversion and Cloning</h2>
<p>Voice conversion transforms speech from one speaker to sound like another while preserving linguistic content.</p>
<h3 id="speaker-embedding-extraction">Speaker Embedding Extraction</h3>
<p>Modern voice conversion uses self-supervised models like HuBERT to extract speaker-independent content representations [14].</p>
<p><strong>HuBERT for Content</strong></p>
<p>HuBERT (Hidden-Unit BERT) is trained via masked prediction on audio:</p>
<ol>
<li>Extract MFCC features</li>
<li>Cluster MFCCs with k-means to create pseudo-labels</li>
<li>Train Transformer to predict masked pseudo-labels</li>
<li>Iterate: use model outputs as new clustering targets</li>
</ol>
<p>The resulting representations capture phonetic content while being somewhat speaker-invariant. RVC uses layer 12 of HuBERT as content features [14].</p>
<p><strong>ECAPA-TDNN for Speaker</strong></p>
<p>ECAPA-TDNN extracts speaker embeddings:</p>
<ul>
<li>Time Delay Neural Network with multi-scale features</li>
<li>Squeeze-and-excitation blocks for channel attention</li>
<li>Attentive statistics pooling</li>
<li>Trained on speaker verification (distinguish speakers)</li>
</ul>
<h3 id="disentanglement-of-content-and-speaker">Disentanglement of Content and Speaker</h3>
<p>The core challenge in voice conversion is separating “what is said” from “who said it” [15].</p>
<p><strong>Information Bottleneck</strong></p>
<p>One approach uses information bottlenecks to force separation:</p>
<figure><figcaption><strong>Voice Conversion Disentanglement</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-9.webp" width="512" height="449" alt="Voice Conversion Disentanglement" loading="lazy" decoding="async"></figure>
<p><strong>Adversarial Disentanglement</strong></p>
<p>Train with adversarial losses to ensure:</p>
<ul>
<li>Content encoder output cannot predict speaker (speaker classifier fails)</li>
<li>Speaker encoder output cannot predict content (ASR fails)</li>
</ul>
<p><strong>CONTENTVEC Approach</strong></p>
<p>CONTENTVEC converts all training audio to a single speaker using an unsupervised VC system, then trains HuBERT on this speaker-normalized data. The resulting representations contain minimal speaker information [15].</p>
<h3 id="rvc-architecture">RVC Architecture</h3>
<p><a href="https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI">Retrieval-based Voice Conversion (RVC)</a> is an open-source system achieving high-quality conversion with minimal data [14].</p>
<p><strong>Pipeline</strong></p>
<figure><figcaption><strong>RVC Voice Conversion Pipeline</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-10.webp" width="512" height="578" alt="RVC Voice Conversion Pipeline" loading="lazy" decoding="async"></figure>
<p><strong>Retrieval Module</strong></p>
<p>RVC’s key innovation is the retrieval step:</p>
<ol>
<li>During training: Store all HuBERT features from target speaker in a Faiss index</li>
<li>During inference: For each source feature, find k nearest neighbors in the index</li>
<li>Blend source features with retrieved features (configurable ratio)</li>
</ol>
<p>This reduces “timbre leakage” by replacing source speaker characteristics with training set features.</p>
<p><strong>Performance</strong></p>
<p>RVC achieves 90ms end-to-end latency with ASIO audio interfaces and learns high-quality transformations from about 10 minutes of target speaker audio [14].</p>
<h3 id="so-vits-svc">So-VITS-SVC</h3>
<p><a href="https://github.com/svc-develop-team/so-vits-svc">So-VITS-SVC</a> (SoftVC VITS Singing Voice Conversion) specializes in singing voice conversion [16].</p>
<p><strong>Differences from Speech VC</strong></p>
<p>Singing voice conversion has additional challenges:</p>
<ul>
<li>Must preserve pitch contour exactly (wrong notes are obvious)</li>
<li>Longer sustained vowels expose synthesis artifacts</li>
<li>Vibrato, breath, and other expressive elements must transfer</li>
</ul>
<p><strong>Architecture</strong></p>
<p>So-VITS-SVC uses:</p>
<ol>
<li><strong>SoftVC content encoder</strong>: Variant of HuBERT trained for voice conversion</li>
<li><strong>F0 predictor</strong>: Optional pitch prediction (disabled for singing to preserve exact pitch)</li>
<li><strong>VITS backbone</strong>: Prior encoder (6-layer Transformer with 2-head attention) + HiFi-GAN decoder</li>
<li><strong>NSF-HiFiGAN vocoder</strong>: Addresses glitching artifacts in original HiFi-GAN</li>
</ol>
<p><strong>Shallow Diffusion Enhancement</strong></p>
<p>Recent versions add optional diffusion refinement:</p>
<ol>
<li>VITS generates initial waveform</li>
<li>Diffusion model (from DDSP-SVC) refines quality</li>
<li>Only runs a few diffusion steps (“shallow”) for efficiency</li>
</ol>
<p><strong>Clustering for Timbre Matching</strong></p>
<p>Similar to RVC’s retrieval, So-VITS-SVC uses feature clustering:</p>
<ul>
<li>K-means cluster centers represent “prototypical” target speaker features</li>
<li>Source features are blended with cluster centers</li>
<li>Tradeoff: More clustering = better timbre match, less clarity</li>
</ul>
<hr>
<h2 id="audio-language-models">Audio Language Models</h2>
<p>Audio language models extend the language modeling paradigm to generate audio directly, enabling unified speech-text systems.</p>
<h3 id="audiolm-and-audio-tokenization">AudioLM and Audio Tokenization</h3>
<p><a href="https://arxiv.org/abs/2209.03143">AudioLM</a>, from Google in 2022, pioneered treating audio generation as language modeling [17].</p>
<p><strong>Hybrid Tokenization</strong></p>
<p>AudioLM uses two types of tokens to capture different aspects of audio:</p>
<ol>
<li><strong>Semantic tokens</strong> (from w2v-BERT): Capture content, phonetics, rhythm, harmony</li>
<li><strong>Acoustic tokens</strong> (from SoundStream): Capture timbre, recording quality, fine acoustic details</li>
</ol>
<figure><figcaption><strong>AudioLM Hybrid Tokenization</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-11.webp" width="512" height="370" alt="AudioLM Hybrid Tokenization" loading="lazy" decoding="async"></figure>
<p><strong>Three-Stage Generation</strong></p>
<p>AudioLM generates hierarchically:</p>
<ol>
<li><strong>Semantic modeling</strong>: Transformer predicts semantic tokens (captures structure)</li>
<li><strong>Coarse acoustic modeling</strong>: Transformer predicts first few acoustic codebook levels given semantics</li>
<li><strong>Fine acoustic modeling</strong>: Transformer adds remaining codebook levels for full quality</li>
</ol>
<p>This cascade allows the model to first get the high-level content right, then progressively add acoustic detail.</p>
<p><strong>Capabilities</strong></p>
<p>Without any text supervision, AudioLM generates:</p>
<ul>
<li>Coherent speech continuations that maintain speaker identity, grammar, and semantic coherence</li>
<li>Piano music with proper harmony and rhythm</li>
<li>General audio with consistent acoustic properties</li>
</ul>
<h3 id="speechgpt-and-multimodal-audio-text-models">SpeechGPT and Multimodal Audio-Text Models</h3>
<p><a href="https://www.alphaxiv.org/overview/2305.11000v2">SpeechGPT</a> extends LLMs to natively understand and generate speech [18].</p>
<p><strong>Architecture</strong></p>
<ol>
<li><strong>Speech tokenization</strong>: HuBERT converts speech to discrete tokens</li>
<li><strong>Vocabulary expansion</strong>: LLM vocabulary extended to include speech tokens</li>
<li><strong>Unified modeling</strong>: Single model processes interleaved text and speech</li>
</ol>
<figure><figcaption><strong>SpeechGPT Multimodal Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-12.webp" width="512" height="449" alt="SpeechGPT Multimodal Architecture" loading="lazy" decoding="async"></figure>
<p><strong>Training Stages</strong></p>
<ol>
<li><strong>Modality adaptation</strong>: Train speech encoder/decoder with frozen LLM</li>
<li><strong>Cross-modal instruction tuning</strong>: Train on speech-text tasks</li>
<li><strong>Chain-of-modality tuning</strong>: Generate “thought” in text before speech output (like chain-of-thought)</li>
</ol>
<p>The key insight is that by representing speech as tokens within the LLM’s vocabulary, knowledge transfers between modalities. The model can answer questions about speech content, translate speech, or generate speech responses.</p>
<h3 id="moshi-real-time-conversational-ai">Moshi: Real-Time Conversational AI</h3>
<p><a href="https://kyutai.org/Moshi.pdf">Moshi</a>, from Kyutai (French AI lab), is the first real-time full-duplex spoken dialogue system [19].</p>
<p><strong>Key Innovation: Parallel Audio Streams</strong></p>
<p>Moshi models two simultaneous audio streams:</p>
<ul>
<li><strong>Moshi’s speech</strong>: What the AI is saying</li>
<li><strong>User’s speech</strong>: What the human is saying</li>
</ul>
<p>This removes turn-taking constraints. The model always listens and always generates (speech or silence), enabling natural interruptions and backchannels (“uh-huh”, “right”).</p>
<figure><figcaption><strong>Full-Duplex Conversation</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-13.webp" width="512" height="288" alt="Full-Duplex Conversation" loading="lazy" decoding="async"></figure>
<p><strong>Architecture</strong></p>
<figure><figcaption><strong>Moshi Architecture</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/voice-audio-models-14.webp" width="512" height="393" alt="Moshi Architecture" loading="lazy" decoding="async"></figure>
<p><strong>Mimi Codec</strong></p>
<p>Moshi uses a custom neural codec called Mimi:</p>
<ul>
<li>Residual vector quantization (RVQ) like EnCodec</li>
<li>Optimized for streaming with low latency</li>
<li>12.5 Hz frame rate for semantic tokens, higher for acoustic</li>
</ul>
<p><strong>Inner Monologue</strong></p>
<p>Moshi generates time-aligned text tokens as a “prefix” to audio tokens:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Internal: [text: &quot;Hello&quot;]  [audio: &quot;Hello&quot;] [text: &quot;how&quot;] [audio: &quot;how&quot;]...</span></span></code></pre>
<p>This provides:</p>
<ul>
<li>Implicit speech recognition (text output)</li>
<li>Better linguistic quality (text grounds the audio)</li>
<li>Debugging capability (see what model “thinks”)</li>
</ul>
<p><strong>Performance</strong></p>
<ul>
<li>160ms theoretical latency (200ms practical)</li>
<li>Full-duplex conversation handling</li>
<li>92+ different voice intonations</li>
<li>Released under CC-BY 4.0 license</li>
</ul>
<h3 id="moe-in-audio-models">MoE in Audio Models</h3>
<p>Mixture of Experts (MoE) enables scaling model capacity without proportionally increasing compute. Recent work applies MoE to audio [20].</p>
<p><strong>MoME (Mixture of Matryoshka Experts)</strong></p>
<p>For audio-visual speech recognition:</p>
<ul>
<li>Integrates sparse MoE into matryoshka representation learning</li>
<li>Top-k routing activates subset of experts per token</li>
<li>Shared experts handle common patterns; routed experts specialize</li>
<li>Achieves SOTA on LRS2/LRS3 with fewer parameters</li>
</ul>
<p><strong>MoHAVE (Mixture of Hierarchical Audio-Visual Experts)</strong></p>
<p>Uses hierarchical gating:</p>
<ul>
<li>Modality-specific expert groups (audio vs visual)</li>
<li>Dynamic activation based on input context</li>
<li>Scales capacity without linear compute increase</li>
</ul>
<p><strong>Practical Considerations</strong></p>
<p>MoE for audio faces challenges:</p>
<ul>
<li>Load balancing: Ensuring experts are utilized evenly</li>
<li>Expert collapse: Preventing all tokens from routing to same expert</li>
<li>Memory: All experts must fit in memory even if only subset is active</li>
</ul>
<hr>
<h2 id="training-and-data">Training and Data</h2>
<p>Training voice models requires careful data preparation and domain-specific techniques.</p>
<h3 id="audio-preprocessing">Audio Preprocessing</h3>
<p><strong>Mel Spectrograms</strong></p>
<p>The standard intermediate representation for speech models [21]:</p>
<ol>
<li><strong>Resampling</strong>: Typically to 16 kHz (ASR) or 22.05/24 kHz (TTS)</li>
<li><strong>STFT</strong>: Short-time Fourier transform with:
<ul>
<li>Window size: 25ms (400 samples at 16 kHz)</li>
<li>Hop size: 10ms (160 samples)</li>
<li>FFT size: 512 or 1024</li>
</ul>
</li>
<li><strong>Mel filterbank</strong>: Apply triangular filters spaced on mel scale (80-128 bins)</li>
<li><strong>Log compression</strong>: log(mel + 1e-5) to compress dynamic range</li>
<li><strong>Normalization</strong>: Per-channel or global mean/variance normalization</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> librosa</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Standard preprocessing</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">audio</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> sr </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> librosa</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(path, sr</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">mel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> librosa</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">feature</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">melspectrogram</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    y</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">audio,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    sr</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">sr,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    n_fft</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    hop_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    n_mels</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">80</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">log_mel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">log</span><span style="color:var(--astro-code-token-punctuation)">(mel </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1e-5</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>MFCC</strong></p>
<p>Mel-Frequency Cepstral Coefficients add DCT to mel spectrograms:</p>
<ol>
<li>Compute log mel spectrogram</li>
<li>Apply Discrete Cosine Transform</li>
<li>Keep first 13-40 coefficients</li>
</ol>
<p>MFCCs decorrelate features and compress representation. They’re more common in traditional ASR but less used in end-to-end neural models which learn their own representations [21].</p>
<p><strong>Raw Waveform</strong></p>
<p>Some models (Wav2Vec2, HuBERT) operate directly on raw waveforms:</p>
<ul>
<li>No information loss from spectrogram conversion</li>
<li>Model learns appropriate filterbanks</li>
<li>Requires more compute and data</li>
</ul>
<h3 id="data-quality-requirements">Data Quality Requirements</h3>
<p><strong>ASR Data</strong></p>
<ul>
<li>Volume: State-of-the-art requires 10,000+ hours (Whisper: 680,000 hours)</li>
<li>Transcription: Can use weak supervision (web captions) but human transcription helps</li>
<li>Diversity: Multiple speakers, accents, recording conditions, noise levels</li>
<li>Alignment: Exact timestamps not required for seq2seq models</li>
</ul>
<p><strong>TTS Data</strong></p>
<ul>
<li>Volume: 10-50 hours for single-speaker high-quality</li>
<li>Recording quality: Studio recordings preferred (low noise, consistent mic)</li>
<li>Transcription: Must be exact (punctuation affects prosody)</li>
<li>Consistency: Same speaker, same emotional register, same recording setup</li>
<li>Alignment: Phone-level timestamps improve training stability</li>
</ul>
<p>For voice cloning:</p>
<ul>
<li>Zero-shot: 3-10 seconds reference audio</li>
<li>Few-shot fine-tuning: 1-5 minutes</li>
<li>Full fine-tuning: 30+ minutes</li>
</ul>
<h3 id="audio-text-alignment">Audio-Text Alignment</h3>
<p>Forced alignment aligns transcripts to audio at the word or phone level [22].</p>
<p><strong>Montreal Forced Aligner (MFA)</strong></p>
<p>Standard tool for TTS data preparation:</p>
<ol>
<li><strong>Input</strong>: Audio + orthographic transcript</li>
<li><strong>Pronunciation dictionary</strong>: Maps words to phoneme sequences</li>
<li><strong>Acoustic model</strong>: GMM-HMM trained on target language</li>
<li><strong>Output</strong>: TextGrid with word/phone timestamps</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Example MFA usage</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">mfa</span><span style="color:var(--astro-code-token-string)"> align</span><span style="color:var(--astro-code-token-string)"> /path/to/audio</span><span style="color:var(--astro-code-token-string)"> /path/to/dictionary</span><span style="color:var(--astro-code-token-string)"> /path/to/model</span><span style="color:var(--astro-code-token-string)"> /path/to/output</span></span></code></pre>
<p>MFA uses Kaldi internally with:</p>
<ul>
<li>Triphone acoustic models (context-dependent phonemes)</li>
<li>Speaker adaptation via CMVN</li>
<li>10ms temporal resolution</li>
</ul>
<p><strong>CTC Segmentation</strong></p>
<p>Alternative using neural ASR models:</p>
<ul>
<li>Use CTC model to get frame-level posterior probabilities</li>
<li>Dynamic programming to find best alignment</li>
<li>Works without pronunciation dictionary</li>
<li>Available in NeMo toolkit</li>
</ul>
<p><strong>Practical Tips</strong></p>
<ul>
<li>Chunk audio to 5-10 second segments</li>
<li>Resample to 16 kHz mono</li>
<li>MFA still outperforms WhisperX/MMS for alignment (despite their ASR accuracy) [22]</li>
</ul>
<h3 id="fine-tuning-on-small-datasets">Fine-Tuning on Small Datasets</h3>
<p><strong>TTS Adaptation</strong></p>
<p>For adapting TTS to new speakers with limited data [23]:</p>
<ol>
<li>
<p><strong>Speaker embedding only</strong>: Freeze model, train only speaker embedding</p>
<ul>
<li>Works with 10 seconds of audio</li>
<li>Captures voice timbre, not speaking style</li>
</ul>
</li>
<li>
<p><strong>Full fine-tuning with mixing</strong>: Mix original speaker data with new speaker</p>
<ul>
<li>Equal sampling from both in each batch</li>
<li>Prevents catastrophic forgetting</li>
<li>Works with 1-5 minutes of data</li>
</ul>
</li>
<li>
<p><strong>LoRA/Adapter tuning</strong>: Add small trainable modules to frozen model</p>
<ul>
<li>1-2% of original parameters</li>
<li>Good quality with 5+ minutes of data</li>
</ul>
</li>
</ol>
<p><strong>Zero-Shot Models</strong></p>
<p>YourTTS and XTTS can adapt to new voices without fine-tuning:</p>
<ul>
<li>Extract speaker embedding from reference audio</li>
<li>Condition synthesis on that embedding</li>
<li>Works immediately with 3-10 seconds of reference</li>
</ul>
<p><strong>ASR Fine-Tuning</strong></p>
<p>For domain-specific ASR [24]:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> WhisperForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> WhisperProcessor</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> peft </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> get_peft_model</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> WhisperForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;openai/whisper-small&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> WhisperProcessor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;openai/whisper-small&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># LoRA config for efficient fine-tuning</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">lora_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;q_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;v_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.05</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> get_peft_model</span><span style="color:var(--astro-code-token-punctuation)">(model, lora_config)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Results in ~99% fewer trainable parameters</span></span></code></pre>
<p>Fine-tuning on ~400 samples can achieve 31+ WER point improvement for domain-specific vocabulary [24].</p>
<hr>
<h2 id="inference-considerations">Inference Considerations</h2>
<p>Deploying audio models requires attention to latency, throughput, and resource constraints.</p>
<h3 id="real-time-factor-and-latency">Real-Time Factor and Latency</h3>
<p><strong>Real-Time Factor (RTF)</strong></p>
<p>RTF = processing_time / audio_duration [25]</p>
<ul>
<li>RTF &lt; 1: Faster than real-time (required for streaming)</li>
<li>RTF = 0.5: Processes 1 second of audio in 0.5 seconds</li>
<li>RTF = 0.1: 10x faster than real-time</li>
</ul>
<p><strong>Latency Components</strong></p>
<p>For streaming ASR:</p>
<ul>
<li><strong>Algorithmic latency</strong>: Time the model needs to “see ahead” (chunk size + right context)</li>
<li><strong>Compute latency</strong>: Processing time for each chunk</li>
<li><strong>Network latency</strong>: Round-trip time to server (if cloud-based)</li>
</ul>
<p>For TTS:</p>
<ul>
<li><strong>First-packet latency</strong>: Time to first audio sample</li>
<li><strong>Full synthesis latency</strong>: Time to complete waveform</li>
</ul>
<p><strong>Benchmarks</strong></p>















































<div class="table-wrap"><table><thead><tr><th>Model</th><th>Task</th><th>RTF (GPU)</th><th>Latency</th></tr></thead><tbody><tr><td>Whisper large-v3</td><td>ASR</td><td>~0.3</td><td>Batch, not streaming</td></tr><tr><td>Conformer-CTC</td><td>Streaming ASR</td><td>~0.1-0.2</td><td>200-400ms</td></tr><tr><td>FastSpeech 2</td><td>TTS</td><td>0.02</td><td>20ms per second of audio</td></tr><tr><td>VITS</td><td>TTS</td><td>0.067</td><td>67ms per second of audio</td></tr><tr><td>XTTS v2</td><td>Zero-shot TTS</td><td>0.48</td><td>200ms first chunk</td></tr><tr><td>HiFi-GAN</td><td>Vocoder</td><td>0.02</td><td>~20ms</td></tr></tbody></table></div>
<h3 id="streaming-inference-for-asr">Streaming Inference for ASR</h3>
<p><strong>Chunk-Based Processing</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Pseudocode for streaming ASR</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">chunk_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 640</span><span style="color:var(--astro-code-token-comment)">  # ms</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">left_context </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 480</span><span style="color:var(--astro-code-token-comment)">  # ms</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">buffer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">while</span><span style="color:var(--astro-code-foreground)"> audio_stream</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">has_data</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    chunk </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> audio_stream</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">read</span><span style="color:var(--astro-code-token-punctuation)">(chunk_size)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    buffer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(chunk)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Keep only necessary context</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    context </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> buffer</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)">left_context_chunks</span><span style="color:var(--astro-code-token-punctuation)">:]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Process with context</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    features </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> extract_features</span><span style="color:var(--astro-code-token-punctuation)">(context </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> [chunk])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(features)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    yield</span><span style="color:var(--astro-code-foreground)"> text</span></span></code></pre>
<p><strong>Endpointer</strong></p>
<p>Detect when user stops speaking to finalize transcription:</p>
<ul>
<li>Voice Activity Detection (VAD) for speech/silence</li>
<li>End-of-query detection for semantic completeness</li>
<li>Typically 400-800ms of silence triggers endpoint</li>
</ul>
<h3 id="vocoder-optimization">Vocoder Optimization</h3>
<p>Vocoders are often the bottleneck in TTS pipelines.</p>
<p><strong>Strategies</strong></p>
<ol>
<li><strong>Caching</strong>: Cache vocoder output for repeated phrases</li>
<li><strong>Streaming vocoder</strong>: Generate audio in chunks as mel frames arrive</li>
<li><strong>Smaller models</strong>: HiFi-GAN V3 (1M params) vs V1 (14M)</li>
<li><strong>INT8 quantization</strong>: 2-4x speedup with minimal quality loss</li>
</ol>
<p><strong>Multi-Band Generation</strong></p>
<p>Split frequency range into bands, generate each with smaller model:</p>
<ul>
<li>Reduces per-band complexity</li>
<li>Enables parallel generation</li>
<li>MultiBand-MelGAN achieves very low RTF</li>
</ul>
<h3 id="quantization-for-audio-models">Quantization for Audio Models</h3>
<p><strong>Post-Training Quantization (PTQ)</strong></p>
<p>Apply quantization after training [26]:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># INT8 quantization for faster inference</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_fp32 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_model</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_int8 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">quantization</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">quantize_dynamic</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_fp32,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {torch.nn.Linear, torch.nn.Conv1d},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.qint8</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Quantization-Aware Training (QAT)</strong></p>
<p>Train with simulated quantization for better accuracy:</p>
<ul>
<li>Fake quantization during forward pass</li>
<li>Full precision gradients during backward pass</li>
<li>Recovers most accuracy loss from PTQ</li>
</ul>
<p><strong>Results</strong></p>
<ul>
<li>Whisper: INT8 gives ~2x speedup on CPU with &lt;1% WER increase</li>
<li>HiFi-GAN: INT8 gives ~2x speedup, some high-frequency quality loss</li>
<li>VITS: INT4 on decoder achieves 40% latency reduction</li>
</ul>
<p><strong>Framework Support</strong></p>
<ul>
<li>ONNX Runtime: Broad model support, CPU/GPU</li>
<li>TensorRT: NVIDIA GPUs, aggressive optimization</li>
<li>OpenVINO: Intel CPUs/GPUs</li>
<li>Core ML: Apple Silicon</li>
</ul>
<hr>
<h2 id="practical-examples">Practical Examples</h2>
<h3 id="running-whisper-locally">Running Whisper Locally</h3>
<p><strong>Installation</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> openai-whisper</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or with faster-whisper (CTranslate2 backend)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> faster-whisper</span></span></code></pre>
<p><strong>Basic Transcription</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> whisper</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> whisper</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;base&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # tiny, base, small, medium, large-v3</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transcribe</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;audio.mp3&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(result[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># With options</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transcribe</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;audio.mp3&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    language</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;en&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    task</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;transcribe&quot;</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># or &quot;translate&quot; for X-&gt;English</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    fp16</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Use FP16 on GPU</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    condition_on_previous_text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Use context</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Faster-Whisper for Production</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> faster_whisper </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> WhisperModel</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># INT8 quantization for faster CPU inference</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> WhisperModel</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;large-v3&quot;</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cpu&quot;</span><span style="color:var(--astro-code-token-punctuation)">, compute_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;int8&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or FP16 on GPU</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> WhisperModel</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;large-v3&quot;</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">, compute_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;float16&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">segments</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> info </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transcribe</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;audio.mp3&quot;</span><span style="color:var(--astro-code-token-punctuation)">, beam_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">5</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> segment </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> segments</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;[</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">segment.start</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s -&gt; </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">segment.end</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s] </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">segment.text</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Streaming with Whisper</strong></p>
<p>Whisper isn’t designed for streaming, but you can approximate it:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> faster_whisper </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> WhisperModel</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> WhisperModel</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;base&quot;</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> process_chunk</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">audio_chunk</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> previous_text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Pad to 30 seconds if needed</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(audio_chunk)</span><span style="color:var(--astro-code-token-keyword)"> &lt;</span><span style="color:var(--astro-code-token-constant)"> 30</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 16000</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        audio_chunk </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">pad</span><span style="color:var(--astro-code-token-punctuation)">(audio_chunk, (</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">30</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 16000</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(audio_chunk)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    segments</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transcribe</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        audio_chunk,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        initial_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">previous_text,  </span><span style="color:var(--astro-code-token-comment)"># Context from previous chunks</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        vad_filter</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Filter silence</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-string-expression)"> &quot; &quot;</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">join</span><span style="color:var(--astro-code-token-punctuation)">([s.text </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> s </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> segments])</span></span></code></pre>
<h3 id="fine-tuning-a-tts-model">Fine-Tuning a TTS Model</h3>
<p><strong>Fine-tuning Coqui TTS</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Install</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># pip install TTS</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">api </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> TTS</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load pre-trained VITS model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tts </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;tts_models/en/ljspeech/vits&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># For fine-tuning, use the training script</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 1. Prepare data in LJSpeech format:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#    wavs/</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#      audio1.wav</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#      audio2.wav</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#    metadata.csv: audio1|transcript one|transcript one</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#                  audio2|transcript two|transcript two</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 2. Create config</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">tts</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">configs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">vits_config </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> VitsConfig</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">tts</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">models</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">vits </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Vits</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> VitsConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    audio</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;sample_rate&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-constant)">22050</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    run_name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;my_voice&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    eval_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_loader_workers</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_eval_loader_workers</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    run_eval</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    test_delay_epochs</span><span style="color:var(--astro-code-token-keyword)">=-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    epochs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    text_cleaner</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;english_cleaners&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    use_phonemes</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    phoneme_language</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;en-us&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;output/&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    datasets</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[{</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;name&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;ljspeech&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;path&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;/path/to/your/data/&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;meta_file_train&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;metadata.csv&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 3. Start from pretrained checkpoint</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_json</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;/path/to/pretrained/config.json&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Vits</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">init_from_config</span><span style="color:var(--astro-code-token-punctuation)">(config)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_checkpoint</span><span style="color:var(--astro-code-token-punctuation)">(config, </span><span style="color:var(--astro-code-token-string-expression)">&quot;/path/to/pretrained/best_model.pth&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 4. Fine-tune</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Trainer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Trainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    TrainerArgs</span><span style="color:var(--astro-code-token-punctuation)">(),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;output/&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">fit</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p><strong>Fine-tuning with XTTS</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">api </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> TTS</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># XTTS supports zero-shot cloning without fine-tuning</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tts </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> TTS</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;tts_models/multilingual/multi-dataset/xtts_v2&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate with voice cloning</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tts</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">tts_to_file</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Hello, this is my cloned voice!&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    speaker_wav</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;reference_audio.wav&quot;</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># 3-10 seconds</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    language</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;en&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    file_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;output.wav&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># For fine-tuning (better quality with more data)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Use the XTTS fine-tuning script with your dataset</span></span></code></pre>
<h3 id="voice-conversion-pipeline">Voice Conversion Pipeline</h3>
<p><strong>Using RVC</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Clone RVC</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> Retrieval-based-Voice-Conversion-WebUI</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> -r</span><span style="color:var(--astro-code-token-string)"> requirements.txt</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Download pretrained models</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># https://huggingface.co/lj1995/VoiceConversionWebUI</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Launch WebUI</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> infer-web.py</span></span></code></pre>
<p><strong>Programmatic Voice Conversion</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Simplified RVC inference (actual implementation more complex)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> fairseq </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> checkpoint_utils</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load HuBERT for content extraction</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">hubert_model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> cfg</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> task </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> checkpoint_utils</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_model_ensemble_and_task</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    [</span><span style="color:var(--astro-code-token-string-expression)">&quot;hubert_base.pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">hubert </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> hubert_model</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].</span><span style="color:var(--astro-code-token-function)">eval</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load RVC model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">rvc_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;target_voice.pth&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> convert_voice</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">source_audio</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 1. Extract content features</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">no_grad</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        content </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> hubert</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">extract_features</span><span style="color:var(--astro-code-token-punctuation)">(source_audio)</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 2. Extract pitch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    f0 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> extract_f0</span><span style="color:var(--astro-code-token-punctuation)">(source_audio)</span><span style="color:var(--astro-code-token-comment)">  # RMVPE or CREPE</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 3. Retrieve similar features from training set (optional)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    retrieved </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> faiss_index</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">search</span><span style="color:var(--astro-code-token-punctuation)">(content, k</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    content </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> blend</span><span style="color:var(--astro-code-token-punctuation)">(content, retrieved)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 4. Generate with RVC model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    audio </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> rvc_model</span><span style="color:var(--astro-code-token-punctuation)">(content, f0)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> audio</span></span></code></pre>
<p><strong>So-VITS-SVC for Singing</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Clone So-VITS-SVC</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/svc-develop-team/so-vits-svc</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> so-vits-svc</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> -r</span><span style="color:var(--astro-code-token-string)"> requirements.txt</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prepare training data</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Place wav files in dataset_raw/speaker_name/</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Preprocess</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> resample.py</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> preprocess_flist_config.py</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> preprocess_hubert_f0.py</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> train.py</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string)"> configs/config.json</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> speaker_name</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Inference</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> inference_main.py</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string-expression)"> &quot;logs/speaker_name/model.pth&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -c</span><span style="color:var(--astro-code-token-string-expression)"> &quot;configs/config.json&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -n</span><span style="color:var(--astro-code-token-string-expression)"> &quot;song.wav&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -t</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-comment)">  # pitch shift in semitones</span></span></code></pre>
<hr>
<h2 id="references">References</h2>
<ol>
<li>
<p>Radford, A., et al. (2022). “Robust Speech Recognition via Large-Scale Weak Supervision.” OpenAI. <a href="https://github.com/openai/whisper">https://github.com/openai/whisper</a></p>
</li>
<li>
<p>OpenAI. (2023). “Whisper large-v3.” Hugging Face. <a href="https://huggingface.co/openai/whisper-large-v3">https://huggingface.co/openai/whisper-large-v3</a></p>
</li>
<li>
<p>Zhou, et al. (2025). “Adapting Whisper for Streaming Speech Recognition via Two-Pass Decoding.” Interspeech 2025. <a href="https://www.isca-archive.org/interspeech_2025/zhou25_interspeech.pdf">https://www.isca-archive.org/interspeech_2025/zhou25_interspeech.pdf</a></p>
</li>
<li>
<p>Gulati, A., et al. (2020). “Conformer: Convolution-augmented Transformer for Speech Recognition.” Interspeech 2020. <a href="https://arxiv.org/abs/2005.08100">https://arxiv.org/abs/2005.08100</a></p>
</li>
<li>
<p>Hugging Face. “CTC Architectures.” Audio Course. <a href="https://huggingface.co/learn/audio-course/chapter3/ctc">https://huggingface.co/learn/audio-course/chapter3/ctc</a></p>
</li>
<li>
<p>SpeechBrain Documentation. “Streaming Speech Recognition with Conformers.” <a href="https://speechbrain.readthedocs.io/en/v1.0.2/tutorials/nn/conformer-streaming-asr.html">https://speechbrain.readthedocs.io/en/v1.0.2/tutorials/nn/conformer-streaming-asr.html</a></p>
</li>
<li>
<p>Kim, J., et al. (2021). “Conditional Variational Autoencoder with Adversarial Learning for End-to-End Text-to-Speech.” ICML 2021. <a href="https://arxiv.org/abs/2106.06103">https://arxiv.org/abs/2106.06103</a></p>
</li>
<li>
<p>Shen, J., et al. (2017). “Natural TTS Synthesis by Conditioning WaveNet on Mel Spectrogram Predictions.” <a href="https://arxiv.org/abs/1712.05884">https://arxiv.org/abs/1712.05884</a></p>
</li>
<li>
<p>Kong, J., et al. (2020). “HiFi-GAN: Generative Adversarial Networks for Efficient and High Fidelity Speech Synthesis.” NeurIPS 2020. <a href="https://github.com/jik876/hifi-gan">https://github.com/jik876/hifi-gan</a></p>
</li>
<li>
<p>Lee, S.-G., et al. (2022). “BigVGAN: A Universal Neural Vocoder with Large-Scale Training.” ICLR 2023. <a href="https://arxiv.org/abs/2206.04658">https://arxiv.org/abs/2206.04658</a></p>
</li>
<li>
<p>Microsoft Research. “VALL-E.” <a href="https://www.microsoft.com/en-us/research/project/vall-e-x/">https://www.microsoft.com/en-us/research/project/vall-e-x/</a></p>
</li>
<li>
<p>Casanova, E., et al. (2024). “XTTS: a Massively Multilingual Zero-Shot Text-to-Speech Model.” <a href="https://arxiv.org/html/2406.04904v1">https://arxiv.org/html/2406.04904v1</a></p>
</li>
<li>
<p>Voice Cloning Survey. (2025). <a href="https://arxiv.org/html/2505.00579v1">https://arxiv.org/html/2505.00579v1</a></p>
</li>
<li>
<p>RVC Project. “Retrieval-based Voice Conversion WebUI.” <a href="https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI">https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI</a></p>
</li>
<li>
<p>Qian, K., et al. (2022). “CONTENTVEC: An Improved Self-Supervised Speech Representation.” ICML 2022. <a href="https://proceedings.mlr.press/v162/qian22b/qian22b.pdf">https://proceedings.mlr.press/v162/qian22b/qian22b.pdf</a></p>
</li>
<li>
<p>So-VITS-SVC. “SoftVC VITS Singing Voice Conversion.” <a href="https://github.com/svc-develop-team/so-vits-svc">https://github.com/svc-develop-team/so-vits-svc</a></p>
</li>
<li>
<p>Borsos, Z., et al. (2022). “AudioLM: a Language Modeling Approach to Audio Generation.” <a href="https://arxiv.org/abs/2209.03143">https://arxiv.org/abs/2209.03143</a></p>
</li>
<li>
<p>Zhang, D., et al. (2023). “SpeechGPT: Empowering Large Language Models with Intrinsic Cross-Modal Conversational Abilities.” <a href="https://www.alphaxiv.org/overview/2305.11000v2">https://www.alphaxiv.org/overview/2305.11000v2</a></p>
</li>
<li>
<p>Défossez, A., et al. (2024). “Moshi: a speech-text foundation model for real-time dialogue.” Kyutai. <a href="https://kyutai.org/Moshi.pdf">https://kyutai.org/Moshi.pdf</a></p>
</li>
<li>
<p>Wu, et al. (2025). “MoME: Mixture of Matryoshka Experts for Audio-Visual Speech Recognition.” NeurIPS 2025. <a href="https://arxiv.org/abs/2510.04136">https://arxiv.org/abs/2510.04136</a></p>
</li>
<li>
<p>Ketanhdoshi. “Audio Deep Learning Made Simple: Data Preparation and Augmentation.” <a href="https://ketanhdoshi.github.io/Audio-Augment/">https://ketanhdoshi.github.io/Audio-Augment/</a></p>
</li>
<li>
<p>McAuliffe, M., et al. (2017). “Montreal Forced Aligner: Trainable Text-Speech Alignment Using Kaldi.” <a href="https://montreal-forced-aligner.readthedocs.io/">https://montreal-forced-aligner.readthedocs.io/</a></p>
</li>
<li>
<p>Arik, S.O., et al. (2018). “Neural Voice Cloning with a Few Samples.” NeurIPS 2018.</p>
</li>
<li>
<p>Hugging Face. “Fine-Tune Whisper For Multilingual ASR.” <a href="https://huggingface.co/blog/fine-tune-whisper">https://huggingface.co/blog/fine-tune-whisper</a></p>
</li>
<li>
<p>Open Voice Technology Wiki. “Real-time-factor.” <a href="https://openvoice-tech.net/index.php/Real-time-factor">https://openvoice-tech.net/index.php/Real-time-factor</a></p>
</li>
<li>
<p>“Model Quantization Techniques for ASR/TTS.” <a href="https://apxml.com/courses/speech-recognition-synthesis-asr-tts/chapter-6-optimization-deployment-toolkits/quantization-speech-models">https://apxml.com/courses/speech-recognition-synthesis-asr-tts/chapter-6-optimization-deployment-toolkits/quantization-speech-models</a></p>
</li>
</ol>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Speech &amp; audio</category>
            <category>Multimodal</category>
            <category>Architecture</category>
            <enclosure length="74569" type="image/png" url="https://blog.ecitis.org/open-graph/voice-audio-models.png"/>
        </item>
        <item>
            <title><![CDATA[The Complete Hugging Face Ecosystem Guide]]></title>
            <link>https://blog.ecitis.org/huggingface-ecosystem-guide/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/huggingface-ecosystem-guide/</guid>
            <pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Navigate the Hub, Transformers, Datasets, Accelerate, PEFT, model formats, and deployment tools that make up Hugging Face.]]></description>
            <content:encoded><![CDATA[<h2 id="what-is-hugging-face">What is Hugging Face</h2>
<p>Hugging Face is an American company founded in 2016 by Clement Delangue and Julien Chaumond, originally as a chatbot startup. It has since evolved into the central hub for open-source machine learning. The platform hosts over 2.4 million models and 730,000 datasets as of January 2026, serving more than 18 million monthly visitors.</p>
<p>The company builds on a core open-source stack: Transformers, Datasets, Diffusers, PEFT, Accelerate, TRL, Timm, and Optimum. Beyond libraries, Hugging Face provides production tools like Inference Endpoints, Text Generation Inference (TGI), and Text Embeddings Inference (TEI). Over 2,000 organizations use their Enterprise Hub for private deployments with SSO, regional data storage, and audit logs.</p>
<p>Hugging Face integrates with major cloud providers. AWS offers Deep Learning Containers and SageMaker integration. Azure Machine Learning and Google Vertex AI both support direct model deployment. In April 2025, the company acquired Pollen Robotics, a humanoid robotics startup, signaling expansion beyond pure software.</p>
<hr>
<h2 id="the-models-hub">The Models Hub</h2>
<p>The Hub is where you discover, evaluate, and download models. Each model repository contains the weights, configuration files, and documentation needed to run inference or continue training.</p>
<h3 id="discovering-models">Discovering Models</h3>
<p>Filter by task (text generation, image classification, audio transcription), library (Transformers, Diffusers, GGUF), license, and language. The search indexes model names, tags, and README content. Sorting by downloads or likes surfaces popular choices; sorting by recent shows the latest uploads.</p>
<h3 id="model-cards">Model Cards</h3>
<p>Every model should have a model card in the README.md file. Cards follow a structured template covering:</p>
<ul>
<li><strong>Model description</strong>: Architecture, training data, intended use</li>
<li><strong>Limitations</strong>: Known failure modes, biases, out-of-scope uses</li>
<li><strong>Training procedure</strong>: Hyperparameters, hardware, preprocessing</li>
<li><strong>Evaluation results</strong>: Benchmarks, metrics, disaggregated performance</li>
</ul>
<p>The metadata block at the top enables Hub features. Specify <code>license</code> for filtering, <code>datasets</code> to link training data, and <code>metrics</code> with <code>eval_results</code> to display benchmark performance in the UI. The Hub parses this YAML and renders widgets showing accuracy, F1, perplexity, or custom metrics.</p>
<h3 id="licenses">Licenses</h3>
<p>Common licenses on the Hub include Apache 2.0, MIT, and various model-specific licenses like Llama’s community license or Gemma’s terms of use. The <code>license</code> field in metadata enables filtering. For custom licenses, use <code>license: other</code> with <code>license_name</code> and <code>license_link</code> fields, or include a LICENSE file in the repository.</p>
<h3 id="evaluation-with-the-evaluate-library">Evaluation with the Evaluate Library</h3>
<p>The Evaluate library provides dozens of metrics with a consistent API:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> evaluate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">accuracy </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> evaluate</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;accuracy&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">results </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> accuracy</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compute</span><span style="color:var(--astro-code-token-punctuation)">(predictions</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">], references</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(results)</span><span style="color:var(--astro-code-token-comment)">  # {&#39;accuracy&#39;: 0.666...}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load multiple metrics</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">clf_metrics </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> evaluate</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">combine</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-string-expression)">&quot;accuracy&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;f1&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;precision&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;recall&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">results </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> clf_metrics</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compute</span><span style="color:var(--astro-code-token-punctuation)">(predictions</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">], references</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<p>Each metric includes a card documenting its formula, value ranges, and appropriate use cases.</p>
<hr>
<h2 id="model-formats-and-download-types">Model Formats and Download Types</h2>
<p>Models come in various formats optimized for different runtimes and hardware. Understanding when to use each saves time and compute.</p>
<figure><figcaption><strong>Model Format Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/huggingface-ecosystem-guide-0.webp" width="512" height="439" alt="Model Format Comparison" loading="lazy" decoding="async"></figure>
<h3 id="pytorch-weights-safetensors-vs-bin">PyTorch Weights: safetensors vs .bin</h3>
<p>The <code>.bin</code> format uses Python’s pickle for serialization. Pickle can execute arbitrary code during deserialization, creating security risks when loading untrusted models.</p>
<p>Safetensors solves this. Developed by Hugging Face, it stores tensors in a binary format without code execution. Loading is 76x faster on CPU and 2x faster on GPU compared to pickle. The BLOOM model loads on 8 GPUs in 45 seconds with safetensors versus 10 minutes with pickle weights.</p>
<p>Safetensors also supports lazy loading through its <code>safe_open()</code> context manager. Tensors load on-demand rather than all at once, critical for distributed inference where each GPU only needs a shard.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> safetensors </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> safe_open</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Lazy loading - tensors loaded only when accessed</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> safe_open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">, framework</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tensor_a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_tensor</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;layer.0.weight&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Transformers defaults to safetensors when available. Legacy <code>.bin</code> files work but carry security and performance penalties.</p>
<h3 id="gguf-for-llamacpp-and-ollama">GGUF for llama.cpp and Ollama</h3>
<p>GGUF (Georgi Gerganov Universal Format) packages everything needed to run a model: architecture details, tokenizer, quantization parameters, and weights in a single file. It evolved from the earlier GGML format for better extensibility.</p>
<p>The format targets llama.cpp and tools built on it like Ollama, LM Studio, and koboldcpp. GGUF excels at CPU inference and mixed CPU/GPU offloading on consumer hardware.</p>
<p>Quantization options range from 8-bit down to 1.5-bit:</p>








































<div class="table-wrap"><table><thead><tr><th>Type</th><th>Description</th><th>Size Reduction</th></tr></thead><tbody><tr><td>Q8_0</td><td>8-bit, block size 32</td><td>~2x</td></tr><tr><td>Q6_K</td><td>6-bit K-Quant</td><td>~2.5x</td></tr><tr><td>Q5_K_M</td><td>5-bit K-Quant medium</td><td>~2.8x</td></tr><tr><td>Q4_K_M</td><td>4-bit K-Quant medium</td><td>~3.3x</td></tr><tr><td>Q4_0</td><td>4-bit basic</td><td>~4x</td></tr><tr><td>Q2_K</td><td>2-bit K-Quant</td><td>~6x</td></tr></tbody></table></div>
<p>Q4_K_M hits the sweet spot for most users. A 13.5GB FP16 model shrinks to 4GB while maintaining acceptable quality. K-Quant variants use different bit allocations per layer based on importance, preserving quality better than uniform quantization.</p>
<p>Convert HuggingFace models to GGUF:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Clone llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/ggml-org/llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> llama.cpp</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Convert to GGUF (F16 baseline)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> convert_hf_to_gguf.py</span><span style="color:var(--astro-code-token-string)"> /path/to/hf-model</span><span style="color:var(--astro-code-token-string)"> --outfile</span><span style="color:var(--astro-code-token-string)"> model-f16.gguf</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize to Q4_K_M</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./llama-quantize</span><span style="color:var(--astro-code-token-string)"> model-f16.gguf</span><span style="color:var(--astro-code-token-string)"> model-q4_k_m.gguf</span><span style="color:var(--astro-code-token-string)"> Q4_K_M</span></span></code></pre>
<p>For better quantization quality, compute an importance matrix from a calibration dataset:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Generate importance matrix</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./llama-imatrix</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> model-f16.gguf</span><span style="color:var(--astro-code-token-string)"> -f</span><span style="color:var(--astro-code-token-string)"> calibration.txt</span><span style="color:var(--astro-code-token-string)"> -o</span><span style="color:var(--astro-code-token-string)"> imatrix.dat</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize with importance matrix</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./llama-quantize</span><span style="color:var(--astro-code-token-string)"> --imatrix</span><span style="color:var(--astro-code-token-string)"> imatrix.dat</span><span style="color:var(--astro-code-token-string)"> model-f16.gguf</span><span style="color:var(--astro-code-token-string)"> model-q4_k_m.gguf</span><span style="color:var(--astro-code-token-string)"> Q4_K_M</span></span></code></pre>
<h3 id="onnx-for-cross-platform-deployment">ONNX for Cross-Platform Deployment</h3>
<p>ONNX (Open Neural Network Exchange) defines a standard graph format for neural networks. Train in PyTorch or TensorFlow, export to ONNX, run anywhere.</p>
<p>ONNX Runtime powers inference across platforms: NVIDIA GPUs via TensorRT, Intel CPUs via OpenVINO, AMD GPUs via ROCm, Apple Silicon via CoreML, and Windows via DirectML. This portability makes ONNX valuable for production deployment across heterogeneous hardware.</p>
<p>Export a Transformers model to ONNX:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> optimum</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">onnxruntime </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ORTModelForSequenceClassification</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_id </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;distilbert-base-uncased-finetuned-sst-2-english&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">save_dir </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;onnx_model&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Export and save</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ORTModelForSequenceClassification</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_id, export</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_id)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(save_dir)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(save_dir)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load and run inference</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ORTModelForSequenceClassification</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(save_dir)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;This movie was great!&quot;</span><span style="color:var(--astro-code-token-punctuation)">, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs)</span></span></code></pre>
<p>The ONNX Model Zoo on Hugging Face hosts pre-exported models at huggingface.co/onnxmodelzoo.</p>
<h3 id="awq-gptq-and-bitsandbytes-quantization">AWQ, GPTQ, and bitsandbytes Quantization</h3>
<p>These methods reduce model precision for faster inference and lower memory usage, each with different tradeoffs.</p>
<p><strong>GPTQ</strong> uses post-training quantization with Hessian-based optimization to minimize output error. It requires a calibration dataset and pre-quantizes weights. Best for GPU inference when you need raw throughput.</p>
<p><strong>AWQ</strong> (Activation-aware Weight Quantization) identifies and protects important weights by observing activations. Like GPTQ, it requires calibration and pre-quantization. AWQ often preserves quality better than GPTQ, especially for instruction-tuned models.</p>
<p><strong>bitsandbytes</strong> quantizes on-the-fly during model loading. No calibration dataset or pre-quantized checkpoint needed. Its NF4 (NormalFloat4) format assumes weights follow a Gaussian distribution and places more quantization levels near zero where weights cluster. Best for fine-tuning with QLoRA and quick experimentation.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> BitsAndBytesConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load with 4-bit quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">bnb_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BitsAndBytesConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_quant_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;nf4&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_compute_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;bfloat16&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    quantization_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">bnb_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Quality comparison from 2025 benchmarks: bitsandbytes has the smallest quality drop, AWQ comes second, GPTQ and Marlin (GPTQ’s optimized kernel) are close behind. The practical difference matters most for your specific use case; test on your evaluation set.</p>
<h3 id="tensorrt-llm-for-maximum-nvidia-performance">TensorRT-LLM for Maximum NVIDIA Performance</h3>
<p>TensorRT-LLM is NVIDIA’s library for optimizing LLM inference on their GPUs. It applies custom attention kernels, inflight batching, paged KV caching, and hardware-specific optimizations.</p>
<p>The library supports quantization formats tied to GPU generations:</p>
<ul>
<li><strong>Blackwell (B200, GB200)</strong>: FP4 with NVFP4 format</li>
<li><strong>Ada Lovelace (L40, RTX 40 series)</strong>: FP8</li>
<li><strong>Ampere (A100, RTX 30 series)</strong>: INT8 SmoothQuant, INT4 AWQ</li>
</ul>
<p>Recent benchmarks show DeepSeek-R1 running 3x faster with speculative decoding on TensorRT-LLM. Pre-quantized NVFP4 models for Llama 3.3 70B and DeepSeek-R1 are available on the Hub.</p>
<p>TensorRT-LLM requires building an engine specific to your GPU and model. This compilation step takes time but produces optimized inference:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompts </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;Explain quantum computing in simple terms&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sampling_params </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SamplingParams</span><span style="color:var(--astro-code-token-punctuation)">(temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(prompts, sampling_params)</span></span></code></pre>
<h3 id="choosing-the-right-format">Choosing the Right Format</h3>

































<div class="table-wrap"><table><thead><tr><th>Use Case</th><th>Format</th></tr></thead><tbody><tr><td>Local CPU/GPU inference, consumer hardware</td><td>GGUF</td></tr><tr><td>Production GPU serving, max throughput</td><td>TensorRT-LLM</td></tr><tr><td>Cross-platform deployment</td><td>ONNX</td></tr><tr><td>Fine-tuning with limited VRAM</td><td>bitsandbytes</td></tr><tr><td>Pre-quantized GPU inference</td><td>AWQ or GPTQ</td></tr><tr><td>Standard Transformers usage</td><td>safetensors</td></tr></tbody></table></div>
<hr>
<h2 id="datasets-on-the-hub">Datasets on the Hub</h2>
<p>The Hub hosts over 730,000 datasets with tools for exploration, streaming, and efficient loading.</p>
<h3 id="the-datasets-library">The Datasets Library</h3>
<p>Load datasets with a single function call:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load from the Hub</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;imdb&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Access splits</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">train </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">test </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;test&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Iterate</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> example </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> train</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(example[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">], example[</span><span style="color:var(--astro-code-token-string-expression)">&quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    break</span></span></code></pre>
<p>The library handles downloading, caching, and format conversion automatically.</p>
<h3 id="arrow-format-and-memory-mapping">Arrow Format and Memory Mapping</h3>
<p>Datasets uses Apache Arrow for storage. Arrow’s columnar layout enables zero-copy reads and memory-mapped access. A dataset backed by an on-disk Arrow cache can exceed available RAM; the OS pages data in and out as needed.</p>
<p>This architecture lets you work with terabyte-scale datasets on machines with limited memory:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Large dataset - only loads what you access</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;HuggingFaceFW/fineweb&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;sample-10BT&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Process without loading everything into memory</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> tokenize</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">examples</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(examples[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">], truncation</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenized </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">map</span><span style="color:var(--astro-code-token-punctuation)">(tokenize, batched</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, num_proc</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="streaming-for-large-datasets">Streaming for Large Datasets</h3>
<p>The English split of FineWeb is 45 terabytes. Streaming lets you use it without downloading:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Stream - no disk space needed</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;HuggingFaceFW/fineweb&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;sample-10BT&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    streaming</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Iterate over examples</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> example </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">take</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(example[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">][:</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<p>Streaming datasets support <code>map</code>, <code>filter</code>, <code>shuffle</code>, and <code>take</code>. Data downloads on demand as you iterate.</p>
<h3 id="parquet-and-the-dataset-viewer">Parquet and the Dataset Viewer</h3>
<p>Datasets on the Hub automatically convert to Parquet, a columnar format that enables efficient partial reads. The Dataset Viewer uses this to display data in your browser without downloading entire files.</p>
<p>Parquet stores column statistics and page indexes enabling:</p>
<ul>
<li>Reading only specific columns</li>
<li>Filtering rows server-side</li>
<li>Random access within files</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Read specific columns from Parquet</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pyarrow</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">parquet </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> pq</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Only download &#39;text&#39; column</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">table </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> pq</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">read_table</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;hf://datasets/username/dataset/data.parquet&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    columns</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="the-transformers-library">The Transformers Library</h2>
<p>Transformers is the core library for working with pretrained models. It provides a unified API across architectures: BERT, GPT, T5, LLaMA, Mistral, Qwen, Gemma, and hundreds more.</p>
<h3 id="the-pipeline-api">The Pipeline API</h3>
<p>Pipelines abstract away tokenization, model loading, and output processing:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pipeline</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Sentiment analysis</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">classifier </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;sentiment-analysis&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> classifier</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;I love this library!&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># [{&#39;label&#39;: &#39;POSITIVE&#39;, &#39;score&#39;: 0.9998}]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Text generation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">generator </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;text-generation&quot;</span><span style="color:var(--astro-code-token-punctuation)">, model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.2-1B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generator</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;The future of AI is&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">50</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Question answering</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">qa </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;question-answering&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> qa</span><span style="color:var(--astro-code-token-punctuation)">(question</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;What is the capital?&quot;</span><span style="color:var(--astro-code-token-punctuation)">, context</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;France&#39;s capital is Paris.&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># {&#39;answer&#39;: &#39;Paris&#39;, &#39;score&#39;: 0.99...}</span></span></code></pre>
<p>Pipelines support GPU acceleration, batch processing, and half-precision:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># GPU with fp16</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;text-generation&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="tokenizers">Tokenizers</h3>
<p>Tokenizers convert text to token IDs the model understands. The library provides both Python (<code>PreTrainedTokenizer</code>) and Rust-backed fast (<code>PreTrainedTokenizerFast</code>) implementations.</p>
<p>Transformers v5 redesigned tokenization. Tokenizers now separate architecture from learned vocabulary, similar to how PyTorch separates network structure from weights:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Encode text</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokens </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Hello world&quot;</span><span style="color:var(--astro-code-token-punctuation)">, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># {&#39;input_ids&#39;: tensor([[...]]), &#39;attention_mask&#39;: tensor([[...]])}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Decode back</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(tokens[</span><span style="color:var(--astro-code-token-string-expression)">&quot;input_ids&quot;</span><span style="color:var(--astro-code-token-punctuation)">][</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<p>Different model families use different tokenization schemes: BPE (GPT, Llama), SentencePiece (T5, Gemma), or WordPiece (BERT). The tokenizer handles this transparently.</p>
<h3 id="loading-models">Loading Models</h3>
<p>Load any model with <code>AutoModel</code> classes:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_id </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_id)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_id,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Automatic device placement</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-comment)"># Use model&#39;s native dtype</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Explain transformers:&quot;</span><span style="color:var(--astro-code-token-punctuation)">, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(model.device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(tokenizer.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]))</span></span></code></pre>
<p>Control memory usage with quantization and offloading:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># 4-bit quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_id,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># CPU offloading for models larger than VRAM</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_id,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    offload_folder</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;offload&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="transformers-library-internals">Transformers Library Internals</h2>
<p>Understanding how Transformers works under the hood helps debug issues and extend functionality.</p>
<h3 id="automodel-and-autotokenizer-resolution">AutoModel and AutoTokenizer Resolution</h3>
<p>When you call <code>AutoModelForCausalLM.from_pretrained(&quot;meta-llama/Llama-3.1-8B&quot;)</code>, the library performs several resolution steps:</p>
<ol>
<li><strong>Download config.json</strong> from the Hub repository</li>
<li><strong>Parse model_type</strong> from the config (e.g., <code>&quot;llama&quot;</code>)</li>
<li><strong>Look up the model class</strong> in an internal registry mapping model types to implementations</li>
<li><strong>Instantiate the correct class</strong> (e.g., <code>LlamaForCausalLM</code>)</li>
</ol>
<p>The registry lives in <code>transformers/models/auto/modeling_auto.py</code>. Each Auto class maintains a mapping like:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">MODEL_FOR_CAUSAL_LM_MAPPING_NAMES </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OrderedDict</span><span style="color:var(--astro-code-token-punctuation)">([</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    (</span><span style="color:var(--astro-code-token-string-expression)">&quot;llama&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;LlamaForCausalLM&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    (</span><span style="color:var(--astro-code-token-string-expression)">&quot;mistral&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;MistralForCausalLM&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    (</span><span style="color:var(--astro-code-token-string-expression)">&quot;gpt2&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;GPT2LMHeadModel&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # ... hundreds more</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<p>For custom models hosted on the Hub, the <code>config.json</code> can include an <code>auto_map</code> field that points to local Python files:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;model_type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;custom_model&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;auto_map&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;AutoConfig&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;configuration_custom.CustomConfig&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;AutoModelForCausalLM&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;modeling_custom.CustomModelForCausalLM&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>This requires <code>trust_remote_code=True</code> when loading, since it executes code from the repository. The <a href="https://huggingface.co/docs/transformers/en/model_doc/auto">Auto Classes documentation</a> covers registration in detail.</p>
<h3 id="the-modelingpy-architecture-pattern">The modeling_*.py Architecture Pattern</h3>
<p>Each model architecture follows a consistent file structure:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>transformers/models/llama/</span></span>
<span class="line"><span>├── __init__.py</span></span>
<span class="line"><span>├── configuration_llama.py    # LlamaConfig class</span></span>
<span class="line"><span>├── modeling_llama.py         # LlamaModel, LlamaForCausalLM, etc.</span></span>
<span class="line"><span>├── tokenization_llama.py     # LlamaTokenizer (slow)</span></span>
<span class="line"><span>└── tokenization_llama_fast.py # LlamaTokenizerFast (Rust)</span></span></code></pre>
<p>The <code>modeling_*.py</code> file contains the actual PyTorch implementation. Models are built from composable pieces:</p>
<ul>
<li><strong>Embeddings</strong>: Token and position embeddings</li>
<li><strong>Attention</strong>: Self-attention with various implementations (eager, SDPA, Flash Attention)</li>
<li><strong>MLP/FFN</strong>: Feed-forward layers</li>
<li><strong>Blocks/Layers</strong>: Stack attention + MLP with residuals and normalization</li>
<li><strong>Model</strong>: The full transformer stack</li>
<li><strong>Head</strong>: Task-specific output layers (LM head, classification head, etc.)</li>
</ul>
<p>A typical file structure:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> LlamaAttention</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Module</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Multi-headed attention from &#39;Attention Is All You Need&#39; paper&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> LlamaMLP</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Module</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Feed-forward network with SiLU activation&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> LlamaDecoderLayer</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Module</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Single transformer block: attention + MLP&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> LlamaModel</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">LlamaPreTrainedModel</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;The bare Llama Model outputting raw hidden-states&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> LlamaForCausalLM</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">LlamaPreTrainedModel</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Llama Model with a language modeling head&quot;&quot;&quot;</span></span></code></pre>
<h3 id="pretrainedmodel-base-class">PreTrainedModel Base Class</h3>
<p>Every model inherits from <code>PreTrainedModel</code>, which provides core functionality according to the <a href="https://huggingface.co/docs/transformers/main_classes/model">Models documentation</a>:</p>
<p><strong>Loading and saving:</strong></p>
<ul>
<li><code>from_pretrained()</code>: Load weights from Hub or local directory</li>
<li><code>save_pretrained()</code>: Save model and config to directory</li>
<li><code>push_to_hub()</code>: Upload to the Hub</li>
</ul>
<p><strong>Weight initialization:</strong></p>
<ul>
<li><code>_init_weights()</code>: Initialize a module’s weights (called recursively)</li>
<li>The <code>_is_hf_initialized</code> flag prevents re-initializing tied parameters</li>
</ul>
<p><strong>Memory management:</strong></p>
<ul>
<li><code>gradient_checkpointing_enable()</code>: Trade compute for memory</li>
<li><code>resize_token_embeddings()</code>: Adjust embedding size for new tokens</li>
</ul>
<p><strong>Device handling:</strong></p>
<ul>
<li><code>to()</code>: Move model to device</li>
<li><code>half()</code>, <code>bfloat16()</code>, <code>float()</code>: Change precision</li>
</ul>
<p>Key class attributes every model defines:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> LlamaPreTrainedModel</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">PreTrainedModel</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    config_class </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LlamaConfig</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    base_model_prefix </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;model&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    supports_gradient_checkpointing </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> True</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    _no_split_modules </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;LlamaDecoderLayer&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    _skip_keys_device_placement </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;past_key_values&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span></code></pre>
<p>The <code>_no_split_modules</code> attribute tells the device placement algorithm which modules must stay on a single device.</p>
<h3 id="generation-mixin-and-decoding-strategies">Generation Mixin and Decoding Strategies</h3>
<p>The <code>GenerationMixin</code> class adds <code>generate()</code> to models. It supports multiple decoding strategies documented in the <a href="https://huggingface.co/docs/transformers/en/generation_strategies">Generation strategies guide</a>:</p>
<p><strong>Greedy decoding</strong>: Select the highest probability token at each step.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, do_sample</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Beam search</strong>: Maintain multiple hypotheses, selecting the sequence with highest total probability.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, num_beams</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">5</span><span style="color:var(--astro-code-token-punctuation)">, early_stopping</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Sampling with temperature</strong>: Sample from the probability distribution, with temperature controlling randomness.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, do_sample</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Top-k sampling</strong>: Sample from the k most likely tokens.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, do_sample</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, top_k</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">50</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Nucleus (top-p) sampling</strong>: Sample from the smallest set of tokens whose cumulative probability exceeds p. Described in Holtzman et al.’s “The Curious Case of Neural Text Degeneration.”</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, do_sample</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, top_p</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.95</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>The generation loop internally:</p>
<ol>
<li>Runs the model forward pass to get logits</li>
<li>Applies logits processors (temperature, top-k/p filtering, repetition penalty)</li>
<li>Selects next token(s) based on the decoding strategy</li>
<li>Appends to the sequence and updates the KV cache</li>
<li>Checks stopping criteria (max length, EOS token, custom criteria)</li>
<li>Repeats until done</li>
</ol>
<h3 id="kv-cache-implementation">KV Cache Implementation</h3>
<p>During autoregressive generation, the model computes key and value projections for all previous tokens at each step. Without caching, this means O(n^2) compute for generating n tokens. The KV cache stores these projections for reuse.</p>
<p>The <a href="https://huggingface.co/docs/transformers/en/kv_cache">KV cache strategies documentation</a> describes available implementations:</p>
<p><strong>DynamicCache</strong> (default): Grows as generation progresses. Simple but can cause memory fragmentation.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, use_cache</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Default</span></span></code></pre>
<p><strong>StaticCache</strong>: Pre-allocates fixed-size tensors. Enables torch.compile() but wastes memory on short sequences.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, cache_implementation</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;static&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>QuantizedCache</strong>: Compresses KV values to lower precision, based on the KIVI paper. Quantizes keys per-channel and values per-token.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> QuantizedCacheConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">cache_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> QuantizedCacheConfig</span><span style="color:var(--astro-code-token-punctuation)">(nbits</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(inputs, cache_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">cache_config)</span></span></code></pre>
<p><strong>OffloadedCache</strong>: Moves KV cache to CPU, prefetching asynchronously. Enables longer sequences on limited VRAM.</p>
<p>The cache is a tuple of (key_states, value_states) for each layer, stored in the model’s <code>past_key_values</code> attribute during generation.</p>
<h3 id="attention-mask-handling">Attention Mask Handling</h3>
<p>Attention masks control which tokens can attend to which. Different model types use different masking strategies:</p>
<p><strong>Padding mask</strong>: Binary mask where 1 = real token, 0 = padding. Prevents attention to padding tokens.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Tokenizer returns attention_mask automatically</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-string-expression)">&quot;short&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;much longer sequence&quot;</span><span style="color:var(--astro-code-token-punctuation)">], padding</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># attention_mask: [[1, 1, 0, 0], [1, 1, 1, 1]]</span></span></code></pre>
<p><strong>Causal mask</strong>: Lower-triangular mask for autoregressive models. Token at position i can only attend to positions 0..i.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Created internally for decoder models</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># [[1, 0, 0, 0],</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#  [1, 1, 0, 0],</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#  [1, 1, 1, 0],</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">#  [1, 1, 1, 1]]</span></span></code></pre>
<p><strong>Bidirectional mask</strong>: Full attention for encoder models like BERT. Every token attends to every other token.</p>
<p><strong>Encoder-decoder mask</strong>: Cross-attention from decoder to encoder uses the encoder’s padding mask.</p>
<p>Models combine these masks. A causal LM with padding combines the causal mask with the padding mask using element-wise multiplication.</p>
<p>The <a href="https://discuss.huggingface.co/t/difference-between-attention-mask-and-causal-mask/104922">attention mask discussion</a> covers the differences in detail.</p>
<hr>
<h2 id="tokenization-deep-dive">Tokenization Deep Dive</h2>
<p>Tokenization converts raw text into the discrete tokens models consume. Modern tokenizers use subword algorithms that balance vocabulary size against sequence length.</p>
<h3 id="tokenization-algorithms-compared">Tokenization Algorithms Compared</h3>
<p>Four algorithms dominate according to the <a href="https://huggingface.co/docs/transformers/tokenizer_summary">tokenizer summary</a>:</p>
<p><strong>Byte-Pair Encoding (BPE)</strong>: Starts with individual characters, iteratively merges the most frequent pair. Used by GPT-2, GPT-3, GPT-4, LLaMA, RoBERTa.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Training corpus: &quot;low lower lowest&quot;</span></span>
<span class="line"><span>Initial: [&#39;l&#39;, &#39;o&#39;, &#39;w&#39;, &#39; &#39;, &#39;l&#39;, &#39;o&#39;, &#39;w&#39;, &#39;e&#39;, &#39;r&#39;, ...]</span></span>
<span class="line"><span>After merges: [&#39;low&#39;, &#39;low&#39;, &#39;er&#39;, &#39;low&#39;, &#39;est&#39;]</span></span></code></pre>
<p><strong>WordPiece</strong>: Similar to BPE but merges based on likelihood maximization rather than frequency. Prefixes continuations with <code>##</code>. Used by BERT, DistilBERT, Electra.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>&quot;unbelievable&quot; → [&quot;un&quot;, &quot;##believ&quot;, &quot;##able&quot;]</span></span></code></pre>
<p><strong>Unigram</strong>: Starts with a large vocabulary, iteratively removes tokens that least affect the training loss. Probabilistic: can produce multiple valid tokenizations. Used within SentencePiece.</p>
<p><strong>SentencePiece</strong>: A framework that treats text as a raw byte stream, enabling language-agnostic tokenization. Can use BPE or Unigram internally. Used by T5, ALBERT, XLNet, Gemma.</p>
<p>The <a href="https://codesignal.com/learn/courses/2-modern-tokenization-techniques-for-ai-llms/lessons/comparing-bpe-wordpiece-and-sentencepiece-in-nlp">tokenization algorithms comparison</a> shows that BPE offers better contextual specialization while SentencePiece achieves better encoding efficiency.</p>
<h3 id="the-tokenizerjson-file-format">The tokenizer.json File Format</h3>
<p>Fast tokenizers serialize their configuration to <code>tokenizer.json</code>. This file encodes:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;version&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;1.0&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;truncation&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> null</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;padding&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> null</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;added_tokens&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [...]</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;normalizer&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Sequence&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;normalizers&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [...]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;pre_tokenizer&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;ByteLevel&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;add_prefix_space&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> false</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;model&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;BPE&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;vocab&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span><span style="color:var(--astro-code-token-keyword)">&quot;&lt;s&gt;&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;&lt;/s&gt;&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ...}</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;merges&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;Ġ t&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Ġ a&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;h e&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ...]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;decoder&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {...}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The <code>merges</code> array encodes BPE merge rules in order of application. During tokenization, the algorithm:</p>
<ol>
<li>Splits input using the pre-tokenizer</li>
<li>For each word, starts with individual bytes/characters</li>
<li>Applies merges in order until no more apply</li>
<li>Maps resulting subwords to vocabulary IDs</li>
</ol>
<h3 id="special-tokens-and-why-they-matter">Special Tokens and Why They Matter</h3>
<p>Special tokens serve structural roles:</p>
<ul>
<li><code>[CLS]</code> / <code>&lt;s&gt;</code>: Sequence start, used for classification</li>
<li><code>[SEP]</code> / <code>&lt;/s&gt;</code>: Sequence end or separator</li>
<li><code>[PAD]</code>: Padding for batching</li>
<li><code>[UNK]</code>: Unknown tokens (rare with subword tokenizers)</li>
<li><code>[MASK]</code>: Masked positions for MLM training</li>
</ul>
<p>Models expect specific special tokens. Using the wrong ones breaks the model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Chat models need proper formatting</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">messages </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Hello&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">formatted </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">apply_chat_template</span><span style="color:var(--astro-code-token-punctuation)">(messages, tokenize</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Adds &lt;|begin_of_text|&gt;, &lt;|start_header_id|&gt;, etc.</span></span></code></pre>
<h3 id="fast-vs-slow-tokenizers">Fast vs Slow Tokenizers</h3>
<p>The <a href="https://huggingface.co/docs/transformers/en/fast_tokenizers">fast tokenizers guide</a> explains the difference:</p>
<p><strong>Slow tokenizers</strong> (<code>PreTrainedTokenizer</code>): Pure Python implementation. Full control, easier to modify, but 10-100x slower.</p>
<p><strong>Fast tokenizers</strong> (<code>PreTrainedTokenizerFast</code>): Rust backend via the <code>tokenizers</code> library. Parallel processing, sub-millisecond tokenization for most inputs.</p>
<p>The fast tokenizer provides additional features:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;bert-base-uncased&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">encoding </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Hello world&quot;</span><span style="color:var(--astro-code-token-punctuation)">, return_offsets_mapping</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Character offsets for each token</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(encoding.offset_mapping)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># [(0, 5), (6, 11)]  # &quot;Hello&quot;, &quot;world&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Map token index to word index</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(encoding.</span><span style="color:var(--astro-code-token-function)">word_ids</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># [0, 1]</span></span></code></pre>
<h3 id="training-tokenizers-from-scratch">Training Tokenizers from Scratch</h3>
<p>The <a href="https://huggingface.co/docs/tokenizers/en/quicktour">tokenizers quicktour</a> shows how to train custom tokenizers:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tokenizers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Tokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tokenizers</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">models </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> BPE</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tokenizers</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">trainers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> BpeTrainer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tokenizers</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">pre_tokenizers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Whitespace</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Initialize with BPE model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">BPE</span><span style="color:var(--astro-code-token-punctuation)">(unk_token</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;[UNK]&quot;</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">pre_tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Whitespace</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure trainer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BpeTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    vocab_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">30000</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    min_frequency</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;[UNK]&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;[CLS]&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;[SEP]&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;[PAD]&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;[MASK]&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train on files</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">files </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;wiki.txt&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;books.txt&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">(files, trainer)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Save</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;my-tokenizer.json&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>For byte-level BPE (like GPT-2):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tokenizers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pre_tokenizers</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tokenizers</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">pre_tokenizers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ByteLevel</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">BPE</span><span style="color:var(--astro-code-token-punctuation)">(unk_token</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;[UNK]&quot;</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">pre_tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ByteLevel</span><span style="color:var(--astro-code-token-punctuation)">(add_prefix_space</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BpeTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    vocab_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">50257</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    initial_alphabet</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">pre_tokenizers.ByteLevel.</span><span style="color:var(--astro-code-token-function)">alphabet</span><span style="color:var(--astro-code-token-punctuation)">(),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;[UNK]&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;[PAD]&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Training a tokenizer on a GB of text takes under 20 seconds with the Rust backend.</p>
<hr>
<h2 id="model-loading-mechanics">Model Loading Mechanics</h2>
<p>Loading large models requires careful memory management. Transformers and Accelerate provide several mechanisms.</p>
<h3 id="sharded-checkpoint-loading">Sharded Checkpoint Loading</h3>
<p>Large models split weights across multiple files (shards). A 70B model might have 15 shards of ~10GB each. The <code>model.safetensors.index.json</code> file maps parameter names to shard files:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;metadata&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span><span style="color:var(--astro-code-token-keyword)">&quot;total_size&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 140000000000</span><span style="color:var(--astro-code-foreground)">}</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;weight_map&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;model.embed_tokens.weight&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;model-00001-of-00015.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;model.layers.0.self_attn.q_proj.weight&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;model-00001-of-00015.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    &quot;model.layers.0.self_attn.k_proj.weight&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;model-00001-of-00015.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ...</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>During loading, Transformers:</p>
<ol>
<li>Downloads/locates all shard files</li>
<li>Loads one shard at a time</li>
<li>Copies relevant tensors to the model</li>
<li>Discards the shard before loading the next</li>
</ol>
<p>This keeps peak memory at (model size) + (largest shard size) rather than (model size) * 2.</p>
<h3 id="devicemapauto-and-layer-distribution">device_map=“auto” and Layer Distribution</h3>
<p>The <a href="https://huggingface.co/docs/accelerate/en/usage_guides/big_modeling">big model inference guide</a> explains automatic device placement:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-70B&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Accelerate computes a device map by:</p>
<ol>
<li>Estimating each layer’s memory requirement</li>
<li>Querying available GPU memory</li>
<li>Assigning layers to devices in order (GPU 0, GPU 1, …, CPU, disk)</li>
<li>Ensuring no module in <code>_no_split_modules</code> crosses device boundaries</li>
</ol>
<p>Available strategies:</p>
<ul>
<li><code>&quot;auto&quot;</code>: Fill GPUs in order, overflow to CPU/disk</li>
<li><code>&quot;balanced&quot;</code>: Distribute evenly across GPUs</li>
<li><code>&quot;balanced_low_0&quot;</code>: Keep GPU 0 lighter (useful when GPU 0 handles other tasks)</li>
<li><code>&quot;sequential&quot;</code>: Fill devices completely before moving to next</li>
</ul>
<p>View the computed map:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> accelerate </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> infer_auto_device_map</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoConfig</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-70B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> init_empty_weights</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_config</span><span style="color:var(--astro-code-token-punctuation)">(config)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">device_map </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> infer_auto_device_map</span><span style="color:var(--astro-code-token-punctuation)">(model, max_memory</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;24GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;24GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;cpu&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;100GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">})</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(device_map)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># {&#39;model.embed_tokens&#39;: 0, &#39;model.layers.0&#39;: 0, ..., &#39;model.layers.40&#39;: 1, ...}</span></span></code></pre>
<p><strong>Important limitation</strong>: <code>device_map=&quot;auto&quot;</code> supports inference only, not training. The hook-based execution doesn’t support backpropagation across devices.</p>
<h3 id="bitsandbytes-quantization-paths">bitsandbytes Quantization Paths</h3>
<p>The <a href="https://huggingface.co/docs/transformers/en/quantization/bitsandbytes">bitsandbytes guide</a> details on-the-fly quantization:</p>
<p><strong>8-bit quantization (LLM.int8)</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_8bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Uses mixed-precision decomposition: outlier features (magnitude &gt; 6.0 by default) stay in FP16, the rest quantizes to INT8. This preserves quality for models with outlier activations.</p>
<p><strong>4-bit quantization (NF4/FP4)</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> BitsAndBytesConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BitsAndBytesConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_quant_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;nf4&quot;</span><span style="color:var(--astro-code-token-punctuation)">,           </span><span style="color:var(--astro-code-token-comment)"># NormalFloat4 or &quot;fp4&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_compute_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,  </span><span style="color:var(--astro-code-token-comment)"># Compute in bf16</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_use_double_quant</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,      </span><span style="color:var(--astro-code-token-comment)"># Quantize the quantization constants</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>NF4 (NormalFloat4) optimizes for normally-distributed weights by placing quantization levels at distribution quantiles. Double quantization applies 8-bit quantization to the scaling factors, saving an additional 0.4 bits per parameter.</p>
<p>PyTorch doesn’t support 4-bit dtypes, so bitsandbytes packs two 4-bit values into one 8-bit value, changing tensor shapes. Computation happens in 16/32-bit after dequantization.</p>
<h3 id="safetensors-lazy-loading">Safetensors Lazy Loading</h3>
<p>The <a href="https://huggingface.co/docs/safetensors">safetensors documentation</a> describes lazy loading:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> safetensors </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> safe_open</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> safe_open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">, framework</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Only metadata loaded at this point</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(f.</span><span style="color:var(--astro-code-token-function)">keys</span><span style="color:var(--astro-code-token-punctuation)">())</span><span style="color:var(--astro-code-token-comment)">  # List all tensor names</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Individual tensors load on-demand</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tensor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_tensor</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model.layers.0.self_attn.q_proj.weight&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Safetensors uses memory-mapping: the OS maps the file into virtual address space, loading pages only when accessed. This enables:</p>
<ul>
<li>Inspecting tensor names without loading weights</li>
<li>Loading specific tensors for distributed inference</li>
<li>Parallel loading from multiple processes sharing the memory map</li>
</ul>
<p>The file layout keeps tensor metadata contiguous at the start, enabling fast scanning without reading weight data.</p>
<h3 id="memory-estimation-before-loading">Memory Estimation Before Loading</h3>
<p>Estimate memory requirements before attempting to load:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> accelerate</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">commands</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">estimate </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> estimate_command</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># CLI</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># accelerate estimate-memory meta-llama/Llama-3.1-70B</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Programmatic</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> accelerate </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> init_empty_weights</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoConfig</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-70B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> init_empty_weights</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_config</span><span style="color:var(--astro-code-token-punctuation)">(config)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">param_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> sum</span><span style="color:var(--astro-code-token-punctuation)">(p.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">() </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-punctuation)"> p.</span><span style="color:var(--astro-code-token-function)">element_size</span><span style="color:var(--astro-code-token-punctuation)">() </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> p </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> model.</span><span style="color:var(--astro-code-token-function)">parameters</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Model size: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">param_bytes </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e9</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB in </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">model.dtype</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>The <code>init_empty_weights()</code> context creates tensors on PyTorch’s “meta” device, which stores only shape and dtype, not actual data.</p>
<hr>
<h2 id="training-infrastructure">Training Infrastructure</h2>
<h3 id="trainer-class-internals">Trainer Class Internals</h3>
<p>The <a href="https://huggingface.co/docs/transformers/en/main_classes/trainer">Trainer documentation</a> covers the main training API. Key components:</p>
<p><strong>Training loop structure:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> epoch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(num_epochs):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> step</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> batch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(train_dataloader):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Forward pass</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">batch)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> outputs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">loss</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Scale loss for gradient accumulation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> loss </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> gradient_accumulation_steps</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Backward pass</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Update weights every N steps</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> (step </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">%</span><span style="color:var(--astro-code-foreground)"> gradient_accumulation_steps </span><span style="color:var(--astro-code-token-keyword)">==</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            lr_scheduler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p><strong>Subclassing for custom behavior:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Trainer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> CustomTrainer</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">Trainer</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> compute_loss</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> inputs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> return_outputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> num_items_in_batch</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">None</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> my_custom_loss</span><span style="color:var(--astro-code-token-punctuation)">(outputs.logits, inputs[</span><span style="color:var(--astro-code-token-string-expression)">&quot;labels&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> (loss</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> outputs) </span><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> return_outputs </span><span style="color:var(--astro-code-token-keyword)">else</span><span style="color:var(--astro-code-foreground)"> loss</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> training_step</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> inputs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> num_items_in_batch</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">None</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Custom training step logic</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        ...</span></span></code></pre>
<h3 id="gradient-accumulation-implementation">Gradient Accumulation Implementation</h3>
<p>The <a href="https://huggingface.co/blog/gradient_accumulation">gradient accumulation blog post</a> explains the details:</p>
<p>Gradient accumulation simulates larger batch sizes by accumulating gradients over multiple forward passes before updating weights.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">training_args </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> TrainingArguments</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    per_device_train_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Effective batch size = 4 * 8 = 32</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Internally, the Trainer scales the loss by <code>1/gradient_accumulation_steps</code> before each backward pass. This ensures the accumulated gradients match what you’d get from a single large batch.</p>
<p>Recent versions fixed a subtle bug: some loss functions (like cross-entropy) already average over batch elements, so the scaling was being applied twice in certain configurations. The fix introduced <code>num_items_in_batch</code> parameter to loss computation functions.</p>
<h3 id="mixed-precision-training-paths">Mixed Precision Training Paths</h3>
<p>The <a href="https://docs.pytorch.org/docs/stable/amp.html">PyTorch AMP documentation</a> covers mixed precision:</p>
<p><strong>FP16 training</strong> (requires loss scaling):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">amp </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GradScaler</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">scaler </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> GradScaler</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> batch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataloader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-token-function)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float16):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">batch)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> outputs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">loss</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">scale</span><span style="color:var(--astro-code-token-punctuation)">(loss).</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">(optimizer)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">update</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p><code>GradScaler</code> prevents gradient underflow by scaling loss up before backward (so gradients are larger), then scaling gradients down before optimizer step.</p>
<p><strong>BF16 training</strong> (no loss scaling needed):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">batch)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> outputs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">loss</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">loss</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>BFloat16 has the same exponent range as FP32, so it doesn’t suffer from underflow. GradScaler is unnecessary and can harm training.</p>
<p><strong>With Trainer:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">training_args </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> TrainingArguments</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bf16</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,   </span><span style="color:var(--astro-code-token-comment)"># or fp16=True</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # ...</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="peft-integration-with-training">PEFT Integration with Training</h3>
<p>The <a href="https://huggingface.co/docs/transformers/en/peft">PEFT integration guide</a> shows how adapters work with Trainer:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> peft </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> TaskType</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> get_peft_model</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> Trainer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> TrainingArguments</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load base model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure LoRA</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">lora_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;q_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;v_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;k_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;o_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.05</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    task_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">TaskType.CAUSAL_LM,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Wrap model with PEFT</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> get_peft_model</span><span style="color:var(--astro-code-token-punctuation)">(model, lora_config)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">print_trainable_parameters</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># trainable params: 13,631,488 || all params: 8,043,892,736 || trainable%: 0.17</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train normally - Trainer handles the PEFT model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Trainer</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model, args</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">training_args, train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>PEFT works by:</p>
<ol>
<li>Identifying target modules (linear layers matching <code>target_modules</code>)</li>
<li>Replacing them with wrapped versions containing LoRA matrices</li>
<li>Freezing base model parameters (requires_grad=False)</li>
<li>Only the small A and B matrices train</li>
</ol>
<p>For QLoRA (quantized base + LoRA):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_quant_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;nf4&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_compute_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> get_peft_model</span><span style="color:var(--astro-code-token-punctuation)">(model, lora_config)</span></span></code></pre>
<p>The quantized weights stay frozen (they can’t be trained anyway), and only the FP16/BF16 LoRA matrices update.</p>
<hr>
<h2 id="hub-api-deep-dive">Hub API Deep Dive</h2>
<h3 id="git-lfs-and-large-file-handling">Git LFS and Large File Handling</h3>
<p>The <a href="https://huggingface.co/docs/hub/en/storage-backends">storage documentation</a> explains file handling:</p>
<p>Historically, Hub repositories used Git LFS (Large File Storage) for files over 10MB. LFS stores file content in a separate server, with Git tracking only pointer files.</p>
<p>As of May 2025, new repositories default to Xet storage, which offers chunk-level deduplication. When you modify a file, only changed chunks upload, not the entire file. This matters for iterative checkpoint uploads during training.</p>
<p><strong>File size limits:</strong></p>
<ul>
<li>Pre-receive hook rejects commits with files &gt; 10MB not tracked by LFS/Xet</li>
<li>Individual files capped at 50GB</li>
<li>Recommended: split large files into ~20GB chunks</li>
</ul>
<p><strong>Repository limits:</strong></p>
<ul>
<li>&lt; 100k files total recommended</li>
<li>&lt; 10k files per directory</li>
<li>For large datasets, use Parquet or WebDataset formats</li>
</ul>
<h3 id="repository-structure-conventions">Repository Structure Conventions</h3>
<p>Model repositories follow conventions that enable auto-loading:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>my-model/</span></span>
<span class="line"><span>├── README.md                    # Model card with YAML frontmatter</span></span>
<span class="line"><span>├── config.json                  # Model architecture configuration</span></span>
<span class="line"><span>├── model.safetensors            # Weights (or sharded)</span></span>
<span class="line"><span>├── model.safetensors.index.json # Shard index if sharded</span></span>
<span class="line"><span>├── tokenizer.json               # Fast tokenizer config</span></span>
<span class="line"><span>├── tokenizer_config.json        # Tokenizer settings</span></span>
<span class="line"><span>├── special_tokens_map.json      # Special token definitions</span></span>
<span class="line"><span>├── vocab.json                   # Vocabulary (some tokenizers)</span></span>
<span class="line"><span>├── merges.txt                   # BPE merges (some tokenizers)</span></span>
<span class="line"><span>└── generation_config.json       # Default generation settings</span></span></code></pre>
<p>Dataset repositories:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>my-dataset/</span></span>
<span class="line"><span>├── README.md                    # Dataset card</span></span>
<span class="line"><span>├── data/</span></span>
<span class="line"><span>│   ├── train-00000-of-00010.parquet</span></span>
<span class="line"><span>│   ├── train-00001-of-00010.parquet</span></span>
<span class="line"><span>│   └── ...</span></span>
<span class="line"><span>└── dataset_info.json            # Dataset metadata</span></span></code></pre>
<h3 id="model-card-yaml-frontmatter">Model Card YAML Frontmatter</h3>
<p>The <a href="https://huggingface.co/docs/hub/en/model-cards">model cards documentation</a> specifies the metadata format:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">---</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">language</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">en</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">zh</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">license</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> apache-2.0</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">library_name</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> transformers</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">pipeline_tag</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> text-generation</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">tags</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">llama</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">chat</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">datasets</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">HuggingFaceH4/ultrachat_200k</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">base_model</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> meta-llama/Llama-3.1-8B</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">model-index</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-keyword)">name</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> my-model</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    results</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">      - </span><span style="color:var(--astro-code-token-keyword)">task</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">          type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> text-generation</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        dataset</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">          name</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> MMLU</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">          type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> cais/mmlu</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        metrics</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">          - </span><span style="color:var(--astro-code-token-keyword)">name</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> accuracy</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> accuracy</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            value</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 0.72</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">---</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># My Model</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">Model description here...</span></span></code></pre>
<p>The Hub parses this YAML to:</p>
<ul>
<li>Enable filtering by language, license, task</li>
<li>Display the inference widget based on <code>pipeline_tag</code></li>
<li>Show benchmark results on the model page</li>
<li>Link to base models and training datasets</li>
</ul>
<h3 id="huggingfacehub-library-internals">huggingface_hub Library Internals</h3>
<p>The <a href="https://huggingface.co/docs/huggingface_hub/package_reference/hf_api">huggingface_hub documentation</a> covers the Python client:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> huggingface_hub </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> HfApi</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> hf_hub_download</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> snapshot_download</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">api </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> HfApi</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># List repository files</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">files </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> api</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">list_repo_files</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Get model info</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">info </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> api</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">model_info</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(info.downloads)</span><span style="color:var(--astro-code-token-comment)">  # Download count</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(info.tags)</span><span style="color:var(--astro-code-token-comment)">       # Tags like &quot;llama&quot;, &quot;text-generation&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Download single file (cached)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">path </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> hf_hub_download</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    repo_id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    filename</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;config.json&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Download entire repo</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">local_dir </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> snapshot_download</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>The library uses a content-addressed cache at <code>~/.cache/huggingface/hub/</code>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>hub/</span></span>
<span class="line"><span>├── models--meta-llama--Llama-3.1-8B/</span></span>
<span class="line"><span>│   ├── refs/</span></span>
<span class="line"><span>│   │   └── main              # Points to commit hash</span></span>
<span class="line"><span>│   ├── blobs/</span></span>
<span class="line"><span>│   │   └── abc123...         # Content-addressed files</span></span>
<span class="line"><span>│   └── snapshots/</span></span>
<span class="line"><span>│       └── def456.../        # Symlinks to blobs</span></span></code></pre>
<p>This deduplicates identical files across model versions and enables instant switching between revisions.</p>
<hr>
<h2 id="datasets-library-internals">Datasets Library Internals</h2>
<h3 id="apache-arrow-format-and-memory-mapping">Apache Arrow Format and Memory Mapping</h3>
<p>The <a href="https://huggingface.co/docs/datasets/en/about_arrow">Datasets Arrow documentation</a> explains the storage layer:</p>
<p>Arrow is a columnar memory format optimized for analytics. Key properties:</p>
<ul>
<li><strong>Columnar</strong>: All values for a column stored contiguously, enabling vectorized operations</li>
<li><strong>Zero-copy</strong>: Data can be shared across processes without serialization</li>
<li><strong>Memory-mapped</strong>: OS handles paging data in/out of RAM</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;imdb&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Dataset is backed by Arrow file on disk</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(dataset.cache_files)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># [{&#39;filename&#39;: &#39;~/.cache/huggingface/datasets/imdb/.../train/dataset.arrow&#39;}]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Access uses memory mapping - doesn&#39;t load into RAM</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(dataset[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">])</span><span style="color:var(--astro-code-token-comment)">  # Pages in just this example</span></span></code></pre>
<p>This lets you iterate over datasets larger than RAM. The OS virtual memory system handles what’s actually in physical memory.</p>
<h3 id="datasetmap-parallelization">Dataset.map() Parallelization</h3>
<p>The <a href="https://discuss.huggingface.co/t/how-does-datasets-dataset-map-parallelize-data/36370">map parallelization discussion</a> explains multiprocessing:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> process</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">examples</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-token-string-expression)">&quot;length&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-function)">len</span><span style="color:var(--astro-code-token-punctuation)">(t)</span><span style="color:var(--astro-code-token-keyword)"> for</span><span style="color:var(--astro-code-foreground)"> t </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> examples</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Single process</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">map</span><span style="color:var(--astro-code-token-punctuation)">(process, batched</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Parallel processing</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">map</span><span style="color:var(--astro-code-token-punctuation)">(process, batched</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, num_proc</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>With <code>num_proc &gt; 1</code>:</p>
<ol>
<li>Dataset splits into <code>num_proc</code> shards</li>
<li>Each shard processes in a separate worker</li>
<li>Workers write results to temporary Arrow files</li>
<li>Results concatenate into final dataset</li>
</ol>
<p>Requirements for parallelization:</p>
<ul>
<li>Function must be picklable (top-level functions work; lambdas and closures may not)</li>
<li>Workers don’t share state</li>
<li>Each worker loads its shard independently via memory mapping</li>
</ul>
<h3 id="streaming-datasets-and-shard-fetching">Streaming Datasets and Shard Fetching</h3>
<p>The <a href="https://huggingface.co/docs/datasets/stream">streaming documentation</a> covers iterable datasets:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;HuggingFaceFW/fineweb&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">, streaming</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> example </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    process</span><span style="color:var(--astro-code-token-punctuation)">(example)</span></span></code></pre>
<p>Streaming datasets:</p>
<ul>
<li>Don’t download the full dataset</li>
<li>Fetch data on-demand as you iterate</li>
<li>Support transformations (map, filter) that apply lazily</li>
</ul>
<p>For sharded datasets, streaming fetches shards sequentially. Shuffling operates within a buffer:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Shuffles within buffer of 10000 examples</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">shuffle</span><span style="color:var(--astro-code-token-punctuation)">(seed</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">42</span><span style="color:var(--astro-code-token-punctuation)">, buffer_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">10000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>To resume from a checkpoint:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Skip to position (fetches from start of current shard)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">skip</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Resuming isn’t instant because it must re-read from the beginning of the current shard.</p>
<h3 id="custom-dataset-loading-scripts">Custom Dataset Loading Scripts</h3>
<p>The <a href="https://huggingface.co/docs/datasets/v3.4.0/en/dataset_script">dataset script documentation</a> shows how to write loaders:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> datasets</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> MyDataset</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">datasets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">GeneratorBasedBuilder</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;My custom dataset.&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    VERSION </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> datasets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Version</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;1.0.0&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> _info</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> datasets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">DatasetInfo</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            description</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;My dataset description&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            features</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">datasets.</span><span style="color:var(--astro-code-token-function)">Features</span><span style="color:var(--astro-code-token-punctuation)">({</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">: datasets.</span><span style="color:var(--astro-code-token-function)">Value</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;string&quot;</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                &quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">: datasets.</span><span style="color:var(--astro-code-token-function)">ClassLabel</span><span style="color:var(--astro-code-token-punctuation)">(names</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;negative&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;positive&quot;</span><span style="color:var(--astro-code-token-punctuation)">]),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            }),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        )</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> _split_generators</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> dl_manager</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Download and extract data</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        data_dir </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dl_manager</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">download_and_extract</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;https://example.com/data.zip&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            datasets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">SplitGenerator</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">                name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">datasets.Split.TRAIN,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">                gen_kwargs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;filepath&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">data_dir</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">/train.jsonl&quot;</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            ),</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            datasets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">SplitGenerator</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">                name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">datasets.Split.TEST,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">                gen_kwargs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;filepath&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">data_dir</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">/test.jsonl&quot;</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            ),</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        ]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> _generate_examples</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> filepath</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        with</span><span style="color:var(--astro-code-token-function)"> open</span><span style="color:var(--astro-code-token-punctuation)">(filepath)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            for</span><span style="color:var(--astro-code-foreground)"> idx</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> line </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(f):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">                data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> json</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">loads</span><span style="color:var(--astro-code-token-punctuation)">(line)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">                yield</span><span style="color:var(--astro-code-foreground)"> idx</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                    &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> data</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                    &quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> data</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">                }</span></span></code></pre>
<p>For Arrow-based builders (better for large datasets):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> MyArrowDataset</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">datasets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">ArrowBasedBuilder</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> _generate_tables</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">self</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> filepath</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Yield PyArrow tables instead of individual examples</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        table </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> pq</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">read_table</span><span style="color:var(--astro-code-token-punctuation)">(filepath)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        yield</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> table</span></span></code></pre>
<hr>
<h2 id="the-diffusers-library">The Diffusers Library</h2>
<p>Diffusers handles diffusion models for image, video, and audio generation. It supports Stable Diffusion, SDXL, Flux, Kandinsky, and other architectures.</p>
<h3 id="basic-image-generation">Basic Image Generation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> diffusers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DiffusionPipeline</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load Stable Diffusion XL</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> DiffusionPipeline</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;stabilityai/stable-diffusion-xl-base-1.0&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipe</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;A cat astronaut floating in space, digital art&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-foreground)">images</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cat_astronaut.png&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="flux-models">FLUX Models</h3>
<p>FLUX is a 12 billion parameter rectified flow transformer from Black Forest Labs. It produces high-quality images with strong text rendering and prompt adherence.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> diffusers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> FluxPipeline</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> FluxPipeline</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;black-forest-labs/FLUX.1-schnell&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">enable_model_cpu_offload</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># FLUX.1-schnell generates in 1-4 steps</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipe</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;A photo of a mountain lake at sunset&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    guidance_scale</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.0</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_inference_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-foreground)">images</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;lake.png&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>FLUX.1-schnell is distilled for speed. FLUX.1-dev offers higher quality with more steps. FLUX.1-Kontext handles image editing with text instructions.</p>
<h3 id="memory-optimization">Memory Optimization</h3>
<p>Diffusers provides multiple strategies for running on limited VRAM:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># CPU offloading - moves components to CPU when not in use</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">enable_model_cpu_offload</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Sequential CPU offloading - more aggressive, slower</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">enable_sequential_cpu_offload</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Attention slicing - trades compute for memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">enable_attention_slicing</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># VAE tiling - generate high-res without OOM</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">enable_vae_tiling</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="lora-and-adapters">LoRA and Adapters</h3>
<p>Load style or concept LoRAs:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load_lora_weights</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;username/style-lora&quot;</span><span style="color:var(--astro-code-token-punctuation)">, weight_name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;lora.safetensors&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Adjust LoRA strength</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">fuse_lora</span><span style="color:var(--astro-code-token-punctuation)">(lora_scale</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.8</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Unload</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">unfuse_lora</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">unload_lora_weights</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<hr>
<h2 id="hugging-face-spaces">Hugging Face Spaces</h2>
<p>Spaces are web applications hosted on Hugging Face. The key insight: Spaces are Git repositories. You can clone them, run them locally, and modify them like any codebase.</p>
<h3 id="cloning-and-running-locally">Cloning and Running Locally</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Clone a Space</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://huggingface.co/spaces/username/my-space</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> my-space</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Install dependencies</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> venv</span><span style="color:var(--astro-code-token-string)"> venv</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">source</span><span style="color:var(--astro-code-token-string)"> venv/bin/activate</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> -r</span><span style="color:var(--astro-code-token-string)"> requirements.txt</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run locally</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> app.py</span><span style="color:var(--astro-code-token-comment)">  # or gradio app.py for Gradio apps</span></span></code></pre>
<p>Spaces typically use Gradio or Streamlit for the frontend. The same code runs identically on your machine and on Hugging Face’s servers.</p>
<h3 id="gradio-space-example">Gradio Space Example</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> gradio </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> gr</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pipeline</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">classifier </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;sentiment-analysis&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> classify</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">text</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> classifier</span><span style="color:var(--astro-code-token-punctuation)">(text)</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">result</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;label&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">result</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;score&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">demo </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> gr</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Interface</span><span style="color:var(--astro-code-token-punctuation)">(fn</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">classify, inputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">, outputs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">demo</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">launch</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="syncing-with-github">Syncing with GitHub</h3>
<p>Configure a GitHub Action to push changes to both GitHub and Hugging Face:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">name</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> Sync to Hugging Face</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">on</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  push</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    branches</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string)">main</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">jobs</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  sync</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    runs-on</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ubuntu-latest</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    steps</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">      - </span><span style="color:var(--astro-code-token-keyword)">uses</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> actions/checkout@v3</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">      - </span><span style="color:var(--astro-code-token-keyword)">name</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> Push to HF</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        run</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-keyword)"> |</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">          git push https://user:${{ secrets.HF_TOKEN }}@huggingface.co/spaces/user/space main</span></span></code></pre>
<p>Spaces support Docker for custom environments, persistent storage for databases, and GPU runtimes for ML inference.</p>
<hr>
<h2 id="other-hugging-face-libraries">Other Hugging Face Libraries</h2>
<h3 id="accelerate">Accelerate</h3>
<p>Accelerate lets you run the same PyTorch training code on any hardware configuration: single GPU, multi-GPU, TPU, or distributed clusters. Add a few lines to existing code:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> accelerate </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Accelerator</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">accelerator </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Accelerator</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Wrap model, optimizer, dataloader</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> optimizer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> dataloader </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> accelerator</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">prepare</span><span style="color:var(--astro-code-token-punctuation)">(model, optimizer, dataloader)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> batch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataloader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">batch)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> outputs</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">loss</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    accelerator</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">(loss)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>Configure distributed training with the CLI:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">accelerate</span><span style="color:var(--astro-code-token-string)"> config</span><span style="color:var(--astro-code-token-comment)">  # Interactive setup</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">accelerate</span><span style="color:var(--astro-code-token-string)"> launch</span><span style="color:var(--astro-code-token-string)"> train.py</span><span style="color:var(--astro-code-token-comment)">  # Launch with config</span></span></code></pre>
<p>Accelerate handles mixed precision (fp16, bf16, fp8), DeepSpeed integration, and FSDP for sharding large models.</p>
<h3 id="peft">PEFT</h3>
<p>PEFT implements parameter-efficient fine-tuning methods. LoRA is the most popular: freeze the base model and train small adapter matrices.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> peft </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> get_peft_model</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">lora_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,                    </span><span style="color:var(--astro-code-token-comment)"># Rank</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">,           </span><span style="color:var(--astro-code-token-comment)"># Scaling</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;all-linear&quot;</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Apply to all linear layers</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.05</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">peft_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> get_peft_model</span><span style="color:var(--astro-code-token-punctuation)">(model, lora_config)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">peft_model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">print_trainable_parameters</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># trainable params: 41,943,040 || all params: 8,030,261,248 || trainable%: 0.52</span></span></code></pre>
<p>A 7B model drops from training billions of parameters to millions. The base model stays frozen; only the adapter trains. Merge adapters back into the base model for inference or keep them separate to swap styles.</p>
<p>PEFT supports LoRA variants like DoRA (magnitude and direction decomposition) and methods beyond LoRA: IA3, AdaLoRA, and prompt tuning.</p>
<h3 id="trl">TRL</h3>
<p>TRL provides trainers for post-training: supervised fine-tuning, RLHF, and preference optimization.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> SFTTrainer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;your-dataset&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SFTTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tokenizer</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_seq_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2048</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>For preference optimization, DPOTrainer implements Direct Preference Optimization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DPOTrainer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> DPOConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DPOConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    beta</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;dpo_model&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DPOTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    ref_model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">ref_model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    args</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">preference_dataset,  </span><span style="color:var(--astro-code-token-comment)"># Has &#39;chosen&#39; and &#39;rejected&#39; columns</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tokenizer</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>GRPOTrainer implements Group Relative Policy Optimization, the algorithm behind DeepSeek R1. It avoids needing a separate reward model by comparing outputs within groups.</p>
<h3 id="optimum">Optimum</h3>
<p>Optimum provides hardware-specific optimizations. Export to ONNX, quantize, and run on specialized accelerators:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> optimum</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">onnxruntime </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ORTModelForSequenceClassification</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load and export to ONNX</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ORTModelForSequenceClassification</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;distilbert-base-uncased-finetuned-sst-2-english&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    export</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or load existing ONNX model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ORTModelForSequenceClassification</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;onnx_model_dir&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Optimum integrates with:</p>
<ul>
<li>Intel Gaudi accelerators via optimum-habana</li>
<li>AWS Trainium via optimum-neuron</li>
<li>Intel CPUs via optimum-intel</li>
<li>AMD GPUs via optimum-amd</li>
</ul>
<p>The optimum-benchmark tool compares performance across backends and quantization schemes.</p>
<hr>
<h2 id="practical-code-examples">Practical Code Examples</h2>
<h3 id="complete-fine-tuning-pipeline">Complete Fine-Tuning Pipeline</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> (</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    TrainingArguments</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> peft </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LoraConfig</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> SFTTrainer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model with 4-bit quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_id </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;meta-llama/Llama-3.1-8B&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_id,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_id)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">pad_token </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">eos_token</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load dataset</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;databricks/databricks-dolly-15k&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># LoRA config for QLoRA</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">peft_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LoraConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;all-linear&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.05</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    task_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;CAUSAL_LM&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Training arguments</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">training_args </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> TrainingArguments</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;./llama-finetuned&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_train_epochs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    per_device_train_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    learning_rate</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2e-4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bf16</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    logging_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    save_strategy</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;epoch&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SFTTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    peft_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">peft_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    args</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">training_args,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tokenizer</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_model</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="building-a-rag-system">Building a RAG System</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> sentence_transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> SentenceTransformer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pipeline</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Embedding model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">embedder </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SentenceTransformer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;BAAI/bge-base-en-v1.5&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generator</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">generator </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;text-generation&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.2-3B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Sample documents</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">documents </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;Python was created by Guido van Rossum and released in 1991.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;JavaScript was created by Brendan Eich in 1995.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;Rust was developed by Mozilla and released in 2015.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Embed documents</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">doc_embeddings </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> embedder</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">(documents, normalize_embeddings</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> retrieve_and_generate</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">query</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> str</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> top_k</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> int</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-punctuation)">-&gt;</span><span style="color:var(--astro-code-token-constant)"> str</span><span style="color:var(--astro-code-foreground)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Embed query</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    query_embedding </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> embedder</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">([query], normalize_embeddings</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Find similar documents</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scores </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">dot</span><span style="color:var(--astro-code-token-punctuation)">(doc_embeddings, query_embedding.T).</span><span style="color:var(--astro-code-token-function)">flatten</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    top_indices </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">argsort</span><span style="color:var(--astro-code-token-punctuation)">(scores)</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)">top_k</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)">][</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    context </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;\n&quot;</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">join</span><span style="color:var(--astro-code-token-punctuation)">([documents[i] </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> i </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-punctuation)"> top_indices])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Generate answer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;&quot;Context: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">context</span><span style="color:var(--astro-code-token-constant)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">Question: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">query</span><span style="color:var(--astro-code-token-constant)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">Answer based on the context above:&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> generator</span><span style="color:var(--astro-code-token-punctuation)">(prompt, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">, do_sample</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> response</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;generated_text&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">answer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> retrieve_and_generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;When was Python created?&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(answer)</span></span></code></pre>
<h3 id="multi-modal-pipeline-with-flux">Multi-Modal Pipeline with FLUX</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> diffusers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> FluxPipeline</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pipeline</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Image generation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">flux </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> FluxPipeline</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;black-forest-labs/FLUX.1-schnell&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">flux</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">enable_model_cpu_offload</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Image captioning</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">captioner </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;image-to-text&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Salesforce/blip2-opt-2.7b&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate image</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;A cozy cabin in the woods during autumn, warm lighting&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> flux</span><span style="color:var(--astro-code-token-punctuation)">(prompt, num_inference_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-foreground)">images</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cabin.png&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Caption the generated image</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">caption </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> captioner</span><span style="color:var(--astro-code-token-punctuation)">(image)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Generated caption: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">caption[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">][</span><span style="color:var(--astro-code-token-string-expression)">&#39;generated_text&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="distributed-training-with-accelerate">Distributed Training with Accelerate</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># train.py</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> accelerate </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Accelerator</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForSequenceClassification</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">utils</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">data </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DataLoader</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">accelerator </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> Accelerator</span><span style="color:var(--astro-code-token-punctuation)">(mixed_precision</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;bf16&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model and tokenizer</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForSequenceClassification</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;bert-base-uncased&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_labels</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;bert-base-uncased&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load and tokenize dataset</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;imdb&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train[:1000]&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> tokenize</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">examples</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(examples[</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">], truncation</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">, padding</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;max_length&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">map</span><span style="color:var(--astro-code-token-punctuation)">(tokenize, batched</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_format</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;torch&quot;</span><span style="color:var(--astro-code-token-punctuation)">, columns</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;input_ids&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;attention_mask&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataloader </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DataLoader</span><span style="color:var(--astro-code-token-punctuation)">(dataset, batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">, shuffle</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">optimizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">optim</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">AdamW</span><span style="color:var(--astro-code-token-punctuation)">(model.</span><span style="color:var(--astro-code-token-function)">parameters</span><span style="color:var(--astro-code-token-punctuation)">(), lr</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2e-5</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prepare for distributed training</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> optimizer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> dataloader </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> accelerator</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">prepare</span><span style="color:var(--astro-code-token-punctuation)">(model, optimizer, dataloader)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Training loop</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> epoch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> batch </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> dataloader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            input_ids</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">batch[</span><span style="color:var(--astro-code-token-string-expression)">&quot;input_ids&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            attention_mask</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">batch[</span><span style="color:var(--astro-code-token-string-expression)">&quot;attention_mask&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            labels</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">batch[</span><span style="color:var(--astro-code-token-string-expression)">&quot;label&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        )</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        accelerator</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">(outputs.loss)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    accelerator</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Epoch </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">epoch</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> complete&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">accelerator</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_model</span><span style="color:var(--astro-code-token-punctuation)">(model, </span><span style="color:var(--astro-code-token-string-expression)">&quot;trained_model&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Launch with:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">accelerate</span><span style="color:var(--astro-code-token-string)"> launch</span><span style="color:var(--astro-code-token-string)"> --multi_gpu</span><span style="color:var(--astro-code-token-string)"> --num_processes</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-string)"> train.py</span></span></code></pre>
<hr>
<h2 id="references">References</h2>
<ul>
<li><a href="https://huggingface.co/docs/hub/en/index">Hugging Face Hub Documentation</a></li>
<li><a href="https://en.wikipedia.org/wiki/Hugging_Face">Hugging Face Wikipedia</a></li>
<li><a href="https://fueler.io/blog/hugging-face-usage-revenue-valuation-growth-statistics">Hugging Face 2026 Statistics - Fueler</a></li>
<li><a href="https://github.com/huggingface/transformers">Transformers GitHub Repository</a></li>
<li><a href="https://huggingface.co/blog/tokenizers">Transformers v5 Tokenization Blog</a></li>
<li><a href="https://huggingface.co/docs/transformers/main_classes/pipelines">Pipeline Documentation</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/model_doc/auto">Auto Classes Documentation</a></li>
<li><a href="https://huggingface.co/docs/transformers/main_classes/model">Models Documentation</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/generation_strategies">Generation Strategies</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/kv_cache">KV Cache Strategies</a></li>
<li><a href="https://huggingface.co/docs/transformers/tokenizer_summary">Tokenizer Summary</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/fast_tokenizers">Fast Tokenizers Guide</a></li>
<li><a href="https://huggingface.co/docs/tokenizers/en/quicktour">Tokenizers Quicktour</a></li>
<li><a href="https://github.com/huggingface/tokenizers">Tokenizers GitHub Repository</a></li>
<li><a href="https://huggingface.co/docs/accelerate/en/usage_guides/big_modeling">Big Model Inference Guide</a></li>
<li><a href="https://huggingface.co/docs/accelerate/en/concept_guides/big_model_inference">Loading Big Models Concept Guide</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/quantization/bitsandbytes">Bitsandbytes Quantization Guide</a></li>
<li><a href="https://huggingface.co/blog/4bit-transformers-bitsandbytes">Making LLMs Accessible with 4-bit Quantization</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/main_classes/trainer">Trainer Documentation</a></li>
<li><a href="https://huggingface.co/blog/gradient_accumulation">Fixing Gradient Accumulation Blog</a></li>
<li><a href="https://docs.pytorch.org/docs/stable/amp.html">PyTorch AMP Documentation</a></li>
<li><a href="https://pytorch.org/blog/what-every-user-should-know-about-mixed-precision-training-in-pytorch/">PyTorch Mixed Precision Blog</a></li>
<li><a href="https://huggingface.co/docs/transformers/en/peft">PEFT Integration Guide</a></li>
<li><a href="https://github.com/huggingface/peft">PEFT GitHub Repository</a></li>
<li><a href="https://huggingface.co/docs/hub/en/storage-backends">Storage Backends Documentation</a></li>
<li><a href="https://huggingface.co/docs/hub/en/model-cards">Model Cards Documentation</a></li>
<li><a href="https://huggingface.co/docs/huggingface_hub/package_reference/hf_api">huggingface_hub Documentation</a></li>
<li><a href="https://huggingface.co/docs/datasets/en/about_arrow">Datasets Arrow Documentation</a></li>
<li><a href="https://huggingface.co/docs/datasets/stream">Streaming Datasets</a></li>
<li><a href="https://huggingface.co/docs/datasets/v3.4.0/en/dataset_script">Dataset Loading Scripts</a></li>
<li><a href="https://github.com/huggingface/diffusers">Diffusers GitHub Repository</a></li>
<li><a href="https://huggingface.co/black-forest-labs/FLUX.1-dev">FLUX.1-dev Model Card</a></li>
<li><a href="https://huggingface.co/docs/safetensors/speed">Safetensors Speed Comparison</a></li>
<li><a href="https://deepwiki.com/huggingface/safetensors">Safetensors DeepWiki</a></li>
<li><a href="https://github.com/ggml-org/llama.cpp">llama.cpp GitHub Repository</a></li>
<li><a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">GGUF Quantization Guide</a></li>
<li><a href="https://apxml.com/courses/practical-llm-quantization/chapter-5-quantization-formats-tooling/gguf-format">GGUF Format Explained</a></li>
<li><a href="https://newsletter.maartengrootendorst.com/p/which-quantization-method-is-right">Quantization Methods Comparison - Maarten Grootendorst</a></li>
<li><a href="https://localaimaster.com/blog/quantization-explained">AWQ vs GPTQ vs GGUF - LocalAI Master</a></li>
<li><a href="https://docs.jarvislabs.ai/blog/vllm-quantization-complete-guide-benchmarks">vLLM Quantization Guide</a></li>
<li><a href="https://onnx.ai/">ONNX Official Site</a></li>
<li><a href="https://onnxruntime.ai/docs/">ONNX Runtime Documentation</a></li>
<li><a href="https://github.com/NVIDIA/TensorRT-LLM">TensorRT-LLM GitHub Repository</a></li>
<li><a href="https://nvidia.github.io/TensorRT-LLM/overview.html">TensorRT-LLM Documentation</a></li>
<li><a href="https://huggingface.co/docs/hub/en/datasets-streaming">Datasets Streaming Documentation</a></li>
<li><a href="https://huggingface.co/blog/cfahlgren1/intro-to-parquet-format">Parquet Introduction - Hugging Face Blog</a></li>
<li><a href="https://github.com/huggingface/evaluate">Evaluate Library GitHub</a></li>
<li><a href="https://huggingface.co/docs/peft/en/package_reference/lora">LoRA PEFT Documentation</a></li>
<li><a href="https://www.philschmid.de/fine-tune-llms-in-2025">Fine-tuning LLMs in 2025 - Phil Schmid</a></li>
<li><a href="https://github.com/huggingface/accelerate">Accelerate GitHub Repository</a></li>
<li><a href="https://huggingface.co/blog/accelerate-v1">Accelerate 1.0.0 Blog Post</a></li>
<li><a href="https://huggingface.co/docs/trl/en/index">TRL Documentation</a></li>
<li><a href="https://github.com/huggingface/trl">TRL GitHub Repository</a></li>
<li><a href="https://github.com/huggingface/optimum">Optimum GitHub Repository</a></li>
<li><a href="https://huggingface.co/docs/optimum/en/index">Optimum Documentation</a></li>
<li><a href="https://huggingface.co/docs/hub/spaces-overview">Spaces Overview</a></li>
<li><a href="https://huggingface.co/docs/hub/repositories-getting-started">Getting Started with Repositories</a></li>
<li><a href="https://codesignal.com/learn/courses/2-modern-tokenization-techniques-for-ai-llms/lessons/comparing-bpe-wordpiece-and-sentencepiece-in-nlp">Tokenization Algorithms Comparison - CodeSignal</a></li>
<li><a href="https://huggingface.co/blog/kv-cache-quantization">KV Cache Quantization Blog</a></li>
</ul>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Hugging Face</category>
            <category>Transformers</category>
            <category>Open source</category>
            <enclosure length="64242" type="image/png" url="https://blog.ecitis.org/open-graph/huggingface-ecosystem-guide.png"/>
        </item>
        <item>
            <title><![CDATA[Advanced Techniques for Local AI Inferencing]]></title>
            <link>https://blog.ecitis.org/local-ai-inferencing-techniques/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/local-ai-inferencing-techniques/</guid>
            <pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Make local models faster and smaller with quantization, speculative decoding, attention optimization, caching, and runtime selection.]]></description>
            <content:encoded><![CDATA[<h2 id="why-local-inferencing-matters">Why local inferencing matters</h2>
<p>Running AI models locally instead of through cloud APIs addresses four problems at once: privacy, latency, cost, and availability.</p>
<p><strong>Privacy</strong>: Cloud-based GenAI tools exposed approximately 3 million sensitive records per organization in the first half of 2025, according to data from Microsoft Copilot deployments [1]. Keeping inference on-device shrinks attack surface and regulatory footprint. For workflows in legal, health, and finance, avoiding data transmission to external servers is often the simplest path to compliance with GDPR and HIPAA.</p>
<p><strong>Latency</strong>: Local GPU inference delivers sub-50ms latency versus 100-500ms for cloud roundtrips. Cactus, a Y Combinator-backed startup, demonstrated sub-50ms time-to-first-token for on-device inference [2]. TinyML solutions can achieve latencies as low as 0-5ms for inference tasks, compared to 10-500ms for cloud IoT solutions [3].</p>
<p><strong>Cost</strong>: Local inference eliminates per-token API costs. Once you own the hardware, inference is free. IDC and Gartner predict that by 2027, over 60% of all AI inference processes will happen locally rather than in the cloud [4].</p>
<p><strong>Offline capability</strong>: Local inference removes network hops and eliminates the need for a constant internet connection. Apps keep working on factory floors, in hospitals, and in low-connectivity environments.</p>
<hr>
<h2 id="reap-router-weighted-expert-activation-pruning">REAP: Router-weighted Expert Activation Pruning</h2>
<p>REAP is a one-shot compression method for Mixture-of-Experts (MoE) language models developed by Cerebras Research. It removes up to 50% of experts from models as large as 1 trillion parameters while largely maintaining baseline model quality [5].</p>
<h3 id="the-problem-reap-solves">The problem REAP solves</h3>
<p>MoE models like Mixtral, DeepSeek, and Qwen achieve high capability by routing tokens to different expert subnetworks. This architecture creates redundancy: not all experts contribute equally to every inference. REAP exploits this by identifying and removing the least important experts.</p>
<h3 id="how-reap-works">How REAP works</h3>
<p>REAP selects experts to prune based on a saliency criterion that considers two factors:</p>
<ol>
<li><strong>Router gate values</strong>: How frequently and strongly the router activates each expert</li>
<li><strong>Expert activation norms</strong>: The magnitude of each expert’s output contributions</li>
</ol>
<p>By combining these factors, REAP identifies experts that are both rarely used and have little impact when they are used. The method is one-shot, meaning it requires no fine-tuning after pruning.</p>
<p>The key insight from the REAP paper is that expert pruning outperforms expert merging for generative tasks. The researchers proved that merging introduces an irreducible error by causing a “functional subspace collapse” due to the loss of the router’s independent, input-dependent control over experts [6].</p>
<h3 id="performance-results">Performance results</h3>
<p>On the Qwen3-480B-Coder-FP8 model, REAP at 50% pruning retains:</p>
<ul>
<li>97.6% of baseline non-agentic coding ability</li>
<li>96.7% on the agentic SWE-Bench benchmark</li>
</ul>
<p>The method achieves near-lossless compression on code generation and tool-calling tasks with Qwen3-Coder-480B and Kimi-K2, even after pruning 50% of experts [5].</p>
<h3 id="implementation">Implementation</h3>
<p>The official REAP implementation is available at <a href="https://github.com/CerebrasResearch/reap">github.com/CerebrasResearch/reap</a>. Pruned models are available on HuggingFace in the Cerebras REAP collection, including models like <code>cerebras/Kimi-Linear-REAP-35B-A3B-Instruct</code> and <code>cerebras/MiniMax-M2-REAP-172B-A10B</code> [7].</p>
<hr>
<h2 id="speculative-decoding">Speculative decoding</h2>
<p>Speculative decoding accelerates LLM inference by predicting and verifying multiple tokens simultaneously. The technique uses a smaller draft model to propose tokens and a larger target model to verify them in parallel.</p>
<h3 id="core-mechanism">Core mechanism</h3>
<p>The draft-target approach works as follows:</p>
<ol>
<li>A small draft model (10-50x smaller than target) generates K candidate tokens autoregressively</li>
<li>The target model verifies those proposals in a single forward pass</li>
<li>Rejection sampling determines which tokens to accept based on probability distributions</li>
<li>The target accepts the longest prefix that matches its own predictions and continues from there</li>
</ol>
<p>Compared with standard autoregressive decoding, which produces one token per pass, speculative decoding generates multiple tokens at once, cutting latency without any impact on accuracy [8].</p>
<h3 id="draft-model-techniques">Draft model techniques</h3>
<p><strong>Traditional draft models</strong>: The draft model must be significantly smaller (10-50x) to achieve inference acceleration. The speedup ratio increases as the target model size increases [9].</p>
<p><strong>EAGLE (Extrapolation Algorithm for Greater Language-model Efficiency)</strong>: EAGLE-3 uses a lightweight autoregressive prediction head attached to the target model’s internal layers to generate candidate tokens, eliminating the need for a separate draft model [10].</p>
<p><strong>N-gram and Suffix Decoding</strong>: Suffix Decoding generates draft tokens by pattern-matching using the last n generated tokens against both the prompt and previous generations, using frequency counts to propose the most likely continuations [10].</p>
<h3 id="implementation-example">Implementation example</h3>
<p>With vLLM, speculative decoding can be enabled via configuration:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    speculative_model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.2-1B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_speculative_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">5</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sampling_params </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SamplingParams</span><span style="color:var(--astro-code-token-punctuation)">(temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-string-expression)">&quot;Explain transformers:&quot;</span><span style="color:var(--astro-code-token-punctuation)">], sampling_params)</span></span></code></pre>
<h3 id="performance">Performance</h3>
<p>SpecForge’s Llama 4 Maverick draft model achieves a 2.18x speedup on MT-Bench, while the Scout variant delivers a 2.0x acceleration [11]. Acceptance rate is the critical metric: high acceptance means more tokens accepted per round, fewer target model forward passes, lower latency, and better GPU utilization.</p>
<hr>
<h2 id="quantization-techniques">Quantization techniques</h2>
<p>Quantization reduces the precision of model weights, trading a small amount of accuracy for large memory and speed gains.</p>
<h3 id="post-training-quantization-vs-quantization-aware-training">Post-training quantization vs quantization-aware training</h3>
<p><strong>Post-Training Quantization (PTQ)</strong> applies quantization after training is complete. It’s fast to implement but can lose accuracy, especially at low bit-widths.</p>
<p><strong>Quantization-Aware Training (QAT)</strong> integrates quantization into the training process. The model learns to handle quantization errors during training. PyTorch demonstrated that QAT can recover up to 96% of the accuracy degradation on HellaSwag and 68% of the perplexity degradation on WikiText for Llama3 compared to PTQ [12].</p>



































<div class="table-wrap"><table><thead><tr><th>Aspect</th><th>PTQ</th><th>QAT</th></tr></thead><tbody><tr><td>Training required</td><td>No</td><td>Yes</td></tr><tr><td>Compute cost</td><td>Low</td><td>High</td></tr><tr><td>Accuracy at INT8</td><td>Good</td><td>Excellent</td></tr><tr><td>Accuracy at INT4</td><td>Variable</td><td>Good</td></tr><tr><td>Time to deploy</td><td>Minutes</td><td>Hours/Days</td></tr></tbody></table></div>
<h3 id="gguf-quantization-levels">GGUF quantization levels</h3>
<p>GGUF (Georgi Gerganov Universal Format) is designed for efficient CPU-based inference within the llama.cpp ecosystem. The format supports multiple quantization levels [13]:</p>
<figure><figcaption><strong>GGUF Quantization Levels</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/local-ai-inferencing-techniques-0.webp" width="512" height="960" alt="GGUF Quantization Levels" loading="lazy" decoding="async"></figure>









































<div class="table-wrap"><table><thead><tr><th>Quantization</th><th>Size (7B model)</th><th>Perplexity increase</th><th>Notes</th></tr></thead><tbody><tr><td>Q8_0</td><td>~7.0 GB</td><td>+0.0004</td><td>Highest quality</td></tr><tr><td>Q5_K_M</td><td>~5.33 GB</td><td>+0.0142</td><td>Higher quality, slightly larger</td></tr><tr><td>Q4_K_M</td><td>~4.58 GB</td><td>+0.0535</td><td>Recommended default, best balance</td></tr><tr><td>Q4_K_S</td><td>~4.37 GB</td><td>+0.0992</td><td>Smaller, lower quality</td></tr><tr><td>Q3_K_M</td><td>~3.52 GB</td><td>+0.2437</td><td>Aggressive compression</td></tr></tbody></table></div>
<p>Q4_K_M is the “safe default” for phones and lighter Macs. Q5_K_M improves detail and reasoning stability. Use an importance matrix (<code>--imatrix</code>) for optimal results [14].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize with llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./llama-quantize</span><span style="color:var(--astro-code-token-string)"> model.gguf</span><span style="color:var(--astro-code-token-string)"> model-Q4_K_M.gguf</span><span style="color:var(--astro-code-token-string)"> Q4_K_M</span></span></code></pre>
<h3 id="awq-gptq-and-bitsandbytes">AWQ, GPTQ, and bitsandbytes</h3>
<p><strong>AWQ (Activation-aware Weight Quantization)</strong>: Developed by MIT-HAN lab, AWQ protects salient weights by observing activations rather than weights themselves. It assumes not all weights are equally important and skips quantizing the most critical ones. AWQ achieves 95% quality retention, outperforming GPTQ (90%) in many benchmarks [15].</p>
<p><strong>GPTQ (Generative Pre-trained Transformer Quantization)</strong>: A layer-wise post-training method that minimizes output error via Hessian-based optimization. Supports 8, 4, 3, or 2-bit quantization. GPTQ tends to overfit on its calibration data, which can hurt out-of-distribution performance [16].</p>
<p><strong>bitsandbytes</strong>: Provides 8-bit (LLM.int8()) and 4-bit (NF4) quantization for PyTorch. The 4-bit NF4 format is specifically designed for neural network weights, assuming weights follow a normal distribution and placing quantization levels where most weights are concentrated (near zero) [17].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> BitsAndBytesConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">quantization_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> BitsAndBytesConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_quant_type</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;nf4&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_compute_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bnb_4bit_use_double_quant</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Quantize the quantization constants</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    quantization_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">quantization_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="exllamav2-quantization">ExLlamaV2 quantization</h3>
<p>ExLlamaV2 is an inference library optimized for consumer GPUs. Its EXL2 format supports 2, 3, 4, 5, 6, and 8-bit quantization and can mix different precisions within a model to preserve the most important weights [18].</p>
<p>ExLlamaV2 achieves 56.44 tokens/second on a T4 GPU, providing the highest tokens-per-second compared to GPTQ or llama.cpp. It now supports paged attention via Flash Attention 2.5.7+ and includes dynamic batching with smart prompt caching [18].</p>

























<div class="table-wrap"><table><thead><tr><th>Use Case</th><th>Recommended Method</th></tr></thead><tbody><tr><td>GPU inference, high throughput</td><td>GPTQ</td></tr><tr><td>Quality-critical applications</td><td>AWQ</td></tr><tr><td>CPU/Apple devices</td><td>GGUF</td></tr><tr><td>Fine-tuned size control</td><td>EXL2</td></tr></tbody></table></div>
<hr>
<h2 id="kv-cache-optimization">KV-cache optimization</h2>
<p>The key-value cache stores intermediate attention states during autoregressive generation. Without optimization, KV cache wastes 60-80% of allocated memory through fragmentation and over-allocation [19].</p>
<figure><figcaption><strong>Context length becomes a KV-cache memory bill</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/local-ai-inferencing-techniques-1.webp" width="512" height="562" alt="Context length becomes a KV-cache memory bill" loading="lazy" decoding="async"></figure>
<h3 id="pagedattention">PagedAttention</h3>
<p>PagedAttention, introduced by vLLM, applies virtual memory concepts to KV cache management. Instead of storing each conversation’s KV cache as one contiguous block, PagedAttention breaks it into small, fixed-size “blocks” that can be stored anywhere in memory.</p>
<p>The results: PagedAttention slashed KV cache waste to under 4%, enabling 2-4x throughput improvements [19].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># vLLM automatically uses PagedAttention</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gpu_memory_utilization</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.90</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_model_len</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">32768</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="continuous-batching">Continuous batching</h3>
<p>Continuous batching operates at the token level. Traditional batching processes requests in fixed groups, wasting compute when some sequences finish before others. Continuous batching walks model layers across a rotating set of in-flight sequences, reusing weight loads across heterogeneous sequence lengths [20].</p>
<p>vLLM achieves up to 24x higher throughput than Hugging Face TGI under high-concurrency workloads through continuous batching combined with PagedAttention [21].</p>
<h3 id="memory-management-strategies">Memory management strategies</h3>
<p><strong>Hierarchical caching</strong>: vLLM takes a hierarchical approach to KV caching. It first checks GPU memory, then CPU memory on cache miss, then retrieves from configured KV connectors (like LMCache for distributed caching) [22].</p>
<p><strong>FP8 KV cache</strong>: Hopper and Blackwell GPUs support native FP8 KV cache, which halves KV cache memory requirements with minimal accuracy impact [19].</p>
<p><strong>Prefix caching</strong>: Automatic Prefix Caching (APC) detects common prefixes across requests and shares KV cache blocks automatically. Shared prefixes during beam search reduce KV memory usage by up to 55% [23].</p>
<hr>
<h2 id="flashattention">FlashAttention</h2>
<p>FlashAttention is an IO-aware attention algorithm that reduces memory bandwidth bottlenecks by computing attention in tiles without materializing the full attention matrix.</p>
<h3 id="flashattention-2">FlashAttention-2</h3>
<p>FlashAttention-2 improved on the original by better parallelizing across thread blocks and reducing synchronization. It achieves 35% utilization on H100 GPUs and speeds up training by 3-5x compared to baseline implementations from Hugging Face, reaching up to 225 TFLOPs/sec per A100 [24].</p>
<h3 id="flashattention-3">FlashAttention-3</h3>
<p>FlashAttention-3 targets Hopper GPUs with three optimizations [25]:</p>
<ol>
<li><strong>Asynchronous execution</strong>: Exploits asynchrony between Tensor Cores and TMA to overlap computation and data movement via warp-specialization</li>
<li><strong>Interleaved operations</strong>: Interleaves block-wise matmul and softmax operations</li>
<li><strong>FP8 quantization</strong>: Uses block quantization with incoherent processing for FP8 low-precision</li>
</ol>
<p>Performance on H100:</p>
<ul>
<li>BF16: Up to 840 TFLOPs/s (85% utilization)</li>
<li>FP8: Up to 1.3 PFLOPs/s</li>
</ul>
<p>FlashAttention-3 is up to 2.0x faster than FlashAttention-2. FP8 FlashAttention-3 achieves 2.6x lower numerical error than baseline FP8 attention because intermediate softmax results are kept in FP32 [25].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># FlashAttention is integrated into transformers</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    attn_implementation</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;flash_attention_2&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.bfloat16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="tensor-parallelism-and-model-sharding">Tensor parallelism and model sharding</h2>
<p>When models exceed single-GPU memory, sharding distributes parameters across multiple devices.</p>
<h3 id="tensor-parallelism">Tensor parallelism</h3>
<p>Tensor parallelism shards individual layers horizontally across GPUs. The two primary techniques are [26]:</p>
<ul>
<li><strong>Column parallelism</strong>: Splits weight matrices along columns, concatenates results after computation</li>
<li><strong>Row parallelism</strong>: Splits matrices along rows, sums partial results post-computation</li>
</ul>
<p>With tensor parallelism across 4 GPUs, a 512 MB weight matrix becomes 128 MB per GPU, a 4x memory reduction for that layer.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Tensor parallelism across 4 GPUs on one node</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tensor_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="communication-overhead">Communication overhead</h3>
<p>Tensor parallelism has high communication volume and presents a synchronization point in forward pass. Data exchange occurs over high-speed interconnects like NVLink (600 GB/s) or PCIe (32 GB/s). Interconnect speed critically affects overall performance, making tensor parallelism costly to scale beyond 1 node [27].</p>
<h3 id="pipeline-parallelism">Pipeline parallelism</h3>
<p>For multi-node inference, combine tensor parallelism with pipeline parallelism:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Multi-node: 4 GPUs per node, 2 nodes</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tensor_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    pipeline_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="helix-parallelism">Helix Parallelism</h3>
<p>Helix Parallelism, introduced in 2025, applies a hybrid execution strategy: KV parallelism during attention to shard KV caches across GPUs, then reuses the same GPUs for tensor parallelism in dense LLMs or TP x Expert Parallel (EP) in MoEs during FFN computation. Compared to conventional approaches, Helix reduces time-to-first-token by up to 1.5x and supports up to 32x larger batches under the same latency budget for DeepSeek-R1 [28].</p>
<hr>
<h2 id="cpu-offloading-strategies">CPU offloading strategies</h2>
<p>When GPU memory is insufficient, offloading parts of the model or KV cache to CPU memory enables inference of larger models.</p>
<figure><figcaption><strong>Larger models fit by moving less frequently used data outward</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/local-ai-inferencing-techniques-2.webp" width="512" height="663" alt="Larger models fit by moving less frequently used data outward" loading="lazy" decoding="async"></figure>
<h3 id="neo-online-llm-inference-with-cpu-offloading">NEO: Online LLM inference with CPU offloading</h3>
<p>NEO offloads part of attention compute and KV cache states from GPU to local host CPU, increasing effective GPU batch size and inference throughput. It achieves up to 14%-6.6x throughput gains using two techniques [29]:</p>
<ol>
<li><strong>Asymmetric pipelining</strong>: Fully leverages compute resources of both GPU and CPU without overloading them</li>
<li><strong>Load-aware scheduling</strong>: Maintains separate prefilling waitqueue, GPU decoding runqueue, and CPU decoding runqueue, making iteration-level adaptive scheduling decisions</li>
</ol>
<h3 id="nvidia-grace-hopper-unified-memory">NVIDIA Grace Hopper unified memory</h3>
<p>The NVLink-C2C connection and unified memory architecture on Grace Hopper enables models to use CPU memory if GPU memory is insufficient without explicit data transfer. When a model loads onto GH200, it uses 96 GB of HBM and accesses 480 GB of LPDDR memory connected to the CPU [30].</p>
<h3 id="trade-offs">Trade-offs</h3>
<p>CPU offloading helps alleviate GPU memory limitations but shifts workload to CPU and increases data movement between CPU and GPU. For CPU-based attention operations, memory bandwidth, not compute power, determines performance. Overall training/inference speed may decrease due to synchronization overhead [30].</p>
<hr>
<h2 id="prompt-caching">Prompt caching</h2>
<p>Prompt caching reuses the model’s intermediate state (KV tensors in attention layers) for prefix tokens rather than recomputing them for each request.</p>
<h3 id="how-it-works">How it works</h3>
<p>During prefill, the model builds a KV cache for the entire input. Prompt caching stores this KV cache keyed by the prompt tokens. For subsequent requests sharing the same prefix, the cached KV state is reused, skipping recomputation.</p>
<p>Anthropic Claude Sonnet offers prompt caching with up to 90% cost savings and 85% latency reduction for long prompts. OpenAI provides automatic caching with 50% cost savings [31].</p>
<h3 id="best-practices">Best practices</h3>
<ul>
<li><strong>Front-load static content</strong>: Place constant information (system messages, context, instructions) at the beginning of prompts</li>
<li><strong>Avoid dynamic elements in prefix</strong>: Don’t insert timestamps, request IDs, or per-request variables early in the prompt</li>
<li><strong>Exact prefix matching</strong>: Even tiny differences (whitespace, JSON key order) break cache hits [23]</li>
</ul>
<h3 id="vllm-automatic-prefix-caching">vLLM Automatic Prefix Caching</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    enable_prefix_caching</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>vLLM hashes prompt tokens and looks up cache blocks. SGLang uses Radix attention with a radix-tree for prefix caching [31].</p>
<hr>
<h2 id="inference-engines-deep-dive">Inference Engines Deep Dive</h2>
<p>Inference engines handle the actual execution of LLM forward passes. Each engine makes different trade-offs between ease of use, performance, and hardware support. This section covers four engines in detail: TensorRT-LLM (NVIDIA’s production stack), vLLM (the open-source throughput leader), SGLang (structured generation specialist), and llama.cpp (the portable C++ implementation).</p>
<hr>
<h2 id="tensorrt-llm">TensorRT-LLM</h2>
<p>TensorRT-LLM is NVIDIA’s open-source library for optimizing LLM inference on NVIDIA GPUs. It builds on TensorRT’s graph compilation and kernel optimization capabilities, adding LLM-specific features like in-flight batching, paged KV caching, and multi-GPU parallelism [32].</p>
<h3 id="how-tensorrt-compiles-graphs">How TensorRT compiles graphs</h3>
<p>TensorRT-LLM converts model definitions into optimized TensorRT engines through a multi-stage compilation process:</p>
<p><strong>1. Graph construction</strong>: The Model Definition API assembles a network graph. Each layer maps to TensorRT operations that can later be traversed or transformed [33].</p>
<p><strong>2. Pattern matching and fusion</strong>: During compilation, TensorRT identifies operation sequences that can be fused into single GPU kernels. For example, a matmul followed by ReLU becomes one kernel without intermediate memory writes. The pattern-matching algorithm identifies fusions automatically, and an advanced kernel compiler converts them to efficient code [34].</p>
<p><strong>3. Kernel autotuning</strong>: TensorRT automatically selects optimal GPU kernels based on GPU architecture, model configuration, precision, and batch size. The AutoTuner framework (added in 2025) provides custom-op-compatible tuning for operations like fused MoE and NVFP4 linear layers [35].</p>
<p><strong>4. Plugin support</strong>: Some fusions (like FlashAttention) cannot be discovered automatically because they interleave operations in complex ways. Engineers can explicitly replace graph sections with plugins at compile time [34].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Building a TensorRT-LLM engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> build</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">models </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLaMAForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model configuration</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LLaMAForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_hugging_face</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;float16&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Build engine with optimizations</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">engine </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> build</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_input_len</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2048</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_seq_len</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4096</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    use_paged_context_fmha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Enable paged KV cache</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">engine</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;llama-8b-engine&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="int8fp8-quantization-calibration">INT8/FP8 quantization calibration</h3>
<p>TensorRT-LLM supports multiple quantization methods, with FP8 recommended as the default for Hopper and Blackwell GPUs [36].</p>
<p><strong>FP8 quantization (fp8_pc_pt)</strong>: Weights are quantized to FP8 per-channel. Activation ranges are calibrated and quantized per-token. FP8 preserves accuracy better than INT8 in most cases [37].</p>
<p><strong>INT8 SmoothQuant (int8_sq)</strong>: Applies smoothing to weights before INT8 channel-wise quantization. Activation ranges are calibrated tensor-wise. Used as a fallback on Ada GPUs [37].</p>
<p>The calibration process:</p>
<ol>
<li>Load a model checkpoint using the appropriate parallelism strategy</li>
<li>Run calibration data through the model to determine quantization scales</li>
<li>Output a quantized checkpoint with model config (JSON), quantized weights (safetensors), and tokenizer config (YAML) [38]</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">quantization </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> quantize</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">quantization </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> CalibConfig</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure calibration</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">calib_config </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> CalibConfig</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    calib_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;cnn_dailymail&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    calib_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    calib_max_seq_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize to FP8</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">quantize</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    output_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;llama-8b-fp8&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    qformat</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;fp8&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    calib_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">calib_config,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="in-flight-batching-implementation">In-flight batching implementation</h3>
<p>In-flight batching (also called continuous batching) eliminates wait times by dynamically managing request execution. Instead of processing fixed batches, TensorRT-LLM merges incoming requests into existing GPU batches, processing context and generation phases together [39].</p>
<p>The scheduler maintains:</p>
<ul>
<li><strong>Prefill queue</strong>: New requests waiting for initial context processing</li>
<li><strong>Generation queue</strong>: Active sequences generating tokens</li>
<li><strong>Memory manager</strong>: Tracks KV cache block allocation</li>
</ul>
<p>When a sequence completes, its slot immediately becomes available for new requests without waiting for other sequences in the batch.</p>
<h3 id="paged-kv-cache-in-tensorrt">Paged KV-cache in TensorRT</h3>
<p>TensorRT-LLM implements paged KV cache with configurable block sizes [40]:</p>
<ul>
<li>KV cache state is stored in blocks, each holding multiple tokens (default: 128 tokens)</li>
<li>Only full blocks can be shared by multiple requests</li>
<li>Block size is set at engine build time via <code>trtllm-build</code> with <code>--tokens_per_block</code> (must be power of 2)</li>
<li>Larger blocks improve kernel efficiency but reduce cache reuse likelihood</li>
</ul>
<p><strong>KV cache reuse</strong>: Pages can be shared by requests with matching prefixes. This reduces time-to-first-token for multi-turn conversations and system prompts. Priority-based eviction (added in 2025) allows specifying priority and duration for token ranges, improving cache hit rates by approximately 20% [41].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build engine with custom KV cache block size</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">trtllm-build</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --checkpoint_dir</span><span style="color:var(--astro-code-token-string)"> ./llama-checkpoint</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --output_dir</span><span style="color:var(--astro-code-token-string)"> ./llama-engine</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --tokens_per_block</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --use_paged_context_fmha</span><span style="color:var(--astro-code-token-string)"> enable</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --kv_cache_type</span><span style="color:var(--astro-code-token-string)"> paged</span></span></code></pre>
<h3 id="building-engines-for-different-gpu-architectures">Building engines for different GPU architectures</h3>
<p>TensorRT engines are architecture-specific. An engine built for H100 will not run on A100 [42].</p>
<p><strong>Specifying architectures at build time</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build for Ada and Hopper only</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">make</span><span style="color:var(--astro-code-token-string)"> -C</span><span style="color:var(--astro-code-token-string)"> docker</span><span style="color:var(--astro-code-token-string)"> release_build</span><span style="color:var(--astro-code-token-string)"> CUDA_ARCHS=</span><span style="color:var(--astro-code-token-string-expression)">&quot;89-real;90-real&quot;</span></span></code></pre>
<p><strong>Using TensorRT Cloud for different GPUs</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build for A100</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">trt-cloud</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> llm</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --src-hf-repo=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --dtype=</span><span style="color:var(--astro-code-token-string-expression)">&quot;float16&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --gpu=</span><span style="color:var(--astro-code-token-string-expression)">&quot;A100&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --os=linux</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Build for RTX 3070 (consumer GPU)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">trt-cloud</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> llm</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --trtllm-checkpoint</span><span style="color:var(--astro-code-token-string)"> checkpoint.zip</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --gpu</span><span style="color:var(--astro-code-token-string)"> RTX3070</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --os</span><span style="color:var(--astro-code-token-string)"> windows</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --dtype</span><span style="color:var(--astro-code-token-string)"> bfloat16</span></span></code></pre>
<p><strong>GPU architecture codes</strong>:</p>






























<div class="table-wrap"><table><thead><tr><th>GPU Family</th><th>Compute Capability</th><th>CMake Code</th></tr></thead><tbody><tr><td>Ampere (A100)</td><td>8.0</td><td>80-real</td></tr><tr><td>Ada (RTX 4090, L40)</td><td>8.9</td><td>89-real</td></tr><tr><td>Hopper (H100, H200)</td><td>9.0</td><td>90-real</td></tr><tr><td>Blackwell (B200, GB200)</td><td>10.0</td><td>100-real</td></tr></tbody></table></div>
<h3 id="practical-code-for-converting-and-serving-models">Practical code for converting and serving models</h3>
<p><strong>Full conversion and serving workflow</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Step 1: Convert HuggingFace model to TensorRT-LLM checkpoint</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">models </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLaMAForCausalLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LLaMAForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_hugging_face</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    mapping</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tensorrt_llm.</span><span style="color:var(--astro-code-token-function)">Mapping</span><span style="color:var(--astro-code-token-punctuation)">(world_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, tp_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;bfloat16&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_checkpoint</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;./llama-checkpoint&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Step 2: Build the engine</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">trtllm-build</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --checkpoint_dir</span><span style="color:var(--astro-code-token-string)"> ./llama-checkpoint</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --output_dir</span><span style="color:var(--astro-code-token-string)"> ./llama-engine</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --gemm_plugin</span><span style="color:var(--astro-code-token-string)"> bfloat16</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --max_batch_size</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --max_input_len</span><span style="color:var(--astro-code-token-constant)"> 2048</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --max_seq_len</span><span style="color:var(--astro-code-token-constant)"> 4096</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --use_paged_context_fmha</span><span style="color:var(--astro-code-token-string)"> enable</span></span></code></pre>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Step 3: Run inference</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> tensorrt_llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">runtime </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ModelRunner</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">runner </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ModelRunner</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_dir</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;./llama-engine&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> runner</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    batch_input_ids</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[[</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">]],  </span><span style="color:var(--astro-code-token-comment)"># Tokenized input</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    top_p</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.9</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Performance on H100 with FP8</strong>: Over 10,000 output tokens/s at peak throughput for 64 concurrent requests, with approximately 100ms time-to-first-token. H100 FP8 achieves up to 4.6x higher max throughput and 4.4x faster TTFT than A100 [43].</p>
<hr>
<h2 id="vllm">vLLM</h2>
<p>vLLM is an open-source inference engine that maximizes throughput through PagedAttention and continuous batching. As of June 2025, it has 49,200 GitHub stars and has become the de facto standard for high-throughput LLM serving [44].</p>
<h3 id="pagedattention-algorithm-in-detail">PagedAttention algorithm in detail</h3>
<p>PagedAttention applies virtual memory concepts to KV cache management. Instead of allocating contiguous memory per request, vLLM divides GPU memory into fixed-size pages (blocks) that can be stored anywhere [45].</p>
<p><strong>Block allocation process</strong>:</p>
<ol>
<li>The KV block manager divides GPU memory (and optionally CPU RAM) into physical KV blocks</li>
<li>Each request maintains a block table mapping logical blocks to physical blocks</li>
<li>Block table entries record physical block addresses and fill counts</li>
<li>Blocks are allocated on demand as tokens are generated</li>
</ol>
<p><strong>Step-by-step allocation example</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Initial state (prompt processed):</span></span>
<span class="line"><span>- Logical blocks: [Block 0: 14/16 tokens]</span></span>
<span class="line"><span>- Physical mapping: Logical 0 -&gt; Physical 5</span></span>
<span class="line"><span></span></span>
<span class="line"><span>After generating 2 tokens:</span></span>
<span class="line"><span>- Logical blocks: [Block 0: 16/16 tokens (full)]</span></span>
<span class="line"><span>- New allocation needed</span></span>
<span class="line"><span></span></span>
<span class="line"><span>After generating 3rd token:</span></span>
<span class="line"><span>- Logical blocks: [Block 0: 16/16], [Block 1: 1/16]</span></span>
<span class="line"><span>- Physical mapping: Logical 0 -&gt; Physical 5, Logical 1 -&gt; Physical 12</span></span></code></pre>
<p><strong>Block size trade-offs</strong>: The default is 16 tokens per block. Larger blocks increase kernel parallelism but also increase memory fragmentation. The vLLM authors tested many configurations and found 16 tokens to be a good balance [46].</p>
<p><strong>Memory sharing</strong>: Sequences with shared prefixes point their block tables to the same physical blocks. This enables prefix caching and reduces memory usage by up to 55% during beam search [47].</p>
<p><strong>Performance impact</strong>: Traditional systems waste 60-80% of KV cache memory. PagedAttention reduces waste to under 4%, enabling 2-4x throughput improvements [45].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># vLLM automatically uses PagedAttention</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gpu_memory_utilization</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.90</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Use 90% of GPU memory for KV cache</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    block_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Tokens per KV block (default)</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    swap_space</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># GB of CPU memory for swapping</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sampling_params </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SamplingParams</span><span style="color:var(--astro-code-token-punctuation)">(temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.7</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">([</span><span style="color:var(--astro-code-token-string-expression)">&quot;Write a poem about caching:&quot;</span><span style="color:var(--astro-code-token-punctuation)">], sampling_params)</span></span></code></pre>
<h3 id="continuous-batching-scheduler">Continuous batching scheduler</h3>
<p>vLLM schedules work at the iteration level, not the request level. The scheduler assembles batches dynamically at each decoding step [48].</p>
<p><strong>Request lifecycle</strong>:</p>
<ol>
<li>Request enters the engine and is wrapped in a Request object with status WAITING</li>
<li>Request is added to the scheduler’s waiting queue (FCFS or priority-based)</li>
<li>At each step, the scheduler selects requests for the current batch</li>
<li>When a sequence emits EOS, its slot is immediately freed for new requests</li>
</ol>
<p><strong>Scheduler architecture</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>                    ┌─────────────────┐</span></span>
<span class="line"><span>                    │   API Server    │</span></span>
<span class="line"><span>                    └────────┬────────┘</span></span>
<span class="line"><span>                             │</span></span>
<span class="line"><span>                    ┌────────▼────────┐</span></span>
<span class="line"><span>                    │   AsyncLLM      │</span></span>
<span class="line"><span>                    └────────┬────────┘</span></span>
<span class="line"><span>                             │</span></span>
<span class="line"><span>                    ┌────────▼────────┐</span></span>
<span class="line"><span>                    │   EngineCore    │</span></span>
<span class="line"><span>                    │                 │</span></span>
<span class="line"><span>                    │  ┌───────────┐  │</span></span>
<span class="line"><span>                    │  │ Scheduler │  │</span></span>
<span class="line"><span>                    │  └─────┬─────┘  │</span></span>
<span class="line"><span>                    │        │        │</span></span>
<span class="line"><span>                    │  ┌─────▼─────┐  │</span></span>
<span class="line"><span>                    │  │  Executor │  │</span></span>
<span class="line"><span>                    │  └───────────┘  │</span></span>
<span class="line"><span>                    └─────────────────┘</span></span></code></pre>
<p><strong>Step-level scheduling</strong>:</p>
<ol>
<li><strong>Schedule phase</strong>: Select which requests to run (decode and/or chunked prefill)</li>
<li><strong>Execute phase</strong>: Run one forward pass for all active sequences</li>
<li><strong>Output phase</strong>: Push results to output queue, free completed sequences</li>
</ol>
<p><strong>Chunked prefill</strong>: Long prompts are split into smaller chunks to prevent single requests from monopolizing GPU time. With chunked prefill enabled, decode requests are prioritized over prefill to minimize latency for in-progress generations [49].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_num_seqs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Max concurrent sequences</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_num_batched_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8192</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Token budget per iteration</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    enable_chunked_prefill</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Enable chunked prefill</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="prefix-caching-mechanism">Prefix caching mechanism</h3>
<p>vLLM’s Automatic Prefix Caching (APC) detects common prefixes and shares KV cache blocks automatically [50].</p>
<p><strong>How it works</strong>:</p>
<ol>
<li>Prompt tokens are hashed to create a cache key</li>
<li>On cache hit, existing KV blocks are reused</li>
<li>On cache miss, new blocks are computed and cached</li>
<li>LRU eviction removes least-recently-used blocks when memory is full</li>
</ol>
<p><strong>Cache hierarchy</strong>: vLLM checks GPU memory first, then CPU memory, then external KV connectors like LMCache for distributed caching [51].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    enable_prefix_caching</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Enable APC</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># First request computes and caches the system prompt</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">([</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;System: You are a helpful assistant.\n\nUser: What is 2+2?&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Second request reuses cached system prompt KV</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">([</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;System: You are a helpful assistant.\n\nUser: What is 3+3?&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<h3 id="speculative-decoding-integration">Speculative decoding integration</h3>
<p>vLLM supports multiple speculative decoding methods, with Eagle 3 as the current state-of-the-art [52].</p>
<p><strong>Supported methods</strong>:</p>
<ul>
<li><strong>Draft model</strong>: Separate smaller model proposes tokens</li>
<li><strong>Eagle 1/3</strong>: Lightweight prediction head attached to target model</li>
<li><strong>Suffix decoding</strong>: Pattern-matching against previous generations (roadmap item)</li>
</ul>
<p><strong>Eagle 3 performance</strong>: Up to 2.5x speedup across diverse scenarios. The Speculators library (v0.3.0, December 2025) provides end-to-end training support for Eagle3 draft models [53].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Using a separate draft model</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    speculative_model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.2-1B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_speculative_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">5</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    speculative_draft_tensor_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Using Eagle 3</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    speculative_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;method&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;eagle&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;model&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;yuhuili/EAGLE3-LLaMA3.1-Instruct-8B&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;num_speculative_tokens&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-constant)">5</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    },</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Arctic Inference</strong>: Snowflake’s Arctic Inference integration achieves 4x faster inference for LLM agents on SWE-Bench tasks and up to 2.8x faster decoding for interactive workloads. It reduces MLP-based proposer latency from 1.47ms/token to 0.47ms/token [54].</p>
<h3 id="tensor-parallelism-for-multi-gpu">Tensor parallelism for multi-GPU</h3>
<p>vLLM supports tensor parallelism (TP), pipeline parallelism (PP), data parallelism (DP), and expert parallelism (EP) [55].</p>
<p><strong>Tensor parallelism</strong>: Shards each layer horizontally across GPUs using column parallelism (split along columns, concatenate results) and row parallelism (split along rows, sum results).</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Single-node 4-GPU tensor parallelism</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tensor_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Multi-node: 4 GPUs per node, 2 nodes</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-70B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tensor_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    pipeline_parallel_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Combined TP + DP (8 GPUs total)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Requires data_parallel_size=4, tensor_parallel_size=2</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># via CLI: --data-parallel-size=4 --tensor-parallel-size=2</span></span></code></pre>
<p><strong>Why TP within nodes</strong>: Tensor parallelism has high communication volume but benefits from fast interconnects. Use TP within nodes (NVLink) and PP across nodes (InfiniBand) when interconnects are slow. With NVLink, TP can extend across nodes [56].</p>
<p><strong>Checking interconnect speed</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">nvidia-smi</span><span style="color:var(--astro-code-token-string)"> topo</span><span style="color:var(--astro-code-token-string)"> -m</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Look for NVLink vs PIX (PCIe) connections</span></span></code></pre>
<h3 id="openai-compatible-api-server-internals">OpenAI-compatible API server internals</h3>
<p>vLLM’s server implements OpenAI’s API specification using FastAPI [57].</p>
<p><strong>Architecture</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>┌─────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│              FastAPI Application                │</span></span>
<span class="line"><span>│                                                 │</span></span>
<span class="line"><span>│  ┌──────────────┐  ┌──────────────────────────┐│</span></span>
<span class="line"><span>│  │   /v1/chat   │  │  /v1/completions         ││</span></span>
<span class="line"><span>│  │  completions │  │                          ││</span></span>
<span class="line"><span>│  └──────┬───────┘  └───────────┬──────────────┘│</span></span>
<span class="line"><span>│         │                      │               │</span></span>
<span class="line"><span>│  ┌──────▼──────────────────────▼──────┐        │</span></span>
<span class="line"><span>│  │        OpenAIServingChat           │        │</span></span>
<span class="line"><span>│  │        OpenAIServingCompletion     │        │</span></span>
<span class="line"><span>│  │        OpenAIServingTokenization   │        │</span></span>
<span class="line"><span>│  └──────────────────┬─────────────────┘        │</span></span>
<span class="line"><span>│                     │                          │</span></span>
<span class="line"><span>│  ┌──────────────────▼─────────────────┐        │</span></span>
<span class="line"><span>│  │            AsyncLLM                │        │</span></span>
<span class="line"><span>│  └────────────────────────────────────┘        │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────┘</span></span></code></pre>
<p><strong>Request handling</strong>:</p>
<ol>
<li>FastAPI receives HTTP request with Pydantic validation</li>
<li>Request is converted to internal format and queued</li>
<li>AsyncLLM handles scheduling via EngineCore</li>
<li>Streaming responses use Server-Sent Events (SSE)</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Start the OpenAI-compatible server</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">vllm</span><span style="color:var(--astro-code-token-string)"> serve</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3.1-8B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --host</span><span style="color:var(--astro-code-token-constant)"> 0.0.0.0</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --port</span><span style="color:var(--astro-code-token-constant)"> 8000</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --tensor-parallel-size</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Use with OpenAI SDK</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> http://localhost:8000/v1/chat/completions</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -H</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Content-Type: application/json&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -d</span><span style="color:var(--astro-code-token-string-expression)"> &#39;{</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;model&quot;: &quot;meta-llama/Llama-3.1-8B-Instruct&quot;,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Hello!&quot;}]</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    }&#39;</span></span></code></pre>
<h3 id="performance-tuning-parameters">Performance tuning parameters</h3>
<p>Key parameters for optimizing vLLM performance [58]:</p>













































<div class="table-wrap"><table><thead><tr><th>Parameter</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>max_num_seqs</code></td><td>256</td><td>Maximum concurrent sequences</td></tr><tr><td><code>max_num_batched_tokens</code></td><td>8192</td><td>Token budget per iteration</td></tr><tr><td><code>gpu_memory_utilization</code></td><td>0.9</td><td>Fraction of GPU memory for KV cache</td></tr><tr><td><code>block_size</code></td><td>16</td><td>Tokens per KV block</td></tr><tr><td><code>swap_space</code></td><td>4</td><td>GB of CPU memory for swapping</td></tr><tr><td><code>enable_chunked_prefill</code></td><td>True</td><td>Split long prompts into chunks</td></tr><tr><td><code>max_model_len</code></td><td>model default</td><td>Maximum sequence length</td></tr></tbody></table></div>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Memory settings</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    gpu_memory_utilization</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.95</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    swap_space</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Batching settings</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_num_seqs</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_num_batched_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16384</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    enable_chunked_prefill</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Performance settings</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    enforce_eager</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># Use CUDA graphs</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    enable_prefix_caching</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="sglang">SGLang</h2>
<p>SGLang is a high-performance serving framework that combines RadixAttention for KV cache reuse with a domain-specific language for structured generation. It achieves up to 6.4x higher throughput than baseline systems on structured workloads [59].</p>
<h3 id="radixattention-and-how-it-differs-from-pagedattention">RadixAttention and how it differs from PagedAttention</h3>
<p>While vLLM’s PagedAttention optimizes memory within individual requests, RadixAttention focuses on maximizing KV cache reuse across multiple requests using a radix tree (compressed prefix tree) [60].</p>
<p><strong>PagedAttention</strong>: Divides KV cache into fixed-size blocks. Blocks are allocated on demand and freed when sequences complete. Prefix sharing requires explicit cache lookup.</p>
<p><strong>RadixAttention</strong>: Stores KV cache in a radix tree structure. The tree enables automatic prefix matching, insertion, and LRU eviction. KV cache persists after request completion for potential reuse.</p>
<p><strong>Key differences</strong>:</p>






























<div class="table-wrap"><table><thead><tr><th>Aspect</th><th>PagedAttention</th><th>RadixAttention</th></tr></thead><tbody><tr><td>Data structure</td><td>Block tables</td><td>Radix tree</td></tr><tr><td>Prefix matching</td><td>Hash lookup</td><td>Tree traversal</td></tr><tr><td>Cache persistence</td><td>Cleared after request</td><td>Retained in LRU cache</td></tr><tr><td>Primary optimization</td><td>Memory efficiency</td><td>KV reuse across calls</td></tr></tbody></table></div>
<p><strong>How RadixAttention works</strong>:</p>
<ol>
<li>After each generation, KV cache is inserted into the radix tree keyed by token sequence</li>
<li>New requests traverse the tree to find the longest matching prefix</li>
<li>Matching prefix KV cache is reused; only new tokens require computation</li>
<li>LRU eviction removes least-recently-used branches when memory is full</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> sglang </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> sgl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># RadixAttention automatically reuses KV cache</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">function</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> multi_turn_chat</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> turns</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">system</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;You are a helpful assistant.&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> user_msg </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> turns</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(user_msg)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;response&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># KV cache for system prompt is computed once and reused</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">runtime </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Runtime</span><span style="color:var(--astro-code-token-punctuation)">(model_path</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_default_backend</span><span style="color:var(--astro-code-token-punctuation)">(runtime)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># These calls reuse cached system prompt</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> multi_turn_chat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">run</span><span style="color:var(--astro-code-token-punctuation)">(turns</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;What is Python?&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> multi_turn_chat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">run</span><span style="color:var(--astro-code-token-punctuation)">(turns</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;What is Rust?&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<h3 id="the-sglang-dsl-and-structured-generation">The SGLang DSL and structured generation</h3>
<p>SGLang provides a domain-specific language for defining complex LLM programs with branching, parallelism, and constraints [61].</p>
<p><strong>Core primitives</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> sglang </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> sgl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">function</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> analyze_code</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> code</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Sequential generation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Analyze this code:</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">code</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;analysis&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">500</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Forking for parallel evaluation</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">fork</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> forks</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        forks</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> +=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Rate the code quality (1-10):&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        forks</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> +=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;quality&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        forks</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> +=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Suggest improvements:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        forks</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> +=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;improvements&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">200</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Select best based on criteria</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">select</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;best_response&quot;</span><span style="color:var(--astro-code-token-punctuation)">, forks, criteria</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;quality&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Structured output with JSON schema</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> pydantic </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> BaseModel</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> sglang </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> sgl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> CodeReview</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">BaseModel</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    quality_score</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> int</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    issues</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> list</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">str</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    suggestions</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> list</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-constant)">str</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">function</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> structured_review</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> code</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Review this code and provide structured feedback:</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">code</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;review&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">500</span><span style="color:var(--astro-code-token-punctuation)">, json_schema</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">CodeReview.</span><span style="color:var(--astro-code-token-function)">model_json_schema</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span></code></pre>
<h3 id="constrained-decoding-with-fsm">Constrained decoding with FSM</h3>
<p>SGLang implements constrained decoding using compressed finite-state machines (FSM), enabling generation that conforms to regular expressions or JSON schemas [62].</p>
<p><strong>How FSM decoding works</strong>:</p>
<ol>
<li>The constraint (regex, JSON schema) is converted to a finite-state machine</li>
<li>At each decoding step, invalid tokens are masked based on the current FSM state</li>
<li>Only tokens leading to valid FSM transitions can be sampled</li>
</ol>
<p><strong>Compressed FSM optimization</strong>: Standard FSM decoding processes one token at a time even when transitions are deterministic. SGLang’s compressed FSM collapses chains of singular transitions into single edges [62].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> sglang </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> sgl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">function</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> extract_email</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> text</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Extract the email address from: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">text</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Constrain output to valid email format</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;email&quot;</span><span style="color:var(--astro-code-token-punctuation)">, regex</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)">r</span><span style="color:var(--astro-code-token-string-expression)">&quot;[a-zA-Z0-9._%+-]</span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-string-expression)">@[a-zA-Z0-9.-]</span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-string-expression)">\.[a-zA-Z]</span><span style="color:var(--astro-code-token-keyword)">{2,}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">function</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> generate_json</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> prompt</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(prompt)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Constrain to valid JSON object</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;data&quot;</span><span style="color:var(--astro-code-token-punctuation)">, regex</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)">r</span><span style="color:var(--astro-code-token-string-expression)">&#39;\{[</span><span style="color:var(--astro-code-token-keyword)">^</span><span style="color:var(--astro-code-token-string-expression)">{}]</span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-string-expression)">\}&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    )</span></span></code></pre>
<h3 id="jump-forward-decoding">Jump-forward decoding</h3>
<p>Jump-forward decoding exploits deterministic sections of constrained output to skip unnecessary LLM forward passes [62].</p>
<p><strong>Example</strong>: When generating JSON, after a key like <code>&quot;name&quot;</code>, the next token must be <code>:</code>. Instead of running the LLM to generate <code>:</code>, SGLang inserts it directly.</p>
<p><strong>How it works</strong>:</p>
<ol>
<li>The compressed FSM identifies deterministic token sequences</li>
<li>When the FSM has only one valid path, those tokens are inserted without LLM computation</li>
<li>RadixAttention automatically handles KV cache for the inserted tokens</li>
</ol>
<p><strong>Performance impact</strong>: For structured formats with fixed elements, jump-forward decoding can skip 30-50% of generation steps. On JSON decoding benchmarks, this optimization alone delivers up to 1.6x throughput improvement [62].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Standard decoding:</span></span>
<span class="line"><span>{&quot;name&quot; -&gt; LLM -&gt; &quot;:&quot; -&gt; LLM -&gt; &quot; &quot; -&gt; LLM -&gt; &quot;\&quot;&quot; -&gt; LLM -&gt; &quot;John&quot; ...</span></span>
<span class="line"><span></span></span>
<span class="line"><span>Jump-forward decoding:</span></span>
<span class="line"><span>{&quot;name&quot; -&gt; INSERT &quot;: \&quot;&quot; -&gt; LLM -&gt; &quot;John&quot; ...</span></span>
<span class="line"><span>(Skipped 3 LLM calls)</span></span></code></pre>
<h3 id="how-sglang-handles-complex-prompts">How SGLang handles complex prompts</h3>
<p>SGLang’s runtime optimizes complex multi-call LLM programs through several mechanisms [63]:</p>
<p><strong>1. Automatic batching</strong>: Multiple <code>sgl.gen()</code> calls across forked branches are batched into single LLM forward passes.</p>
<p><strong>2. KV cache sharing via RadixAttention</strong>: Forks share the KV cache of their common prefix. New branches only compute KV for diverging tokens.</p>
<p><strong>3. Scheduling optimization</strong>: The runtime schedules calls to maximize GPU utilization, interleaving prefill and decode across requests.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> sglang </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> sgl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">function</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> tree_of_thought</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">s</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> problem</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Solve: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">problem</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Generate 3 candidate solutions in parallel</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">fork</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> candidates</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> i</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> c </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(candidates):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            c </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;solution_</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">i</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">200</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Evaluate each solution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    evaluations </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> i</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> c </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(candidates):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        c </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">user</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Rate this solution 1-10:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        c </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">assistant</span><span style="color:var(--astro-code-token-punctuation)">(sgl.</span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;rating_</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">i</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">5</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        evaluations</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">(c)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Select best</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-foreground)"> sgl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">select</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;best&quot;</span><span style="color:var(--astro-code-token-punctuation)">, evaluations, key</span><span style="color:var(--astro-code-token-keyword)">=lambda</span><span style="color:var(--astro-code-token-parameter)"> x</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-constant)">int</span><span style="color:var(--astro-code-token-punctuation)">(x[</span><span style="color:var(--astro-code-token-string-expression)">&quot;rating&quot;</span><span style="color:var(--astro-code-token-punctuation)">]))</span></span></code></pre>
<p><strong>Server deployment</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Start SGLang server</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> sglang.launch_server</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model-path</span><span style="color:var(--astro-code-token-string)"> meta-llama/Llama-3.1-8B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --port</span><span style="color:var(--astro-code-token-constant)"> 30000</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --tp</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># OpenAI-compatible endpoint</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> http://localhost:30000/v1/chat/completions</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -H</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Content-Type: application/json&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -d</span><span style="color:var(--astro-code-token-string-expression)"> &#39;{&quot;model&quot;: &quot;default&quot;, &quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Hello&quot;}]}&#39;</span></span></code></pre>
<hr>
<h2 id="llamacpp-deep-dive">llama.cpp Deep Dive</h2>
<p>llama.cpp is a C/C++ inference engine that prioritizes portability and minimal dependencies. It runs on CPUs, Apple Silicon, NVIDIA GPUs, AMD GPUs, and various accelerators. As of August 2025, it has over 85,000 GitHub stars and 1,200 contributors [64].</p>
<h3 id="ggml-tensor-library-internals">GGML tensor library internals</h3>
<p>GGML (Georgi Gerganov Machine Learning) is the tensor library underlying llama.cpp. It provides a portable foundation for neural network operations [65].</p>
<p><strong>Core design principles</strong>:</p>
<ol>
<li><strong>No external dependencies</strong>: GGML is self-contained C code</li>
<li><strong>Static memory allocation</strong>: Tensor sizes are known at graph construction time</li>
<li><strong>Pluggable backends</strong>: CPU, Metal, CUDA, Vulkan, SYCL, etc.</li>
</ol>
<p><strong>Tensor representation</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">struct</span><span style="color:var(--astro-code-foreground)"> ggml_tensor {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    enum</span><span style="color:var(--astro-code-foreground)"> ggml_type type;</span><span style="color:var(--astro-code-token-comment)">      // Data type (F32, F16, Q4_K, etc.)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    int</span><span style="color:var(--astro-code-foreground)"> n_dims;</span><span style="color:var(--astro-code-token-comment)">               // Number of dimensions</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    int64_t</span><span style="color:var(--astro-code-foreground)"> ne[</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-foreground)">];</span><span style="color:var(--astro-code-token-comment)">            // Number of elements per dimension</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    size_t</span><span style="color:var(--astro-code-foreground)"> nb[</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-foreground)">];</span><span style="color:var(--astro-code-token-comment)">             // Stride in bytes per dimension</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    void</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> data;</span><span style="color:var(--astro-code-token-comment)">              // Pointer to data</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    struct</span><span style="color:var(--astro-code-foreground)"> ggml_tensor </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> src[</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-foreground)">];</span><span style="color:var(--astro-code-token-comment)">  // Source tensors (for operations)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // ...</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">};</span></span></code></pre>
<p><strong>Computation graph</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// Create context</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">struct</span><span style="color:var(--astro-code-foreground)"> ggml_context </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> ctx </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ggml_init</span><span style="color:var(--astro-code-foreground)">(params);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Define tensors</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">struct</span><span style="color:var(--astro-code-foreground)"> ggml_tensor </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ggml_new_tensor_2d</span><span style="color:var(--astro-code-foreground)">(ctx</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GGML_TYPE_F32</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 768</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 768</span><span style="color:var(--astro-code-foreground)">);</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">struct</span><span style="color:var(--astro-code-foreground)"> ggml_tensor </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ggml_new_tensor_2d</span><span style="color:var(--astro-code-foreground)">(ctx</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GGML_TYPE_F32</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 768</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 768</span><span style="color:var(--astro-code-foreground)">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Define operation (creates graph node)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">struct</span><span style="color:var(--astro-code-foreground)"> ggml_tensor </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ggml_mul_mat</span><span style="color:var(--astro-code-foreground)">(ctx</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> b);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Build computation graph</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">struct</span><span style="color:var(--astro-code-foreground)"> ggml_cgraph </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> graph </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> ggml_new_graph</span><span style="color:var(--astro-code-foreground)">(ctx);</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ggml_build_forward_expand</span><span style="color:var(--astro-code-foreground)">(graph</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> c);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">// Execute on backend</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ggml_backend_graph_compute</span><span style="color:var(--astro-code-foreground)">(backend</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> graph);</span></span></code></pre>
<p><strong>Backend architecture</strong>: GGML’s pluggable backend system enables the same graph to execute on different hardware. Each backend implements a standard interface for memory allocation, data transfer, and kernel execution [66].</p>
<h3 id="metal-backend-implementation">Metal backend implementation</h3>
<p>The Metal backend provides GPU acceleration on Apple Silicon through hand-optimized compute shaders [67].</p>
<p><strong>Key features</strong>:</p>
<ul>
<li>Optimized matrix multiplication kernels for M1/M2/M3 chips</li>
<li>Unified memory eliminates CPU-GPU data transfers</li>
<li>Flash attention implementation for memory-efficient attention</li>
<li>Support for all GGML quantization types</li>
</ul>
<p><strong>Memory management</strong>: Metal uses shared buffers that both CPU and GPU can access directly. This is a significant advantage over discrete GPUs that require explicit data transfers.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build llama.cpp with Metal (default on macOS)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> -B</span><span style="color:var(--astro-code-token-string)"> build</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> --build</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> Release</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run with Metal acceleration</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-cli</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -m</span><span style="color:var(--astro-code-token-string)"> llama-3.1-8b-instruct-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -ngl</span><span style="color:var(--astro-code-token-constant)"> 99</span><span style="color:var(--astro-code-foreground)"> \  </span><span style="color:var(--astro-code-token-comment)"># Offload all layers to GPU</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    -p</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Hello, world!&quot;</span></span></code></pre>
<p><strong>Metal performance tuning</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Use flash attention (reduces memory bandwidth)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-cli</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> model.gguf</span><span style="color:var(--astro-code-token-string)"> -fa</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Adjust batch size for throughput</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-cli</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> model.gguf</span><span style="color:var(--astro-code-token-string)"> -b</span><span style="color:var(--astro-code-token-constant)"> 512</span></span></code></pre>
<h3 id="cuda-backend-and-kernel-optimizations">CUDA backend and kernel optimizations</h3>
<p>The CUDA backend provides GPU acceleration on NVIDIA hardware with hand-tuned kernels [68].</p>
<p><strong>Recent optimizations (2025)</strong>:</p>
<ul>
<li><strong>FlashAttention</strong>: Memory-efficient attention with tiling and kernel fusion</li>
<li><strong>Split-K optimization</strong>: Divides KV sequence across workgroups for increased parallelism</li>
<li><strong>Wavefront tuning</strong>: Optimizations for NVIDIA’s 32-thread warps</li>
<li><strong>NVFP4 support</strong>: Native 4-bit floating point on Blackwell GPUs (25% faster prompt processing)</li>
</ul>
<p><strong>CUDA kernel micro-optimizations</strong>:</p>
<ul>
<li>Memory coalescing in <code>mul_mat_id</code> function</li>
<li>fastdiv and fastmodulo optimizations for Ada and Blackwell</li>
<li>PAD_REFLECT_1D kernel optimization (1-11% memory bandwidth improvement)</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Build with CUDA support</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> -B</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> -DGGML_CUDA=ON</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cmake</span><span style="color:var(--astro-code-token-string)"> --build</span><span style="color:var(--astro-code-token-string)"> build</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> Release</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Enable unified memory for larger models</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">export</span><span style="color:var(--astro-code-foreground)"> GGML_CUDA_ENABLE_UNIFIED_MEMORY</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-cli</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -m</span><span style="color:var(--astro-code-token-string)"> llama-3.1-70b-instruct-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -ngl</span><span style="color:var(--astro-code-token-constant)"> 99</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -fa</span><span style="color:var(--astro-code-token-comment)">  # Flash attention</span></span></code></pre>
<p><strong>Multi-GPU with ik_llama.cpp</strong>: The ik_llama.cpp fork implements tensor parallelism at the GGML graph level (Split Mode Graph). This distributes compute graph nodes across GPUs rather than just assigning layers, achieving 3-4x performance gains over standard multi-GPU methods [69].</p>
<h3 id="quantization-kernel-implementations">Quantization kernel implementations</h3>
<p>llama.cpp supports various quantization types with specialized dequantization kernels [70].</p>
<p><strong>Quantization type families</strong>:</p>






























<div class="table-wrap"><table><thead><tr><th>Family</th><th>Types</th><th>Description</th></tr></thead><tbody><tr><td>Type-0</td><td>Q4_0, Q5_0, Q8_0</td><td>Global scale: <code>w = d * q</code></td></tr><tr><td>Type-1</td><td>Q4_1, Q5_1</td><td>Scale + minimum: <code>w = d * q + m</code></td></tr><tr><td>K-quants</td><td>Q4_K, Q5_K, Q6_K</td><td>Block-wise with super-blocks of 256</td></tr><tr><td>I-quants</td><td>IQ4_XS, IQ3_XXS</td><td>Importance-based with lookup tables</td></tr></tbody></table></div>
<p><strong>K-quant implementation details</strong>:</p>
<p>K-quants use super-blocks of 256 quantized values, subdivided into blocks of 16 or 32. Each super-block stores:</p>
<ul>
<li>Scale factors for each sub-block</li>
<li>Minimum values (for some types)</li>
<li>Quantized weights</li>
</ul>
<p><strong>Q4_K_M and Q5_K_M</strong> implement mixed precision: most weights use the base precision, but half of <code>attention.wv</code> and <code>feed_forward.w2</code> tensors use higher precision for better accuracy [71].</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize a model</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-quantize</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    model-f16.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    model-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    Q4_K_M</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Quantize with importance matrix for better quality</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-quantize</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    model-f16.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    model-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    Q4_K_M</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --imatrix</span><span style="color:var(--astro-code-token-string)"> imatrix.dat</span></span></code></pre>
<p><strong>Advanced quantization options</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Mix quantization types for different tensors</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">./build/bin/llama-quantize</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    model-f16.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    model-mixed.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    Q4_K_M</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --output-tensor-type</span><span style="color:var(--astro-code-token-string)"> Q8_0</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --token-embedding-type</span><span style="color:var(--astro-code-token-string)"> Q8_0</span></span></code></pre>
<h3 id="memory-layout-for-different-quant-types">Memory layout for different quant types</h3>
<p>Each quantization type has a specific memory layout optimized for efficient dequantization [72].</p>
<p><strong>Q4_0 layout</strong> (block of 32 weights):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>[scale: fp16 (2 bytes)] [quants: 16 bytes (32 x 4-bit)]</span></span>
<span class="line"><span>Total: 18 bytes for 32 weights = 4.5 bits/weight</span></span></code></pre>
<p><strong>Q4_K layout</strong> (super-block of 256 weights):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>[d: fp16] [dmin: fp16] [scales: 12 bytes] [quants: 128 bytes]</span></span>
<span class="line"><span>- 8 sub-blocks of 32 weights each</span></span>
<span class="line"><span>- Each sub-block has its own 6-bit scale</span></span>
<span class="line"><span>Total: 144 bytes for 256 weights = 4.5 bits/weight</span></span></code></pre>
<p><strong>Q5_K layout</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>[d: fp16] [dmin: fp16] [scales: 12 bytes] [qh: 32 bytes] [ql: 128 bytes]</span></span>
<span class="line"><span>- qh contains the 5th bit for each weight</span></span>
<span class="line"><span>- ql contains lower 4 bits</span></span>
<span class="line"><span>Total: 176 bytes for 256 weights = 5.5 bits/weight</span></span></code></pre>
<p><strong>Dequantization kernels</strong>: Each quantization type has optimized dequantization code for each backend. The CUDA kernels use warp-level primitives for efficient parallel dequantization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// Simplified Q4_K dequantization (CUDA)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">__global__ </span><span style="color:var(--astro-code-token-keyword)">void</span><span style="color:var(--astro-code-token-function)"> dequantize_q4_k</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-keyword)"> void</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> src</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> float</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> dst) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    const</span><span style="color:var(--astro-code-foreground)"> block_q4_k </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-foreground)"> block_q4_k </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)">)src </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> blockIdx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">x;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    const</span><span style="color:var(--astro-code-token-keyword)"> float</span><span style="color:var(--astro-code-foreground)"> d </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> __half2float(</span><span style="color:var(--astro-code-token-constant)">block</span><span style="color:var(--astro-code-token-punctuation)">-&gt;</span><span style="color:var(--astro-code-token-function)">d)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    const</span><span style="color:var(--astro-code-token-keyword)"> float</span><span style="color:var(--astro-code-foreground)"> dmin </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> __half2float(</span><span style="color:var(--astro-code-token-constant)">block</span><span style="color:var(--astro-code-token-punctuation)">-&gt;</span><span style="color:var(--astro-code-token-function)">dmin)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Parallel dequantization across warp</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    int</span><span style="color:var(--astro-code-foreground)"> lane </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> threadIdx</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">x </span><span style="color:var(--astro-code-token-keyword)">%</span><span style="color:var(--astro-code-token-constant)"> 32</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    uint8_t</span><span style="color:var(--astro-code-foreground)"> q </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> block</span><span style="color:var(--astro-code-token-punctuation)">-&gt;</span><span style="color:var(--astro-code-foreground)">qs[lane];</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float</span><span style="color:var(--astro-code-foreground)"> scale </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> d </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">block</span><span style="color:var(--astro-code-token-punctuation)">-&gt;</span><span style="color:var(--astro-code-foreground)">scales[lane </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 32</span><span style="color:var(--astro-code-foreground)">] </span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-token-keyword)"> 0x</span><span style="color:var(--astro-code-token-constant)">3F</span><span style="color:var(--astro-code-foreground)">);</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float</span><span style="color:var(--astro-code-foreground)"> min </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dmin </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">block</span><span style="color:var(--astro-code-token-punctuation)">-&gt;</span><span style="color:var(--astro-code-foreground)">scales[lane </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 32</span><span style="color:var(--astro-code-foreground)">] </span><span style="color:var(--astro-code-token-keyword)">&gt;&gt;</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-foreground)">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    dst[lane </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-foreground)">] </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> scale </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> (q </span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-token-keyword)"> 0x</span><span style="color:var(--astro-code-token-constant)">F</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> min;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    dst[lane </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-keyword)"> +</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)">] </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> scale </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> (q </span><span style="color:var(--astro-code-token-keyword)">&gt;&gt;</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> min;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<hr>
<h2 id="inference-engine-comparison">Inference Engine Comparison</h2>
<h3 id="latency-vs-throughput-trade-offs">Latency vs throughput trade-offs</h3>
<p>Each engine optimizes for different points on the latency-throughput curve [73].</p>
<p><strong>Benchmark results</strong> (Llama 3.1 8B, H100, 1000 ShareGPT prompts):</p>



































<div class="table-wrap"><table><thead><tr><th>Engine</th><th>Throughput (tok/s)</th><th>TTFT (ms)</th><th>Per-token latency (ms)</th></tr></thead><tbody><tr><td>SGLang</td><td>16,215</td><td>156</td><td>4-21</td></tr><tr><td>LMDeploy</td><td>16,132</td><td>142</td><td>12-35</td></tr><tr><td>vLLM</td><td>12,553</td><td>123</td><td>8-42</td></tr><tr><td>TensorRT-LLM</td><td>10,847</td><td>187</td><td>15-28</td></tr></tbody></table></div>
<p><strong>Key observations</strong>:</p>
<ul>
<li><strong>vLLM</strong>: Fastest time-to-first-token (TTFT), best scaling at high concurrency</li>
<li><strong>SGLang</strong>: Highest raw throughput, most stable per-token latency</li>
<li><strong>TensorRT-LLM</strong>: Lower throughput in benchmarks but excels on B200/Blackwell GPUs</li>
<li><strong>llama.cpp</strong>: Not designed for high-throughput serving but offers best portability</li>
</ul>
<p><strong>At different concurrency levels</strong> (GPT-OSS-120B):</p>





























<div class="table-wrap"><table><thead><tr><th>Concurrency</th><th>vLLM (tok/s)</th><th>SGLang (tok/s)</th><th>TensorRT-LLM (tok/s)</th></tr></thead><tbody><tr><td>1</td><td>892</td><td>756</td><td>1,024</td></tr><tr><td>10</td><td>2,341</td><td>2,567</td><td>2,189</td></tr><tr><td>100</td><td>4,741</td><td>4,523</td><td>3,892</td></tr></tbody></table></div>
<h3 id="memory-efficiency-comparison">Memory efficiency comparison</h3>



































<div class="table-wrap"><table><thead><tr><th>Engine</th><th>KV Cache Approach</th><th>Memory Waste</th><th>Max Context (8B model, 24GB)</th></tr></thead><tbody><tr><td>vLLM</td><td>PagedAttention</td><td>&lt;4%</td><td>128K tokens</td></tr><tr><td>SGLang</td><td>RadixAttention</td><td>&lt;4%</td><td>128K tokens</td></tr><tr><td>TensorRT-LLM</td><td>Paged + Priority eviction</td><td>&lt;5%</td><td>128K tokens</td></tr><tr><td>llama.cpp</td><td>Contiguous</td><td>10-20%</td><td>32K tokens</td></tr></tbody></table></div>
<p><strong>KV cache quantization support</strong>:</p>



































<div class="table-wrap"><table><thead><tr><th>Engine</th><th>FP8 KV</th><th>INT8 KV</th><th>NVFP4 KV</th></tr></thead><tbody><tr><td>TensorRT-LLM</td><td>Yes</td><td>Yes</td><td>Yes (Blackwell)</td></tr><tr><td>vLLM</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td>SGLang</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td>llama.cpp</td><td>Q8_0, Q4_0</td><td>Via type</td><td>No</td></tr></tbody></table></div>
<h3 id="feature-matrix">Feature matrix</h3>































































































































































<div class="table-wrap"><table><thead><tr><th>Feature</th><th>TensorRT-LLM</th><th>vLLM</th><th>SGLang</th><th>llama.cpp</th></tr></thead><tbody><tr><td><strong>Quantization</strong></td><td></td><td></td><td></td><td></td></tr><tr><td>FP8</td><td>Yes (Hopper+)</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td>INT8</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes (Q8_0)</td></tr><tr><td>INT4</td><td>Yes (AWQ)</td><td>Yes (AWQ/GPTQ)</td><td>Yes</td><td>Yes (Q4_K)</td></tr><tr><td>2-bit</td><td>No</td><td>No</td><td>No</td><td>Yes (Q2_K)</td></tr><tr><td><strong>Parallelism</strong></td><td></td><td></td><td></td><td></td></tr><tr><td>Tensor parallel</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Limited</td></tr><tr><td>Pipeline parallel</td><td>Yes</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td>Expert parallel</td><td>Yes</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td><strong>Features</strong></td><td></td><td></td><td></td><td></td></tr><tr><td>Speculative decoding</td><td>Yes</td><td>Yes (Eagle3)</td><td>Yes</td><td>Yes</td></tr><tr><td>Prefix caching</td><td>Yes</td><td>Yes (APC)</td><td>Yes (Radix)</td><td>Yes</td></tr><tr><td>Constrained decoding</td><td>No</td><td>Yes</td><td>Yes (FSM)</td><td>Yes (grammar)</td></tr><tr><td>Multi-LoRA</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>Vision models</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes (LLaVA)</td></tr><tr><td><strong>Deployment</strong></td><td></td><td></td><td></td><td></td></tr><tr><td>OpenAI API</td><td>Via Triton</td><td>Yes</td><td>Yes</td><td>Yes (server)</td></tr><tr><td>NVIDIA GPUs</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td>AMD GPUs</td><td>No</td><td>Yes (ROCm)</td><td>Yes (ROCm)</td><td>Yes (HIP)</td></tr><tr><td>Apple Silicon</td><td>No</td><td>No</td><td>No</td><td>Yes (Metal)</td></tr><tr><td>CPU only</td><td>No</td><td>No</td><td>No</td><td>Yes</td></tr></tbody></table></div>
<h3 id="when-to-use-each-engine">When to use each engine</h3>
<p><strong>TensorRT-LLM</strong>:</p>
<ul>
<li>Production deployments on NVIDIA GPUs</li>
<li>Maximum throughput on H100/B200</li>
<li>When using NVIDIA’s full stack (Triton, NIM)</li>
<li>Willing to invest in engine building and tuning</li>
</ul>
<p><strong>vLLM</strong>:</p>
<ul>
<li>High-concurrency API serving</li>
<li>Fast time-to-first-token requirements</li>
<li>OpenAI API compatibility</li>
<li>General-purpose GPU inference with good defaults</li>
</ul>
<p><strong>SGLang</strong>:</p>
<ul>
<li>Structured output generation (JSON, code)</li>
<li>Complex multi-turn or branching LLM programs</li>
<li>Maximum KV cache reuse across requests</li>
<li>Constrained decoding with FSM</li>
</ul>
<p><strong>llama.cpp</strong>:</p>
<ul>
<li>Local deployment on consumer hardware</li>
<li>Apple Silicon (M1/M2/M3/M4)</li>
<li>CPU-only environments</li>
<li>Edge devices and mobile</li>
<li>Maximum quantization flexibility</li>
</ul>
<h3 id="practical-deployment-recommendations">Practical deployment recommendations</h3>
<p><strong>For startup API products</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>vLLM + OpenAI-compatible server</span></span>
<span class="line"><span>- Fast setup, good defaults</span></span>
<span class="line"><span>- Scale with tensor parallelism</span></span>
<span class="line"><span>- Add prefix caching for repeated prompts</span></span></code></pre>
<p><strong>For enterprise on NVIDIA</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>TensorRT-LLM + Triton Inference Server</span></span>
<span class="line"><span>- Maximum throughput per GPU dollar</span></span>
<span class="line"><span>- Production-grade monitoring</span></span>
<span class="line"><span>- Integration with NVIDIA enterprise support</span></span></code></pre>
<p><strong>For structured outputs</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>SGLang</span></span>
<span class="line"><span>- Native JSON schema support</span></span>
<span class="line"><span>- Jump-forward decoding for speed</span></span>
<span class="line"><span>- RadixAttention for multi-turn efficiency</span></span></code></pre>
<p><strong>For local/edge deployment</strong>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>llama.cpp</span></span>
<span class="line"><span>- Q4_K_M for balance of quality and size</span></span>
<span class="line"><span>- Metal for Apple, CUDA for NVIDIA</span></span>
<span class="line"><span>- Works offline with no cloud dependency</span></span></code></pre>
<hr>
<h2 id="practical-considerations">Practical considerations</h2>
<h3 id="when-to-use-each-technique">When to use each technique</h3>























































<div class="table-wrap"><table><thead><tr><th>Technique</th><th>Best for</th><th>Hardware requirements</th></tr></thead><tbody><tr><td>REAP</td><td>MoE models (Mixtral, DeepSeek, Qwen)</td><td>Same as original model</td></tr><tr><td>Speculative decoding</td><td>High-latency applications, large target models</td><td>GPU memory for both draft and target</td></tr><tr><td>Q4_K_M quantization</td><td>CPU/Apple devices, limited RAM</td><td>4GB+ RAM for 7B models</td></tr><tr><td>AWQ/GPTQ</td><td>GPU inference, quality-sensitive tasks</td><td>CUDA GPU</td></tr><tr><td>PagedAttention</td><td>High-concurrency serving</td><td>vLLM-compatible GPU</td></tr><tr><td>FlashAttention-3</td><td>H100/H200 deployments</td><td>Hopper architecture</td></tr><tr><td>Tensor parallelism</td><td>Models exceeding single GPU memory</td><td>Multiple GPUs with NVLink</td></tr><tr><td>CPU offloading</td><td>Memory-constrained setups</td><td>High CPU memory bandwidth</td></tr><tr><td>Prompt caching</td><td>Repeated prompts, RAG, chat systems</td><td>Sufficient memory for cache</td></tr></tbody></table></div>
<h3 id="combining-techniques">Combining techniques</h3>
<p>These techniques are not mutually exclusive. A production deployment might use:</p>
<ol>
<li><strong>REAP</strong> to prune an MoE model to 50% experts</li>
<li><strong>AWQ quantization</strong> to reduce memory footprint further</li>
<li><strong>FlashAttention-3</strong> for efficient attention computation</li>
<li><strong>PagedAttention + continuous batching</strong> for high-throughput serving</li>
<li><strong>Prefix caching</strong> to avoid redundant computation for common prefixes</li>
<li><strong>Speculative decoding</strong> to reduce latency for interactive applications</li>
</ol>
<p>The optimal combination depends on your specific constraints: available hardware, latency requirements, throughput targets, and accuracy tolerances.</p>
<hr>
<h2 id="references">References</h2>
<p>[1] InfoQ - Cactus v1: Cross-Platform LLM Inference on Mobile with Zero Latency and Full Privacy. <a href="https://www.infoq.com/news/2025/12/cactus-on-device-inference/">https://www.infoq.com/news/2025/12/cactus-on-device-inference/</a></p>
<p>[2] InfoQ - Cactus v1: Cross-Platform LLM Inference on Mobile. <a href="https://www.infoq.com/news/2025/12/cactus-on-device-inference/">https://www.infoq.com/news/2025/12/cactus-on-device-inference/</a></p>
<p>[3] PMC - Tiny Machine Learning and On-Device Inference: A Survey. <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12115890/">https://pmc.ncbi.nlm.nih.gov/articles/PMC12115890/</a></p>
<p>[4] Novus - The Rise of Local AI Models: Going Small to Go Big. <a href="https://www.novusasi.com/blog/the-rise-of-local-ai-models-going-small-to-go-big">https://www.novusasi.com/blog/the-rise-of-local-ai-models-going-small-to-go-big</a></p>
<p>[5] Cerebras - REAP: One-Shot Pruning for Trillion-Parameter MoE Models. <a href="https://www.cerebras.ai/blog/reap">https://www.cerebras.ai/blog/reap</a></p>
<p>[6] arXiv - REAP the Experts: Why Pruning Prevails for One-Shot MoE compression. <a href="https://arxiv.org/abs/2510.13999">https://arxiv.org/abs/2510.13999</a></p>
<p>[7] HuggingFace - Cerebras REAP Collection. <a href="https://huggingface.co/collections/cerebras/cerebras-reap">https://huggingface.co/collections/cerebras/cerebras-reap</a></p>
<p>[8] NVIDIA Technical Blog - An Introduction to Speculative Decoding. <a href="https://developer.nvidia.com/blog/an-introduction-to-speculative-decoding-for-reducing-latency-in-ai-inference/">https://developer.nvidia.com/blog/an-introduction-to-speculative-decoding-for-reducing-latency-in-ai-inference/</a></p>
<p>[9] BentoML - Speculative Decoding. <a href="https://bentoml.com/llm/inference-optimization/speculative-decoding">https://bentoml.com/llm/inference-optimization/speculative-decoding</a></p>
<p>[10] vLLM Documentation - Speculative Decoding. <a href="https://docs.vllm.ai/en/latest/features/spec_decode/">https://docs.vllm.ai/en/latest/features/spec_decode/</a></p>
<p>[11] LMSYS - SpecForge: Accelerating Speculative Decoding Training for SGLang. <a href="https://lmsys.org/blog/2025-07-25-spec-forge/">https://lmsys.org/blog/2025-07-25-spec-forge/</a></p>
<p>[12] PyTorch Blog - Quantization-Aware Training for Large Language Models. <a href="https://pytorch.org/blog/quantization-aware-training/">https://pytorch.org/blog/quantization-aware-training/</a></p>
<p>[13] llama.cpp GitHub - Quantize README. <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md</a></p>
<p>[14] Local AI Zone - AI Model Quantization 2025 Guide. <a href="https://local-ai-zone.github.io/guides/what-is-ai-quantization-q4-k-m-q8-gguf-guide-2025.html">https://local-ai-zone.github.io/guides/what-is-ai-quantization-q4-k-m-q8-gguf-guide-2025.html</a></p>
<p>[15] Local AI Master - AWQ vs GPTQ vs GGUF Comparison. <a href="https://localaimaster.com/blog/quantization-explained">https://localaimaster.com/blog/quantization-explained</a></p>
<p>[16] Maarten Grootendorst - Which Quantization Method is Right for You? <a href="https://newsletter.maartengrootendorst.com/p/which-quantization-method-is-right">https://newsletter.maartengrootendorst.com/p/which-quantization-method-is-right</a></p>
<p>[17] HuggingFace - Making LLMs even more accessible with bitsandbytes. <a href="https://huggingface.co/blog/4bit-transformers-bitsandbytes">https://huggingface.co/blog/4bit-transformers-bitsandbytes</a></p>
<p>[18] GitHub - ExLlamaV2. <a href="https://github.com/turboderp-org/exllamav2">https://github.com/turboderp-org/exllamav2</a></p>
<p>[19] arXiv - Efficient Memory Management for LLM Serving with PagedAttention. <a href="https://arxiv.org/abs/2309.06180">https://arxiv.org/abs/2309.06180</a></p>
<p>[20] HuggingFace Blog - Continuous batching from first principles. <a href="https://huggingface.co/blog/continuous_batching">https://huggingface.co/blog/continuous_batching</a></p>
<p>[21] arXiv - Comparative Analysis of LLM Inference Serving Systems. <a href="https://arxiv.org/html/2511.17593v1">https://arxiv.org/html/2511.17593v1</a></p>
<p>[22] Ceph.io - KV Caching with vLLM, LMCache, and Ceph. <a href="https://ceph.io/en/news/blog/2025/vllm-kv-caching/">https://ceph.io/en/news/blog/2025/vllm-kv-caching/</a></p>
<p>[23] vLLM Documentation - Automatic Prefix Caching. <a href="https://docs.vllm.ai/en/stable/design/prefix_caching/">https://docs.vllm.ai/en/stable/design/prefix_caching/</a></p>
<p>[24] GitHub - Dao-AILab/flash-attention. <a href="https://github.com/Dao-AILab/flash-attention">https://github.com/Dao-AILab/flash-attention</a></p>
<p>[25] PyTorch Blog - FlashAttention-3: Fast and Accurate Attention. <a href="https://pytorch.org/blog/flashattention-3/">https://pytorch.org/blog/flashattention-3/</a></p>
<p>[26] Meta Engineering - Scaling LLM Inference: Innovations in Parallelism. <a href="https://engineering.fb.com/2025/10/17/ai-research/scaling-llm-inference-innovations-tensor-parallelism-context-parallelism-expert-parallelism/">https://engineering.fb.com/2025/10/17/ai-research/scaling-llm-inference-innovations-tensor-parallelism-context-parallelism-expert-parallelism/</a></p>
<p>[27] AWS Neuron Documentation - Parallelism Techniques for LLM Inference. <a href="https://awsdocs-neuron.readthedocs-hosted.com/en/latest/libraries/nxd-inference/app-notes/parallelism.html">https://awsdocs-neuron.readthedocs-hosted.com/en/latest/libraries/nxd-inference/app-notes/parallelism.html</a></p>
<p>[28] arXiv - Helix Parallelism: Rethinking Sharding Strategies. <a href="https://arxiv.org/html/2507.07120v1">https://arxiv.org/html/2507.07120v1</a></p>
<p>[29] arXiv - NEO: Saving GPU Memory Crisis with CPU Offloading. <a href="https://arxiv.org/abs/2411.01142">https://arxiv.org/abs/2411.01142</a></p>
<p>[30] NVIDIA Technical Blog - Accelerate Large-Scale LLM Inference with CPU-GPU Memory Sharing. <a href="https://developer.nvidia.com/blog/accelerate-large-scale-llm-inference-and-kv-cache-offload-with-cpu-gpu-memory-sharing/">https://developer.nvidia.com/blog/accelerate-large-scale-llm-inference-and-kv-cache-offload-with-cpu-gpu-memory-sharing/</a></p>
<p>[31] ngrok Blog - Prompt caching: 10x cheaper LLM tokens, but how? <a href="https://ngrok.com/blog/prompt-caching/">https://ngrok.com/blog/prompt-caching/</a></p>
<p>[32] NVIDIA TensorRT-LLM Documentation - Overview. <a href="https://nvidia.github.io/TensorRT-LLM/overview.html">https://nvidia.github.io/TensorRT-LLM/overview.html</a></p>
<p>[33] NVIDIA TensorRT-LLM - Model Definition. <a href="https://nvidia.github.io/TensorRT-LLM/architecture/core-concepts.html">https://nvidia.github.io/TensorRT-LLM/architecture/core-concepts.html</a></p>
<p>[34] NVIDIA Technical Blog - Optimizing Inference on LLMs with TensorRT-LLM. <a href="https://developer.nvidia.com/blog/optimizing-inference-on-llms-with-tensorrt-llm-now-publicly-available/">https://developer.nvidia.com/blog/optimizing-inference-on-llms-with-tensorrt-llm-now-publicly-available/</a></p>
<p>[35] NVIDIA TensorRT-LLM - Release Notes. <a href="https://nvidia.github.io/TensorRT-LLM/release-notes.html">https://nvidia.github.io/TensorRT-LLM/release-notes.html</a></p>
<p>[36] NVIDIA Technical Blog - Optimizing LLMs for Performance and Accuracy with Post-Training Quantization. <a href="https://developer.nvidia.com/blog/optimizing-llms-for-performance-and-accuracy-with-post-training-quantization/">https://developer.nvidia.com/blog/optimizing-llms-for-performance-and-accuracy-with-post-training-quantization/</a></p>
<p>[37] NVIDIA TensorRT-LLM - Numerical Precision. <a href="https://nvidia.github.io/TensorRT-LLM/reference/precision.html">https://nvidia.github.io/TensorRT-LLM/reference/precision.html</a></p>
<p>[38] NVIDIA NeMo Framework - Quantization. <a href="https://docs.nvidia.com/nemo-framework/user-guide/latest/nemotoolkit/nlp/quantization.html">https://docs.nvidia.com/nemo-framework/user-guide/latest/nemotoolkit/nlp/quantization.html</a></p>
<p>[39] MarkTechPost - vLLM vs TensorRT-LLM vs HF TGI vs LMDeploy Technical Comparison. <a href="https://www.marktechpost.com/2025/11/19/vllm-vs-tensorrt-llm-vs-hf-tgi-vs-lmdeploy-a-deep-technical-comparison-for-production-llm-inference/">https://www.marktechpost.com/2025/11/19/vllm-vs-tensorrt-llm-vs-hf-tgi-vs-lmdeploy-a-deep-technical-comparison-for-production-llm-inference/</a></p>
<p>[40] NVIDIA TensorRT-LLM - KV Cache Reuse. <a href="https://nvidia.github.io/TensorRT-LLM/advanced/kv-cache-reuse.html">https://nvidia.github.io/TensorRT-LLM/advanced/kv-cache-reuse.html</a></p>
<p>[41] NVIDIA Technical Blog - KV Cache Reuse Optimizations in TensorRT-LLM. <a href="https://developer.nvidia.com/blog/introducing-new-kv-cache-reuse-optimizations-in-nvidia-tensorrt-llm/">https://developer.nvidia.com/blog/introducing-new-kv-cache-reuse-optimizations-in-nvidia-tensorrt-llm/</a></p>
<p>[42] NVIDIA TensorRT-LLM - Building from Source. <a href="https://nvidia.github.io/TensorRT-LLM/installation/build-from-source-linux.html">https://nvidia.github.io/TensorRT-LLM/installation/build-from-source-linux.html</a></p>
<p>[43] BentoML - Best Practices for Tuning TensorRT-LLM. <a href="https://www.bentoml.com/blog/tuning-tensor-rt-llm-for-optimal-serving-with-bentoml">https://www.bentoml.com/blog/tuning-tensor-rt-llm-for-optimal-serving-with-bentoml</a></p>
<p>[44] vLLM Blog - Inside vLLM: Anatomy of a High-Throughput LLM Inference System. <a href="https://blog.vllm.ai/2025/09/05/anatomy-of-vllm.html">https://blog.vllm.ai/2025/09/05/anatomy-of-vllm.html</a></p>
<p>[45] vLLM Documentation - Paged Attention. <a href="https://docs.vllm.ai/en/stable/design/paged_attention/">https://docs.vllm.ai/en/stable/design/paged_attention/</a></p>
<p>[46] Red Hat Developer - How PagedAttention resolves memory waste of LLM systems. <a href="https://developers.redhat.com/articles/2025/07/24/how-pagedattention-resolves-memory-waste-llm-systems">https://developers.redhat.com/articles/2025/07/24/how-pagedattention-resolves-memory-waste-llm-systems</a></p>
<p>[47] arXiv - Efficient Memory Management for LLM Serving with PagedAttention. <a href="https://arxiv.org/abs/2309.06180">https://arxiv.org/abs/2309.06180</a></p>
<p>[48] Ubicloud Blog - Life of an inference request (vLLM V1). <a href="https://www.ubicloud.com/blog/life-of-an-inference-request-vllm-v1">https://www.ubicloud.com/blog/life-of-an-inference-request-vllm-v1</a></p>
<p>[49] vLLM Documentation - Scheduler. <a href="https://docs.vllm.ai/en/stable/api/vllm/v1/core/sched/scheduler/">https://docs.vllm.ai/en/stable/api/vllm/v1/core/sched/scheduler/</a></p>
<p>[50] vLLM Documentation - Automatic Prefix Caching. <a href="https://docs.vllm.ai/en/stable/design/prefix_caching/">https://docs.vllm.ai/en/stable/design/prefix_caching/</a></p>
<p>[51] Ceph.io - KV Caching with vLLM and LMCache. <a href="https://ceph.io/en/news/blog/2025/vllm-kv-caching/">https://ceph.io/en/news/blog/2025/vllm-kv-caching/</a></p>
<p>[52] vLLM Documentation - Speculative Decoding. <a href="https://docs.vllm.ai/en/latest/features/spec_decode/">https://docs.vllm.ai/en/latest/features/spec_decode/</a></p>
<p>[53] vLLM Blog - Speculators v0.3.0. <a href="https://blog.vllm.ai/2025/12/13/speculators-v030.html">https://blog.vllm.ai/2025/12/13/speculators-v030.html</a></p>
<p>[54] Snowflake Engineering Blog - Fastest Speculative Decoding with Arctic Inference. <a href="https://www.snowflake.com/en/engineering-blog/fast-speculative-decoding-vllm-arctic/">https://www.snowflake.com/en/engineering-blog/fast-speculative-decoding-vllm-arctic/</a></p>
<p>[55] vLLM Documentation - Parallelism and Scaling. <a href="https://docs.vllm.ai/en/stable/serving/parallelism_scaling/">https://docs.vllm.ai/en/stable/serving/parallelism_scaling/</a></p>
<p>[56] Red Hat Developer - Distributed Inference with vLLM. <a href="https://developers.redhat.com/articles/2025/02/06/distributed-inference-with-vllm">https://developers.redhat.com/articles/2025/02/06/distributed-inference-with-vllm</a></p>
<p>[57] vLLM Documentation - OpenAI-Compatible Server. <a href="https://docs.vllm.ai/en/stable/serving/openai_compatible_server/">https://docs.vllm.ai/en/stable/serving/openai_compatible_server/</a></p>
<p>[58] Java Code Geeks - Under the Hood of vLLM: Memory, Scheduling &amp; Batching Strategies. <a href="https://www.javacodegeeks.com/2025/10/under-the-hood-of-vllm-memory-scheduling-batching-strategies.html">https://www.javacodegeeks.com/2025/10/under-the-hood-of-vllm-memory-scheduling-batching-strategies.html</a></p>
<p>[59] LMSYS Blog - Fast and Expressive LLM Inference with RadixAttention and SGLang. <a href="https://lmsys.org/blog/2024-01-17-sglang/">https://lmsys.org/blog/2024-01-17-sglang/</a></p>
<p>[60] SugiV Blog - SGLang Deep Dive: Inside SGLang. <a href="https://blog.sugiv.fyi/sglang-deep-dive-inside-sglang">https://blog.sugiv.fyi/sglang-deep-dive-inside-sglang</a></p>
<p>[61] SGLang Paper - Efficient Execution of Structured Language Model Programs. <a href="https://proceedings.neurips.cc/paper_files/paper/2024/file/724be4472168f31ba1c9ac630f15dec8-Paper-Conference.pdf">https://proceedings.neurips.cc/paper_files/paper/2024/file/724be4472168f31ba1c9ac630f15dec8-Paper-Conference.pdf</a></p>
<p>[62] LMSYS Blog - Fast JSON Decoding with Compressed Finite State Machine. <a href="https://lmsys.org/blog/2024-02-05-compressed-fsm/">https://lmsys.org/blog/2024-02-05-compressed-fsm/</a></p>
<p>[63] ROCm Blogs - SGLang: Fast Serving Framework on AMD Instinct GPUs. <a href="https://rocm.blogs.amd.com/artificial-intelligence/sglang/README.html">https://rocm.blogs.amd.com/artificial-intelligence/sglang/README.html</a></p>
<p>[64] llama.cpp Wikipedia. <a href="https://en.wikipedia.org/wiki/Llama.cpp">https://en.wikipedia.org/wiki/Llama.cpp</a></p>
<p>[65] DeepWiki - ggml-org/llama.cpp. <a href="https://deepwiki.com/ggml-org/llama.cpp">https://deepwiki.com/ggml-org/llama.cpp</a></p>
<p>[66] DeepWiki - Backend Architecture and Registration. <a href="https://deepwiki.com/ggml-org/llama.cpp/4.1-cpu-backend">https://deepwiki.com/ggml-org/llama.cpp/4.1-cpu-backend</a></p>
<p>[67] DeepWiki - Metal Backend. <a href="https://deepwiki.com/ggml-org/llama.cpp/4.5-sycl-backend">https://deepwiki.com/ggml-org/llama.cpp/4.5-sycl-backend</a></p>
<p>[68] DeepWiki - Flash Attention and Optimizations. <a href="https://deepwiki.com/ggml-org/llama.cpp/7.4-flash-attention-and-optimizations">https://deepwiki.com/ggml-org/llama.cpp/7.4-flash-attention-and-optimizations</a></p>
<p>[69] Medium - llama.cpp performance breakthrough for multi-GPU setups. <a href="https://medium.com/@jagusztinl/llama-cpp-performance-breakthrough-for-multi-gpu-setups-04c83a66feb2">https://medium.com/@jagusztinl/llama-cpp-performance-breakthrough-for-multi-gpu-setups-04c83a66feb2</a></p>
<p>[70] llama.cpp GitHub - Quantize README. <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md</a></p>
<p>[71] Enclave AI - The Practical Quantization Guide for iPhone and Mac. <a href="https://enclaveai.app/blog/2025/11/12/practical-quantization-guide-iphone-mac-gguf/">https://enclaveai.app/blog/2025/11/12/practical-quantization-guide-iphone-mac-gguf/</a></p>
<p>[72] llama.cpp GitHub Discussion - Difference in quantization methods. <a href="https://github.com/ggml-org/llama.cpp/discussions/2094">https://github.com/ggml-org/llama.cpp/discussions/2094</a></p>
<p>[73] AIMultiple Research - LLM Inference Engines: vLLM vs LMDeploy vs SGLang. <a href="https://research.aimultiple.com/inference-engines/">https://research.aimultiple.com/inference-engines/</a></p>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Inference &amp; Deployment</category>
            <category>Edge &amp; on-device</category>
            <category>Quantization</category>
            <category>LLM serving</category>
            <enclosure length="60653" type="image/png" url="https://blog.ecitis.org/open-graph/local-ai-inferencing-techniques.png"/>
        </item>
        <item>
            <title><![CDATA[Fine-Tuning LLMs: A Practical Guide to Tools, Techniques, and Best Practices]]></title>
            <link>https://blog.ecitis.org/llm-finetuning-guide/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/llm-finetuning-guide/</guid>
            <pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Decide when fine-tuning is worth it, then choose the right data, method, tooling, and evaluation path for an efficient training run.]]></description>
            <content:encoded><![CDATA[<p>Fine-tuning large language models has become accessible to individual developers and small teams. What once required clusters of expensive GPUs can now run on consumer hardware with the right techniques and tools. This guide covers the practical aspects of fine-tuning: when to do it, how to do it efficiently, and which tools to use.</p>
<h2 id="when-fine-tuning-makes-sense">When Fine-Tuning Makes Sense</h2>
<figure><figcaption><strong>When to Fine-Tune</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/llm-finetuning-guide-0.webp" width="512" height="572" alt="When to Fine-Tune" loading="lazy" decoding="async"></figure>
<p>Before investing in fine-tuning, consider whether simpler approaches will work. The decision framework looks like this:</p>
<p><strong>Start with prompt engineering.</strong> If you can solve your problem by crafting better prompts, do that. Prompt engineering takes hours to days and requires no infrastructure changes. It works well for prototypes, MVPs, and cases where the base model’s capabilities are sufficient.</p>
<p><strong>Use RAG when you need external knowledge.</strong> If your model hallucinates or lacks company-specific information, Retrieval-Augmented Generation is the answer. RAG connects the model to a knowledge base and retrieves relevant context before generating responses. Customer service chatbots and documentation assistants benefit from this approach.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-1" id="user-content-fnref-1">1</a></sup></p>
<p><strong>Fine-tune when you need behavioral changes.</strong> If your system fails on reasoning, planning, or strict policies even with good prompts and retrieval, fine-tuning makes the difference. Use cases include:</p>
<ul>
<li>Interpreting clinical notes or legal documents</li>
<li>Enforcing a specific output format consistently</li>
<li>Teaching domain-specific terminology and relationships</li>
<li>Matching a particular writing style or tone</li>
</ul>
<p>Fine-tuning is also appropriate when you want to distill capabilities from a larger model into a smaller one for faster inference or reduced costs.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-2" id="user-content-fnref-2">2</a></sup></p>
<p>The tradeoff is clear: prompt engineering is a sprint, RAG is a marathon with hydration stations, and fine-tuning is building a Formula 1 car from scratch. Each serves different purposes.</p>
<h2 id="types-of-fine-tuning">Types of Fine-Tuning</h2>
<h3 id="full-fine-tuning">Full Fine-Tuning</h3>
<p>Full fine-tuning updates every parameter in the model. For a 7B parameter model, this requires over 28GB of GPU memory just for the weights in full precision. Training consumes additional memory for gradients and optimizer states.</p>
<p>The results can be excellent. Full fine-tuning gives the model maximum flexibility to adapt to new tasks. But the costs are significant:</p>
<ul>
<li>High compute requirements (multiple high-end GPUs)</li>
<li>Risk of catastrophic forgetting (the model forgets pre-trained knowledge)</li>
<li>One full-size checkpoint per task<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-3" id="user-content-fnref-3">3</a></sup></li>
</ul>
<h3 id="parameter-efficient-fine-tuning">Parameter-Efficient Fine-Tuning</h3>
<p>PEFT methods update only a fraction of parameters while keeping the base model frozen. The Hugging Face PEFT library demonstrates this: training bigscience/mt0-large with PEFT touches just 2,359,296 parameters out of 1,231,940,608 total. That is 0.19% of the model.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-4" id="user-content-fnref-4">4</a></sup></p>
<p>Performance remains competitive. With just 200 parameters projected into a space of millions of dimensions, you can achieve 90% of full fine-tuning performance. Some studies show PEFT techniques outperforming full fine-tuning on certain code intelligence tasks.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-5" id="user-content-fnref-5">5</a></sup></p>
<h3 id="lora">LoRA</h3>
<p>Low-Rank Adaptation, introduced by Microsoft Research in 2021, freezes the pre-trained weights and injects trainable low-rank decomposition matrices into transformer layers. Instead of updating billions of parameters, you train small adapter matrices amounting to 1-5% of the original parameters.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-6" id="user-content-fnref-6">6</a></sup></p>
<p>The key insight: weight updates during fine-tuning have low intrinsic rank. You can represent them as the product of two smaller matrices (A and B) without losing much information.</p>
<p>LoRA has virtually no downsides for most use cases: memory usage is minimal, training is fast, and quality is high. The adapter weights can merge into the base model for inference, eliminating latency overhead.</p>
<h3 id="qlora">QLoRA</h3>
<p>QLoRA extends LoRA by adding 4-bit quantization of the base model. The frozen weights are stored in 4-bit precision while LoRA adapters train in higher precision. Gradients backpropagate through the quantized model.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-7" id="user-content-fnref-7">7</a></sup></p>
<p>Three innovations make QLoRA work:</p>
<ol>
<li><strong>NF4 quantization</strong>: Uses the known distribution of neural network weights (zero-centered normal) to quantize effectively</li>
<li><strong>Double quantization</strong>: Quantizes the quantization constants themselves to reduce memory overhead</li>
<li><strong>Paged optimizers</strong>: Manages memory spikes during training</li>
</ol>
<p>QLoRA enables fine-tuning 70B parameter models on hardware that would struggle with 7B models using full fine-tuning. A single A100 80GB handles models that would otherwise require 4-8 GPUs.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-8" id="user-content-fnref-8">8</a></sup></p>
<p>The tradeoff: QLoRA achieves 80-90% of full fine-tuning quality compared to LoRA’s 90-95%. For many applications, this is acceptable given the massive resource savings.</p>
<h3 id="adapters">Adapters</h3>
<p>Adapters are small neural networks inserted into transformer layers, typically after attention or feed-forward sublayers. They have a bottleneck architecture similar to autoencoders: the input is projected down to a smaller dimension, transformed, then projected back up.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-9" id="user-content-fnref-9">9</a></sup></p>
<p>The original adapter paper showed BERT trained with adapters reached performance comparable to full fine-tuning while training only 3.6% of parameters. More recent work demonstrates that adapter-based PEFT in 7B parameter models can match or exceed the zero-shot performance of 175B parameter models on reasoning tasks.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-10" id="user-content-fnref-10">10</a></sup></p>
<h2 id="unsloth">Unsloth</h2>
<p><a href="https://unsloth.ai/">Unsloth</a> is an open-source library that accelerates LLM fine-tuning. The claims are substantial: 2x faster training with 70% less VRAM compared to standard Hugging Face methods.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-11" id="user-content-fnref-11">11</a></sup></p>
<h3 id="why-unsloth-is-fast">Why Unsloth is Fast</h3>
<p>The speed comes from several optimizations:</p>
<ul>
<li>Custom Triton kernels for RoPE embeddings and MLP layers</li>
<li>Fused operations that reduce memory transfers</li>
<li>“Uncontaminated sequence packing” that combines sequences efficiently</li>
<li>Chunked cross-entropy loss computation</li>
</ul>
<p>A December 2025 update combined these optimizations to achieve up to 3x faster training throughput with 60% lower VRAM usage.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-12" id="user-content-fnref-12">12</a></sup></p>
<h3 id="supported-models">Supported Models</h3>
<p>Unsloth supports the models people actually use:</p>
<ul>
<li>Llama 3.x and Llama 4 (including multimodal variants)</li>
<li>Qwen 2.5 and Qwen 3</li>
<li>Mistral and Mixtral</li>
<li>Gemma 2 and Gemma 3</li>
<li>DeepSeek models</li>
<li>Phi-3 and Phi-4</li>
<li>Vision models: Llama 3.2 Vision, Qwen 2.5 VL, Pixtral</li>
</ul>
<p>The library also supports NVIDIA GPUs from Tesla T4 to H100, with portability to AMD and Intel GPUs.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-13" id="user-content-fnref-13">13</a></sup></p>
<h3 id="memory-requirements">Memory Requirements</h3>
<p>VRAM requirements depend on model size and quantization:</p>






























<div class="table-wrap"><table><thead><tr><th>Model</th><th>QLoRA 4-bit</th><th>LoRA 16-bit</th></tr></thead><tbody><tr><td>7-9B parameters</td><td>6.5GB</td><td>24GB</td></tr><tr><td>20B parameters</td><td>14GB</td><td>-</td></tr><tr><td>70B parameters</td><td>48GB</td><td>-</td></tr><tr><td>120B parameters</td><td>65GB</td><td>-</td></tr></tbody></table></div>
<p>These numbers make consumer GPU fine-tuning realistic. A 24GB RTX 4090 handles most 7-9B parameter models with LoRA.</p>
<h3 id="installation">Installation</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> unsloth</span></span></code></pre>
<p>For Conda environments:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">conda</span><span style="color:var(--astro-code-token-string)"> create</span><span style="color:var(--astro-code-token-string)"> --name</span><span style="color:var(--astro-code-token-string)"> unsloth</span><span style="color:var(--astro-code-token-string)"> python=</span><span style="color:var(--astro-code-token-constant)">3.11</span><span style="color:var(--astro-code-token-string)"> pytorch-cuda=</span><span style="color:var(--astro-code-token-constant)">12.1</span><span style="color:var(--astro-code-token-string)"> pytorch</span><span style="color:var(--astro-code-token-string)"> cudatoolkit</span><span style="color:var(--astro-code-token-string)"> xformers</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string)"> pytorch</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string)"> nvidia</span><span style="color:var(--astro-code-token-string)"> -c</span><span style="color:var(--astro-code-token-string)"> xformers</span><span style="color:var(--astro-code-token-string)"> -y</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">conda</span><span style="color:var(--astro-code-token-string)"> activate</span><span style="color:var(--astro-code-token-string)"> unsloth</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> unsloth</span></span></code></pre>
<h3 id="code-example">Code Example</h3>
<p>Here is a complete example fine-tuning Llama 3.2 3B with QLoRA:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> unsloth </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> FastLanguageModel</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> SFTTrainer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> TrainingArguments</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model with 4-bit quantization</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> FastLanguageModel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;unsloth/Llama-3.2-3B-Instruct-bnb-4bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_seq_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2048</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Add LoRA adapters</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> FastLanguageModel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_peft_model</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    r</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,  </span><span style="color:var(--astro-code-token-comment)"># LoRA rank</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    target_modules</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;q_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;k_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;v_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;o_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                    &quot;gate_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;up_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;down_proj&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_alpha</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">16</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    lora_dropout</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    bias</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;none&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    use_gradient_checkpointing</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;unsloth&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load and format dataset</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;yahma/alpaca-cleaned&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> format_prompt</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">example</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    instruction </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> example</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;instruction&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    input_text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> example</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;input&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> example</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&quot;output&quot;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-foreground)"> input_text</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;&quot;### Instruction:</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">instruction</span><span style="color:var(--astro-code-token-constant)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">### Input:</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">input_text</span><span style="color:var(--astro-code-token-constant)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">### Response:</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">output</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    else</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> f</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;&quot;### Instruction:</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">instruction</span><span style="color:var(--astro-code-token-constant)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">### Response:</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-foreground)">output</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-punctuation)"> {</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> text</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dataset </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dataset</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">map</span><span style="color:var(--astro-code-token-punctuation)">(format_prompt)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Configure training</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SFTTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tokenizer</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    dataset_text_field</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_seq_length</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2048</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    args</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)">TrainingArguments</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        per_device_train_batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        warmup_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        max_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        learning_rate</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">2e-4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        fp16</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        logging_steps</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        output_dir</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;outputs&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    ),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Train</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Save adapter weights</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;lora_model&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># For inference</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">FastLanguageModel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">for_inference</span><span style="color:var(--astro-code-token-punctuation)">(model)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> tokenizer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;### Instruction:\nWrite a haiku about coding.\n\n### Response:\n&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">                   return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(tokenizer.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">], skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">))</span></span></code></pre>
<p>The <code>FastLanguageModel.for_inference()</code> call enables Unsloth’s optimized inference mode, providing 2x speedup over standard generation.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-14" id="user-content-fnref-14">14</a></sup></p>
<h2 id="thinking-machines-tinker">Thinking Machines’ Tinker</h2>
<p><a href="https://thinkingmachines.ai/">Tinker</a> is a training API from Mira Murati’s Thinking Machines Lab, released in October 2025. The premise is handling infrastructure complexity while giving you control over algorithms and data.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-15" id="user-content-fnref-15">15</a></sup></p>
<h3 id="how-tinker-works">How Tinker Works</h3>
<p>You write simple Python scripts with four core functions, and Tinker runs them across distributed GPUs. The platform handles:</p>
<ul>
<li>Multi-GPU and multi-node distribution</li>
<li>Checkpoint management</li>
<li>Memory optimization</li>
<li>Gradient synchronization</li>
</ul>
<p>Research teams at Princeton, Stanford, and Berkeley use Tinker for their work. The platform supports models up to 1 trillion parameters, including Kimi K2.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-16" id="user-content-fnref-16">16</a></sup></p>
<h3 id="supported-training-approaches">Supported Training Approaches</h3>
<p>Tinker supports the full range of post-training methods:</p>
<p><strong>Supervised fine-tuning</strong> for instruction following and task adaptation</p>
<p><strong>Preference learning</strong> with a three-stage RLHF pipeline:</p>
<ol>
<li>Supervised fine-tuning on demonstrations</li>
<li>Training a reward model on preferences</li>
<li>RL optimization against the reward model</li>
</ol>
<p><strong>Prompt distillation</strong> for internalizing long instructions into model weights</p>
<p><strong>Multi-agent optimization</strong> for training models to interact with other models or themselves</p>
<p>The <a href="https://github.com/thinking-machines-lab/tinker-cookbook">Tinker cookbook</a> on GitHub contains examples for these approaches.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-17" id="user-content-fnref-17">17</a></sup></p>
<h3 id="lora-research">LoRA Research</h3>
<p>Thinking Machines published research on LoRA hyperparameters. Key finding: when picking optimal learning rates for each setting, training progresses almost identically for LoRAs with different sizes and full fine-tuning. Similar results appeared on AIME 2024 and AIME 2025 evaluations.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-18" id="user-content-fnref-18">18</a></sup></p>
<h2 id="other-fine-tuning-tools">Other Fine-Tuning Tools</h2>
<h3 id="hugging-face-trl">Hugging Face TRL</h3>
<p>TRL (Transformer Reinforcement Learning) is Hugging Face’s library for post-training foundation models. It supports supervised fine-tuning, GRPO (Group Relative Policy Optimization), DPO (Direct Preference Optimization), and PPO.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-19" id="user-content-fnref-19">19</a></sup></p>
<p>Key features:</p>
<ul>
<li>Built on the Transformers ecosystem</li>
<li>Native support for distributed training (DDP, DeepSpeed, FSDP)</li>
<li>Integration with PEFT for memory-efficient training</li>
<li>OpenEnv integration for RL and agentic workflows</li>
</ul>
<p>The GRPOTrainer implements the algorithm used to train DeepSeek’s R1. It is more memory-efficient than PPO.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-20" id="user-content-fnref-20">20</a></sup></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> trl </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> SFTTrainer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> DPOTrainer</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GRPOTrainer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Basic SFT example</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.2-3B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">tokenizer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoTokenizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Llama-3.2-3B&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SFTTrainer</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">model,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    train_dataset</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    tokenizer</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">tokenizer,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">trainer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">train</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>The combination of TRL and PEFT enables fine-tuning gpt-neo-x 20B (40GB in bfloat16) on a 24GB consumer GPU.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-21" id="user-content-fnref-21">21</a></sup></p>
<h3 id="axolotl">Axolotl</h3>
<p><a href="https://github.com/axolotl-ai-cloud/axolotl">Axolotl</a> is Modal’s recommended framework for beginners. It offers flexibility, ease of use, and rapid adoption of new models and techniques.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-22" id="user-content-fnref-22">22</a></sup></p>
<p>2025 brought significant updates:</p>
<ul>
<li>February: LoRA optimizations for memory and speed, GRPO support</li>
<li>May: Quantization Aware Training (QAT)</li>
<li>August: NVFP4 support, GPT-OSS model support</li>
<li>ND Parallelism combining Context Parallelism, Tensor Parallelism, and FSDP</li>
</ul>
<p>Axolotl supports multi-GPU training, unlike Unsloth. If you have a large GPU cluster, Axolotl is the better choice.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-23" id="user-content-fnref-23">23</a></sup></p>
<p>Configuration uses YAML files that span the full pipeline: dataset preprocessing, training, evaluation, quantization, and inference.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">base_model</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> meta-llama/Llama-3.2-3B-Instruct</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">model_type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> AutoModelForCausalLM</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">tokenizer_type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> AutoTokenizer</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">load_in_4bit</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> true</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">adapter</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> qlora</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_r</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 32</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_alpha</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 16</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_dropout</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 0.05</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lora_target_modules</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">q_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">v_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">k_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">o_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">gate_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">down_proj</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-string-expression)">up_proj</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">datasets</span><span style="color:var(--astro-code-token-keyword)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  - </span><span style="color:var(--astro-code-token-keyword)">path</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> yahma/alpaca-cleaned</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    type</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> alpaca</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">sequence_len</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 4096</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">micro_batch_size</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">gradient_accumulation_steps</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">num_epochs</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-constant)"> 3</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">learning_rate</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> 2e-4</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">optimizer</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> adamw_torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">lr_scheduler</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> cosine</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">output_dir</span><span style="color:var(--astro-code-token-keyword)">:</span><span style="color:var(--astro-code-token-string-expression)"> ./outputs</span></span></code></pre>
<h3 id="llama-factory">LLaMA-Factory</h3>
<p><a href="https://github.com/hiyouga/LlamaFactory">LLaMA-Factory</a> provides a WebUI for fine-tuning, making it accessible to non-technical users. The toolkit supports over 100 models and received the ACL 2024 best paper award.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-24" id="user-content-fnref-24">24</a></sup></p>
<p>2025 additions include:</p>
<ul>
<li>Orthogonal Finetuning (OFT and OFTv2) in August</li>
<li>GPT-OSS and Intern-S1-mini support</li>
<li>GLM-4.1V, Qwen3, InternVL3, Llama 4, Qwen2.5-Omni</li>
</ul>
<p>Training approaches span supervised fine-tuning, continuous pre-training, and preference tuning (PPO, DPO, KTO, ORPO). The toolkit integrates FlashAttention-2, DeepSpeed, GaLore, and BAdam optimization.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-25" id="user-content-fnref-25">25</a></sup></p>
<p>Inference uses OpenAI-style API, Gradio UI, or CLI with vLLM or SGLang workers.</p>
<h3 id="torchtune">torchtune</h3>
<p><a href="https://github.com/meta-pytorch/torchtune">torchtune</a> is PyTorch’s native post-training library. It offers composable building blocks without framework abstractions.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-26" id="user-content-fnref-26">26</a></sup></p>
<p>If you prefer working directly with PyTorch, torchtune is the choice. The library is designed with memory efficiency in mind, with recipes tested on consumer GPUs with 24GB VRAM.</p>
<p>Features include:</p>
<ul>
<li>LoRA fine-tuning on single device</li>
<li>Knowledge distillation</li>
<li>DPO training</li>
<li>Multi-node training (added February 2025)</li>
<li>Export to ExecuTorch for mobile and edge inference</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Single-device LoRA fine-tuning</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">tune</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> lora_finetune_single_device</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> llama3_2/3B_lora_single_device</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Knowledge distillation</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">tune</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> knowledge_distillation_distributed</span><span style="color:var(--astro-code-token-string)"> --config</span><span style="color:var(--astro-code-token-string)"> qwen2/1.5B_to_0.5B_KD_lora_distributed</span></span></code></pre>
<h2 id="data-preparation">Data Preparation</h2>
<h3 id="dataset-formats">Dataset Formats</h3>
<p>Two formats dominate: Alpaca and ShareGPT.</p>
<p><strong>Alpaca format</strong> suits instruction-following tasks. Each example has three fields:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;instruction&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Write a function to calculate factorial&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;input&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;5&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;output&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;def factorial(n):\n    if n &lt;= 1:\n        return 1\n    return n * factorial(n-1)\n\nprint(factorial(5))  # 120&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>The original Alpaca dataset contains 52,000 instruction-output pairs generated by GPT-4 from 175 seed instructions.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-27" id="user-content-fnref-27">27</a></sup></p>
<p><strong>ShareGPT format</strong> handles multi-turn conversations:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">  &quot;conversations&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    { </span><span style="color:var(--astro-code-token-keyword)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;What is the capital of France?&quot;</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    { </span><span style="color:var(--astro-code-token-keyword)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;assistant&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Paris is the capital of France.&quot;</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    { </span><span style="color:var(--astro-code-token-keyword)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;What is its population?&quot;</span><span style="color:var(--astro-code-foreground)"> }</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;assistant&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">      &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Paris has a population of about 2.1 million in the city proper, and over 12 million in the metropolitan area.&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">  ]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<p>ShareGPT supports additional roles: human, GPT, observation, and function. Human and observation entries appear in odd positions; GPT and function in even positions.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-28" id="user-content-fnref-28">28</a></sup></p>
<p>Use Alpaca for single-turn question-and-answer tasks. Use ShareGPT for conversational chatbots, especially those with function calling.</p>
<h3 id="data-quality">Data Quality</h3>
<p>Quality matters more than quantity. A few thousand well-curated examples often outperform hundreds of thousands of noisy ones.</p>
<p>Check for:</p>
<ul>
<li>Correct answers (verify factual claims)</li>
<li>Consistent formatting</li>
<li>Diverse task coverage</li>
<li>Balanced class distribution</li>
<li>No personally identifiable information</li>
</ul>
<p>Clean datasets exist for common starting points. <a href="https://github.com/gururise/AlpacaDataCleaned">AlpacaDataCleaned</a> removes problematic examples from the original Alpaca dataset.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-29" id="user-content-fnref-29">29</a></sup></p>
<h3 id="synthetic-data-generation">Synthetic Data Generation</h3>
<p>When human-labeled data is scarce, synthetic data fills the gap. Teacher-student distillation uses a larger model (like GPT-4) to generate training examples for a smaller model.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-30" id="user-content-fnref-30">30</a></sup></p>
<p>Strategies for synthetic data:</p>
<p><strong>Question-answer generation</strong>: Use retrieval-augmented pipelines to generate QA pairs from documents. A multi-stage framework with retriever, generator, and refinement model produces higher quality than single-pass generation.</p>
<p><strong>Active synthetic data generation</strong>: Generate data iteratively based on the current model’s weaknesses. Simple selection criteria from active learning perform well.</p>
<p><strong>Rephrasing</strong>: Augment existing questions with paraphrased versions. This is robust even with weaker augmentation models.</p>
<p>Challenges exist. LLMs can hallucinate, producing incorrect labels or logically inconsistent examples. Validate synthetic data before training, either through automated checks or sampling for human review.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-31" id="user-content-fnref-31">31</a></sup></p>
<p>Red Hat’s <a href="https://developers.redhat.com/articles/2025/11/25/building-domain-specific-llms-synthetic-data-and-sdg-hub">SDG Hub</a> provides an open-source toolkit for synthetic data workflows.</p>
<h2 id="training-configurations">Training Configurations</h2>
<h3 id="learning-rate">Learning Rate</h3>
<p>The learning rate is the most important hyperparameter. For LoRA and QLoRA fine-tuning:</p>
<ul>
<li>Start with 2e-4 for small models (7B)</li>
<li>Scale down to 2e-5 for larger models (70B+)</li>
<li>If training is unstable, reduce to 1e-4 or 3e-5</li>
</ul>
<p>Learning rate of 1e-4 has become standard for LoRA fine-tuning. Reducing it further helps with occasional training loss instabilities.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-32" id="user-content-fnref-32">32</a></sup></p>
<p>Use warmup steps (5-10% of total steps) and a scheduler. Cosine annealing works well for most cases.</p>
<h3 id="batch-size">Batch Size</h3>
<p>Maximize tokens-per-second without running out of memory. Use gradient accumulation to achieve larger effective batch sizes.</p>
<p>A common configuration:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">per_device_train_batch_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">gradient_accumulation_steps </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Effective batch size = 16</span></span></code></pre>
<p>Larger batch sizes provide more stable gradients but require more memory. If you hit OOM errors, reduce per-device batch size and increase gradient accumulation.</p>
<h3 id="lora-rank-and-alpha">LoRA Rank and Alpha</h3>
<p><strong>Rank (r)</strong>: Controls the capacity of the adapter. Common values are 8, 16, 32, or 64.</p>
<ul>
<li>Higher rank = more parameters = more capacity for diverse tasks</li>
<li>Lower rank = fewer parameters = faster training, less overfitting risk</li>
<li>Research suggests the most critical factor is applying LoRA to all linear transformer layers, not the specific rank value<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-33" id="user-content-fnref-33">33</a></sup></li>
</ul>
<p><strong>Alpha</strong>: Scaling factor for LoRA weights. Recommendations vary:</p>
<ul>
<li>Set alpha equal to rank (alpha/rank = 1)</li>
<li>Set alpha to 2x rank (alpha/rank = 2), as Microsoft does in their examples</li>
<li>The original LoRA paper suggests fixing alpha at 16</li>
</ul>
<p>If rank is 16, start with alpha of 16 or 32. Adjust based on results.</p>
<p><strong>Target modules</strong>: Apply LoRA to both attention and MLP layers for best results. The minimal set is attention projections (q, k, v, o). Adding gate_proj, up_proj, and down_proj improves performance.</p>
<h3 id="number-of-epochs">Number of Epochs</h3>
<p>For instruction-based datasets, 1-3 epochs work well. Training beyond 3 epochs offers diminishing returns and increases overfitting risk.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-34" id="user-content-fnref-34">34</a></sup></p>
<p>Monitor validation loss. If it starts increasing while training loss continues decreasing, you are overfitting.</p>
<h2 id="evaluation-and-deployment">Evaluation and Deployment</h2>
<h3 id="benchmarks">Benchmarks</h3>
<p>No single benchmark suffices. Select benchmarks relevant to your use case:</p>
<p><strong>Instruction following</strong>: MT-Bench, AlpacaEval, LMSYS Chatbot Arena</p>
<p><strong>Reasoning</strong>: GSM8K (math), MATH (competition math), BBH (Big Bench Hard), DROP</p>
<p><strong>Knowledge</strong>: MMLU (15,000+ multiple-choice questions across 57 subjects)</p>
<p><strong>Truthfulness</strong>: TruthfulQA (800+ questions across 38 subjects)</p>
<p><strong>Domain-specific</strong>: Build custom evaluation sets matching your deployment scenario<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-35" id="user-content-fnref-35">35</a></sup></p>
<h3 id="llm-as-judge">LLM-as-Judge</h3>
<p>MT-Bench introduced using LLMs to evaluate other LLMs. GPT-4 serves as a judge to score response quality. This scales better than human evaluation for rapid iteration.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-36" id="user-content-fnref-36">36</a></sup></p>
<p>Open-source alternatives exist. Prometheus is a Llama-2-Chat model fine-tuned on 100K GPT-4 feedback examples. It achieves comparable evaluation capabilities when given appropriate reference materials.<sup><a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fn-37" id="user-content-fnref-37">37</a></sup></p>
<h3 id="tracking-progress">Tracking Progress</h3>
<p>Use AlpacaEval to compute win-rate deltas before and after fine-tuning. Track metrics on held-out validation sets throughout training.</p>
<p>Tools for experiment tracking:</p>
<ul>
<li>Weights &amp; Biases</li>
<li>MLflow</li>
<li>TensorBoard</li>
<li>LlamaBoard (LLaMA-Factory specific)</li>
</ul>
<h3 id="deployment">Deployment</h3>
<p>After fine-tuning, merge LoRA weights into the base model for inference:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># With Unsloth</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_pretrained_merged</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;merged_model&quot;</span><span style="color:var(--astro-code-token-punctuation)">, tokenizer, save_method</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;merged_16bit&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or save as GGUF for llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">save_pretrained_gguf</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;model_gguf&quot;</span><span style="color:var(--astro-code-token-punctuation)">, tokenizer, quantization_method</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;q4_k_m&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Quantization reduces model size for deployment. Common options:</p>
<ul>
<li>GGUF format for llama.cpp and Ollama</li>
<li>AWQ for vLLM</li>
<li>GPTQ for text-generation-inference</li>
</ul>
<p>For production inference, use vLLM, SGLang, or TGI rather than Hugging Face Transformers directly.</p>
<h3 id="continuous-monitoring">Continuous Monitoring</h3>
<p>Static benchmarks alone will not catch performance drift. Monitor in production:</p>
<ul>
<li>Response latency</li>
<li>User feedback signals (thumbs up/down, regeneration requests)</li>
<li>Domain-specific quality metrics</li>
<li>Hallucination rates on known-answer queries</li>
</ul>
<p>Retrain periodically as your data distribution shifts.</p>
<h2 id="references">References</h2>
<section class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p><a href="https://www.news.aakashg.com/p/rag-vs-fine-tuning-vs-prompt-engineering" rel="noopener noreferrer">RAG vs. Fine-tuning vs. Prompt Engineering: The Complete Guide to AI Optimization</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p><a href="https://moveo.ai/blog/fine-tuning-rag-or-prompt-engineering" rel="noopener noreferrer">Fine-Tuning, RAG, or Prompt Engineering? LLM Decision Guide - Moveo.AI</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p><a href="https://apxml.com/courses/introduction-to-llm-fine-tuning/chapter-4-parameter-efficient-fine-tuning-peft/comparing-peft-full-fine-tuning" rel="noopener noreferrer">PEFT vs Full Fine-Tuning Comparison - APXML</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-3" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-4">
<p><a href="https://huggingface.co/blog/peft" rel="noopener noreferrer">Parameter-Efficient Fine-Tuning using PEFT - Hugging Face</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-4" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-5">
<p><a href="https://dl.acm.org/doi/10.1145/3714461" rel="noopener noreferrer">Exploring Parameter-Efficient Fine-Tuning Techniques for Code Generation - ACM</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-5" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-6">
<p><a href="https://www.analyticsvidhya.com/blog/2023/08/lora-and-qlora/" rel="noopener noreferrer">Parameter-Efficient Fine-Tuning of Large Language Models with LoRA and QLoRA - Analytics Vidhya</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-6" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-7">
<p><a href="https://introl.com/blog/fine-tuning-infrastructure-lora-qlora-peft-scale-guide-2025" rel="noopener noreferrer">Fine-Tuning Infrastructure: LoRA, QLoRA, and PEFT at Scale - Introl</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-7" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-8">
<p><a href="https://www.index.dev/blog/top-ai-fine-tuning-tools-lora-vs-qlora-vs-full" rel="noopener noreferrer">Best GenAI Fine-Tuning Tools for 2025 - LoRA vs QLoRA - Index.dev</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-8" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-9">
<p><a href="https://magazine.sebastianraschka.com/p/finetuning-llms-with-adapters" rel="noopener noreferrer">Finetuning LLMs with Adapters - Sebastian Raschka</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-9" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-10">
<p><a href="https://aclanthology.org/2023.emnlp-main.319/" rel="noopener noreferrer">LLM-Adapters: An Adapter Family for Parameter-Efficient Fine-Tuning - ACL Anthology</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-10" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-11">
<p><a href="https://github.com/unslothai/unsloth" rel="noopener noreferrer">Unsloth GitHub Repository</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-11" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-12">
<p><a href="https://medium.com/coding-nexus/unsloth-ai-makes-llm-fine-tuning-3-faster-and-90-cheaper-love-you-0f32eb3d7b98" rel="noopener noreferrer">Unsloth AI Makes LLM Fine-Tuning 3x Faster and 90% Cheaper - Medium</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-12" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-13">
<p><a href="https://unsloth.ai/" rel="noopener noreferrer">Unsloth AI - Open Source Fine-tuning &amp; RL for LLMs</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-13" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-14">
<p><a href="https://medium.com/@matteo28/qlora-fine-tuning-with-unsloth-a-complete-guide-8652c9c7edb3" rel="noopener noreferrer">QLoRA Fine-Tuning with Unsloth: A Complete Guide - Medium</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-14" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-15">
<p><a href="https://siliconangle.com/2025/12/12/thinking-machines-makes-tinker-ai-fine-tuning-service-generally-available/" rel="noopener noreferrer">Thinking Machines makes its Tinker AI fine-tuning service generally available - SiliconANGLE</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-15" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-16">
<p><a href="https://www.deeplearning.ai/the-batch/thinking-machines-new-tinker-api-makes-it-easier-to-fine-tune-models-on-many-gpus/" rel="noopener noreferrer">Thinking Machines’ New Tinker API Makes It Easier To Fine-Tune Models On Many GPUs - DeepLearning.AI</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-16" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-17">
<p><a href="https://github.com/thinking-machines-lab/tinker-cookbook" rel="noopener noreferrer">Tinker Cookbook - GitHub</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-17" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-18">
<p><a href="https://thinkingmachines.ai/blog/lora/" rel="noopener noreferrer">LoRA Without Regret - Thinking Machines Lab</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-18" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-19">
<p><a href="https://huggingface.co/docs/trl/en/index" rel="noopener noreferrer">TRL - Transformer Reinforcement Learning - Hugging Face</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-19" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-20">
<p><a href="https://github.com/huggingface/trl" rel="noopener noreferrer">TRL GitHub Repository</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-20" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-21">
<p><a href="https://huggingface.co/blog/trl-peft" rel="noopener noreferrer">Fine-tuning 20B LLMs with RLHF on a 24GB consumer GPU - Hugging Face</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-21" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-22">
<p><a href="https://modal.com/blog/fine-tuning-llms" rel="noopener noreferrer">Best frameworks for fine-tuning LLMs in 2025 - Modal</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-22" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-23">
<p><a href="https://github.com/axolotl-ai-cloud/axolotl" rel="noopener noreferrer">Axolotl GitHub Repository</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-23" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-24">
<p><a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer">LLaMA-Factory GitHub Repository</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-24" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-25">
<p><a href="https://llamafactory.readthedocs.io/en/latest/" rel="noopener noreferrer">LLaMA-Factory Documentation</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-25" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-26">
<p><a href="https://pytorch.org/blog/torchtune-fine-tune-llms/" rel="noopener noreferrer">torchtune: Easily fine-tune LLMs using PyTorch - PyTorch</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-26" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-27">
<p><a href="https://unsloth.ai/docs/get-started/fine-tuning-llms-guide/datasets-guide" rel="noopener noreferrer">Datasets Guide - Unsloth Documentation</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-27" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-28">
<p><a href="https://blog.usee.ai/fine-tuning-llms-with-llama-factory-guidance-and-insights-5cc619f56a7f" rel="noopener noreferrer">Fine-Tuning LLMs with LLaMA-Factory: Guidance and Insights - Usee.ai</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-28" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-29">
<p><a href="https://github.com/gururise/AlpacaDataCleaned" rel="noopener noreferrer">AlpacaDataCleaned - GitHub</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-29" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-30">
<p><a href="https://scale.com/blog/synthetic-data-fine-tuning-llms" rel="noopener noreferrer">Synthetic Data Generation Strategies for Fine-Tuning LLMs - Scale</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-30" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-31">
<p><a href="https://labelyourdata.com/articles/llm-fine-tuning/synthetic-data" rel="noopener noreferrer">Synthetic Data: Benefits and Techniques for LLM Fine-Tuning in 2025 - Label Your Data</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-31" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-32">
<p><a href="https://magazine.sebastianraschka.com/p/practical-tips-for-finetuning-llms" rel="noopener noreferrer">Practical Tips for Finetuning LLMs Using LoRA - Sebastian Raschka</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-32" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-33">
<p><a href="https://unsloth.ai/docs/get-started/fine-tuning-llms-guide/lora-hyperparameters-guide" rel="noopener noreferrer">LoRA fine-tuning Hyperparameters Guide - Unsloth Documentation</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-33" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-34">
<p><a href="https://amirteymoori.com/fine-tuning-llms-with-lora-a-practical-guide-for-2025/" rel="noopener noreferrer">Fine-Tuning LLMs with LoRA: 2025 Guide - Amir Teymoori</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-34" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-35">
<p><a href="https://labelyourdata.com/articles/llm-fine-tuning/llm-evaluation" rel="noopener noreferrer">LLM Evaluation: Benchmarks to Test Model Quality in 2025 - Label Your Data</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-35" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-36">
<p><a href="https://www.evidentlyai.com/llm-guide/llm-benchmarks" rel="noopener noreferrer">30 LLM evaluation benchmarks and how they work - Evidently AI</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-36" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-37">
<p><a href="https://alopatenko.github.io/LLMEvaluation/" rel="noopener noreferrer">Awesome LLM Evaluation - GitHub</a> <a href="https://blog.ecitis.org/llm-finetuning-guide/#user-content-fnref-37" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Fine-tuning</category>
            <category>Training</category>
            <category>Quantization</category>
            <enclosure length="80678" type="image/png" url="https://blog.ecitis.org/open-graph/llm-finetuning-guide.png"/>
        </item>
        <item>
            <title><![CDATA[Vision Language Models: Architecture, Inference, and Practical Deployment]]></title>
            <link>https://blog.ecitis.org/vision-language-models/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/vision-language-models/</guid>
            <pubDate>Wed, 07 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Trace how visual encoders, projectors, and language models combine, and what it takes to train and deploy multimodal systems.]]></description>
            <content:encoded><![CDATA[<p>Vision Language Models (VLMs) combine computer vision and natural language processing into unified systems capable of reasoning about images and text together. Unlike earlier approaches that treated vision and language as separate pipelines, modern VLMs learn joint representations that enable tasks like visual question answering, image captioning, document analysis, and multimodal reasoning.</p>
<p>This article covers VLM architecture from the ground up: how vision encoders transform pixels into tokens, how projection layers bridge modalities, and why inference is more complex than text-only LLMs. We also survey popular architectures, quantization strategies, and practical deployment options.</p>
<hr>
<h2 id="what-vlms-can-do">What VLMs Can Do</h2>
<p>VLMs accept images (or videos) alongside text prompts and generate text responses. Concrete capabilities include:</p>
<ul>
<li><strong>Visual question answering</strong>: “What color is the car in the background?”</li>
<li><strong>Document understanding</strong>: Extract text, tables, and structure from PDFs, invoices, forms</li>
<li><strong>Chart and diagram analysis</strong>: Interpret graphs, flowcharts, architectural diagrams</li>
<li><strong>OCR and text extraction</strong>: Read text embedded in images without separate OCR pipelines</li>
<li><strong>Object localization</strong>: Identify bounding boxes or regions corresponding to natural language queries</li>
<li><strong>Image captioning</strong>: Generate descriptions of visual content</li>
<li><strong>Multi-image reasoning</strong>: Compare multiple images, summarize differences</li>
<li><strong>Video understanding</strong>: Summarize events, answer questions about temporal sequences</li>
</ul>
<p>The key shift from earlier vision models: VLMs use the same interface as chat LLMs. You prompt them with natural language, optionally include images, and receive text back. This unified interface simplifies integration into existing LLM-based applications.</p>
<hr>
<h2 id="how-vlms-work-layer-by-layer">How VLMs Work: Layer by Layer</h2>
<p>Most VLMs follow a three-component architecture:</p>
<figure><figcaption><strong>Vision Encoder</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/vision-language-models-0.webp" width="512" height="302" alt="Vision Encoder" loading="lazy" decoding="async"></figure>
<p>Let’s examine each component.</p>
<h3 id="vision-encoder">Vision Encoder</h3>
<p>The vision encoder transforms raw pixels into a sequence of feature vectors. The dominant architecture is the Vision Transformer (ViT), introduced by Dosovitskiy et al. in 2020.</p>
<p><strong>How ViT works:</strong></p>
<ol>
<li>
<p><strong>Patch extraction</strong>: The input image is divided into fixed-size patches (typically 14x14 or 16x16 pixels). A 224x224 image with 14x14 patches yields 256 patches.</p>
</li>
<li>
<p><strong>Linear embedding</strong>: Each patch is flattened and projected through a linear layer to create patch embeddings. For a 14x14x3 patch (588 values), this produces a vector of dimension D (commonly 768, 1024, or 1152).</p>
</li>
<li>
<p><strong>Position encoding</strong>: Since transformers have no inherent notion of spatial order, learnable position embeddings are added to each patch embedding. These encode the patch’s location in the original image grid.</p>
</li>
<li>
<p><strong>CLS token</strong>: A learnable classification token is prepended to the sequence. After processing, this token aggregates global image information.</p>
</li>
<li>
<p><strong>Transformer blocks</strong>: The sequence passes through standard transformer encoder layers (self-attention + feedforward). Each patch attends to all other patches, capturing global context.</p>
</li>
</ol>
<p>The output is a sequence of vectors: one per patch plus the CLS token. For a 224x224 image with 14x14 patches, you get 257 vectors (256 patches + 1 CLS).</p>
<p><strong>Popular vision encoders:</strong></p>








































<div class="table-wrap"><table><thead><tr><th>Encoder</th><th>Parameters</th><th>Patch Size</th><th>Output Dim</th><th>Notes</th></tr></thead><tbody><tr><td>CLIP ViT-L/14</td><td>428M</td><td>14x14</td><td>1024</td><td>Trained on 400M image-text pairs</td></tr><tr><td>SigLIP-So400m</td><td>400M</td><td>14x14</td><td>1152</td><td>Sigmoid loss, better small-batch training</td></tr><tr><td>InternViT-6B</td><td>6B</td><td>14x14</td><td>3200</td><td>Scaled up for InternVL</td></tr><tr><td>DaViT</td><td>88M</td><td>varies</td><td>varies</td><td>Dual attention (spatial + channel)</td></tr></tbody></table></div>
<p><strong>CLIP vs SigLIP</strong></p>
<p>CLIP (Contrastive Language-Image Pre-training) trains vision and text encoders jointly using a contrastive softmax loss. Given a batch of N image-text pairs, the model learns to match correct pairs while pushing incorrect pairs apart. The softmax normalization requires computing similarities across the entire batch.</p>
<p>SigLIP (Sigmoid Loss for Language-Image Pre-training) replaces softmax with sigmoid loss that operates on each image-text pair independently. This removes the need for cross-batch normalization, enabling:</p>
<ul>
<li>Better performance with smaller batch sizes</li>
<li>Reduced memory usage (4096 batch on 4 TPUs vs 2048 for CLIP)</li>
<li>Comparable or better accuracy on downstream tasks</li>
</ul>
<p>SigLIP 2 (February 2025) adds self-distillation, masked prediction, and captioning objectives for improved localization and dense prediction.</p>
<h3 id="projectionadapter-layers">Projection/Adapter Layers</h3>
<p>Vision encoders and language models operate in different representation spaces. The projection layer bridges this gap by transforming vision features into the language model’s embedding space.</p>
<p><strong>Simple linear projection (LLaVA 1.0)</strong></p>
<p>The original LLaVA used a single linear layer:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>vision_features: [num_patches, 1024]</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>      ▼</span></span>
<span class="line"><span> Linear(1024, 4096)</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>      ▼</span></span>
<span class="line"><span>projected_features: [num_patches, 4096]</span></span></code></pre>
<p>This works but limits how much the vision representation can be transformed.</p>
<p><strong>MLP projection (LLaVA 1.5+)</strong></p>
<p>LLaVA 1.5 switched to a two-layer MLP with GELU activation:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>vision_features: [num_patches, 1024]</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>      ▼</span></span>
<span class="line"><span> Linear(1024, 4096)</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>      ▼</span></span>
<span class="line"><span>    GELU</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>      ▼</span></span>
<span class="line"><span> Linear(4096, 4096)</span></span>
<span class="line"><span>      │</span></span>
<span class="line"><span>      ▼</span></span>
<span class="line"><span>projected_features: [num_patches, 4096]</span></span></code></pre>
<p>The additional nonlinearity allows more complex transformations and improved multimodal alignment.</p>
<p><strong>Cross-attention adapters (Flamingo-style)</strong></p>
<p>Some architectures use cross-attention layers that let language model tokens attend to vision features rather than concatenating them directly. This approach:</p>
<ul>
<li>Keeps vision and language computations more separate</li>
<li>Allows selective attention to relevant image regions</li>
<li>Can be more parameter-efficient</li>
</ul>
<p><strong>Perceiver resampler</strong></p>
<p>Perceiver-based adapters use a fixed number of learnable query tokens that cross-attend to the variable-length vision features, producing a constant number of output tokens regardless of image resolution.</p>
<h3 id="language-model-backbone">Language Model Backbone</h3>
<p>The language model processes the projected vision tokens alongside text tokens. Any decoder-only transformer can serve this role:</p>
<ul>
<li>LLaMA family (7B, 13B, 70B)</li>
<li>Vicuna (instruction-tuned LLaMA)</li>
<li>Qwen (7B, 14B, 72B)</li>
<li>Phi-3 (3.8B)</li>
<li>Gemma (2B, 7B, 27B)</li>
<li>InternLM (7B, 20B)</li>
</ul>
<p>The vision tokens are typically inserted at the position of special <code>&lt;image&gt;</code> tokens in the input. The language model then processes the combined sequence autoregressively, attending to both vision and text tokens.</p>
<p><strong>Token sequence structure:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>[BOS] [system prompt tokens] [image_tok_1] [image_tok_2] ... [image_tok_N] [user prompt tokens] [assistant response]</span></span></code></pre>
<p>The number of image tokens depends on the vision encoder’s output and any token compression applied.</p>
<h3 id="position-encoding-for-images">Position Encoding for Images</h3>
<p>Standard language models use 1D position encoding. Images require 2D spatial information. VLMs handle this in several ways:</p>
<p><strong>Flattened 1D positions</strong>: Simply assign positions sequentially to flattened patch tokens. Row-major order: patch (0,0) gets position 0, patch (0,1) gets position 1, etc.</p>
<p><strong>Separate 2D position embeddings in ViT</strong>: The vision encoder learns its own 2D-aware position embeddings during pretraining. These spatial relationships are then encoded into the patch representations before projection.</p>
<p><strong>M-RoPE (Multimodal Rotary Position Embedding)</strong>: Qwen2-VL introduced M-RoPE, which encodes position information across text, images, and videos in a unified way. For images, it captures both spatial positions and temporal positions for video frames.</p>
<p><strong>Variable Visual Position Encoding (V2PE)</strong>: InternVL3 uses smaller, more flexible position increments for visual tokens, improving long-context understanding.</p>
<hr>
<h2 id="popular-vlm-architectures">Popular VLM Architectures</h2>
<h3 id="llava-family">LLaVA Family</h3>
<p>LLaVA (Large Language and Vision Assistant) established the dominant paradigm: frozen CLIP encoder + MLP projector + instruction-tuned LLM.</p>
<p><strong>LLaVA 1.0 (2023)</strong></p>
<ul>
<li>Vision: CLIP ViT-L/14 (frozen)</li>
<li>Projector: Linear layer</li>
<li>LLM: Vicuna-13B</li>
<li>Training: Two-stage (projector pretraining, then full fine-tuning)</li>
</ul>
<p><strong>LLaVA 1.5 (2023)</strong></p>
<ul>
<li>Projector: Two-layer MLP (significant improvement)</li>
<li>Higher resolution support: 336x336</li>
<li>Better instruction-following data</li>
</ul>
<p><strong>LLaVA-NeXT / LLaVA 1.6 (2024)</strong></p>
<ul>
<li>Dynamic high resolution: Up to 672x672</li>
<li>AnyRes: Handles varying aspect ratios by tiling</li>
<li>Multiple LLM backends (Vicuna, Mistral, Hermes)</li>
</ul>
<p><strong>Training process:</strong></p>
<p>Stage 1: Projector pretraining</p>
<ul>
<li>Freeze vision encoder and LLM</li>
<li>Train only the MLP projector</li>
<li>Use image-caption pairs</li>
<li>Goal: Align vision features with language embedding space</li>
</ul>
<p>Stage 2: Full fine-tuning</p>
<ul>
<li>Freeze vision encoder</li>
<li>Unfreeze projector and LLM</li>
<li>Train on visual instruction data</li>
<li>Goal: Learn to follow multimodal instructions</li>
</ul>
<h3 id="qwen-vl-qwen2-vl-qwen25-vl">Qwen-VL / Qwen2-VL / Qwen2.5-VL</h3>
<p>Alibaba’s Qwen vision models introduced Naive Dynamic Resolution, processing images at their native resolution rather than forcing fixed sizes.</p>
<p><strong>Key innovations:</strong></p>
<p><strong>Dynamic resolution</strong>: Images are converted to a variable number of visual tokens based on their actual dimensions. A small icon might use 256 tokens; a high-resolution document might use 4096+.</p>
<p><strong>M-RoPE (Multimodal RoPE)</strong>: Unified position encoding for text, images, and video. Enables the model to understand:</p>
<ul>
<li>Absolute text positions</li>
<li>2D spatial positions within images</li>
<li>Temporal positions across video frames</li>
</ul>
<p><strong>2D RoPE in ViT</strong>: The vision encoder itself uses 2D rotary position embeddings, enabling better adaptation to varying resolutions during inference.</p>
<p><strong>Model sizes</strong>: 2B, 8B, 72B parameters.</p>
<p><strong>Qwen2.5-VL improvements (2025)</strong>:</p>
<ul>
<li>ViT trained from scratch with native dynamic resolution</li>
<li>Window attention for reduced compute</li>
<li>Dynamic FPS sampling for video</li>
<li>Hours-long video understanding with second-level localization</li>
<li>Strong document/chart/table extraction</li>
</ul>
<h3 id="internvl">InternVL</h3>
<p>OpenGVLab’s InternVL scales both vision and language components, with InternViT reaching 6B parameters.</p>
<p><strong>Architecture</strong>: ViT-MLP-LLM paradigm with a pixel unshuffle operation that reduces visual tokens to 1/4 of the original count.</p>
<p><strong>InternVL 2.5 / 3.0 (2025)</strong>:</p>
<ul>
<li>Native Multimodal Pre-Training: Interleaves vision-language data with text corpora in a single pretraining stage (rather than adapting a text-only model)</li>
<li>Variable Visual Position Encoding (V2PE)</li>
<li>Dynamic High Resolution from InternVL 1.5</li>
<li>Mixed Preference Optimization for alignment</li>
</ul>
<p><strong>InternVL 3.5 (August 2025)</strong>:</p>
<ul>
<li>Cascade Reinforcement Learning for improved reasoning</li>
<li>Visual Resolution Router (ViR): Dynamically adjusts visual token resolution</li>
<li>Decoupled Vision-Language Deployment: Separate vision and language servers for async pipelining</li>
<li>4.05x inference speedup over InternVL3</li>
<li>State-of-the-art among open-source models</li>
</ul>
<h3 id="paligemma">PaliGemma</h3>
<p>Google’s PaliGemma combines SigLIP with Gemma in a straightforward architecture.</p>
<p><strong>Components</strong>:</p>
<ul>
<li>Vision: SigLIP-So400m (400M params)</li>
<li>Language: Gemma-2B</li>
<li>Projection: Linear layer (1152 to 2048 dimensions)</li>
</ul>
<p><strong>Resolution and tokens</strong>:</p>
<ul>
<li>Pretrained at 224x224, 448x448, or 896x896</li>
<li>Patch size: 14</li>
<li>Token counts: 256 (224px), 1024 (448px), 4096 (896px)</li>
</ul>
<p><strong>PaliGemma 2 (2024)</strong>:</p>
<ul>
<li>Upgraded to Gemma 2</li>
<li>Sizes: 3B, 10B, 28B</li>
<li>Multiple resolution support per model</li>
</ul>
<p><strong>Gemma 3 with vision (2025)</strong>:</p>
<ul>
<li>Custom SigLIP encoder</li>
<li>Pan and Scan algorithm for varying aspect ratios</li>
<li>896x896 fixed encoder input</li>
</ul>
<h3 id="phi-3-vision">Phi-3 Vision</h3>
<p>Microsoft’s Phi-3 Vision packs vision capabilities into a 4.2B parameter model.</p>
<p><strong>Architecture</strong>:</p>
<ul>
<li>Image encoder</li>
<li>Connector</li>
<li>Projector</li>
<li>Phi-3 Mini language model</li>
</ul>
<p><strong>Specifications</strong>:</p>
<ul>
<li>4.2B total parameters</li>
<li>128K context length</li>
<li>Inputs: Text and images</li>
</ul>
<p><strong>Strengths</strong>:</p>
<ul>
<li>Chart, graph, and table understanding</li>
<li>Document analysis</li>
<li>Small footprint suitable for edge deployment</li>
</ul>
<p><strong>Phi-3.5 Vision</strong>:</p>
<ul>
<li>Multi-frame capabilities (image comparison, video summarization)</li>
<li>Improved single-image benchmarks</li>
<li>Training: 6 days on 256 A100-80G GPUs with 500B tokens</li>
</ul>
<h3 id="cogvlm">CogVLM</h3>
<p>CogVLM introduces a Visual Expert module that adds trainable parameters to the language model specifically for processing vision features.</p>
<p><strong>Architecture difference</strong>: Instead of just projecting vision features into the LLM’s input space, CogVLM adds parallel QKV matrices and MLP layers for visual tokens at each transformer layer.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Standard layer:</span></span>
<span class="line"><span>  text_tokens ──▶ [QKV + MLP] ──▶ output</span></span>
<span class="line"><span></span></span>
<span class="line"><span>CogVLM layer:</span></span>
<span class="line"><span>  text_tokens  ──▶ [QKV_text + MLP_text]  ──┐</span></span>
<span class="line"><span>                                            ├──▶ output</span></span>
<span class="line"><span>  image_tokens ──▶ [QKV_vision + MLP_vision]──┘</span></span></code></pre>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Deep fusion of vision and language (not just early concatenation)</li>
<li>Preserves language model performance on text-only tasks</li>
<li>Visual expert parameters initialized from pretrained weights</li>
</ul>
<p><strong>Scale</strong>: CogVLM-17B has 10B vision parameters + 7B language parameters.</p>
<p><strong>CogVLM2 (2024)</strong>:</p>
<ul>
<li>Improved training recipes</li>
<li>Up to 1344x1344 input resolution</li>
</ul>
<h3 id="florence-2">Florence-2</h3>
<p>Microsoft’s Florence-2 takes a different approach: a unified sequence-to-sequence model for all vision tasks.</p>
<p><strong>Architecture</strong>:</p>
<ul>
<li>Vision encoder: DaViT (Dual Attention Vision Transformer)</li>
<li>Text processing: BART-style encoder-decoder</li>
<li>Sizes: 0.2B and 0.7B parameters</li>
</ul>
<p><strong>Key insight</strong>: All vision tasks (detection, segmentation, captioning, grounding) can be formulated as sequence-to-sequence problems. The model outputs text, including coordinate tokens for localization tasks.</p>
<p><strong>Training data</strong>: FLD-5B dataset with 5.4B annotations across 126M images (boxes, masks, captions, grounding).</p>
<p><strong>Performance</strong>: Despite small size, Florence-2 outperforms much larger models on zero-shot captioning. On COCO, the 232M model (score: 133) and 771M model (score: 135.6) both beat DeepMind’s 80B Flamingo.</p>
<hr>
<h2 id="why-vlm-inference-is-more-complex">Why VLM Inference is More Complex</h2>
<p>VLM inference presents challenges beyond text-only LLMs.</p>
<h3 id="variable-image-resolutions">Variable Image Resolutions</h3>
<p>Text inputs have predictable token counts (roughly 4 chars per token). Images vary wildly:</p>
<ul>
<li>Thumbnail: 224x224 = 256 tokens (with 14x14 patches)</li>
<li>Document: 896x896 = 4096 tokens</li>
<li>High-res photo with dynamic tiling: 10000+ tokens</li>
</ul>
<p>Dynamic resolution models like Qwen2-VL convert resolution directly to token count. Memory and compute scale accordingly.</p>
<h3 id="token-count-variability">Token Count Variability</h3>
<p>A batch of requests might contain:</p>
<ul>
<li>Request 1: 100 text tokens, no images</li>
<li>Request 2: 50 text tokens, 256 image tokens</li>
<li>Request 3: 200 text tokens, 2048 image tokens (high-res document)</li>
</ul>
<p>This variability complicates batching and memory allocation.</p>
<h3 id="image-preprocessing-overhead">Image Preprocessing Overhead</h3>
<p>Before the vision encoder runs, images require preprocessing:</p>
<ol>
<li><strong>Decode</strong>: Decompress JPEG/PNG to raw pixels</li>
<li><strong>Resize</strong>: Scale to encoder’s expected resolution</li>
<li><strong>Normalize</strong>: Convert to float, apply mean/std normalization (typically ImageNet stats: mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])</li>
<li><strong>Patch/tile</strong>: For dynamic resolution, split into tiles</li>
<li><strong>Tensor conversion</strong>: Move to GPU memory</li>
</ol>
<p>This preprocessing happens on CPU and can bottleneck throughput when image counts are high.</p>
<h3 id="memory-requirements">Memory Requirements</h3>
<p>High-resolution images consume memory at multiple stages:</p>
<ul>
<li>Raw pixels: 896x896x3 = 2.4MB per image</li>
<li>Vision encoder activations: Intermediate states during forward pass</li>
<li>Projected tokens: 4096 tokens x 4096 dim x 2 bytes (fp16) = 33MB per high-res image</li>
<li>KV cache: Each image token needs KV cache entries for all subsequent generation</li>
</ul>
<p>For a 72B model processing a high-res document, the image tokens alone can consume several GB of KV cache memory.</p>
<h3 id="batching-challenges">Batching Challenges</h3>
<p>Text-only continuous batching works because all tokens are homogeneous. With images:</p>
<ul>
<li>Vision encoder runs separately from LLM</li>
<li>Different images in a batch may have different resolutions</li>
<li>Prefill (processing prompt + images) is much heavier than decode (generating tokens)</li>
</ul>
<p><strong>vLLM’s solution</strong>: Hybrid parallelism where the vision encoder uses data parallelism (each GPU processes different images) while the LLM uses tensor parallelism. This avoids synchronization overhead during vision encoding.</p>
<p><strong>Prefix caching</strong>: vLLM V1 supports prefix caching for multimodal inputs, so repeated images don’t require re-encoding.</p>
<hr>
<h2 id="vlm-quantization">VLM Quantization</h2>
<p>Quantization reduces model size and speeds inference by using lower-precision weights and activations.</p>
<h3 id="quantizing-the-vision-encoder">Quantizing the Vision Encoder</h3>
<p>Vision encoders are often left at higher precision because:</p>
<ul>
<li>They’re smaller than the LLM (typically &lt; 1B params)</li>
<li>Visual features are sensitive to quantization artifacts</li>
<li>The encoder runs once per image (not autoregressive), so speed gains are less impactful</li>
</ul>
<p>However, for edge deployment, encoder quantization matters. Research shows:</p>
<ul>
<li>INT8 quantization typically maintains quality</li>
<li>INT4 can work with careful calibration</li>
<li>The vision modality is generally less sensitive than language</li>
</ul>
<h3 id="quantizing-the-language-model">Quantizing the Language Model</h3>
<p>Standard LLM quantization techniques apply:</p>
<ul>
<li><strong>GPTQ</strong>: Post-training quantization using calibration data</li>
<li><strong>AWQ</strong>: Activation-aware weight quantization</li>
<li><strong>GGUF</strong>: llama.cpp’s format with various quantization levels (Q4_K_M, Q5_K_S, etc.)</li>
<li><strong>bitsandbytes</strong>: 4-bit and 8-bit for training/inference</li>
</ul>
<p>The language model dominates parameter count (e.g., 72B LLM vs 400M vision encoder), so LLM quantization provides most of the size/speed benefits.</p>
<h3 id="joint-quantization-strategies">Joint Quantization Strategies</h3>
<p><strong>Q-VLM</strong> (NeurIPS 2024) proposes cross-layer dependency mining for VLM quantization. Rather than quantizing layer-by-layer, it considers how quantization errors propagate through the full vision-language pipeline.</p>
<p>Results: 2.78x memory compression, 1.44x speedup on 13B LLaVA without performance degradation.</p>
<p><strong>MBQ (Modality-Balanced Quantization)</strong> (CVPR 2025) observes that language modules are more sensitive to quantization than vision modules. Their approach:</p>
<ul>
<li>4-bit quantization for vision (ViT modules)</li>
<li>8-bit quantization for language</li>
<li>Up to 4% improvement under W3A16 and 11% under W4A8 vs uniform quantization</li>
</ul>
<h3 id="quality-tradeoffs">Quality Tradeoffs</h3>
<p>Quantization affects different VLM capabilities differently:</p>
<ul>
<li>OCR accuracy can degrade with aggressive quantization</li>
<li>Fine-grained visual details may be lost</li>
<li>Reasoning over complex diagrams is sensitive</li>
<li>General image description is more robust</li>
</ul>
<p>Recommendation: Benchmark your specific use case. A model that’s fine for photo captioning might fail on document analysis when heavily quantized.</p>
<hr>
<h2 id="practical-vlm-inference">Practical VLM Inference</h2>
<h3 id="hugging-face-transformers">Hugging Face Transformers</h3>
<p>The most straightforward approach for experimentation.</p>
<p><strong>LLaVA example:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> LlavaForConditionalGeneration</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> PIL </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Image</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> requests</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model_id </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;llava-hf/llava-1.5-7b-hf&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Load model and processor</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LlavaForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model_id,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(model_id)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prepare inputs</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">url </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">open</span><span style="color:var(--astro-code-token-punctuation)">(requests.</span><span style="color:var(--astro-code-token-function)">get</span><span style="color:var(--astro-code-token-punctuation)">(url, stream</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">).raw)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">conversation </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            {</span><span style="color:var(--astro-code-token-string-expression)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            {</span><span style="color:var(--astro-code-token-string-expression)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;What animal is in this image? Describe it briefly.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> processor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">apply_chat_template</span><span style="color:var(--astro-code-token-punctuation)">(conversation, add_generation_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> processor</span><span style="color:var(--astro-code-token-punctuation)">(images</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">image, text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">prompt, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(model.device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Generate</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">, do_sample</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> processor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(output[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">], skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(response)</span></span></code></pre>
<p><strong>Qwen2-VL example:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Qwen2VLForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> PIL </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Image</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Qwen2VLForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;Qwen/Qwen2-VL-7B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Qwen/Qwen2-VL-7B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Qwen2-VL supports dynamic resolution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;document.png&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Any resolution</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">messages </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            {</span><span style="color:var(--astro-code-token-string-expression)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> image</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">            {</span><span style="color:var(--astro-code-token-string-expression)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Extract all text from this document.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">text </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> processor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">apply_chat_template</span><span style="color:var(--astro-code-token-punctuation)">(messages, tokenize</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">, add_generation_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> processor</span><span style="color:var(--astro-code-token-punctuation)">(text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[text], images</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[image], return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(model.device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">generated_ids </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> processor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">batch_decode</span><span style="color:var(--astro-code-token-punctuation)">(generated_ids, skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p><strong>Pipeline API (simpler):</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> pipeline</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">pipe </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipeline</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;image-text-to-text&quot;</span><span style="color:var(--astro-code-token-punctuation)">, model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;llava-hf/llava-1.5-7b-hf&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> pipe</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    images</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;https://example.com/image.jpg&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Describe this image in detail.&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">200</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<h3 id="llamacpp-multimodal">llama.cpp Multimodal</h3>
<p>llama.cpp supports vision models via the <code>mmproj</code> (multimodal projector) architecture.</p>
<p><strong>Installation:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># macOS</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">brew</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> llama.cpp</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or build from source</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">git</span><span style="color:var(--astro-code-token-string)"> clone</span><span style="color:var(--astro-code-token-string)"> https://github.com/ggml-org/llama.cpp</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">cd</span><span style="color:var(--astro-code-token-string)"> llama.cpp</span><span style="color:var(--astro-code-token-punctuation)"> &amp;&amp;</span><span style="color:var(--astro-code-token-function)"> make</span></span></code></pre>
<p><strong>Download model and projector:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Pre-quantized models available from ggml-org</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># https://huggingface.co/collections/ggml-org/multimodal-ggufs</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Example: Qwen2.5-VL-7B</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">wget</span><span style="color:var(--astro-code-token-string)"> https://huggingface.co/ggml-org/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/qwen2.5-vl-7b-instruct-q4_k_m.gguf</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">wget</span><span style="color:var(--astro-code-token-string)"> https://huggingface.co/ggml-org/Qwen2.5-VL-7B-Instruct-GGUF/resolve/main/qwen2.5-vl-7b-instruct-vision.gguf</span></span></code></pre>
<p><strong>Run inference:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">./llama-mtmd-cli</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -m</span><span style="color:var(--astro-code-token-string)"> qwen2.5-vl-7b-instruct-q4_k_m.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --mmproj</span><span style="color:var(--astro-code-token-string)"> qwen2.5-vl-7b-instruct-vision.gguf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -p</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Describe this image:&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --image</span><span style="color:var(--astro-code-token-string)"> photo.jpg</span></span></code></pre>
<p><strong>Supported models:</strong></p>
<ul>
<li>Gemma 3 (4B, 12B, 27B)</li>
<li>Qwen 2 VL (2B, 7B)</li>
<li>Qwen 2.5 VL (3B, 7B, 32B, 72B)</li>
<li>SmolVLM variants</li>
<li>Pixtral 12B</li>
<li>InternVL 2.5</li>
<li>Mistral Small 3.1 24B</li>
</ul>
<p><strong>Limitations</strong>: Currently supports static images only. Video processing is not yet implemented.</p>
<h3 id="vllm">vLLM</h3>
<p>vLLM provides high-throughput VLM serving with continuous batching.</p>
<p><strong>Installation:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> vllm</span></span></code></pre>
<p><strong>Offline inference:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">multimodal </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> MultiModalData</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;llava-hf/llava-1.5-7b-hf&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;USER: &lt;image&gt;\nWhat is in this image?\nASSISTANT:&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sampling_params </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SamplingParams</span><span style="color:var(--astro-code-token-punctuation)">(temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.0</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Single image</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;prompt&quot;</span><span style="color:var(--astro-code-token-punctuation)">: prompt,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;multi_modal_data&quot;</span><span style="color:var(--astro-code-token-punctuation)">: {</span><span style="color:var(--astro-code-token-string-expression)">&quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;path/to/image.jpg&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    },</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    sampling_params</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].text)</span></span></code></pre>
<p><strong>Server deployment:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Start server</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> vllm.entrypoints.openai.api_server</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model</span><span style="color:var(--astro-code-token-string)"> llava-hf/llava-1.5-7b-hf</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --chat-template</span><span style="color:var(--astro-code-token-string)"> llava</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Query via OpenAI-compatible API</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> http://localhost:8000/v1/chat/completions</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -H</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Content-Type: application/json&quot;</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    -d</span><span style="color:var(--astro-code-token-string-expression)"> &#39;{</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;model&quot;: &quot;llava-hf/llava-1.5-7b-hf&quot;,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &quot;messages&quot;: [</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                &quot;role&quot;: &quot;user&quot;,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                &quot;content&quot;: [</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                    {&quot;type&quot;: &quot;text&quot;, &quot;text&quot;: &quot;What is in this image?&quot;},</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                    {&quot;type&quot;: &quot;image_url&quot;, &quot;image_url&quot;: {&quot;url&quot;: &quot;https://example.com/image.jpg&quot;}}</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            }</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    }&#39;</span></span></code></pre>
<p><strong>Performance tuning:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Use data parallelism for vision encoder</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> vllm.entrypoints.openai.api_server</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --model</span><span style="color:var(--astro-code-token-string)"> Qwen/Qwen2-VL-7B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">    --mm-encoder-tp-mode</span><span style="color:var(--astro-code-token-string)"> data</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Limit image tokens to control memory</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">--limit-mm-per-prompt</span><span style="color:var(--astro-code-token-string-expression)"> &#39;{&quot;image&quot;: 2048}&#39;</span></span></code></pre>
<p><strong>Supported models (partial list)</strong>:</p>
<ul>
<li>LLaVA 1.5, LLaVA-NeXT</li>
<li>Qwen-VL, Qwen2-VL</li>
<li>InternVL, InternVL2</li>
<li>PaliGemma</li>
<li>Phi-3 Vision</li>
<li>BLIP-2</li>
<li>Chameleon</li>
</ul>
<h3 id="ollama">Ollama</h3>
<p>Ollama provides the simplest local VLM experience.</p>
<p><strong>Installation:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># macOS/Linux</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> -fsSL</span><span style="color:var(--astro-code-token-string)"> https://ollama.com/install.sh</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-function)"> sh</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Or download from ollama.com</span></span></code></pre>
<p><strong>Run vision models:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Pull a vision model</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> llava</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Interactive mode</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> llava</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">&gt;&gt;&gt; [paste image path or URL]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">&gt;&gt;&gt; </span><span style="color:var(--astro-code-token-function)">What</span><span style="color:var(--astro-code-token-function)">&#39;s in this image?</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)"># Or with llava-phi3 (smaller, faster)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama pull llava-phi3</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama run llava-phi3</span></span></code></pre>
<p><strong>API usage:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> ollama</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">response </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ollama</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">chat</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;llava&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[{</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;role&#39;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&#39;user&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;content&#39;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&#39;Describe this image&#39;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;images&#39;</span><span style="color:var(--astro-code-token-punctuation)">: [</span><span style="color:var(--astro-code-token-string-expression)">&#39;./photo.jpg&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(response[</span><span style="color:var(--astro-code-token-string-expression)">&#39;message&#39;</span><span style="color:var(--astro-code-token-punctuation)">][</span><span style="color:var(--astro-code-token-string-expression)">&#39;content&#39;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span></code></pre>
<p><strong>Available vision models on Ollama:</strong></p>
<ul>
<li><code>llava</code> (7B and 13B variants)</li>
<li><code>llava-phi3</code> (3.8B, faster)</li>
<li><code>bakllava</code></li>
<li><code>moondream</code> (smaller, 1.8B)</li>
</ul>
<hr>
<h2 id="code-examples-for-common-tasks">Code Examples for Common Tasks</h2>
<h3 id="document-ocr">Document OCR</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Qwen2VLForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> PIL </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Image</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Qwen2VLForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;Qwen/Qwen2-VL-7B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Qwen/Qwen2-VL-7B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Use high resolution for documents</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;invoice.png&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">messages </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-punctuation)">{</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        {</span><span style="color:var(--astro-code-token-string-expression)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> image</span><span style="color:var(--astro-code-token-punctuation)">},</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        {</span><span style="color:var(--astro-code-token-string-expression)">&quot;type&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Extract all text from this invoice. Format as structured data.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ]</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">}</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> processor</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">processor.</span><span style="color:var(--astro-code-token-function)">apply_chat_template</span><span style="color:var(--astro-code-token-punctuation)">(messages, tokenize</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">, add_generation_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    images</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[image],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(model.device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(processor.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(output[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">], skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">))</span></span></code></pre>
<h3 id="multi-image-comparison">Multi-Image Comparison</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LlavaNextProcessor</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> LlavaNextForConditionalGeneration</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> PIL </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Image</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LlavaNextForConditionalGeneration</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;llava-hf/llava-v1.6-mistral-7b-hf&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float16,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    device_map</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> LlavaNextProcessor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;llava-hf/llava-v1.6-mistral-7b-hf&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;product_v1.jpg&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;product_v2.jpg&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;[INST] &lt;image&gt;\n&lt;image&gt;\nCompare these two product images. What are the differences? [/INST]&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> processor</span><span style="color:var(--astro-code-token-punctuation)">(prompt, [image1, image2], return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(model.device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-punctuation)">inputs, max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">300</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(processor.</span><span style="color:var(--astro-code-token-function)">decode</span><span style="color:var(--astro-code-token-punctuation)">(output[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">], skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">))</span></span></code></pre>
<h3 id="chart-analysis">Chart Analysis</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Using Florence-2 for detailed chart understanding</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> PIL </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Image</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoModelForCausalLM</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    &quot;microsoft/Florence-2-large&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    torch_dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;auto&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    trust_remote_code</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">processor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> AutoProcessor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">from_pretrained</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;microsoft/Florence-2-large&quot;</span><span style="color:var(--astro-code-token-punctuation)">, trust_remote_code</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Image</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">open</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;sales_chart.png&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Florence-2 uses task-specific prompts</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prompt </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;&lt;MORE_DETAILED_CAPTION&gt;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">inputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> processor</span><span style="color:var(--astro-code-token-punctuation)">(text</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">prompt, images</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">image, return_tensors</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;pt&quot;</span><span style="color:var(--astro-code-token-punctuation)">).</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;cuda&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">generated_ids </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    input_ids</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">inputs[</span><span style="color:var(--astro-code-token-string-expression)">&quot;input_ids&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    pixel_values</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">inputs[</span><span style="color:var(--astro-code-token-string-expression)">&quot;pixel_values&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    max_new_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_beams</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">3</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> processor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">batch_decode</span><span style="color:var(--astro-code-token-punctuation)">(generated_ids, skip_special_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(result)</span></span></code></pre>
<h3 id="batch-processing-with-vllm">Batch Processing with vLLM</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> vllm </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> SamplingParams</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">llm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> LLM</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;Qwen/Qwen2-VL-7B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    limit_mm_per_prompt</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">sampling_params </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SamplingParams</span><span style="color:var(--astro-code-token-punctuation)">(temperature</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.0</span><span style="color:var(--astro-code-token-punctuation)">, max_tokens</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prepare batch of requests</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">image_dir </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;./images&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">requests </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> []</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> img_file </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> os</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">listdir</span><span style="color:var(--astro-code-token-punctuation)">(image_dir):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-foreground)"> img_file</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">endswith</span><span style="color:var(--astro-code-token-punctuation)">((</span><span style="color:var(--astro-code-token-string-expression)">&#39;.jpg&#39;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&#39;.png&#39;</span><span style="color:var(--astro-code-token-punctuation)">)):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        requests</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">append</span><span style="color:var(--astro-code-token-punctuation)">({</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;prompt&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;USER: &lt;image&gt;\nDescribe this image briefly.\nASSISTANT:&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &quot;multi_modal_data&quot;</span><span style="color:var(--astro-code-token-punctuation)">: {</span><span style="color:var(--astro-code-token-string-expression)">&quot;image&quot;</span><span style="color:var(--astro-code-token-punctuation)">: os.path.</span><span style="color:var(--astro-code-token-function)">join</span><span style="color:var(--astro-code-token-punctuation)">(image_dir, img_file)}</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        })</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Process batch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> llm</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">generate</span><span style="color:var(--astro-code-token-punctuation)">(requests, sampling_params)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> i</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> output </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(outputs):</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Image </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">i</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">output.outputs[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].text</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<h2 id="references">References</h2>
<h3 id="papers">Papers</h3>
<ul>
<li>Dosovitskiy et al. “An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale” (ViT, 2020)</li>
<li>Radford et al. “Learning Transferable Visual Models From Natural Language Supervision” (CLIP, 2021)</li>
<li>Zhai et al. “Sigmoid Loss for Language Image Pre-Training” (SigLIP, ICCV 2023) - <a href="https://arxiv.org/abs/2303.15343">https://arxiv.org/abs/2303.15343</a></li>
<li>Liu et al. “Visual Instruction Tuning” (LLaVA, NeurIPS 2023)</li>
<li>Wang et al. “Qwen2-VL: Enhancing Vision-Language Model’s Perception of the World at Any Resolution” (2024) - <a href="https://arxiv.org/abs/2409.12191">https://arxiv.org/abs/2409.12191</a></li>
<li>Qwen Team. “Qwen2.5-VL Technical Report” (2025) - <a href="https://arxiv.org/abs/2502.13923">https://arxiv.org/abs/2502.13923</a></li>
<li>Chen et al. “InternVL: Scaling up Vision Foundation Models” (CVPR 2024)</li>
<li>OpenGVLab. “InternVL3” (2025) - <a href="https://internvl.github.io/blog/2025-04-11-InternVL-3.0/">https://internvl.github.io/blog/2025-04-11-InternVL-3.0/</a></li>
<li>Wang et al. “CogVLM: Visual Expert for Pretrained Language Models” (NeurIPS 2024) - <a href="https://arxiv.org/abs/2311.03079">https://arxiv.org/abs/2311.03079</a></li>
<li>Xiao et al. “Florence-2: Advancing a Unified Representation for a Variety of Vision Tasks” (2024)</li>
<li>Shao et al. “Q-VLM: Post-training Quantization for Large Vision-Language Models” (NeurIPS 2024) - <a href="https://arxiv.org/abs/2410.08119">https://arxiv.org/abs/2410.08119</a></li>
<li>Li et al. “MBQ: Modality-Balanced Quantization for Large Vision-Language Models” (CVPR 2025)</li>
<li>Vasu et al. “FastVLM: Efficient Vision Encoding for Vision Language Models” (CVPR 2025)</li>
</ul>
<h3 id="model-resources">Model Resources</h3>
<ul>
<li>LLaVA: <a href="https://llava-vl.github.io/">https://llava-vl.github.io/</a></li>
<li>Qwen2-VL: <a href="https://huggingface.co/Qwen/Qwen2-VL-7B-Instruct">https://huggingface.co/Qwen/Qwen2-VL-7B-Instruct</a></li>
<li>Qwen2.5-VL: <a href="https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct">https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct</a></li>
<li>InternVL: <a href="https://github.com/OpenGVLab/InternVL">https://github.com/OpenGVLab/InternVL</a></li>
<li>PaliGemma: <a href="https://ai.google.dev/gemma/docs/paligemma">https://ai.google.dev/gemma/docs/paligemma</a></li>
<li>Florence-2: <a href="https://huggingface.co/microsoft/Florence-2-large">https://huggingface.co/microsoft/Florence-2-large</a></li>
<li>Phi-3 Vision: <a href="https://huggingface.co/microsoft/Phi-3-vision-128k-instruct">https://huggingface.co/microsoft/Phi-3-vision-128k-instruct</a></li>
<li>CogVLM: <a href="https://github.com/THUDM/CogVLM">https://github.com/THUDM/CogVLM</a></li>
</ul>
<h3 id="inference-tools">Inference Tools</h3>
<ul>
<li>Hugging Face Transformers: <a href="https://huggingface.co/docs/transformers/en/model_doc/llava">https://huggingface.co/docs/transformers/en/model_doc/llava</a></li>
<li>vLLM: <a href="https://docs.vllm.ai/en/latest/models/supported_models/">https://docs.vllm.ai/en/latest/models/supported_models/</a></li>
<li>llama.cpp multimodal: <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/multimodal.md">https://github.com/ggml-org/llama.cpp/blob/master/docs/multimodal.md</a></li>
<li>Ollama vision models: <a href="https://ollama.com/search?c=vision">https://ollama.com/search?c=vision</a></li>
</ul>
<h3 id="tutorials-and-guides">Tutorials and Guides</h3>
<ul>
<li>Hugging Face VLM blog: <a href="https://huggingface.co/blog/vlms">https://huggingface.co/blog/vlms</a></li>
<li>Hugging Face VLMs 2025: <a href="https://huggingface.co/blog/vlms-2025">https://huggingface.co/blog/vlms-2025</a></li>
<li>SigLIP 2 announcement: <a href="https://huggingface.co/blog/siglip2">https://huggingface.co/blog/siglip2</a></li>
<li>PaliGemma architecture: <a href="https://developers.googleblog.com/gemma-explained-paligemma-architecture/">https://developers.googleblog.com/gemma-explained-paligemma-architecture/</a></li>
<li>LLaVA architecture deep dive: <a href="https://learnopencv.com/llava-training-a-visual-assistant/">https://learnopencv.com/llava-training-a-visual-assistant/</a></li>
</ul>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Models &amp; Training</category>
            <category>Multimodal</category>
            <category>Architecture</category>
            <category>Training</category>
            <enclosure length="79540" type="image/png" url="https://blog.ecitis.org/open-graph/vision-language-models.png"/>
        </item>
        <item>
            <title><![CDATA[Understanding GPUs and Parallel Computing]]></title>
            <link>https://blog.ecitis.org/understanding-gpus/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/understanding-gpus/</guid>
            <pubDate>Fri, 19 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Build a mental model of GPU architecture, memory, kernels, and parallel execution for modern machine-learning workloads.]]></description>
            <content:encoded><![CDATA[<h2 id="why-this-guide">Why this guide</h2>
<p>Modern research in machine learning and artificial intelligence requires understanding the fundamental architecture that powers computational workloads. Whether you’re training large language models, processing computer vision tasks, or running complex simulations, Graphics Processing Units (GPUs) have become the backbone of high-performance computing. This comprehensive guide explores the intricate details of GPU architecture, parallel computing paradigms, and practical implementation strategies for research-grade applications on state-of-the-art hardware.</p>
<p>Understanding these concepts isn’t just about knowing how to run code faster. It’s about fundamentally rethinking how we approach computational problems. The shift from CPU-centric to GPU-accelerated computing enabled breakthroughs in deep learning, scientific computing, and data processing that were previously impossible.</p>
<hr>
<h2 id="what-is-a-computer-understanding-the-foundation">What is a Computer? Understanding the Foundation</h2>
<h3 id="hardware-software-the-fundamental-duality">Hardware + Software: The Fundamental Duality</h3>
<p>At its most basic level, a computer is a sophisticated machine that transforms electrical signals into meaningful computation through the interaction of hardware and software components. The hardware provides the physical substrate (transistors, memory cells, interconnects) while software provides the logical instructions that orchestrate these components into useful work.</p>
<p>This duality is crucial to understand because modern GPU programming requires intimate knowledge of both layers. Unlike traditional CPU programming where hardware details are often abstracted away, GPU programming demands awareness of the underlying architecture to achieve optimal performance.</p>
<p>The hardware layer consists of:</p>
<ul>
<li><strong>Processing units</strong>: CPU cores, GPU cores, specialized accelerators</li>
<li><strong>Memory hierarchy</strong>: Registers, caches, main memory, storage</li>
<li><strong>Interconnects</strong>: Buses, networks, on-chip communication paths</li>
<li><strong>Control systems</strong>: Instruction decoders, schedulers, memory controllers</li>
</ul>
<p>The software layer encompasses:</p>
<ul>
<li><strong>System software</strong>: Operating systems, drivers, runtime libraries</li>
<li><strong>Development tools</strong>: Compilers, debuggers, profilers</li>
<li><strong>Application software</strong>: Your actual programs and algorithms</li>
<li><strong>Middleware</strong>: Libraries, frameworks, abstraction layers</li>
</ul>
<h3 id="how-is-computation-performed">How is Computation Performed?</h3>
<p>Computation at the hardware level involves the coordinated execution of billions of transistor switches that can be in one of two states: conducting (1) or non-conducting (0). These binary states form the foundation of all digital computation through Boolean algebra and logical operations.</p>
<p>The basic computation cycle follows the von Neumann architecture:</p>
<ol>
<li><strong>Fetch</strong>: Retrieve instruction from memory</li>
<li><strong>Decode</strong>: Interpret what the instruction means</li>
<li><strong>Execute</strong>: Perform the required operation</li>
<li><strong>Store</strong>: Write results back to memory</li>
</ol>
<p>This cycle repeats billions of times per second, with modern processors executing multiple instructions simultaneously through techniques like:</p>
<ul>
<li><strong>Pipelining</strong>: Overlapping instruction execution stages</li>
<li><strong>Superscalar execution</strong>: Multiple execution units working in parallel</li>
<li><strong>Out-of-order execution</strong>: Reordering instructions for efficiency</li>
<li><strong>Speculative execution</strong>: Predicting and pre-executing likely code paths</li>
</ul>
<hr>
<h2 id="cpus-and-serial-computing-the-traditional-approach">CPUs and Serial Computing: The Traditional Approach</h2>
<h3 id="single-core-architecture-and-sequential-execution">Single-Core Architecture and Sequential Execution</h3>
<p>Central Processing Units (CPUs) were historically designed around the principle of sequential execution: processing one instruction at a time in a predetermined order. This design philosophy prioritizes:</p>
<ul>
<li><strong>Low latency</strong>: Minimize time between instruction issue and completion</li>
<li><strong>Complex control logic</strong>: Handle arbitrary branching, exceptions, and interrupts</li>
<li><strong>Large caches</strong>: Reduce memory access latency through prediction and prefetching</li>
<li><strong>Sophisticated branch prediction</strong>: Anticipate program flow to maintain instruction pipeline efficiency</li>
</ul>
<p>A typical CPU core contains:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>┌─────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                CPU Core Architecture                │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  Instruction Fetch Unit                             │</span></span>
<span class="line"><span>│  ├─ Branch Predictor                                │</span></span>
<span class="line"><span>│  ├─ Instruction Cache (L1-I)                        │</span></span>
<span class="line"><span>│  └─ Prefetch Buffer                                 │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  Decode and Dispatch                                │</span></span>
<span class="line"><span>│  ├─ Instruction Decoder                             │</span></span>
<span class="line"><span>│  ├─ Micro-op Cache                                  │</span></span>
<span class="line"><span>│  └─ Reorder Buffer                                  │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  Execution Units                                    │</span></span>
<span class="line"><span>│  ├─ Integer ALU (multiple units)                    │</span></span>
<span class="line"><span>│  ├─ Floating Point Unit                             │</span></span>
<span class="line"><span>│  ├─ Vector/SIMD Unit                                │</span></span>
<span class="line"><span>│  └─ Load/Store Unit                                 │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  Memory Subsystem                                   │</span></span>
<span class="line"><span>│  ├─ L1 Data Cache                                   │</span></span>
<span class="line"><span>│  ├─ L2 Cache                                        │</span></span>
<span class="line"><span>│  └─ Memory Management Unit                          │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────┘</span></span></code></pre>
<h3 id="multi-core-cpus-parallel-but-still-serial">Multi-core CPUs: Parallel But Still Serial</h3>
<p>The introduction of multi-core CPUs represented the first major shift toward parallelism in mainstream computing. However, each core still operates on the same serial computing principles; they provide multiple independent serial processors on the same chip.</p>
<p>Multi-core systems excel at:</p>
<ul>
<li><strong>Task-level parallelism</strong>: Running different programs simultaneously</li>
<li><strong>Coarse-grained parallelism</strong>: Dividing work into large, independent chunks</li>
<li><strong>Thread-level parallelism</strong>: Multiple execution contexts with shared memory</li>
</ul>
<p>However, they face fundamental limitations:</p>
<ul>
<li><strong>Amdahl’s Law</strong>: The sequential portions of code limit overall speedup</li>
<li><strong>Memory bandwidth</strong>: Multiple cores competing for the same memory subsystem</li>
<li><strong>Cache coherence</strong>: Overhead of maintaining consistent data across cores</li>
<li><strong>Load balancing</strong>: Difficulty in evenly distributing work across cores</li>
</ul>
<strong>Code Example: Multi-core CPU Utilization (Python)</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> multiprocessing </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> mp</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> cpu_intensive_task</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">data_chunk</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Simulate CPU-intensive computation on a data chunk&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zeros_like</span><span style="color:var(--astro-code-token-punctuation)">(data_chunk)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">len</span><span style="color:var(--astro-code-token-punctuation)">(data_chunk)):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Simulate complex computation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        result</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sin</span><span style="color:var(--astro-code-token-punctuation)">(data_chunk[i])</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cos</span><span style="color:var(--astro-code-token-punctuation)">(data_chunk[i])</span><span style="color:var(--astro-code-token-keyword)"> +</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sqrt</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">abs</span><span style="color:var(--astro-code-token-punctuation)">(data_chunk[i]))</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> result</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> serial_computation</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">data</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Traditional serial computation&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> cpu_intensive_task</span><span style="color:var(--astro-code-token-punctuation)">(data)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    end_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> result</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> end_time </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> parallel_computation</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">data</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> num_cores</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Multi-core parallel computation&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Split data into chunks</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    chunk_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> len</span><span style="color:var(--astro-code-token-punctuation)">(data)</span><span style="color:var(--astro-code-token-keyword)"> //</span><span style="color:var(--astro-code-foreground)"> num_cores</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    chunks </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [data</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)">i </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> chunk_size</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> for</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-function)">len</span><span style="color:var(--astro-code-token-punctuation)">(data), chunk_size)</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Process chunks in parallel</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> mp</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Pool</span><span style="color:var(--astro-code-token-punctuation)">(processes</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">num_cores)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> pool</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        results </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> pool</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">map</span><span style="color:var(--astro-code-token-punctuation)">(cpu_intensive_task, chunks)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Combine results</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    final_result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">concatenate</span><span style="color:var(--astro-code-token-punctuation)">(results)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    end_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> final_result</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> end_time </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Example usage</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-token-constant)"> __name__</span><span style="color:var(--astro-code-token-keyword)"> ==</span><span style="color:var(--astro-code-token-string-expression)"> &quot;__main__&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Generate test data</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    data_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1000000</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    test_data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">random</span><span style="color:var(--astro-code-token-punctuation)">(data_size)</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 100</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Compare serial vs parallel performance</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    num_cores </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mp</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cpu_count</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Available CPU cores: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">num_cores</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Serial execution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    serial_result</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> serial_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> serial_computation</span><span style="color:var(--astro-code-token-punctuation)">(test_data)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Serial execution time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">serial_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> seconds&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Parallel execution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    parallel_result</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> parallel_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> parallel_computation</span><span style="color:var(--astro-code-token-punctuation)">(test_data, num_cores)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Parallel execution time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">parallel_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> seconds&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Speedup: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">serial_time </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> parallel_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<hr>
<figure><figcaption><strong>CPU vs GPU Architecture Comparison</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/understanding-gpus-0.webp" width="512" height="468" alt="CPU vs GPU Architecture Comparison" loading="lazy" decoding="async"></figure>
<h2 id="gpus-and-parallel-computing-the-paradigm-shift">GPUs and Parallel Computing: The Paradigm Shift</h2>
<h3 id="understanding-parallel-computing-fundamentals">Understanding Parallel Computing Fundamentals</h3>
<p>Graphics Processing Units represent a fundamental departure from CPU design philosophy. Instead of optimizing for serial performance with complex control logic, GPUs are designed around the principle of <strong>massive parallelism</strong>: executing thousands of simple operations simultaneously.</p>
<p>This architectural difference stems from their original purpose: rendering graphics. Graphics operations are inherently parallel; each pixel can be processed independently, and the same operations (like texture sampling, lighting calculations, or color transformations) are applied to millions of pixels simultaneously.</p>
<p>The key philosophical differences between CPU and GPU design:</p>



































<div class="table-wrap"><table><thead><tr><th>Aspect</th><th>CPU</th><th>GPU</th></tr></thead><tbody><tr><td><strong>Design Goal</strong></td><td>Minimize latency for complex tasks</td><td>Maximize throughput for simple tasks</td></tr><tr><td><strong>Core Count</strong></td><td>Few (2-64) powerful cores</td><td>Thousands of simple cores</td></tr><tr><td><strong>Memory</strong></td><td>Large caches, complex hierarchy</td><td>High bandwidth, simpler hierarchy</td></tr><tr><td><strong>Control Logic</strong></td><td>Complex branch prediction, out-of-order</td><td>Simple, in-order execution</td></tr><tr><td><strong>Best For</strong></td><td>Sequential code, complex branching</td><td>Parallel code, regular patterns</td></tr></tbody></table></div>
<h3 id="simd-vs-simt-two-approaches-to-parallelism">SIMD vs SIMT: Two Approaches to Parallelism</h3>
<p>Understanding the distinction between SIMD (Single Instruction, Multiple Data) and SIMT (Single Instruction, Multiple Threads) is crucial for effective GPU programming.</p>
<h4 id="simd---single-instruction-multiple-data">SIMD - Single Instruction Multiple Data</h4>
<p>SIMD represents the traditional approach to data parallelism found in CPU vector units (like AVX-512) and some GPU architectures. In SIMD:</p>
<ul>
<li><strong>Single control unit</strong>: One instruction decoder feeds multiple execution units</li>
<li><strong>Lockstep execution</strong>: All units execute the same instruction simultaneously</li>
<li><strong>Data-level parallelism</strong>: Each unit operates on different data elements</li>
<li><strong>Rigid structure</strong>: All units must execute the same operation or remain idle</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> demonstrate_simd_concept</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Demonstrate SIMD-style operations using NumPy&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Create large arrays</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">random</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">random</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # SIMD-style operation: same instruction applied to all elements</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # This gets compiled to vectorized instructions (AVX, SSE, etc.)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b  </span><span style="color:var(--astro-code-token-comment)"># Single instruction, multiple data elements</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Element-wise operations are also SIMD-friendly</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    complex_result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sin</span><span style="color:var(--astro-code-token-punctuation)">(a)</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cos</span><span style="color:var(--astro-code-token-punctuation)">(b)</span><span style="color:var(--astro-code-token-keyword)"> +</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sqrt</span><span style="color:var(--astro-code-token-punctuation)">(np.</span><span style="color:var(--astro-code-token-function)">abs</span><span style="color:var(--astro-code-token-punctuation)">(a </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-punctuation)"> b))</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> result</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> complex_result</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Contrast with scalar operations</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> scalar_equivalent</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Show what the SIMD operation looks like in scalar form&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">random</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">random</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zeros</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1000000</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # This would be much slower - one operation at a time</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-function)">len</span><span style="color:var(--astro-code-token-punctuation)">(a)):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        result</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> =</span><span style="color:var(--astro-code-foreground)"> a</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> +</span><span style="color:var(--astro-code-foreground)"> b</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> result</span></span></code></pre>
<h4 id="simt---single-instruction-multiple-threads">SIMT - Single Instruction Multiple Threads</h4>
<p>SIMT, pioneered by NVIDIA’s CUDA architecture, provides a more flexible approach:</p>
<ul>
<li><strong>Thread-level parallelism</strong>: Each thread has its own program counter and registers</li>
<li><strong>Flexible execution</strong>: Threads can follow different execution paths (with caveats)</li>
<li><strong>Warp-based execution</strong>: Threads are grouped into warps (typically 32 threads) for execution</li>
<li><strong>Dynamic scheduling</strong>: Hardware scheduler manages thread execution</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>// CUDA kernel demonstrating SIMT execution</span></span>
<span class="line"><span>__global__ void simt_vector_add(float* a, float* b, float* c, int n) {</span></span>
<span class="line"><span>    // Each thread computes its unique index</span></span>
<span class="line"><span>    int tid = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Boundary check - threads can have different execution paths</span></span>
<span class="line"><span>    if (tid &lt; n) {</span></span>
<span class="line"><span>        c[tid] = a[tid] + b[tid];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Conditional execution - some threads may execute this, others may not</span></span>
<span class="line"><span>        if (a[tid] &gt; 0.5f) {</span></span>
<span class="line"><span>            c[tid] *= 2.0f;</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>    // Threads that fail the boundary check essentially become idle</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="task-parallelism-vs-warp-execution">Task Parallelism vs Warp Execution</h3>
<p>GPUs implement parallelism through a hierarchical execution model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>Grid (entire kernel launch)</span></span>
<span class="line"><span>├─ Block 0</span></span>
<span class="line"><span>│  ├─ Warp 0 (threads 0-31)</span></span>
<span class="line"><span>│  ├─ Warp 1 (threads 32-63)</span></span>
<span class="line"><span>│  └─ ...</span></span>
<span class="line"><span>├─ Block 1</span></span>
<span class="line"><span>│  ├─ Warp 0 (threads 0-31)</span></span>
<span class="line"><span>│  └─ ...</span></span>
<span class="line"><span>└─ ...</span></span></code></pre>
<p><strong>Warp Execution Model</strong>:</p>
<ul>
<li><strong>Warp</strong>: Group of 32 threads executed in lockstep</li>
<li><strong>SIMT execution</strong>: All threads in a warp execute the same instruction</li>
<li><strong>Divergence</strong>: When threads take different paths, some become inactive</li>
<li><strong>Convergence</strong>: Threads rejoin at control flow merge points</li>
</ul>
<p>Here’s a detailed CUDA example showing warp behavior:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#include &lt;cuda_runtime.h&gt;</span></span>
<span class="line"><span>#include &lt;stdio.h&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>__global__ void demonstrate_warp_behavior(int* data, int n) {</span></span>
<span class="line"><span>    int tid = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span>    int warp_id = tid / 32;</span></span>
<span class="line"><span>    int lane_id = tid % 32;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    if (tid &lt; n) {</span></span>
<span class="line"><span>        // All threads in warp execute this</span></span>
<span class="line"><span>        data[tid] = tid;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Warp divergence example</span></span>
<span class="line"><span>        if (lane_id &lt; 16) {</span></span>
<span class="line"><span>            // First half of warp executes this</span></span>
<span class="line"><span>            data[tid] *= 2;</span></span>
<span class="line"><span>        } else {</span></span>
<span class="line"><span>            // Second half executes this</span></span>
<span class="line"><span>            data[tid] *= 3;</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span>        // Warp reconverges here</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        data[tid] += warp_id;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Host code</span></span>
<span class="line"><span>int main() {</span></span>
<span class="line"><span>    const int n = 1024;</span></span>
<span class="line"><span>    int* h_data = new int[n];</span></span>
<span class="line"><span>    int* d_data;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Allocate GPU memory</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_data, n * sizeof(int));</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Launch kernel with appropriate grid/block dimensions</span></span>
<span class="line"><span>    dim3 block_size(256);  // 8 warps per block</span></span>
<span class="line"><span>    dim3 grid_size((n + block_size.x - 1) / block_size.x);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    demonstrate_warp_behavior&lt;&lt;&lt;grid_size, block_size&gt;&gt;&gt;(d_data, n);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Copy result back</span></span>
<span class="line"><span>    cudaMemcpy(h_data, d_data, n * sizeof(int), cudaMemcpyDeviceToHost);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Print results for first few elements</span></span>
<span class="line"><span>    for (int i = 0; i &lt; 64; i++) {</span></span>
<span class="line"><span>        printf(&quot;Thread %d: %d\n&quot;, i, h_data[i]);</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    delete[] h_data;</span></span>
<span class="line"><span>    cudaFree(d_data);</span></span>
<span class="line"><span>    return 0;</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="why-parallelism-videos-matrices-and-modern-applications">Why Parallelism? Videos, Matrices, and Modern Applications</h3>
<p>The motivation for parallel computing extends far beyond graphics:</p>
<p><strong>Graphics and Video Processing</strong>:</p>
<ul>
<li><strong>Pixel independence</strong>: Each pixel can be processed without knowledge of others</li>
<li><strong>Regular memory patterns</strong>: Predictable access patterns enable efficient memory usage</li>
<li><strong>High computational density</strong>: Simple operations repeated millions of times</li>
</ul>
<p><strong>Matrix Operations</strong>:
Modern AI workloads are fundamentally based on linear algebra operations that are highly parallelizable:</p>
<strong>Code Example: GPU vs CPU Matrix &amp; Convolution Operations (PyTorch)</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> demonstrate_gpu_matrix_operations</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Show the performance difference between CPU and GPU matrix operations&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Create large matrices</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 4096</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cpu_a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, size, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cpu_b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, size, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # GPU versions</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> cpu_a</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> cpu_b</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # CPU matrix multiplication</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cpu_result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(cpu_a, cpu_b)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cpu_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # GPU matrix multiplication</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-comment)">  # Ensure clean timing</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(gpu_a, gpu_b)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-comment)">  # Wait for GPU to complete</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Matrix size: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;CPU time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">cpu_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> seconds&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;GPU time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">gpu_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> seconds&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Speedup: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">cpu_time </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> gpu_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Verify results are equivalent (within floating point precision)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    difference </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">max</span><span style="color:var(--astro-code-token-punctuation)">(torch.</span><span style="color:var(--astro-code-token-function)">abs</span><span style="color:var(--astro-code-token-punctuation)">(cpu_result </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-punctuation)"> gpu_result.</span><span style="color:var(--astro-code-token-function)">cpu</span><span style="color:var(--astro-code-token-punctuation)">()))</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Maximum difference: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">difference</span><span style="color:var(--astro-code-token-keyword)">:.2e</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># More complex example: Convolution operations</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> demonstrate_convolution_parallelism</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Show how convolution operations benefit from GPU parallelism&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> F</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Create sample input (batch_size=32, channels=256, height=64, width=64)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    input_tensor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Convolution kernel (out_channels=512, in_channels=256, kernel_size=3x3)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    kernel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # CPU convolution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cpu_output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> F</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">conv2d</span><span style="color:var(--astro-code-token-punctuation)">(input_tensor, kernel, padding</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cpu_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # GPU convolution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    input_gpu </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> input_tensor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    kernel_gpu </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> kernel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cuda</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> F</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">conv2d</span><span style="color:var(--astro-code-token-punctuation)">(input_gpu, kernel_gpu, padding</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-string-expression)">Convolution operation:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Input shape: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">input_tensor.shape</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Output shape: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">cpu_output.shape</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;CPU time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">cpu_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> seconds&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;GPU time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">gpu_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> seconds&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Speedup: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">cpu_time </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> gpu_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-token-constant)"> __name__</span><span style="color:var(--astro-code-token-keyword)"> ==</span><span style="color:var(--astro-code-token-string-expression)"> &quot;__main__&quot;</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    demonstrate_gpu_matrix_operations</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    demonstrate_convolution_parallelism</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<hr>
<h2 id="other-key-hardware-elements-the-complete-system">Other Key Hardware Elements: The Complete System</h2>
<h3 id="memory-hierarchy-understanding-the-storage-pyramid">Memory Hierarchy: Understanding the Storage Pyramid</h3>
<p>Modern computing systems implement a sophisticated memory hierarchy designed to balance capacity, speed, and cost. Understanding this hierarchy is crucial for writing efficient GPU code.</p>
<h4 id="system-ram-random-access-memory">System RAM (Random Access Memory)</h4>
<p>System RAM serves as the primary storage for data and programs currently in use. Key characteristics:</p>
<ul>
<li><strong>Capacity</strong>: Typically 16GB to 1TB in modern systems</li>
<li><strong>Bandwidth</strong>: 100-400 GB/s for high-end systems</li>
<li><strong>Latency</strong>: 50-100 nanoseconds access time</li>
<li><strong>Volatile</strong>: Data is lost when power is removed</li>
<li><strong>Shared</strong>: Accessible by CPU and (through PCIe) GPU</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> psutil</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> analyze_system_memory</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Analyze system memory characteristics&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    memory </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> psutil</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">virtual_memory</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;System Memory Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Total RAM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">memory.total </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Available RAM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">memory.available </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Used RAM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">memory.used </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Memory utilization: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">memory.percent</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">%&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Demonstrate memory bandwidth measurement</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> measure_memory_bandwidth</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        &quot;&quot;&quot;Rough estimate of memory bandwidth&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 100_000_000</span><span style="color:var(--astro-code-token-comment)">  # 100M floats = ~400MB</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">random</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">random</span><span style="color:var(--astro-code-token-punctuation)">(size).</span><span style="color:var(--astro-code-token-function)">astype</span><span style="color:var(--astro-code-token-punctuation)">(np.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Memory copy operation</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        copy </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> np</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">copy</span><span style="color:var(--astro-code-token-punctuation)">(data)</span><span style="color:var(--astro-code-token-comment)">  # This tests memory bandwidth</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        end_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-comment)">  # 4 bytes per float, read + write</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bandwidth </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> (end_time </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> start_time) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Estimated memory bandwidth: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">bandwidth</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    measure_memory_bandwidth</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">analyze_system_memory</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h4 id="cache-memory-sram---static-random-access-memory">Cache Memory (SRAM - Static Random Access Memory)</h4>
<p>Cache memory provides ultra-fast access to frequently used data through a hierarchy of increasingly smaller but faster storage levels:</p>
<p><strong>L1 Cache</strong>:</p>
<ul>
<li><strong>Size</strong>: 32-64 KB per core</li>
<li><strong>Latency</strong>: 1-2 CPU cycles</li>
<li><strong>Organization</strong>: Separate instruction and data caches</li>
<li><strong>Bandwidth</strong>: ~1 TB/s per core</li>
</ul>
<p><strong>L2 Cache</strong>:</p>
<ul>
<li><strong>Size</strong>: 256KB - 1MB per core</li>
<li><strong>Latency</strong>: 3-10 CPU cycles</li>
<li><strong>Shared</strong>: Often shared between cores</li>
<li><strong>Bandwidth</strong>: ~500 GB/s per core</li>
</ul>
<p><strong>L3 Cache</strong>:</p>
<ul>
<li><strong>Size</strong>: 8-64MB total</li>
<li><strong>Latency</strong>: 10-20 CPU cycles</li>
<li><strong>Shared</strong>: Across all cores</li>
<li><strong>Bandwidth</strong>: ~200 GB/s total</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">#include</span><span style="color:var(--astro-code-token-string-expression)"> &lt;chrono&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">#include</span><span style="color:var(--astro-code-token-string-expression)"> &lt;iostream&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">#include</span><span style="color:var(--astro-code-token-string-expression)"> &lt;vector&gt;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">#include</span><span style="color:var(--astro-code-token-string-expression)"> &lt;random&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">class</span><span style="color:var(--astro-code-token-function)"> CacheAnalyzer</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">public</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    static</span><span style="color:var(--astro-code-token-keyword)"> void</span><span style="color:var(--astro-code-token-function)"> demonstrate_cache_hierarchy</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        // Test different data sizes to show cache effects</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">vector</span><span style="color:var(--astro-code-token-keyword)">&lt;size_t&gt;</span><span style="color:var(--astro-code-foreground)"> sizes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">            1024</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">        // 4KB - fits in L1</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">            64</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">   // 256KB - fits in L2</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">            4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">  // 16MB - fits in L3</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">            64</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-comment)">  // 256MB - exceeds cache</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        };</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">size_t</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> sizes) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            double</span><span style="color:var(--astro-code-foreground)"> time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> measure_access_time</span><span style="color:var(--astro-code-foreground)">(size);</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">cout </span><span style="color:var(--astro-code-token-keyword)">&lt;&lt;</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Size: &quot;</span><span style="color:var(--astro-code-token-keyword)"> &lt;&lt;</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-keyword)"> sizeof</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">int</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-keyword)"> &lt;&lt;</span><span style="color:var(--astro-code-token-string-expression)"> &quot; KB, &quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">                      &lt;&lt;</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Time per access: &quot;</span><span style="color:var(--astro-code-token-keyword)"> &lt;&lt;</span><span style="color:var(--astro-code-foreground)"> time </span><span style="color:var(--astro-code-token-keyword)">&lt;&lt;</span><span style="color:var(--astro-code-token-string-expression)"> &quot; ns&quot;</span><span style="color:var(--astro-code-token-keyword)"> &lt;&lt;</span><span style="color:var(--astro-code-foreground)"> std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">endl;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">private</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    static</span><span style="color:var(--astro-code-token-keyword)"> double</span><span style="color:var(--astro-code-token-function)"> measure_access_time</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">size_t</span><span style="color:var(--astro-code-foreground)"> size) {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">vector</span><span style="color:var(--astro-code-token-keyword)">&lt;int&gt;</span><span style="color:var(--astro-code-token-function)"> data</span><span style="color:var(--astro-code-foreground)">(size);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        // Initialize with random values</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">random_device rd;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">mt19937 </span><span style="color:var(--astro-code-token-function)">gen</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-function)">rd</span><span style="color:var(--astro-code-foreground)">());</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">size_t</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)">; i </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> size; i</span><span style="color:var(--astro-code-token-keyword)">++</span><span style="color:var(--astro-code-foreground)">) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-constant)">            data</span><span style="color:var(--astro-code-foreground)">[i] </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> gen</span><span style="color:var(--astro-code-foreground)">() </span><span style="color:var(--astro-code-token-keyword)">%</span><span style="color:var(--astro-code-foreground)"> size;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        // Measure random access time</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        volatile</span><span style="color:var(--astro-code-token-keyword)"> int</span><span style="color:var(--astro-code-foreground)"> sum </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)">;</span><span style="color:var(--astro-code-token-comment)">  // Prevent optimization</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        auto</span><span style="color:var(--astro-code-foreground)"> start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">chrono</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">high_resolution_clock</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-token-function)">now</span><span style="color:var(--astro-code-foreground)">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        size_t</span><span style="color:var(--astro-code-foreground)"> iterations </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1000000</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">size_t</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)">; i </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> iterations; i</span><span style="color:var(--astro-code-token-keyword)">++</span><span style="color:var(--astro-code-foreground)">) {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            sum </span><span style="color:var(--astro-code-token-keyword)">+=</span><span style="color:var(--astro-code-token-constant)"> data</span><span style="color:var(--astro-code-foreground)">[i </span><span style="color:var(--astro-code-token-keyword)">%</span><span style="color:var(--astro-code-foreground)"> size];</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        auto</span><span style="color:var(--astro-code-foreground)"> end </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">chrono</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">high_resolution_clock</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-token-function)">now</span><span style="color:var(--astro-code-foreground)">();</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        auto</span><span style="color:var(--astro-code-foreground)"> duration </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">chrono</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-token-function)">duration_cast</span><span style="color:var(--astro-code-foreground)">&lt;std</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-foreground)">chrono</span><span style="color:var(--astro-code-token-punctuation)">::</span><span style="color:var(--astro-code-token-function)">nanoseconds</span><span style="color:var(--astro-code-foreground)">&gt;(end </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> start);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-token-keyword)"> static_cast&lt;double&gt;</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-constant)">duration</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">count</span><span style="color:var(--astro-code-foreground)">()) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> iterations;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    }</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">};</span></span></code></pre>
<h4 id="vram-video-random-access-memory">VRAM (Video Random Access Memory)</h4>
<p>VRAM is specialized high-bandwidth memory attached directly to the GPU:</p>
<ul>
<li><strong>Capacity</strong>: 8GB to 80GB on modern GPUs</li>
<li><strong>Bandwidth</strong>: 500GB/s to 3TB/s (much higher than system RAM)</li>
<li><strong>Latency</strong>: Similar to system RAM but with much higher throughput</li>
<li><strong>Architecture</strong>: Wide memory buses (384-bit, 512-bit, or wider)</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> analyze_vram_characteristics</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Analyze GPU memory characteristics&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    if</span><span style="color:var(--astro-code-token-keyword)"> not</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;CUDA not available&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Get GPU memory info</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">current_device</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    gpu_props </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_device_properties</span><span style="color:var(--astro-code-token-punctuation)">(device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;GPU: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">gpu_props.name</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Total VRAM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">gpu_props.total_memory </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Multiprocessors: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">gpu_props.multi_processor_count</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Measure memory bandwidth</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> measure_gpu_bandwidth</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        &quot;&quot;&quot;Measure GPU memory bandwidth&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 100_000_000</span><span style="color:var(--astro-code-token-comment)">  # 100M floats</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Allocate GPU memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Warm up</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            copy </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> data</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">clone</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Measure bandwidth</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        iterations </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 50</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(iterations):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            copy </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> data</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">clone</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        end_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bytes_per_iteration </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-comment)">  # read + write</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        total_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> bytes_per_iteration </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> iterations</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bandwidth </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> total_bytes </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> (end_time </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)"> start_time) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Measured GPU memory bandwidth: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">bandwidth</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Compare with theoretical peak</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # This is approximate and varies by GPU</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        theoretical_peak </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &#39;A100&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1555</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">  # GB/s</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &#39;V100&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 900</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &#39;RTX 4090&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1008</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">            &#39;RTX 3090&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 936</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> gpu_name</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> peak </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> theoretical_peak</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">items</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            if</span><span style="color:var(--astro-code-foreground)"> gpu_name</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">lower</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> in</span><span style="color:var(--astro-code-foreground)"> gpu_props</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">name</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">lower</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">                efficiency </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (bandwidth </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> peak) </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 100</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">                print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Theoretical peak: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">peak</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">                print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Achieved efficiency: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">efficiency</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">%&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">                break</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    measure_gpu_bandwidth</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">analyze_vram_characteristics</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="storage-the-persistent-foundation">Storage: The Persistent Foundation</h3>
<h4 id="ssds-solid-state-drives">SSDs (Solid State Drives)</h4>
<p>Modern SSDs provide fast persistent storage crucial for handling large datasets:</p>
<ul>
<li><strong>Technology</strong>: NAND flash memory with sophisticated controllers</li>
<li><strong>Performance</strong>: 3-7 GB/s sequential read/write (NVMe SSDs)</li>
<li><strong>Latency</strong>: 50-100 microseconds (much lower than HDDs)</li>
<li><strong>Durability</strong>: Limited write cycles but very reliable for reads</li>
</ul>
<h4 id="hdds-hard-disk-drives">HDDs (Hard Disk Drives)</h4>
<p>Traditional magnetic storage still used for large, infrequently accessed datasets:</p>
<ul>
<li><strong>Capacity</strong>: Up to 20TB+ per drive</li>
<li><strong>Performance</strong>: 100-250 MB/s sequential throughput</li>
<li><strong>Latency</strong>: 3-10 milliseconds (due to mechanical movement)</li>
<li><strong>Cost</strong>: Much cheaper per GB than SSDs</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> tempfile</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> benchmark_storage</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Benchmark storage performance characteristics&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> benchmark_write_read</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">file_path</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> size_mb</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        &quot;&quot;&quot;Benchmark write and read performance&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> b</span><span style="color:var(--astro-code-token-string-expression)">&#39;0&#39;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-foreground)">)  </span><span style="color:var(--astro-code-token-comment)"># 1MB chunk</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Write benchmark</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        with</span><span style="color:var(--astro-code-token-function)"> open</span><span style="color:var(--astro-code-token-punctuation)">(file_path, </span><span style="color:var(--astro-code-token-string-expression)">&#39;wb&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(size_mb):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">                f</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">write</span><span style="color:var(--astro-code-token-punctuation)">(data)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        write_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Read benchmark</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        with</span><span style="color:var(--astro-code-token-function)"> open</span><span style="color:var(--astro-code-token-punctuation)">(file_path, </span><span style="color:var(--astro-code-token-string-expression)">&#39;rb&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">            while</span><span style="color:var(--astro-code-foreground)"> f</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">read</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">                pass</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        read_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Clean up</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        os</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">remove</span><span style="color:var(--astro-code-token-punctuation)">(file_path)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        write_speed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> size_mb </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> write_time</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        read_speed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> size_mb </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> read_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> write_speed</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> read_speed</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Test with different file sizes</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    test_sizes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1000</span><span style="color:var(--astro-code-foreground)">]  </span><span style="color:var(--astro-code-token-comment)"># 100MB, 1GB</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> test_sizes</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        with</span><span style="color:var(--astro-code-foreground)"> tempfile</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">NamedTemporaryFile</span><span style="color:var(--astro-code-token-punctuation)">(delete</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">False</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> tmp</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            write_speed</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> read_speed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> benchmark_write_read</span><span style="color:var(--astro-code-token-punctuation)">(tmp.name, size)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">            print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;File size: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> MB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">            print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Write speed: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">write_speed</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> MB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">            print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Read speed: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">read_speed</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> MB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">            print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;-&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">benchmark_storage</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<hr>
<h2 id="a-short-history-of-gpus-evolution-of-parallel-computing">A Short History of GPUs: Evolution of Parallel Computing</h2>
<p>Understanding the historical development of GPUs provides crucial context for their current capabilities and future directions.</p>
<h3 id="1990-1992-the-dawn-of-3d-rendering">1990-1992: The Dawn of 3D Rendering</h3>
<p>The early 1990s marked the beginning of consumer 3D graphics acceleration. Before this era, all graphics rendering was performed by the CPU, limiting the complexity and realism of computer graphics.</p>
<p><strong>Key developments</strong>:</p>
<ul>
<li><strong>Software rendering</strong>: Early 3D games like Wolfenstein 3D used clever CPU-based techniques</li>
<li><strong>Fixed-function pipelines</strong>: Early 3D accelerators implemented fixed graphics operations in hardware</li>
<li><strong>Limited programmability</strong>: Graphics cards could only perform predefined operations</li>
</ul>
<h3 id="1996-3dfx-voodoo-graphics-the-first-consumer-3d-accelerator">1996: 3DFX Voodoo Graphics - The First Consumer 3D Accelerator</h3>
<p>The 3DFX Voodoo Graphics card revolutionized PC gaming by providing hardware acceleration for 3D graphics operations:</p>
<p><strong>Technical specifications</strong>:</p>
<ul>
<li><strong>Dedicated 3D acceleration</strong>: Separate card just for 3D rendering</li>
<li><strong>Texture mapping</strong>: Hardware support for applying textures to 3D surfaces</li>
<li><strong>Z-buffering</strong>: Hardware depth testing for proper 3D object ordering</li>
<li><strong>Glide API</strong>: Proprietary programming interface for 3D applications</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// Example of early 3D graphics programming (pseudo-Glide API)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">void</span><span style="color:var(--astro-code-token-function)"> render_triangle</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Set rendering state</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    grColorCombine</span><span style="color:var(--astro-code-foreground)">(GR_COMBINE_FUNCTION_SCALE_OTHER</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">                   GR_COMBINE_FACTOR_ONE</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">                   GR_COMBINE_LOCAL_NONE</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">                   GR_COMBINE_OTHER_TEXTURE);</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Define triangle vertices</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    GrVertex </span><span style="color:var(--astro-code-token-constant)">vertices</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-foreground)">] </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        {</span><span style="color:var(--astro-code-token-constant)">100.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 100.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.5</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-foreground)">}</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">  // x, y, z, s, t, w</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        {</span><span style="color:var(--astro-code-token-constant)">200.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 100.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.5</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-foreground)">}</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        {</span><span style="color:var(--astro-code-token-constant)">150.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 200.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.5</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0.5</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    };</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Render the triangle</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    grDrawTriangle</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">&amp;</span><span style="color:var(--astro-code-token-constant)">vertices</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-constant)">vertices</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-foreground)">]</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-constant)">vertices</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-foreground)">]);</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<h3 id="1997-nvidia-riva-128-and-ati-rage-pro">1997: NVIDIA RIVA 128 and ATI Rage Pro</h3>
<p>This period saw the integration of 2D and 3D graphics capabilities:</p>
<p><strong>NVIDIA RIVA 128</strong>:</p>
<ul>
<li><strong>Unified architecture</strong>: Combined 2D GUI acceleration with 3D rendering</li>
<li><strong>AGP interface</strong>: Faster connection to system memory than PCI</li>
<li><strong>Hardware transform</strong>: Basic 3D coordinate transformation in hardware</li>
</ul>
<p><strong>ATI Rage Pro</strong>:</p>
<ul>
<li><strong>Video acceleration</strong>: Hardware support for video decoding and playback</li>
<li><strong>Dual-head support</strong>: Multiple monitor outputs</li>
<li><strong>Improved memory bandwidth</strong>: Better utilization of available memory</li>
</ul>
<h3 id="1990s-gpgpu-research-begins">1990s: GPGPU Research Begins</h3>
<p>Forward-thinking researchers recognized that graphics hardware could be useful for non-graphics computations:</p>
<p><strong>Early GPGPU techniques</strong>:</p>
<ul>
<li><strong>Texture-based computing</strong>: Encoding data as textures and using pixel shaders for computation</li>
<li><strong>Fragment shader programming</strong>: Using graphics shaders for general-purpose calculations</li>
<li><strong>Render-to-texture</strong>: Computing results and storing them in textures for further processing</li>
</ul>
<p><strong>1996: Stanford BrookGPU - Stream/Kernel Programming Model</strong></p>
<p>Stanford researchers developed BrookGPU, one of the first systematic approaches to general-purpose GPU programming. This pioneering work introduced the concept of <strong>stream processing</strong> - treating data as streams that flow through computational kernels.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// BrookGPU example: vector addition using stream processing</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">kernel </span><span style="color:var(--astro-code-token-keyword)">void</span><span style="color:var(--astro-code-token-function)"> vector_add</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">float</span><span style="color:var(--astro-code-foreground)"> a</span><span style="color:var(--astro-code-token-keyword)">&lt;&gt;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> float</span><span style="color:var(--astro-code-foreground)"> b</span><span style="color:var(--astro-code-token-keyword)">&lt;&gt;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> out </span><span style="color:var(--astro-code-token-keyword)">float</span><span style="color:var(--astro-code-foreground)"> result</span><span style="color:var(--astro-code-token-keyword)">&lt;&gt;</span><span style="color:var(--astro-code-foreground)">) {</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b;</span><span style="color:var(--astro-code-token-comment)">  // Applied to entire streams element-wise</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">void</span><span style="color:var(--astro-code-token-function)"> main</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float</span><span style="color:var(--astro-code-foreground)"> input_stream_a</span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-foreground)">;</span><span style="color:var(--astro-code-token-comment)">    // Stream of 1000 floats</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float</span><span style="color:var(--astro-code-foreground)"> input_stream_b</span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-foreground)">;</span><span style="color:var(--astro-code-token-comment)">    // Stream of 1000 floats</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float</span><span style="color:var(--astro-code-foreground)"> output_stream</span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-foreground)">;</span><span style="color:var(--astro-code-token-comment)">     // Output stream</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Initialize streams</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    streamRead(input_stream_a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> host_array_a)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    streamRead(input_stream_b</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> host_array_b)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Execute kernel on GPU</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    vector_add(input_stream_a</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> input_stream_b</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> output_stream)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Read results back</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    streamWrite(output_stream</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> host_result_array)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<h3 id="2007-cuda-revolution-direct-gpu-programming">2007: CUDA Revolution - Direct GPU Programming</h3>
<p>NVIDIA’s introduction of CUDA (Compute Unified Device Architecture) marked the true beginning of mainstream GPGPU computing. For the first time, developers could write GPU code using familiar C/C++ syntax instead of graphics shaders.</p>
<p><strong>CUDA’s revolutionary features</strong>:</p>
<ul>
<li><strong>C/C++ programming model</strong>: No need to encode computation as graphics operations</li>
<li><strong>Explicit memory management</strong>: Direct control over GPU memory allocation and transfers</li>
<li><strong>Thread hierarchy</strong>: Organized parallel execution through grids, blocks, and threads</li>
<li><strong>Shared memory</strong>: Fast on-chip memory for thread cooperation</li>
<li><strong>Synchronization primitives</strong>: Tools for coordinating parallel threads</li>
</ul>
<strong>Code Example: CUDA Matrix Multiplication (Naive Implementation)</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>// CUDA example: Matrix multiplication (naive implementation)</span></span>
<span class="line"><span>__global__ void matrix_multiply(float* A, float* B, float* C, int N) {</span></span>
<span class="line"><span>    // Calculate thread&#39;s position in the grid</span></span>
<span class="line"><span>    int row = blockIdx.y * blockDim.y + threadIdx.y;</span></span>
<span class="line"><span>    int col = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    if (row &lt; N &amp;&amp; col &lt; N) {</span></span>
<span class="line"><span>        float sum = 0.0f;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Compute dot product of row and column</span></span>
<span class="line"><span>        for (int k = 0; k &lt; N; k++) {</span></span>
<span class="line"><span>            sum += A[row * N + k] * B[k * N + col];</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        C[row * N + col] = sum;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Host code</span></span>
<span class="line"><span>int main() {</span></span>
<span class="line"><span>    const int N = 1024;</span></span>
<span class="line"><span>    size_t size = N * N * sizeof(float);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Allocate host memory</span></span>
<span class="line"><span>    float *h_A, *h_B, *h_C;</span></span>
<span class="line"><span>    h_A = (float*)malloc(size);</span></span>
<span class="line"><span>    h_B = (float*)malloc(size);</span></span>
<span class="line"><span>    h_C = (float*)malloc(size);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Allocate device memory</span></span>
<span class="line"><span>    float *d_A, *d_B, *d_C;</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_A, size);</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_B, size);</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_C, size);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Initialize matrices (omitted for brevity)</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Copy data to GPU</span></span>
<span class="line"><span>    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);</span></span>
<span class="line"><span>    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Launch kernel</span></span>
<span class="line"><span>    dim3 blockSize(16, 16);</span></span>
<span class="line"><span>    dim3 gridSize((N + blockSize.x - 1) / blockSize.x,</span></span>
<span class="line"><span>                  (N + blockSize.y - 1) / blockSize.y);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    matrix_multiply&lt;&lt;&lt;gridSize, blockSize&gt;&gt;&gt;(d_A, d_B, d_C, N);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Copy result back</span></span>
<span class="line"><span>    cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Cleanup</span></span>
<span class="line"><span>    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);</span></span>
<span class="line"><span>    free(h_A); free(h_B); free(h_C);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    return 0;</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="2009-opencl-cross-platform-parallel-computing">2009: OpenCL - Cross-Platform Parallel Computing</h3>
<p>The Khronos Group introduced OpenCL (Open Computing Language) to provide vendor-neutral parallel computing across different hardware platforms.</p>
<p><strong>OpenCL advantages</strong>:</p>
<ul>
<li><strong>Cross-platform</strong>: Works on NVIDIA, AMD, Intel GPUs, and CPUs</li>
<li><strong>Heterogeneous computing</strong>: Can coordinate CPU and GPU execution</li>
<li><strong>Fine-grained control</strong>: Explicit management of compute devices and memory</li>
</ul>
<strong>Code Example: OpenCL Vector Addition</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)">// OpenCL example: Vector addition</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">const</span><span style="color:var(--astro-code-token-keyword)"> char*</span><span style="color:var(--astro-code-foreground)"> kernel_source </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> R</span><span style="color:var(--astro-code-token-string-expression)">&quot;(</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">__kernel void vector_add(__global const float* A,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                        __global const float* B,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">                        __global float* C) {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    int idx = get_global_id(0);</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">    C[idx] = A[idx] + B[idx];</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">}</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">)&quot;</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">int</span><span style="color:var(--astro-code-token-function)"> main</span><span style="color:var(--astro-code-foreground)">() {</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    const</span><span style="color:var(--astro-code-token-keyword)"> int</span><span style="color:var(--astro-code-foreground)"> ARRAY_SIZE </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Get platform and device info</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_platform_id platform_id;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_device_id device_id;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clGetPlatformIDs(</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">platform_id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clGetDeviceIDs(platform_id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> CL_DEVICE_TYPE_GPU</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">device_id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Create context and command queue</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_context context </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateContext(</span><span style="color:var(--astro-code-token-constant)">NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">device_id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_command_queue queue </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateCommandQueue(context</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> device_id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Create and build program</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_program program </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateProgramWithSource(context</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">kernel_source</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clBuildProgram(program</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">device_id</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Create kernel</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_kernel kernel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateKernel(program</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;vector_add&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Create buffers</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    size_t</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ARRAY_SIZE </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-keyword)"> sizeof</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-keyword)">float</span><span style="color:var(--astro-code-foreground)">);</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_mem buffer_A </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateBuffer(context</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> CL_MEM_READ_ONLY</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_mem buffer_B </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateBuffer(context</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> CL_MEM_READ_ONLY</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    cl_mem buffer_C </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> clCreateBuffer(context</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> CL_MEM_WRITE_ONLY</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Set kernel arguments</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clSetKernelArg(kernel</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> sizeof</span><span style="color:var(--astro-code-token-function)">(cl_mem)</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">buffer_A)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clSetKernelArg(kernel</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> sizeof</span><span style="color:var(--astro-code-token-function)">(cl_mem)</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">buffer_B)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clSetKernelArg(kernel</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> sizeof</span><span style="color:var(--astro-code-token-function)">(cl_mem)</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">buffer_C)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Execute kernel</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    size_t</span><span style="color:var(--astro-code-foreground)"> global_work_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ARRAY_SIZE;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clEnqueueNDRangeKernel(queue</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> kernel</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-keyword)"> &amp;</span><span style="color:var(--astro-code-token-function)">global_work_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Read result</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    float*</span><span style="color:var(--astro-code-foreground)"> result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-keyword)">float*</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-function)">malloc(size)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    clEnqueueReadBuffer(queue</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> buffer_C</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> CL_TRUE</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-function)"> result</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> NULL</span><span style="color:var(--astro-code-token-function)">)</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    // Cleanup (omitted for brevity)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-foreground)">;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">}</span></span></code></pre>
<h3 id="2014-cublascudnn-optimized-libraries-for-deep-learning">2014: cuBLAS/cuDNN - Optimized Libraries for Deep Learning</h3>
<p>As deep learning gained momentum, NVIDIA recognized the need for highly optimized library implementations of common operations.</p>
<p><strong>cuBLAS (CUDA Basic Linear Algebra Subprograms)</strong>:</p>
<ul>
<li><strong>Optimized BLAS</strong>: Hardware-specific implementations of linear algebra operations</li>
<li><strong>Multiple precision</strong>: Support for FP64, FP32, FP16, and mixed-precision</li>
<li><strong>Batched operations</strong>: Efficient processing of multiple small matrices</li>
</ul>
<strong>Code Example: cuBLAS Optimized Matrix Multiplication</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#include &lt;cublas_v2.h&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>void optimized_matrix_multiply() {</span></span>
<span class="line"><span>    const int M = 2048, N = 2048, K = 2048;</span></span>
<span class="line"><span>    const float alpha = 1.0f, beta = 0.0f;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Allocate GPU memory</span></span>
<span class="line"><span>    float *d_A, *d_B, *d_C;</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_A, M * K * sizeof(float));</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_B, K * N * sizeof(float));</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_C, M * N * sizeof(float));</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Create cuBLAS handle</span></span>
<span class="line"><span>    cublasHandle_t handle;</span></span>
<span class="line"><span>    cublasCreate(&amp;handle);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Perform matrix multiplication: C = alpha * A * B + beta * C</span></span>
<span class="line"><span>    // Note: cuBLAS assumes column-major storage</span></span>
<span class="line"><span>    cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,</span></span>
<span class="line"><span>                N, M, K,  // Dimensions (swapped for column-major)</span></span>
<span class="line"><span>                &amp;alpha,   // Scaling factor</span></span>
<span class="line"><span>                d_B, N,   // Matrix B and leading dimension</span></span>
<span class="line"><span>                d_A, K,   // Matrix A and leading dimension</span></span>
<span class="line"><span>                &amp;beta,    // Beta scaling factor</span></span>
<span class="line"><span>                d_C, N);  // Result matrix C and leading dimension</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // This single call is typically 10-100x faster than naive implementation</span></span>
<span class="line"><span>    // due to:</span></span>
<span class="line"><span>    // - Optimized memory access patterns</span></span>
<span class="line"><span>    // - Use of Tensor Cores on supported hardware</span></span>
<span class="line"><span>    // - Advanced tiling and blocking strategies</span></span>
<span class="line"><span>    // - Assembly-level optimizations</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    cublasDestroy(handle);</span></span>
<span class="line"><span>    cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p><strong>cuDNN (CUDA Deep Neural Network library)</strong>:</p>
<ul>
<li><strong>Convolution operations</strong>: Highly optimized 2D and 3D convolutions</li>
<li><strong>Activation functions</strong>: Hardware-accelerated ReLU, sigmoid, tanh</li>
<li><strong>Normalization</strong>: Batch normalization, layer normalization</li>
<li><strong>RNN support</strong>: LSTM, GRU implementations</li>
</ul>
<strong>Code Example: cuDNN Optimized Convolution</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#include &lt;cudnn.h&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>void optimized_convolution() {</span></span>
<span class="line"><span>    cudnnHandle_t cudnn;</span></span>
<span class="line"><span>    cudnnCreate(&amp;cudnn);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Create tensor descriptors</span></span>
<span class="line"><span>    cudnnTensorDescriptor_t input_desc, output_desc, bias_desc;</span></span>
<span class="line"><span>    cudnnFilterDescriptor_t filter_desc;</span></span>
<span class="line"><span>    cudnnConvolutionDescriptor_t conv_desc;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    cudnnCreateTensorDescriptor(&amp;input_desc);</span></span>
<span class="line"><span>    cudnnCreateTensorDescriptor(&amp;output_desc);</span></span>
<span class="line"><span>    cudnnCreateTensorDescriptor(&amp;bias_desc);</span></span>
<span class="line"><span>    cudnnCreateFilterDescriptor(&amp;filter_desc);</span></span>
<span class="line"><span>    cudnnCreateConvolutionDescriptor(&amp;conv_desc);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Set descriptors (batch=32, channels=256, height=32, width=32)</span></span>
<span class="line"><span>    cudnnSetTensorNdDescriptor(input_desc, CUDNN_DATA_FLOAT, 4,</span></span>
<span class="line"><span>                              (int[]){32, 256, 32, 32},    // dimensions</span></span>
<span class="line"><span>                              (int[]){262144, 1024, 32, 1}); // strides</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Set filter descriptor (512 output channels, 256 input channels, 3x3 kernel)</span></span>
<span class="line"><span>    cudnnSetFilterNdDescriptor(filter_desc, CUDNN_DATA_FLOAT, CUDNN_TENSOR_NCHW, 4,</span></span>
<span class="line"><span>                              (int[]){512, 256, 3, 3});</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Set convolution parameters</span></span>
<span class="line"><span>    cudnnSetConvolutionNdDescriptor(conv_desc, 2,           // 2D convolution</span></span>
<span class="line"><span>                                   (int[]){1, 1},           // padding</span></span>
<span class="line"><span>                                   (int[]){1, 1},           // stride</span></span>
<span class="line"><span>                                   (int[]){1, 1},           // dilation</span></span>
<span class="line"><span>                                   CUDNN_CROSS_CORRELATION, // correlation mode</span></span>
<span class="line"><span>                                   CUDNN_DATA_FLOAT);       // data type</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Find best algorithm</span></span>
<span class="line"><span>    cudnnConvolutionFwdAlgoPerf_t algo_perf[10];</span></span>
<span class="line"><span>    int returned_algo_count;</span></span>
<span class="line"><span>    cudnnFindConvolutionForwardAlgorithm(cudnn, input_desc, filter_desc, conv_desc, output_desc,</span></span>
<span class="line"><span>                                        10, &amp;returned_algo_count, algo_perf);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // cuDNN automatically selects optimal algorithm based on:</span></span>
<span class="line"><span>    // - Hardware capabilities (Tensor Cores, memory bandwidth)</span></span>
<span class="line"><span>    // - Input dimensions and data types</span></span>
<span class="line"><span>    // - Available memory for workspace</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    cudnnDestroy(cudnn);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="2020-present-modern-gpu-computing-era">2020-Present: Modern GPU Computing Era</h3>
<p>The current era of GPU computing is characterized by:</p>
<p><strong>Advanced Data Types and Precision</strong>:</p>
<ul>
<li><strong>FP8 (8-bit floating point)</strong>: Extreme compression for inference</li>
<li><strong>BF16 (Brain Float 16)</strong>: Better numerical stability than FP16</li>
<li><strong>TF32</strong>: Tensor Float 32 for automatic mixed precision</li>
</ul>
<p><strong>Sparsity Acceleration</strong>:
Modern GPUs include hardware support for sparse computations, crucial for efficient neural network inference:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">utils</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">prune </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> prune</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Example: Structured sparsity with 2:4 pattern (2 non-zeros in every 4 elements)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> apply_structured_sparsity</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">model</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Apply 2:4 structured sparsity pattern&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> module </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">modules</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-token-function)"> isinstance</span><span style="color:var(--astro-code-token-punctuation)">(module, torch.nn.Linear):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">            # Apply structured pruning that&#39;s hardware-accelerated on A100/H100</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            prune</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">structured</span><span style="color:var(--astro-code-token-punctuation)">(module, </span><span style="color:var(--astro-code-token-string-expression)">&#39;weight&#39;</span><span style="color:var(--astro-code-token-punctuation)">, amount</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0.5</span><span style="color:var(--astro-code-token-punctuation)">, dim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> model</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Modern sparse operations can achieve:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># - 2x speedup in compute</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># - 2x reduction in memory bandwidth</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># - Minimal accuracy loss when properly trained</span></span></code></pre>
<p><strong>Attention-Specific Kernels</strong>:
The transformer architecture has driven development of specialized kernels:</p>
<strong>Code Example: Flash Attention Concept (Python)</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> F</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> flash_attention_concept</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">Q</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> K</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> V</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    Conceptual implementation of Flash Attention algorithm</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    Real implementation uses custom CUDA kernels for optimal memory usage</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    B</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> H</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> N</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> D </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Q</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">shape  </span><span style="color:var(--astro-code-token-comment)"># Batch, Heads, Sequence, Dimension</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Traditional attention: O(N²) memory usage</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # scores = Q @ K.transpose(-2, -1) / math.sqrt(D)  # N x N matrix</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # attn = F.softmax(scores, dim=-1)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # output = attn @ V</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Flash Attention: Tiled computation with O(N) memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    block_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 128</span><span style="color:var(--astro-code-token-comment)">  # Tile size chosen based on SRAM capacity</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zeros_like</span><span style="color:var(--astro-code-token-punctuation)">(Q)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> i </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, N, block_size):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Load Q tile into SRAM</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        Q_block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Q</span><span style="color:var(--astro-code-token-punctuation)">[:,</span><span style="color:var(--astro-code-token-punctuation)"> :,</span><span style="color:var(--astro-code-foreground)"> i</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)">block_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> :]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> j </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, N, block_size):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">            # Load K,V tile into SRAM</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            K_block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> K</span><span style="color:var(--astro-code-token-punctuation)">[:,</span><span style="color:var(--astro-code-token-punctuation)"> :,</span><span style="color:var(--astro-code-foreground)"> j</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)">j</span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)">block_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> :]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            V_block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> V</span><span style="color:var(--astro-code-token-punctuation)">[:,</span><span style="color:var(--astro-code-token-punctuation)"> :,</span><span style="color:var(--astro-code-foreground)"> j</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)">j</span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)">block_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> :]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">            # Compute attention block entirely in SRAM</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            scores_block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> Q_block </span><span style="color:var(--astro-code-token-keyword)">@</span><span style="color:var(--astro-code-foreground)"> K_block</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">transpose</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">2</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            attn_block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> F</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">softmax</span><span style="color:var(--astro-code-token-punctuation)">(scores_block, dim</span><span style="color:var(--astro-code-token-keyword)">=-</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            output_block </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> attn_block </span><span style="color:var(--astro-code-token-keyword)">@</span><span style="color:var(--astro-code-foreground)"> V_block</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">            # Accumulate results (with proper scaling for softmax)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            output</span><span style="color:var(--astro-code-token-punctuation)">[:,</span><span style="color:var(--astro-code-token-punctuation)"> :,</span><span style="color:var(--astro-code-foreground)"> i</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)">i</span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)">block_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-punctuation)"> :]</span><span style="color:var(--astro-code-token-keyword)"> +=</span><span style="color:var(--astro-code-foreground)"> output_block</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> output</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Benefits of Flash Attention:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># - Reduces memory usage from O(N²) to O(N)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># - Enables training on much longer sequences</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># - 2-4x faster than standard attention on A100/H100</span></span></code></pre>
<p><strong>Advanced Compiler Stacks</strong>:</p>
<strong>Code Example: Triton GPU Kernel Programming</strong><p><strong>Triton</strong>: Python-based GPU kernel programming</p><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> triton</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> triton</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">language </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> tl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@triton</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">jit</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> vector_add_kernel</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x_ptr</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y_ptr</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> output_ptr</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> n_elements</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> BLOCK_SIZE</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">constexpr):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;High-performance vector addition using Triton&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    pid </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">program_id</span><span style="color:var(--astro-code-token-punctuation)">(axis</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    block_start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> pid </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> BLOCK_SIZE</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    offsets </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> block_start </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">arange</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, BLOCK_SIZE)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Mask for boundary checking</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    mask </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> offsets </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> n_elements</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Load data</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(x_ptr </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> offsets, mask</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mask)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(y_ptr </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> offsets, mask</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mask)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Compute</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> y</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Store result</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">store</span><span style="color:var(--astro-code-token-punctuation)">(output_ptr </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> offsets, output, mask</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mask)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> vector_add</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> y</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">empty_like</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    n_elements </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> output</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Auto-tuning finds optimal block size</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    grid </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> lambda</span><span style="color:var(--astro-code-token-parameter)"> meta</span><span style="color:var(--astro-code-foreground)">: (triton</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cdiv</span><span style="color:var(--astro-code-token-punctuation)">(n_elements, meta[</span><span style="color:var(--astro-code-token-string-expression)">&#39;BLOCK_SIZE&#39;</span><span style="color:var(--astro-code-token-punctuation)">]),</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    vector_add_kernel</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">grid</span><span style="color:var(--astro-code-token-punctuation)">](x, y, output, n_elements, BLOCK_SIZE</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> output</span></span></code></pre><p><strong>CUDA Graphs</strong>: Reduce kernel launch overhead</p><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> cuda_graph_optimization</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Demonstrate CUDA Graphs for reducing launch overhead&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Warmup</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10000</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10000</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Capture computation graph</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    g </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">CUDAGraph</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Warmup</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">Stream</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    s</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">wait_stream</span><span style="color:var(--astro-code-token-punctuation)">(torch.cuda.</span><span style="color:var(--astro-code-token-function)">current_stream</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">stream</span><span style="color:var(--astro-code-token-punctuation)">(s):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">):</span><span style="color:var(--astro-code-token-comment)">  # Warmup runs</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> y</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> z </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">relu</span><span style="color:var(--astro-code-token-punctuation)">(z)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">current_stream</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">wait_stream</span><span style="color:var(--astro-code-token-punctuation)">(s)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Capture</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">graph</span><span style="color:var(--astro-code-token-punctuation)">(g):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> y</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> z </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        z </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">relu</span><span style="color:var(--astro-code-token-punctuation)">(z)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Replay graph (much faster than individual kernel launches)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    g</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">replay</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Benefits:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # - Eliminates CPU-GPU synchronization overhead</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # - Reduces kernel launch latency from ~10µs to ~1µs</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # - Enables optimization across kernel boundaries</span></span></code></pre>
<p>This evolution from graphics-centric hardware to general-purpose parallel computing platforms represents one of the most significant developments in modern computer science, enabling the current AI revolution and continuing to drive innovations in scientific computing, machine learning, and high-performance computing applications.</p>
<hr>
<h2 id="how-does-a-gpu-work-understanding-modern-gpu-architecture">How Does a GPU Work? Understanding Modern GPU Architecture</h2>
<h3 id="streaming-multiprocessors-the-core-of-gpu-parallelism">Streaming Multiprocessors: The Core of GPU Parallelism</h3>
<p>Modern GPUs are built around <strong>Streaming Multiprocessors (SMs)</strong>, which are the fundamental compute units that execute parallel workloads. Each SM contains hundreds of cores, shared memory, registers, and specialized compute units like Tensor Cores.</p>
<p>The GPU execution hierarchy follows a well-defined structure:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>GPU Device</span></span>
<span class="line"><span>├─ Streaming Multiprocessor 0 (SM0)</span></span>
<span class="line"><span>│  ├─ CUDA Cores (FP32/INT32): ~64-128 cores</span></span>
<span class="line"><span>│  ├─ Tensor Cores: 4-8 units (for AI workloads)</span></span>
<span class="line"><span>│  ├─ Shared Memory: 48-164 KB (configurable)</span></span>
<span class="line"><span>│  ├─ Register File: 256 KB (65,536 32-bit registers)</span></span>
<span class="line"><span>│  ├─ L1 Cache/Texture Cache: 128 KB</span></span>
<span class="line"><span>│  └─ Warp Schedulers: 4 units</span></span>
<span class="line"><span>├─ Streaming Multiprocessor 1 (SM1)</span></span>
<span class="line"><span>│  └─ [Same structure as SM0]</span></span>
<span class="line"><span>└─ ... (up to 132 SMs in A100)</span></span></code></pre>
<h3 id="thread-hierarchy-threads-warps-blocks-grid">Thread Hierarchy: Threads → Warps → Blocks → Grid</h3>
<p>Understanding the GPU thread hierarchy is crucial for writing efficient parallel code:</p>
<p><strong>1. Thread</strong>: The smallest unit of execution</p>
<ul>
<li>Each thread has its own program counter and register space</li>
<li>Executes the same kernel function with different data</li>
<li>Can have unique thread ID for data indexing</li>
</ul>
<p><strong>2. Warp</strong>: Group of 32 threads executed in lockstep</p>
<ul>
<li>All threads in a warp execute the same instruction simultaneously</li>
<li>Hardware scheduling unit - warps are scheduled for execution</li>
<li>Divergence occurs when threads take different code paths</li>
</ul>
<p><strong>3. Block</strong>: Collection of threads (up to 1024) that can cooperate</p>
<ul>
<li>Threads within a block can synchronize using <code>__syncthreads()</code></li>
<li>Share fast on-chip shared memory</li>
<li>Entire block is assigned to one SM</li>
</ul>
<p><strong>4. Grid</strong>: Collection of blocks that compose the entire kernel launch</p>
<ul>
<li>Blocks are independent and can execute in any order</li>
<li>Grid dimensions define the problem size</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#include &lt;cuda_runtime.h&gt;</span></span>
<span class="line"><span>#include &lt;stdio.h&gt;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>__global__ void demonstrate_thread_hierarchy(int* data, int n) {</span></span>
<span class="line"><span>    // Thread identification within the hierarchy</span></span>
<span class="line"><span>    int global_thread_id = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span>    int warp_id = global_thread_id / 32;</span></span>
<span class="line"><span>    int lane_id = global_thread_id % 32;</span></span>
<span class="line"><span>    int block_id = blockIdx.x;</span></span>
<span class="line"><span>    int thread_in_block = threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    if (global_thread_id &lt; n) {</span></span>
<span class="line"><span>        // Each thread processes its unique data element</span></span>
<span class="line"><span>        data[global_thread_id] = global_thread_id;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Demonstrate warp-level operations</span></span>
<span class="line"><span>        // All threads in the same warp see the same warp_id</span></span>
<span class="line"><span>        if (lane_id == 0) {</span></span>
<span class="line"><span>            printf(&quot;Warp %d in Block %d starting execution\n&quot;, warp_id, block_id);</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Warp-level primitive: ballot (which threads meet condition)</span></span>
<span class="line"><span>        int mask = __ballot_sync(0xFFFFFFFF, data[global_thread_id] % 2 == 0);</span></span>
<span class="line"><span>        if (lane_id == 0) {</span></span>
<span class="line"><span>            printf(&quot;Warp %d: threads with even values: 0x%08x\n&quot;, warp_id, mask);</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>void launch_hierarchical_kernel() {</span></span>
<span class="line"><span>    const int N = 2048;</span></span>
<span class="line"><span>    int* h_data = new int[N];</span></span>
<span class="line"><span>    int* d_data;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    cudaMalloc(&amp;d_data, N * sizeof(int));</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Configure kernel launch parameters</span></span>
<span class="line"><span>    int threads_per_block = 256;  // 8 warps per block</span></span>
<span class="line"><span>    int blocks_per_grid = (N + threads_per_block - 1) / threads_per_block;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    printf(&quot;Launching kernel with:\n&quot;);</span></span>
<span class="line"><span>    printf(&quot;  Threads per block: %d\n&quot;, threads_per_block);</span></span>
<span class="line"><span>    printf(&quot;  Blocks per grid: %d\n&quot;, blocks_per_grid);</span></span>
<span class="line"><span>    printf(&quot;  Total threads: %d\n&quot;, blocks_per_grid * threads_per_block);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    demonstrate_thread_hierarchy&lt;&lt;&lt;blocks_per_grid, threads_per_block&gt;&gt;&gt;(d_data, N);</span></span>
<span class="line"><span>    cudaDeviceSynchronize();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    cudaMemcpy(h_data, d_data, N * sizeof(int), cudaMemcpyDeviceToHost);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    delete[] h_data;</span></span>
<span class="line"><span>    cudaFree(d_data);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="kernel-execution-functions-launched-over-a-grid-of-blocks">Kernel Execution: Functions Launched Over a Grid of Blocks</h3>
<p>A <strong>kernel</strong> is a function that runs on the GPU and is executed by thousands of threads in parallel. Unlike CPU functions that execute once, kernels are launched over a grid of thread blocks:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>// Kernel declaration with __global__ qualifier</span></span>
<span class="line"><span>__global__ void matrix_vector_multiply(float* matrix, float* vector, float* result, int rows, int cols) {</span></span>
<span class="line"><span>    // Each thread computes one element of the result vector</span></span>
<span class="line"><span>    int row = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    if (row &lt; rows) {</span></span>
<span class="line"><span>        float sum = 0.0f;</span></span>
<span class="line"><span>        for (int col = 0; col &lt; cols; col++) {</span></span>
<span class="line"><span>            sum += matrix[row * cols + col] * vector[col];</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span>        result[row] = sum;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>void launch_matrix_vector_kernel() {</span></span>
<span class="line"><span>    const int ROWS = 4096, COLS = 4096;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Host memory allocation</span></span>
<span class="line"><span>    float* h_matrix = new float[ROWS * COLS];</span></span>
<span class="line"><span>    float* h_vector = new float[COLS];</span></span>
<span class="line"><span>    float* h_result = new float[ROWS];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Initialize data</span></span>
<span class="line"><span>    for (int i = 0; i &lt; ROWS * COLS; i++) h_matrix[i] = (float)rand() / RAND_MAX;</span></span>
<span class="line"><span>    for (int i = 0; i &lt; COLS; i++) h_vector[i] = (float)rand() / RAND_MAX;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Device memory allocation</span></span>
<span class="line"><span>    float *d_matrix, *d_vector, *d_result;</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_matrix, ROWS * COLS * sizeof(float));</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_vector, COLS * sizeof(float));</span></span>
<span class="line"><span>    cudaMalloc(&amp;d_result, ROWS * sizeof(float));</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Data transfer to device</span></span>
<span class="line"><span>    cudaMemcpy(d_matrix, h_matrix, ROWS * COLS * sizeof(float), cudaMemcpyHostToDevice);</span></span>
<span class="line"><span>    cudaMemcpy(d_vector, h_vector, COLS * sizeof(float), cudaMemcpyHostToDevice);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Kernel launch configuration</span></span>
<span class="line"><span>    int block_size = 256;</span></span>
<span class="line"><span>    int grid_size = (ROWS + block_size - 1) / block_size;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Launch kernel over grid of blocks</span></span>
<span class="line"><span>    matrix_vector_multiply&lt;&lt;&lt;grid_size, block_size&gt;&gt;&gt;(d_matrix, d_vector, d_result, ROWS, COLS);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Copy result back</span></span>
<span class="line"><span>    cudaMemcpy(h_result, d_result, ROWS * sizeof(float), cudaMemcpyDeviceToHost);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Cleanup</span></span>
<span class="line"><span>    delete[] h_matrix; delete[] h_vector; delete[] h_result;</span></span>
<span class="line"><span>    cudaFree(d_matrix); cudaFree(d_vector); cudaFree(d_result);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="thread-cooperation-via-shared-memory">Thread Cooperation via Shared Memory</h3>
<p>Threads within the same block can cooperate through <strong>shared memory</strong>, a fast on-chip memory space that enables data sharing and synchronization:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>__global__ void cooperative_reduction(float* input, float* output, int n) {</span></span>
<span class="line"><span>    // Shared memory allocation - shared by all threads in the block</span></span>
<span class="line"><span>    __shared__ float shared_data[256];  // Assuming block size of 256</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    int tid = threadIdx.x;</span></span>
<span class="line"><span>    int global_id = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Load data into shared memory</span></span>
<span class="line"><span>    if (global_id &lt; n) {</span></span>
<span class="line"><span>        shared_data[tid] = input[global_id];</span></span>
<span class="line"><span>    } else {</span></span>
<span class="line"><span>        shared_data[tid] = 0.0f;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Synchronize to ensure all threads have loaded their data</span></span>
<span class="line"><span>    __syncthreads();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Parallel reduction in shared memory</span></span>
<span class="line"><span>    for (int stride = blockDim.x / 2; stride &gt; 0; stride &gt;&gt;= 1) {</span></span>
<span class="line"><span>        if (tid &lt; stride) {</span></span>
<span class="line"><span>            shared_data[tid] += shared_data[tid + stride];</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span>        __syncthreads();  // Synchronize after each reduction step</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Thread 0 writes the block&#39;s result</span></span>
<span class="line"><span>    if (tid == 0) {</span></span>
<span class="line"><span>        output[blockIdx.x] = shared_data[0];</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span></code></pre>
<hr>
<h2 id="gpu-memory-architecture-the-foundation-of-performance">GPU Memory Architecture: The Foundation of Performance</h2>
<h3 id="global-memory-hbmvram-the-primary-storage">Global Memory (HBM/VRAM): The Primary Storage</h3>
<p><strong>High Bandwidth Memory (HBM)</strong> or <strong>VRAM</strong> serves as the main memory for GPU computations:</p>
<p><strong>Characteristics</strong>:</p>
<ul>
<li><strong>Capacity</strong>: 40-80GB on modern data center GPUs (A100, H100)</li>
<li><strong>Bandwidth</strong>: 1.5-3.0 TB/s peak throughput</li>
<li><strong>Latency</strong>: 200-800 cycles (relatively high)</li>
<li><strong>Access Pattern Sensitivity</strong>: Coalesced access patterns crucial for performance</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> demonstrate_memory_coalescing</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Show the importance of memory access patterns&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-keyword)"> if</span><span style="color:var(--astro-code-token-punctuation)"> torch.cuda.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">() </span><span style="color:var(--astro-code-token-keyword)">else</span><span style="color:var(--astro-code-token-string-expression)"> &#39;cpu&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Large matrix to stress memory system</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    N </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 4096</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    matrix </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(N, N, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Coalesced access: consecutive threads access consecutive memory</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> coalesced_access</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">mat</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Row-major access - coalesced for C-style arrays</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(dim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Sum along rows</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Non-coalesced access: strided memory access</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> strided_access</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">mat</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Column-major access - non-coalesced, poor cache usage</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> mat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">sum</span><span style="color:var(--astro-code-token-punctuation)">(dim</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # Sum along columns</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Memory bandwidth test</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> measure_bandwidth</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        size_mb </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1000</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elements </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (size_mb </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">//</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-comment)">  # 4 bytes per float32</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        data </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(elements, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Simple copy operation to measure raw bandwidth</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        copy </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> data</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">clone</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elapsed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Calculate bandwidth (read + write)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> elements </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-comment)">  # read + write</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bandwidth_gb_s </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> elapsed </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> (</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> bandwidth_gb_s</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    coalesced_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> coalesced_access</span><span style="color:var(--astro-code-token-punctuation)">(matrix)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    strided_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> strided_access</span><span style="color:var(--astro-code-token-punctuation)">(matrix)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    bandwidth </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> measure_bandwidth</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Coalesced access time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">coalesced_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Strided access time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">strided_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Performance ratio: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">strided_time</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)">coalesced_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x slower for strided&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Measured memory bandwidth: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">bandwidth</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">demonstrate_memory_coalescing</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="l2l1-caches-hardware-managed-performance">L2/L1 Caches: Hardware-Managed Performance</h3>
<p>Modern GPUs implement a sophisticated cache hierarchy to reduce memory latency:</p>
<p><strong>L2 Cache</strong>:</p>
<ul>
<li><strong>Size</strong>: 40-50MB on A100/H100</li>
<li><strong>Shared</strong>: Across all SMs on the GPU</li>
<li><strong>Management</strong>: Hardware-managed, transparent to programmer</li>
<li><strong>Purpose</strong>: Reduce Global Memory access latency</li>
</ul>
<p><strong>L1 Cache/Texture Cache</strong>:</p>
<ul>
<li><strong>Size</strong>: 128KB per SM</li>
<li><strong>Management</strong>: Hardware-managed with some programmer control</li>
<li><strong>Configurable</strong>: Can be partitioned with shared memory</li>
<li><strong>Optimization</strong>: Benefits from spatial and temporal locality</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>__global__ void cache_friendly_stencil(float* input, float* output, int width, int height) {</span></span>
<span class="line"><span>    int x = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span>    int y = blockIdx.y * blockDim.y + threadIdx.y;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    if (x &gt; 0 &amp;&amp; x &lt; width-1 &amp;&amp; y &gt; 0 &amp;&amp; y &lt; height-1) {</span></span>
<span class="line"><span>        int idx = y * width + x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // 5-point stencil operation - benefits from L1 cache due to spatial locality</span></span>
<span class="line"><span>        float center = input[idx];</span></span>
<span class="line"><span>        float left   = input[idx - 1];</span></span>
<span class="line"><span>        float right  = input[idx + 1];</span></span>
<span class="line"><span>        float up     = input[idx - width];</span></span>
<span class="line"><span>        float down   = input[idx + width];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Weighted average - neighboring elements likely in L1 cache</span></span>
<span class="line"><span>        output[idx] = 0.2f * (center + left + right + up + down);</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="shared-memory-sram-programmer-managed-performance">Shared Memory (SRAM): Programmer-Managed Performance</h3>
<p><strong>Shared Memory</strong> is the programmer’s primary tool for optimizing memory access patterns:</p>
<p><strong>Characteristics</strong>:</p>
<ul>
<li><strong>Size</strong>: 48-164KB per SM (configurable)</li>
<li><strong>Speed</strong>: ~19 TB/s per SM (much faster than global memory)</li>
<li><strong>Scope</strong>: Shared among threads in the same block</li>
<li><strong>Management</strong>: Explicitly managed by programmer</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#define TILE_SIZE 16</span></span>
<span class="line"><span></span></span>
<span class="line"><span>__global__ void tiled_matrix_multiply(float* A, float* B, float* C, int N) {</span></span>
<span class="line"><span>    // Shared memory tiles - allocated per thread block</span></span>
<span class="line"><span>    __shared__ float tile_A[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span>    __shared__ float tile_B[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    int row = blockIdx.y * blockDim.y + threadIdx.y;</span></span>
<span class="line"><span>    int col = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    float sum = 0.0f;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Loop over tiles</span></span>
<span class="line"><span>    for (int tile = 0; tile &lt; (N + TILE_SIZE - 1) / TILE_SIZE; tile++) {</span></span>
<span class="line"><span>        // Collaborative loading: each thread loads one element</span></span>
<span class="line"><span>        int tile_row = tile * TILE_SIZE + threadIdx.x;</span></span>
<span class="line"><span>        int tile_col = tile * TILE_SIZE + threadIdx.y;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Load tile of A</span></span>
<span class="line"><span>        if (row &lt; N &amp;&amp; tile_row &lt; N) {</span></span>
<span class="line"><span>            tile_A[threadIdx.y][threadIdx.x] = A[row * N + tile_row];</span></span>
<span class="line"><span>        } else {</span></span>
<span class="line"><span>            tile_A[threadIdx.y][threadIdx.x] = 0.0f;</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Load tile of B</span></span>
<span class="line"><span>        if (tile_col &lt; N &amp;&amp; col &lt; N) {</span></span>
<span class="line"><span>            tile_B[threadIdx.y][threadIdx.x] = B[tile_col * N + col];</span></span>
<span class="line"><span>        } else {</span></span>
<span class="line"><span>            tile_B[threadIdx.y][threadIdx.x] = 0.0f;</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Synchronize to ensure tile loading is complete</span></span>
<span class="line"><span>        __syncthreads();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Compute using data in shared memory (much faster than global memory)</span></span>
<span class="line"><span>        for (int k = 0; k &lt; TILE_SIZE; k++) {</span></span>
<span class="line"><span>            sum += tile_A[threadIdx.y][k] * tile_B[k][threadIdx.x];</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Synchronize before loading next tile</span></span>
<span class="line"><span>        __syncthreads();</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Write result</span></span>
<span class="line"><span>    if (row &lt; N &amp;&amp; col &lt; N) {</span></span>
<span class="line"><span>        C[row * N + col] = sum;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="registers-per-thread-fastest-access">Registers: Per-Thread Fastest Access</h3>
<p><strong>Registers</strong> provide the fastest memory access but are limited in quantity:</p>
<p><strong>Characteristics</strong>:</p>
<ul>
<li><strong>Size</strong>: 256KB per SM (65,536 32-bit registers)</li>
<li><strong>Distribution</strong>: Divided among active threads</li>
<li><strong>Speed</strong>: Single-cycle access</li>
<li><strong>Limitation</strong>: Too many registers per thread reduces occupancy</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>__global__ void register_usage_example(float* data, int n) {</span></span>
<span class="line"><span>    int tid = blockIdx.x * blockDim.x + threadIdx.x;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    if (tid &lt; n) {</span></span>
<span class="line"><span>        // These variables are stored in registers (fastest access)</span></span>
<span class="line"><span>        float reg1 = data[tid];</span></span>
<span class="line"><span>        float reg2 = reg1 * 2.0f;</span></span>
<span class="line"><span>        float reg3 = reg2 + 1.0f;</span></span>
<span class="line"><span>        float reg4 = reg3 / 3.0f;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Complex computation using registers</span></span>
<span class="line"><span>        for (int i = 0; i &lt; 10; i++) {</span></span>
<span class="line"><span>            reg1 = sinf(reg1) + cosf(reg2);</span></span>
<span class="line"><span>            reg2 = sqrtf(reg3) * reg4;</span></span>
<span class="line"><span>            reg3 = reg1 + reg2;</span></span>
<span class="line"><span>            reg4 = reg3 - reg1;</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        data[tid] = reg4;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span></code></pre>
<hr>
<h2 id="performance-analysis-understanding-gpu-bottlenecks">Performance Analysis: Understanding GPU Bottlenecks</h2>
<h3 id="memory-bound-vs-compute-bound-kernels">Memory-Bound vs Compute-Bound Kernels</h3>
<p>Understanding whether your kernel is <strong>memory-bound</strong> or <strong>compute-bound</strong> is crucial for optimization:</p>
<p><strong>Memory-Bound Kernels</strong>:</p>
<ul>
<li>Runtime dominated by data transfer between Global Memory and SMs</li>
<li>Low arithmetic intensity (few operations per byte transferred)</li>
<li>Examples: Simple element-wise operations, reductions, transposes</li>
</ul>
<p><strong>Compute-Bound Kernels</strong>:</p>
<ul>
<li>Runtime dominated by actual computation (ALU or Tensor Core operations)</li>
<li>High arithmetic intensity (many operations per byte transferred)</li>
<li>Examples: Dense matrix multiplication, complex mathematical functions</li>
</ul>
<h3 id="arithmetic-intensity-the-key-metric">Arithmetic Intensity: The Key Metric</h3>
<p><strong>Arithmetic Intensity (AI) = FLOPs / Bytes transferred from HBM</strong></p>
<p>This metric determines the performance ceiling of your kernel:</p>
<strong>Code Example: Arithmetic Intensity Analysis</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> analyze_arithmetic_intensity</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Demonstrate the concept of arithmetic intensity&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    N </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 4096</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Example 1: Low arithmetic intensity (memory-bound)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> vector_add_analysis</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(N, N, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(N, N, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b  </span><span style="color:var(--astro-code-token-comment)"># 1 FLOP per element</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elapsed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elements </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> N </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> N</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        flops </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> elements  </span><span style="color:var(--astro-code-token-comment)"># 1 FLOP per element</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> elements </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 3</span><span style="color:var(--astro-code-token-comment)">  # 3 arrays × 4 bytes per float32</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        arithmetic_intensity </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> bytes_transferred</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Vector Addition:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Arithmetic Intensity: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">arithmetic_intensity</span><span style="color:var(--astro-code-token-keyword)">:.3f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> FLOPs/Byte&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">elapsed</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> elapsed </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e9</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> arithmetic_intensity</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Example 2: High arithmetic intensity (compute-bound)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> matrix_multiply_analysis</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(N, N, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(N, N, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(a, b)</span><span style="color:var(--astro-code-token-comment)">  # N³ FLOPs for N×N matrices</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elapsed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        flops </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> N </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> N </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> N  </span><span style="color:var(--astro-code-token-comment)"># N³ multiply-add operations</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> N </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> N </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 3</span><span style="color:var(--astro-code-token-comment)">  # 3 matrices × 4 bytes per float32</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        arithmetic_intensity </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> bytes_transferred</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-string-expression)">Matrix Multiplication:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Arithmetic Intensity: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">arithmetic_intensity</span><span style="color:var(--astro-code-token-keyword)">:.3f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> FLOPs/Byte&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">elapsed</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> elapsed </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e12</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> arithmetic_intensity</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Example 3: Medium arithmetic intensity</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> convolution_analysis</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # 3D convolution: batch=32, channels=256, spatial=64x64, kernel=3x3</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        input_tensor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">32</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        kernel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">256</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">conv2d</span><span style="color:var(--astro-code-token-punctuation)">(input_tensor, kernel, padding</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elapsed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Approximate FLOP count for convolution</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        batch_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> in_channels</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> height</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> width </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> input_tensor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">shape</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        out_channels</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> _</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> kernel_h</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> kernel_w </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> kernel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">shape</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        flops </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> batch_size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> out_channels </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> height </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> width </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> in_channels </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> kernel_h </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> kernel_w </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        input_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> input_tensor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        kernel_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> kernel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        output_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> output</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        bytes_transferred </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> input_bytes </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> kernel_bytes </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> output_bytes</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        arithmetic_intensity </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> bytes_transferred</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-string-expression)">Convolution:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Arithmetic Intensity: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">arithmetic_intensity</span><span style="color:var(--astro-code-token-keyword)">:.3f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> FLOPs/Byte&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">elapsed</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> elapsed </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e12</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> arithmetic_intensity</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ai_low </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> vector_add_analysis</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ai_high </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> matrix_multiply_analysis</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ai_medium </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> convolution_analysis</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> ai_low</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ai_medium</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ai_high</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">analyze_arithmetic_intensity</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="a100-performance-analysis-understanding-theoretical-limits">A100 Performance Analysis: Understanding Theoretical Limits</h3>
<p>Let’s analyze the NVIDIA A100’s performance characteristics:</p>
<strong>Code Example: A100 Performance Model</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> a100_performance_model</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Model A100 performance characteristics&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # A100 specifications</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    specs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;peak_fp32_compute&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 19.5</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">      # TFLOPS</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;peak_hbm_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1.5</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">      # TB/s</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;hbm_capacity&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">             # GB (base model)</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;sm_count&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 108</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;tensor_core_fp16&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 312</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">        # TFLOPS with sparsity</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;l2_cache&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">                 # MB</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;shared_memory_per_sm&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 164</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">    # KB</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Calculate ridge point (arithmetic intensity threshold)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ridge_point </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> specs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak_fp32_compute&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> /</span><span style="color:var(--astro-code-foreground)"> specs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak_hbm_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;NVIDIA A100 Performance Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Peak FP32 Compute: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak_fp32_compute&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Peak HBM Bandwidth: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak_hbm_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Ridge Point: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">ridge_point</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> FLOPs/Byte&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;L2 Cache: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;l2_cache&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> MB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Shared Memory per SM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;shared_memory_per_sm&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> KB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;\nPerformance Predictions:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Analyze different operation types</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    operations </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        (</span><span style="color:var(--astro-code-token-string-expression)">&quot;Vector Add&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Memory-bound&quot;</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        (</span><span style="color:var(--astro-code-token-string-expression)">&quot;Convolution (typical)&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 50</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Balanced&quot;</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        (</span><span style="color:var(--astro-code-token-string-expression)">&quot;Dense GEMM&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 100</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Compute-bound&quot;</span><span style="color:var(--astro-code-foreground)">)</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        (</span><span style="color:var(--astro-code-token-string-expression)">&quot;Sparse GEMM (2:4)&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 200</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Highly compute-bound&quot;</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> op_name</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ai</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> classification </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> operations</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> ai </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> ridge_point</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            max_performance </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> ai </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> specs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak_hbm_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            bottleneck </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Memory bandwidth&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        else</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            max_performance </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> specs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;peak_fp32_compute&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            bottleneck </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Compute throughput&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">op_name</span><span style="color:var(--astro-code-token-keyword)">:20</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> | AI: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">ai</span><span style="color:var(--astro-code-token-keyword)">:3.0f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> | Max: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">max_performance</span><span style="color:var(--astro-code-token-keyword)">:5.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS | </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">bottleneck</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> specs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ridge_point</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">specs</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ridge_point </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> a100_performance_model</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<h3 id="overhead-bound-operations">Overhead-Bound Operations</h3>
<p>Many applications suffer from <strong>overhead-bound</strong> performance, where kernel launch and dispatch overhead dominates:</p>
<strong>Code Example: Kernel Launch Overhead Analysis</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> demonstrate_overhead_bound</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Show how small kernels can be overhead-bound&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> measure_kernel_overhead</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> num_launches</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        &quot;&quot;&quot;Measure overhead for small vs large kernels&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        a </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        b </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Warm up</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(num_launches):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            c </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> b</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elapsed </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        time_per_kernel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> elapsed </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> num_launches </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 1000</span><span style="color:var(--astro-code-token-comment)">  # milliseconds</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        elements_per_second </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> num_launches </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> elapsed </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1e9</span><span style="color:var(--astro-code-token-comment)">  # billion elements/sec</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> time_per_kernel</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> elements_per_second</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Kernel Launch Overhead Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Size\t\tTime/Kernel\tBandwidth&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;-&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    sizes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1000</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 10000</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 100000</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1000000</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 10000000</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    num_launches </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1000</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> sizes</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        time_per_kernel</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> throughput </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> measure_kernel_overhead</span><span style="color:var(--astro-code-token-punctuation)">(size, num_launches)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">size</span><span style="color:var(--astro-code-token-keyword)">:8d</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-punctuation)">\t</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">time_per_kernel</span><span style="color:var(--astro-code-token-keyword)">:8.3f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">ms</span><span style="color:var(--astro-code-token-punctuation)">\t</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">throughput</span><span style="color:var(--astro-code-token-keyword)">:8.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GEl/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;\nObservations:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;- Small kernels show high overhead (low throughput)&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;- Large kernels amortize launch overhead&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;- Async execution can help hide overhead&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">demonstrate_overhead_bound</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<hr>
<h2 id="performance-optimization-strategies">Performance Optimization Strategies</h2>
<h3 id="strategy-1-operator-fusion-for-memory-bound-operations">Strategy 1: Operator Fusion for Memory-Bound Operations</h3>
<p><strong>Operator Fusion</strong> combines multiple operations to reduce memory traffic and kernel launch overhead:</p>
<strong>Code Example: Operator Fusion with Triton</strong><pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> triton</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> triton</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">language </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> tl</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Traditional unfused operations</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> unfused_operations</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Separate kernels for each operation&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    temp1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1.0</span><span style="color:var(--astro-code-token-comment)">      # Kernel 1: load x, store temp1</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    temp2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">relu</span><span style="color:var(--astro-code-token-punctuation)">(temp1)</span><span style="color:var(--astro-code-token-comment)">  # Kernel 2: load temp1, store temp2</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> temp2 </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2.0</span><span style="color:var(--astro-code-token-comment)">       # Kernel 3: load temp2, store result</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> result</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Fused kernel using Triton</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">@triton</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">jit</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> fused_kernel</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x_ptr</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> output_ptr</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> n_elements</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> BLOCK_SIZE</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">constexpr):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Fused kernel: x -&gt; (x + 1) -&gt; ReLU -&gt; (* 2) in one pass&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    pid </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">program_id</span><span style="color:var(--astro-code-token-punctuation)">(axis</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    block_start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> pid </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> BLOCK_SIZE</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    offsets </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> block_start </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">arange</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">, BLOCK_SIZE)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    mask </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> offsets </span><span style="color:var(--astro-code-token-keyword)">&lt;</span><span style="color:var(--astro-code-foreground)"> n_elements</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Load input once</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">load</span><span style="color:var(--astro-code-token-punctuation)">(x_ptr </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> offsets, mask</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mask)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Perform all operations in registers</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    temp </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-constant)"> 1.0</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    temp </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">where</span><span style="color:var(--astro-code-token-punctuation)">(temp </span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-punctuation)">, temp, </span><span style="color:var(--astro-code-token-constant)">0.0</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # ReLU</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    result </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> temp </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2.0</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Store result once</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tl</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">store</span><span style="color:var(--astro-code-token-punctuation)">(output_ptr </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-token-punctuation)"> offsets, result, mask</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">mask)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> fused_operations</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">x</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Fused version using Triton&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">empty_like</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    n_elements </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> output</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Auto-tune block size for optimal performance</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    grid </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-keyword)"> lambda</span><span style="color:var(--astro-code-token-parameter)"> meta</span><span style="color:var(--astro-code-foreground)">: (triton</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cdiv</span><span style="color:var(--astro-code-token-punctuation)">(n_elements, meta[</span><span style="color:var(--astro-code-token-string-expression)">&#39;BLOCK_SIZE&#39;</span><span style="color:var(--astro-code-token-punctuation)">]),</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    fused_kernel</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-foreground)">grid</span><span style="color:var(--astro-code-token-punctuation)">](x, output, n_elements, BLOCK_SIZE</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1024</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> output</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> benchmark_fusion</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Compare fused vs unfused performance&quot;&quot;&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10_000_000</span><span style="color:var(--astro-code-token-punctuation)">, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Benchmark unfused</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        result1 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> unfused_operations</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    unfused_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Benchmark fused</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        result2 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> fused_operations</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    fused_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Verify results are equivalent</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    assert</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">allclose</span><span style="color:var(--astro-code-token-punctuation)">(result1, result2, atol</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1e-6</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Unfused time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">unfused_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Fused time: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">fused_time</span><span style="color:var(--astro-code-token-keyword)">:.4f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Speedup: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">unfused_time </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> fused_time</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Memory traffic analysis</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    element_count </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    unfused_traffic </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> element_count </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 6</span><span style="color:var(--astro-code-token-comment)">  # 6 memory transactions</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    fused_traffic </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> element_count </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-comment)">    # 2 memory transactions</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Memory traffic reduction: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">unfused_traffic </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> fused_traffic</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># benchmark_fusion()  # Uncomment to run</span></span></code></pre>
<h3 id="strategy-2-tiling-for-compute-intensive-operations">Strategy 2: Tiling for Compute-Intensive Operations</h3>
<p><strong>Tiling</strong> is a fundamental optimization technique that improves the performance of compute-intensive operations by exploiting the GPU’s memory hierarchy. The core idea is to load data into fast shared memory and reuse it multiple times, thereby increasing arithmetic intensity and reducing the number of expensive global memory accesses.</p>
<p><strong>Why Tiling Works:</strong></p>
<p>When performing operations like matrix multiplication, the naive approach requires each thread to load data from global memory multiple times. For example, in a matrix multiplication C = A × B, each element of the result matrix C[i,j] requires loading an entire row from matrix A and an entire column from matrix B. Without tiling, this leads to:</p>
<ol>
<li><strong>Redundant memory accesses</strong>: Multiple threads load the same data from global memory</li>
<li><strong>Poor cache utilization</strong>: Data is loaded from global memory and immediately discarded</li>
<li><strong>Low arithmetic intensity</strong>: The ratio of computation to memory access is suboptimal</li>
</ol>
<p>Tiling addresses these issues by:</p>
<ol>
<li><strong>Cooperative loading</strong>: Threads within a block collaborate to load data into shared memory</li>
<li><strong>Data reuse</strong>: Once loaded, data in shared memory is reused by multiple threads</li>
<li><strong>Increased arithmetic intensity</strong>: More computation per byte transferred from global memory</li>
</ol>
<p><strong>Detailed Implementation Example:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>#define TILE_SIZE 32</span></span>
<span class="line"><span></span></span>
<span class="line"><span>__global__ void tiled_gemm_optimized(float* A, float* B, float* C, int M, int N, int K) {</span></span>
<span class="line"><span>    // Shared memory tiles - allocated per thread block</span></span>
<span class="line"><span>    // Each tile can hold TILE_SIZE × TILE_SIZE elements</span></span>
<span class="line"><span>    __shared__ float As[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span>    __shared__ float Bs[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Block and thread indices for navigation</span></span>
<span class="line"><span>    int bx = blockIdx.x, by = blockIdx.y;</span></span>
<span class="line"><span>    int tx = threadIdx.x, ty = threadIdx.y;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Global indices for this thread</span></span>
<span class="line"><span>    int row = by * TILE_SIZE + ty;  // Row in matrix C</span></span>
<span class="line"><span>    int col = bx * TILE_SIZE + tx;  // Column in matrix C</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    float sum = 0.0f;  // Accumulator for this thread&#39;s result</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Main tiling loop: iterate through tiles of A and B</span></span>
<span class="line"><span>    // Number of tiles needed to cover dimension K</span></span>
<span class="line"><span>    int num_tiles = (K + TILE_SIZE - 1) / TILE_SIZE;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    for (int tile_idx = 0; tile_idx &lt; num_tiles; tile_idx++) {</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Phase 1: Collaborative loading of tiles into shared memory</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Load tile from matrix A</span></span>
<span class="line"><span>        int a_row = row;  // Same row as output</span></span>
<span class="line"><span>        int a_col = tile_idx * TILE_SIZE + tx;  // Column shifts with tile</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        if (a_row &lt; M &amp;&amp; a_col &lt; K) {</span></span>
<span class="line"><span>            As[ty][tx] = A[a_row * K + a_col];</span></span>
<span class="line"><span>        } else {</span></span>
<span class="line"><span>            As[ty][tx] = 0.0f;  // Handle boundary cases</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Load tile from matrix B</span></span>
<span class="line"><span>        int b_row = tile_idx * TILE_SIZE + ty;  // Row shifts with tile</span></span>
<span class="line"><span>        int b_col = col;  // Same column as output</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        if (b_row &lt; K &amp;&amp; b_col &lt; N) {</span></span>
<span class="line"><span>            Bs[ty][tx] = B[b_row * N + b_col];</span></span>
<span class="line"><span>        } else {</span></span>
<span class="line"><span>            Bs[ty][tx] = 0.0f;  // Handle boundary cases</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Synchronization barrier: ensure all threads have finished loading</span></span>
<span class="line"><span>        __syncthreads();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Phase 2: Computation using shared memory data</span></span>
<span class="line"><span>        // Each thread computes its portion using the loaded tiles</span></span>
<span class="line"><span>        for (int k = 0; k &lt; TILE_SIZE; k++) {</span></span>
<span class="line"><span>            // This access is to shared memory (very fast)</span></span>
<span class="line"><span>            sum += As[ty][k] * Bs[k][tx];</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Another synchronization barrier before loading next tile</span></span>
<span class="line"><span>        __syncthreads();</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Phase 3: Write result to global memory</span></span>
<span class="line"><span>    if (row &lt; M &amp;&amp; col &lt; N) {</span></span>
<span class="line"><span>        C[row * N + col] = sum;</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span></span></span>
<span class="line"><span>// Advanced tiling with register blocking for even better performance</span></span>
<span class="line"><span>__global__ void tiled_gemm_register_blocked(float* A, float* B, float* C, int M, int N, int K) {</span></span>
<span class="line"><span>    // Shared memory tiles</span></span>
<span class="line"><span>    __shared__ float As[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span>    __shared__ float Bs[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Register blocking: each thread computes multiple output elements</span></span>
<span class="line"><span>    #define REG_TILE_M 4</span></span>
<span class="line"><span>    #define REG_TILE_N 4</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    float reg_c[REG_TILE_M][REG_TILE_N] = {{0.0f}};  // Register tile for results</span></span>
<span class="line"><span>    float reg_a[REG_TILE_M];  // Register cache for A values</span></span>
<span class="line"><span>    float reg_b[REG_TILE_N];  // Register cache for B values</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    int bx = blockIdx.x, by = blockIdx.y;</span></span>
<span class="line"><span>    int tx = threadIdx.x, ty = threadIdx.y;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Each thread block handles a larger tile due to register blocking</span></span>
<span class="line"><span>    int block_row_start = by * (TILE_SIZE * REG_TILE_M);</span></span>
<span class="line"><span>    int block_col_start = bx * (TILE_SIZE * REG_TILE_N);</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // This thread&#39;s starting position within the block&#39;s output region</span></span>
<span class="line"><span>    int thread_row_start = block_row_start + ty * REG_TILE_M;</span></span>
<span class="line"><span>    int thread_col_start = block_col_start + tx * REG_TILE_N;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Main computation loop over K dimension</span></span>
<span class="line"><span>    for (int tile_k = 0; tile_k &lt; K; tile_k += TILE_SIZE) {</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Load tiles into shared memory (similar to previous example)</span></span>
<span class="line"><span>        // ... loading code omitted for brevity ...</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        __syncthreads();</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Inner loop over the tile in shared memory</span></span>
<span class="line"><span>        for (int k = 0; k &lt; TILE_SIZE; k++) {</span></span>
<span class="line"><span>            // Load values into registers for reuse</span></span>
<span class="line"><span>            for (int i = 0; i &lt; REG_TILE_M; i++) {</span></span>
<span class="line"><span>                reg_a[i] = As[ty * REG_TILE_M + i][k];</span></span>
<span class="line"><span>            }</span></span>
<span class="line"><span>            for (int j = 0; j &lt; REG_TILE_N; j++) {</span></span>
<span class="line"><span>                reg_b[j] = Bs[k][tx * REG_TILE_N + j];</span></span>
<span class="line"><span>            }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>            // Compute outer product of register vectors</span></span>
<span class="line"><span>            for (int i = 0; i &lt; REG_TILE_M; i++) {</span></span>
<span class="line"><span>                for (int j = 0; j &lt; REG_TILE_N; j++) {</span></span>
<span class="line"><span>                    reg_c[i][j] += reg_a[i] * reg_b[j];</span></span>
<span class="line"><span>                }</span></span>
<span class="line"><span>            }</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        __syncthreads();</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Write register tile back to global memory</span></span>
<span class="line"><span>    for (int i = 0; i &lt; REG_TILE_M; i++) {</span></span>
<span class="line"><span>        for (int j = 0; j &lt; REG_TILE_N; j++) {</span></span>
<span class="line"><span>            int global_row = thread_row_start + i;</span></span>
<span class="line"><span>            int global_col = thread_col_start + j;</span></span>
<span class="line"><span>            if (global_row &lt; M &amp;&amp; global_col &lt; N) {</span></span>
<span class="line"><span>                C[global_row * N + global_col] = reg_c[i][j];</span></span>
<span class="line"><span>            }</span></span>
<span class="line"><span>        }</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span></code></pre>
<p><strong>Performance Analysis of Tiling:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> numpy </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> np</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> analyze_tiling_performance</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Comprehensive analysis of tiling performance benefits&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Test different matrix sizes</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    sizes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-constant)">512</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 2048</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 4096</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Tiling Performance Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;=&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 60</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-string-expression)">&#39;Size&#39;</span><span style="color:var(--astro-code-token-keyword)">:&gt;6</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-string-expression)">&#39;Naive (ms)&#39;</span><span style="color:var(--astro-code-token-keyword)">:&gt;12</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-string-expression)">&#39;Tiled (ms)&#39;</span><span style="color:var(--astro-code-token-keyword)">:&gt;12</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-string-expression)">&#39;Speedup&#39;</span><span style="color:var(--astro-code-token-keyword)">:&gt;10</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-string-expression)">&#39;AI Improvement&#39;</span><span style="color:var(--astro-code-token-keyword)">:&gt;15</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;-&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 60</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> size </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> sizes</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Create matrices</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        A </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, size, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        B </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(size, size, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device, dtype</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.float32)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Naive implementation (PyTorch automatically uses optimized kernels)</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # For demonstration, we&#39;ll use a simple operation that isn&#39;t optimized</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Simulate naive memory access pattern</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            C_naive </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(A, B)</span><span style="color:var(--astro-code-token-comment)">  # This is actually optimized</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        naive_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 10</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Optimized tiled implementation (cuBLAS)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">10</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            C_tiled </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">matmul</span><span style="color:var(--astro-code-token-punctuation)">(A, B)</span><span style="color:var(--astro-code-token-comment)">  # Uses highly optimized tiled kernels</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        tiled_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time) </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 10</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Calculate theoretical arithmetic intensity improvement</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Naive: each element loaded once per use</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Tiled: each element loaded once per tile and reused TILE_SIZE times</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        tile_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 32</span><span style="color:var(--astro-code-token-comment)">  # Typical tile size</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        ai_improvement </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> tile_size  </span><span style="color:var(--astro-code-token-comment)"># Simplified calculation</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        speedup </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> naive_time </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> tiled_time </span><span style="color:var(--astro-code-token-keyword)">if</span><span style="color:var(--astro-code-foreground)"> tiled_time </span><span style="color:var(--astro-code-token-keyword)">&gt;</span><span style="color:var(--astro-code-token-constant)"> 0</span><span style="color:var(--astro-code-token-keyword)"> else</span><span style="color:var(--astro-code-token-constant)"> float</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;inf&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">        print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">size</span><span style="color:var(--astro-code-token-keyword)">:6d</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-punctuation)">naive_time</span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-keyword)">:11.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-punctuation)">tiled_time</span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-keyword)">:11.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-constant)"> {</span><span style="color:var(--astro-code-token-punctuation)">speedup</span><span style="color:var(--astro-code-token-keyword)">:9.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">ai_improvement</span><span style="color:var(--astro-code-token-keyword)">:14.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;\nMemory Access Pattern Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;-&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Demonstrate memory access patterns</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    def</span><span style="color:var(--astro-code-token-function)"> calculate_memory_accesses</span><span style="color:var(--astro-code-foreground)">(</span><span style="color:var(--astro-code-token-parameter)">matrix_size</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-parameter)"> tile_size</span><span style="color:var(--astro-code-foreground)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        &quot;&quot;&quot;Calculate memory accesses for tiled vs naive GEMM&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Naive GEMM: each output element requires loading full row and column</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        naive_loads </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> matrix_size </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 3</span><span style="color:var(--astro-code-token-comment)">  # N³ elements loaded</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Tiled GEMM: each tile loaded once and reused</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        tiles_per_dim </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (matrix_size </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> tile_size </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-foreground)">) </span><span style="color:var(--astro-code-token-keyword)">//</span><span style="color:var(--astro-code-foreground)"> tile_size</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        tile_loads </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> tiles_per_dim </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 3</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-foreground)"> tile_size </span><span style="color:var(--astro-code-token-keyword)">**</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-token-comment)">  # A and B tiles</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        return</span><span style="color:var(--astro-code-foreground)"> naive_loads</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tile_loads</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    matrix_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 1024</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    tile_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 32</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    naive_loads</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tiled_loads </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> calculate_memory_accesses</span><span style="color:var(--astro-code-token-punctuation)">(matrix_size, tile_size)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Matrix size: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">matrix_size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">×</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">matrix_size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Tile size: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">tile_size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">×</span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">tile_size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Naive memory loads: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">naive_loads</span><span style="color:var(--astro-code-token-keyword)">:,</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Tiled memory loads: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">tiled_loads</span><span style="color:var(--astro-code-token-keyword)">:,</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Memory access reduction: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">naive_loads</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)">tiled_loads</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Run the analysis</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">analyze_tiling_performance</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p><strong>Advanced Tiling Strategies:</strong></p>
<p><strong>1. Hierarchical Tiling:</strong>
Uses multiple levels of the memory hierarchy by combining L2 cache, shared memory, and register tiling:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>__global__ void hierarchical_tiled_gemm(float* A, float* B, float* C, int M, int N, int K) {</span></span>
<span class="line"><span>    // Level 1: L2 cache tiling (handled by block scheduling)</span></span>
<span class="line"><span>    // Level 2: Shared memory tiling</span></span>
<span class="line"><span>    __shared__ float As[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span>    __shared__ float Bs[TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Level 3: Register tiling</span></span>
<span class="line"><span>    float reg_tile[4][4] = {{0.0f}};</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Implementation combines all three levels for maximum performance</span></span>
<span class="line"><span>    // ... (detailed implementation would be quite complex)</span></span>
<span class="line"><span>}</span></span></code></pre>
<p><strong>2. Double Buffering:</strong>
Overlaps computation with memory loading to hide latency:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>__global__ void double_buffered_gemm(float* A, float* B, float* C, int M, int N, int K) {</span></span>
<span class="line"><span>    // Double buffer: two sets of shared memory</span></span>
<span class="line"><span>    __shared__ float As[2][TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span>    __shared__ float Bs[2][TILE_SIZE][TILE_SIZE];</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    int read_buf = 0, compute_buf = 1;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>    // Pipeline: load next tile while computing current tile</span></span>
<span class="line"><span>    for (int tile = 0; tile &lt; num_tiles; tile++) {</span></span>
<span class="line"><span>        // Swap buffers</span></span>
<span class="line"><span>        read_buf = 1 - read_buf;</span></span>
<span class="line"><span>        compute_buf = 1 - compute_buf;</span></span>
<span class="line"><span></span></span>
<span class="line"><span>        // Asynchronously load next tile</span></span>
<span class="line"><span>        // Compute using current tile</span></span>
<span class="line"><span>        // Overlap memory and compute operations</span></span>
<span class="line"><span>    }</span></span>
<span class="line"><span>}</span></span></code></pre>
<p><strong>Real-World Application: Convolution Optimization:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">functional </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> F</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> optimized_convolution_tiling</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Demonstrate tiling in convolution operations&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Large convolution problem</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    batch_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 32</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    in_channels </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 256</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    out_channels </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 512</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    height</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> width </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-constant)"> 64</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    kernel_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 3</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Input tensor and kernel</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    input_tensor </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(batch_size, in_channels, height, width, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    kernel </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">randn</span><span style="color:var(--astro-code-token-punctuation)">(out_channels, in_channels, kernel_size, kernel_size, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">device)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;Convolution Tiling Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;-&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Standard convolution (uses optimized tiled kernels internally)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    start_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> _ </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> range</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        output </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> F</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">conv2d</span><span style="color:var(--astro-code-token-punctuation)">(input_tensor, kernel, padding</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">synchronize</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimized_time </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> time</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">time</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> -</span><span style="color:var(--astro-code-foreground)"> start_time</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Calculate theoretical arithmetic intensity</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Each output pixel requires in_channels × kernel_size² operations</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    flops </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> (batch_size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> out_channels </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> height </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> width </span><span style="color:var(--astro-code-token-keyword)">*</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">             in_channels </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> kernel_size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-foreground)"> kernel_size </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)"> 2</span><span style="color:var(--astro-code-foreground)">)  </span><span style="color:var(--astro-code-token-comment)"># multiply-add</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Memory traffic (input + kernel + output)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    input_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> input_tensor</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    kernel_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> kernel</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    output_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> output</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">numel</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 4</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    total_bytes </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> input_bytes </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> kernel_bytes </span><span style="color:var(--astro-code-token-keyword)">+</span><span style="color:var(--astro-code-foreground)"> output_bytes</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    arithmetic_intensity </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> flops </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-foreground)"> total_bytes</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Convolution performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">optimized_time</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)">1000</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> ms per forward pass&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Arithmetic intensity: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">arithmetic_intensity</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> FLOPs/byte&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Throughput: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">flops</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)">optimized_time</span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)">1e12</span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-constant)">100</span><span style="color:var(--astro-code-token-keyword)">:.2f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Tiling strategy for convolution:</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 1. Tile input feature maps to fit in shared memory</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 2. Load kernel weights once per tile</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 3. Compute multiple output channels simultaneously</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # 4. Use register blocking for output accumulation</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> arithmetic_intensity</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">optimized_convolution_tiling</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p><strong>Tiling Best Practices:</strong></p>
<ol>
<li>
<p><strong>Choose Optimal Tile Size:</strong></p>
<ul>
<li>Consider shared memory capacity (typically 48-164KB per SM)</li>
<li>Balance between memory efficiency and occupancy</li>
<li>Power-of-2 sizes often work well (16, 32, 64)</li>
</ul>
</li>
<li>
<p><strong>Minimize Bank Conflicts:</strong></p>
<ul>
<li>Avoid simultaneous access to the same memory bank</li>
<li>Use padding or data layout transformations when necessary</li>
</ul>
</li>
<li>
<p><strong>Maximize Data Reuse:</strong></p>
<ul>
<li>Each element loaded into shared memory should be used multiple times</li>
<li>Consider register blocking for even more reuse</li>
</ul>
</li>
<li>
<p><strong>Handle Boundary Conditions:</strong></p>
<ul>
<li>Properly handle matrices that don’t divide evenly by tile size</li>
<li>Use conditional statements or padding as appropriate</li>
</ul>
</li>
<li>
<p><strong>Consider Memory Coalescing:</strong></p>
<ul>
<li>Ensure global memory accesses are coalesced</li>
<li>Adjacent threads should access adjacent memory locations</li>
</ul>
</li>
</ol>
<p>Tiling is one of the most powerful optimization techniques for GPU computing, often providing 5-50x performance improvements for memory-bound operations by fundamentally changing the arithmetic intensity profile of algorithms.</p>
<hr>
<h2 id="modern-gpu-computing-state-of-the-art-hardware-for-research">Modern GPU Computing: State-of-the-Art Hardware for Research</h2>
<h3 id="understanding-high-performance-computing-hpc-infrastructure">Understanding High-Performance Computing (HPC) Infrastructure</h3>
<p>Modern research computing relies heavily on <strong>High-Performance Computing (HPC)</strong> systems that combine powerful hardware with sophisticated software stacks. Understanding how to effectively utilize these systems is crucial for researchers working at the cutting edge of computational science, machine learning, and artificial intelligence.</p>
<p><strong>HPC System Architecture:</strong></p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>┌─────────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                       HPC Cluster Overview                      │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│  Login Nodes        │  Compute Nodes           │  Storage       │</span></span>
<span class="line"><span>│  ┌─────────────────┐ │  ┌─────────────────────┐ │  ┌───────────┐ │</span></span>
<span class="line"><span>│  │ User Access     │ │  │ CPU: 2x64-core AMD  │ │  │ Parallel  │ │</span></span>
<span class="line"><span>│  │ Job Submission  │ │  │ RAM: 512GB-2TB      │ │  │ File      │ │</span></span>
<span class="line"><span>│  │ Code Development│ │  │ GPU: 4-8x A100/H100 │ │  │ System    │ │</span></span>
<span class="line"><span>│  │ Data Analysis   │ │  │ NVLink/Infiniband   │ │  │ (Lustre/  │ │</span></span>
<span class="line"><span>│  └─────────────────┘ │  └─────────────────────┘ │  │ GPFS)     │ │</span></span>
<span class="line"><span>│                      │                         │  │ 10-100 PB │ │</span></span>
<span class="line"><span>│  Resource Mgmt       │  Scheduler (SLURM)      │  └───────────┘ │</span></span>
<span class="line"><span>│  ┌─────────────────┐ │  ┌─────────────────────┐ │               │</span></span>
<span class="line"><span>│  │ Job Queues      │ │  │ GPU Allocation      │ │  Networking   │</span></span>
<span class="line"><span>│  │ User Limits     │ │  │ Memory Management   │ │  ┌───────────┐ │</span></span>
<span class="line"><span>│  │ Fair Sharing    │ │  │ Process Isolation   │ │  │ InfiniBand│ │</span></span>
<span class="line"><span>│  └─────────────────┘ │  └─────────────────────┘ │  │ 200-400   │ │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────────┤  Gbps     │ │</span></span>
<span class="line"><span>                                                  │  │ Low Latency│ │</span></span>
<span class="line"><span>                                                  │  └───────────┘ │</span></span>
<span class="line"><span>                                                  └─────────────────┘</span></span></code></pre>
<h3 id="advanced-gpu-architectures-for-research">Advanced GPU Architectures for Research</h3>
<p>Understanding modern GPU architectures is fundamental for researchers working with high-performance computing. The evolution from graphics-focused processors to general-purpose parallel computing engines has created unprecedented opportunities for scientific discovery and computational research.</p>
<p><strong>NVIDIA A100 (Ampere Architecture) - The Research Workhorse:</strong></p>
<p>The A100 represents a paradigm shift in GPU design, specifically engineered for AI and HPC workloads. Unlike consumer GPUs that balance gaming and compute performance, the A100 is purpose-built for sustained computational throughput with features that directly address the needs of research computing.</p>
<p><strong>Architectural Deep Dive:</strong></p>
<p>The A100’s architecture is built around the concept of <strong>Streaming Multiprocessors (SMs)</strong>, each containing multiple types of processing units optimized for different computational patterns:</p>
<ul>
<li><strong>CUDA Cores</strong>: Traditional scalar processors optimized for single-precision floating-point operations</li>
<li><strong>Tensor Cores</strong>: Specialized matrix processing units that can perform mixed-precision matrix operations at high throughput</li>
<li><strong>RT Cores</strong>: Ray-tracing acceleration units (present in gaming variants but not emphasized in data center models)</li>
</ul>
<p><strong>Memory Subsystem Design:</strong></p>
<p>The A100’s memory architecture reflects a deep understanding of modern workload requirements:</p>
<ul>
<li><strong>HBM2 Memory</strong>: High Bandwidth Memory providing massive parallel access to large datasets</li>
<li><strong>Memory Controllers</strong>: Multiple independent controllers enabling concurrent access patterns</li>
<li><strong>Error Correction</strong>: Full ECC support ensuring data integrity for long-running scientific computations</li>
</ul>
<p><strong>Multi-Instance GPU (MIG) Technology:</strong></p>
<p>One of the A100’s most innovative features is MIG, which allows a single GPU to be partitioned into up to seven independent instances. This is particularly valuable for research environments where:</p>
<ul>
<li>Multiple researchers need guaranteed GPU resources</li>
<li>Different experiments have varying resource requirements</li>
<li>Cost efficiency requires maximizing utilization across diverse workloads</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">def</span><span style="color:var(--astro-code-token-function)"> analyze_a100_capabilities</span><span style="color:var(--astro-code-foreground)">():</span></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    &quot;&quot;&quot;Comprehensive analysis of A100 computational capabilities&quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # A100 specifications demonstrate the scale of modern research hardware</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    a100_specs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)"> {</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;sm_count&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 108</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">                    # Massive parallelism through many SMs</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;cuda_cores_per_sm&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">           # Traditional scalar processing power</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;tensor_cores_per_sm&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 4</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">          # Matrix acceleration per SM</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;total_cuda_cores&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 6912</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">          # Total scalar processing units</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;hbm2_memory&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">                 # GB - Can be 80GB in larger variants</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;memory_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1555</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">          # GB/s - Critical for data-intensive research</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;nvlink_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 600</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">           # GB/s bidirectional for multi-GPU scaling</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;pcie_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 64</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">              # GB/s (PCIe 4.0) for host communication</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Compute Performance demonstrates specialization for different precisions</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;fp32_performance&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 19.5</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">          # TFLOPS - Traditional scientific computing</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;tensor_fp16&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 312</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">                # TFLOPS - AI training acceleration</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;tensor_bfloat16&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 312</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">            # TFLOPS - Improved numerical stability</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;tensor_int8&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 624</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">                # TOPS - Inference optimization</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;tensor_int4&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 1248</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">               # TOPS - Extreme quantization with sparsity</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">        # Research-specific features</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;mig_support&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> True</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">               # Multi-tenancy for shared research clusters</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;sparsity_support&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> True</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">          # 2:4 structured sparsity acceleration</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;multi_precision&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> True</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">           # Mixed-precision training capabilities</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;nvlink_generation&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> 3</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">            # High-speed inter-GPU communication</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">        &#39;memory_error_correction&#39;</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-constant)"> True</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-comment)">   # Critical for long-running simulations</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;NVIDIA A100 Research Capabilities Analysis:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;=&quot;</span><span style="color:var(--astro-code-token-keyword)"> *</span><span style="color:var(--astro-code-token-constant)"> 60</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # The ridge point calculation reveals the balance between compute and memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    ridge_point </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> a100_specs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;fp32_performance&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> /</span><span style="color:var(--astro-code-foreground)"> (a100_specs</span><span style="color:var(--astro-code-token-punctuation)">[</span><span style="color:var(--astro-code-token-string-expression)">&#39;memory_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)"> /</span><span style="color:var(--astro-code-token-constant)"> 1000</span><span style="color:var(--astro-code-foreground)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Peak FP32 Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;fp32_performance&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Peak Memory Bandwidth: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;memory_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Ridge Point (AI threshold): </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">ridge_point</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> FLOPs/Byte&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Operations with AI &lt; </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">ridge_point</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> are memory-bound&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Operations with AI &gt; </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">ridge_point</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> can achieve compute-bound performance&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-string-expression)">Specialized Compute Units for Research:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;FP16 Tensor Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;tensor_fp16&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TFLOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;tensor_fp16&#39;</span><span style="color:var(--astro-code-token-punctuation)">] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;fp32_performance&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x faster than FP32 for compatible workloads&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;INT8 Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;tensor_int8&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;tensor_int8&#39;</span><span style="color:var(--astro-code-token-punctuation)">] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;fp32_performance&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x throughput for quantized inference&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Sparse INT4 Performance: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;tensor_int4&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> TOPS&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;tensor_int4&#39;</span><span style="color:var(--astro-code-token-punctuation)">] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-punctuation)"> a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;fp32_performance&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)">x theoretical peak with aggressive optimization&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Memory hierarchy analysis shows the importance of data locality</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    l2_cache_size </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 40</span><span style="color:var(--astro-code-token-comment)">  # MB - Shared across all SMs</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    shared_mem_per_sm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 164</span><span style="color:var(--astro-code-token-comment)">  # KB - Programmer-controlled fast memory</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    register_file_per_sm </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> 256</span><span style="color:var(--astro-code-token-comment)">  # KB - Fastest per-thread storage</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-string-expression)">Memory Hierarchy for Optimal Performance:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;L2 Cache: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">l2_cache_size</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> MB (shared across all </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;sm_count&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> SMs)&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Hardware-managed, reduces global memory pressure&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Shared Memory per SM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">shared_mem_per_sm</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> KB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Programmer-controlled, enables collaborative algorithms&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Register File per SM: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">register_file_per_sm</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> KB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Per-thread storage, highest bandwidth but limited capacity&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Total Fast Memory: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">shared_mem_per_sm </span><span style="color:var(--astro-code-token-keyword)">*</span><span style="color:var(--astro-code-token-punctuation)"> a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;sm_count&#39;</span><span style="color:var(--astro-code-token-punctuation)">] </span><span style="color:var(--astro-code-token-keyword)">/</span><span style="color:var(--astro-code-token-constant)"> 1024</span><span style="color:var(--astro-code-token-keyword)">:.1f</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> MB&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Critical for staging data and avoiding memory bottlenecks&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)">    # Research implications</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;</span><span style="color:var(--astro-code-token-punctuation)">\n</span><span style="color:var(--astro-code-token-string-expression)">Research Computing Implications:&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Multi-Instance GPU: Up to 7 independent partitions&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Enables secure multi-tenancy in shared research environments&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;NVLink Bandwidth: </span><span style="color:var(--astro-code-token-constant)">{</span><span style="color:var(--astro-code-token-punctuation)">a100_specs[</span><span style="color:var(--astro-code-token-string-expression)">&#39;nvlink_bandwidth&#39;</span><span style="color:var(--astro-code-token-punctuation)">]</span><span style="color:var(--astro-code-token-constant)">}</span><span style="color:var(--astro-code-token-string-expression)"> GB/s&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Supports large model training across multiple GPUs&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;Error Correction: Full ECC support&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-keyword)">f</span><span style="color:var(--astro-code-token-string-expression)">&quot;  -&gt; Essential for long-running simulations and reliable results&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    return</span><span style="color:var(--astro-code-foreground)"> a100_specs</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Demonstrate A100 capabilities for research applications</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">a100_specs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> analyze_a100_capabilities</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p><strong>NVIDIA H100 (Hopper Architecture) - The Next Generation:</strong></p>
<p>The H100 represents the latest evolution in GPU architecture, incorporating lessons learned from the AI revolution and pushing the boundaries of what’s possible in computational research. Built on advanced process technology and featuring revolutionary architectural improvements, the H100 addresses the scaling challenges faced by modern AI research.</p>
<p><strong>Key Architectural Innovations:</strong></p>
<p><strong>Transformer Engine:</strong> The H100 introduces hardware specifically designed for transformer architectures, the foundation of modern large language models. This includes:</p>
<ul>
<li><strong>Attention-specific acceleration:</strong> Hardware units optimized for the attention mechanism</li>
<li><strong>Dynamic precision management:</strong> Automatic adjustment of numerical precision based on training stability</li>
<li><strong>Sparsity acceleration:</strong> Hardware support for structured sparsity patterns common in modern neural networks</li>
</ul>
<p><strong>Advanced Memory Architecture:</strong></p>
<ul>
<li><strong>HBM3 Integration:</strong> Next-generation memory providing higher bandwidth and capacity</li>
<li><strong>Memory Deduplication:</strong> Hardware-level optimization for memory-efficient model serving</li>
<li><strong>Confidential Computing:</strong> Hardware-level security features for sensitive research data
<strong>GPU Generation Evolution for Research Computing:</strong></li>
</ul>
<p>This comparison illustrates the exponential improvement in research computing capabilities across three major GPU generations:</p>
<h4 id="nvidia-v100-volta-2017---foundation-of-modern-ai">NVIDIA V100 (Volta, 2017) - Foundation of Modern AI</h4>









































<div class="table-wrap"><table><thead><tr><th>Specification</th><th>Value</th></tr></thead><tbody><tr><td><strong>Memory</strong></td><td>32 GB</td></tr><tr><td><strong>Memory Bandwidth</strong></td><td>900 GB/s</td></tr><tr><td><strong>FP32 Performance</strong></td><td>15.7 TFLOPS</td></tr><tr><td><strong>Tensor FP16 Performance</strong></td><td>125 TFLOPS</td></tr><tr><td><strong>Architecture</strong></td><td>Volta (1st gen Tensor Cores)</td></tr><tr><td><strong>Process Node</strong></td><td>12nm TSMC</td></tr><tr><td><strong>Key Innovation</strong></td><td>First Tensor Cores - mixed-precision training</td></tr><tr><td><strong>Research Impact</strong></td><td>Enabled modern deep learning research</td></tr></tbody></table></div>
<h4 id="nvidia-a100-ampere-2020---large-scale-ai-training">NVIDIA A100 (Ampere, 2020) - Large-Scale AI Training</h4>













































<div class="table-wrap"><table><thead><tr><th>Specification</th><th>Value</th></tr></thead><tbody><tr><td><strong>Memory</strong></td><td>80 GB (max variant)</td></tr><tr><td><strong>Memory Bandwidth</strong></td><td>2,039 GB/s</td></tr><tr><td><strong>FP32 Performance</strong></td><td>19.5 TFLOPS</td></tr><tr><td><strong>Tensor FP16 Performance</strong></td><td>312 TFLOPS</td></tr><tr><td><strong>Architecture</strong></td><td>Ampere (2nd gen with sparsity)</td></tr><tr><td><strong>Process Node</strong></td><td>7nm</td></tr><tr><td><strong>Tensor Cores</strong></td><td>Gen 3 - more data types</td></tr><tr><td><strong>Key Innovation</strong></td><td>MIG + 2<div></div> structured sparsity</td></tr><tr><td><strong>Research Impact</strong></td><td>Large-scale language model training</td></tr></tbody></table></div>
<h4 id="nvidia-h100-hopper-2022---trillion-parameter-research">NVIDIA H100 (Hopper, 2022) - Trillion-Parameter Research</h4>













































<div class="table-wrap"><table><thead><tr><th>Specification</th><th>Value</th></tr></thead><tbody><tr><td><strong>Memory</strong></td><td>80 GB</td></tr><tr><td><strong>Memory Bandwidth</strong></td><td>3,350 GB/s</td></tr><tr><td><strong>FP32 Performance</strong></td><td>67 TFLOPS (with sparsity)</td></tr><tr><td><strong>Tensor FP16 Performance</strong></td><td>1,979 TFLOPS (with sparsity)</td></tr><tr><td><strong>Architecture</strong></td><td>Hopper (4th gen with transformer engine)</td></tr><tr><td><strong>Process Node</strong></td><td>4nm</td></tr><tr><td><strong>Tensor Cores</strong></td><td>Gen 4 - transformer-optimized</td></tr><tr><td><strong>Key Innovation</strong></td><td>Transformer Engine, FP8 support, confidential computing</td></tr><tr><td><strong>Research Impact</strong></td><td>Trillion-parameter model research</td></tr></tbody></table></div>
<h4 id="performance-evolution-baseline-v100">Performance Evolution (baseline: V100)</h4>



































<div class="table-wrap"><table><thead><tr><th>Metric</th><th>A100 vs V100</th><th>H100 vs V100</th></tr></thead><tbody><tr><td><strong>Memory Capacity</strong></td><td>2.5x</td><td>2.5x</td></tr><tr><td><strong>Memory Bandwidth</strong></td><td>2.3x</td><td>3.7x</td></tr><tr><td><strong>Tensor Compute</strong></td><td>2.5x</td><td>15.8x</td></tr><tr><td><strong>Research Capability</strong></td><td>~5.7x</td><td>~9.3x</td></tr><tr><td><strong>Training Efficiency</strong></td><td>~5.7x</td><td>~58.5x</td></tr></tbody></table></div>
<h4 id="research-milestones-by-generation">Research Milestones by Generation</h4>

























<div class="table-wrap"><table><thead><tr><th>V100 (2017)</th><th>A100 (2020)</th><th>H100 (2022)</th></tr></thead><tbody><tr><td>BERT</td><td>GPT-3</td><td>GPT-4 class models</td></tr><tr><td>GPT-1</td><td>PaLM</td><td>Multi-modal transformers</td></tr><tr><td>ResNet at scale</td><td>Stable Diffusion</td><td>Real-time inference</td></tr></tbody></table></div>
<h3 id="framework-and-library-ecosystem">Framework and Library Ecosystem</h3>
<p>The software ecosystem surrounding GPU computing has evolved, creating a complex landscape of tools, frameworks, and libraries. Understanding this ecosystem is crucial for making informed decisions about research infrastructure and development approaches.</p>
<p><strong>The Modern Deep Learning Framework Landscape:</strong></p>
<p>The choice of deep learning framework fundamentally affects research productivity, debugging capabilities, and ultimate performance. Each framework embodies different philosophies about how computational graphs should be constructed, optimized, and executed.</p>
<p><strong>PyTorch - The Research Standard:</strong></p>
<p>PyTorch’s success in research environments stems from its <strong>eager execution model</strong>, which means operations are executed immediately as they are encountered in Python code. This design philosophy prioritizes developer experience and debugging ease over execution efficiency.</p>
<p><strong>Key Research Advantages:</strong></p>
<ul>
<li>
<p><strong>Dynamic Computation Graphs:</strong> The computational graph is built on-the-fly, enabling easy implementation of dynamic architectures like recursive neural networks, attention mechanisms with variable sequence lengths, and reinforcement learning algorithms where the computation depends on runtime decisions.</p>
</li>
<li>
<p><strong>Pythonic Interface:</strong> PyTorch feels like native Python, making it intuitive for researchers who are primarily domain experts rather than systems programmers. This reduces the cognitive overhead of implementing complex algorithms.</p>
</li>
<li>
<p><strong>Debugging Experience:</strong> Standard Python debugging tools work seamlessly with PyTorch, enabling researchers to set breakpoints, inspect tensor values, and trace execution flow using familiar tools.</p>
</li>
<li>
<p><strong>Research Ecosystem:</strong> The majority of recent research papers provide PyTorch implementations, creating a network effect where researchers can easily build upon previous work.</p>
</li>
</ul>
<p><strong>JAX - High-Performance Scientific Computing:</strong></p>
<p>JAX represents a different approach, emphasizing <strong>functional programming</strong> and <strong>mathematical composability</strong>. It’s designed around the principle that research code should be mathematically clear and amenable to automatic optimization.</p>
<p><strong>Key Research Advantages:</strong></p>
<ul>
<li>
<p><strong>Automatic Differentiation:</strong> JAX can automatically compute derivatives of arbitrary Python functions, including higher-order derivatives, which is crucial for certain research areas like meta-learning and physics-informed neural networks.</p>
</li>
<li>
<p><strong>Transformation System:</strong> JAX provides composable transformations (jit, grad, vmap, pmap) that can be combined to automatically parallelize, vectorize, and optimize research code without manual kernel writing.</p>
</li>
<li>
<p><strong>NumPy Compatibility:</strong> JAX provides a drop-in replacement for NumPy, making it easier to accelerate existing scientific computing code.</p>
</li>
<li>
<p><strong>XLA Backend:</strong> Leverages Google’s XLA compiler for aggressive optimization, often achieving better performance than PyTorch for research code that fits the functional programming paradigm.</p>
</li>
</ul>
<p><strong>TensorFlow - Production and Scale:</strong></p>
<p>While less popular in pure research settings, TensorFlow remains important for research that needs to scale to production or requires specific ecosystem features.</p>
<p><strong>Key Capabilities:</strong></p>
<ul>
<li><strong>TensorBoard Integration:</strong> Sophisticated visualization and monitoring tools</li>
<li><strong>Serving Infrastructure:</strong> Built-in support for model deployment and serving</li>
<li><strong>Mobile/Edge Deployment:</strong> TensorFlow Lite for edge device research</li>
<li><strong>Federated Learning:</strong> TensorFlow Federated for distributed learning research</li>
</ul>
<h4 id="deep-learning-framework-comparison">Deep Learning Framework Comparison</h4>





















































<div class="table-wrap"><table><thead><tr><th>Aspect</th><th>PyTorch</th><th>JAX</th><th>TensorFlow</th></tr></thead><tbody><tr><td><strong>Execution Model</strong></td><td>Eager with dynamic graphs</td><td>Functional with transformations</td><td>Graph with eager option</td></tr><tr><td><strong>Research Suitability</strong></td><td>Excellent</td><td>Excellent</td><td>Moderate</td></tr><tr><td><strong>Debugging</strong></td><td>Excellent</td><td>Good</td><td>Moderate</td></tr><tr><td><strong>Performance</strong></td><td>Good</td><td>Excellent</td><td>Excellent</td></tr><tr><td><strong>Learning Curve</strong></td><td>Moderate</td><td>Steep</td><td>Moderate-High</td></tr><tr><td><strong>Auto-optimization</strong></td><td>Limited</td><td>Excellent (XLA)</td><td>Good</td></tr><tr><td><strong>Community</strong></td><td>Academic research</td><td>Scientific computing</td><td>Industry/production</td></tr></tbody></table></div>
<p><strong>Primary Use Cases:</strong></p>





















<div class="table-wrap"><table><thead><tr><th>Framework</th><th>Best For</th></tr></thead><tbody><tr><td><strong>PyTorch</strong></td><td>Novel architectures, rapid prototyping, CV, NLP, RL, generative models</td></tr><tr><td><strong>JAX</strong></td><td>Scientific ML, physics-informed NNs, Bayesian DL, meta-learning, optimization</td></tr><tr><td><strong>TensorFlow</strong></td><td>Production systems, distributed training, federated learning, mobile/edge AI</td></tr></tbody></table></div>
<p><strong>Framework Selection Quick Reference:</strong></p>

























































<div class="table-wrap"><table><thead><tr><th>Research Area</th><th>Recommended Framework</th></tr></thead><tbody><tr><td>Novel Architecture Development</td><td>PyTorch</td></tr><tr><td>High-Performance Scientific Computing</td><td>JAX</td></tr><tr><td>Large-Scale Production Research</td><td>TensorFlow</td></tr><tr><td>Rapid Prototyping</td><td>PyTorch</td></tr><tr><td>Mathematical/Physics-Informed ML</td><td>JAX</td></tr><tr><td>Distributed Training Research</td><td>PyTorch or TensorFlow</td></tr><tr><td>Edge/Mobile AI Research</td><td>TensorFlow</td></tr><tr><td>Optimization Algorithm Development</td><td>JAX</td></tr><tr><td>Computer Vision Research</td><td>PyTorch</td></tr><tr><td>Natural Language Processing</td><td>PyTorch</td></tr><tr><td>Federated Learning</td><td>TensorFlow</td></tr><tr><td>Bayesian Deep Learning</td><td>JAX</td></tr></tbody></table></div>
<p><strong>Performance-Optimized Libraries - The Hidden Performance Multipliers:</strong></p>
<p>Beyond the main frameworks, the choice of supporting libraries can impact research productivity and computational efficiency. Many researchers unknowingly accept performance bottlenecks by using standard libraries when high-performance alternatives exist.</p>
<p><strong>The Performance Library Ecosystem:</strong></p>
<p>Modern research computing benefits enormously from libraries specifically designed for high-performance workloads. These libraries often provide 5-50x performance improvements over standard Python libraries through techniques like:</p>
<ul>
<li><strong>Lazy Evaluation:</strong> Deferring computation until necessary and optimizing entire operation chains</li>
<li><strong>Parallel Processing:</strong> Automatically utilizing multiple CPU cores and SIMD instructions</li>
<li><strong>Memory Optimization:</strong> Minimizing memory allocations and improving cache utilization</li>
<li><strong>Native Code Generation:</strong> JIT compilation to machine code rather than Python bytecode interpretation</li>
</ul>
<h4 id="performance-optimized-library-ecosystem">Performance-Optimized Library Ecosystem</h4>















































<div class="table-wrap"><table><thead><tr><th>Domain</th><th>Standard</th><th>Optimized</th><th>Speedup</th><th>Memory Improvement</th></tr></thead><tbody><tr><td><strong>Data Processing</strong></td><td>pandas</td><td>polars</td><td>5-50x</td><td>2-10x less</td></tr><tr><td><strong>Numerical Computing</strong></td><td>numpy</td><td>numba</td><td>10-100x</td><td>Near C-level</td></tr><tr><td><strong>Deep Learning</strong></td><td>PyTorch (eager)</td><td>JAX + XLA</td><td>2-10x</td><td>Better fusion</td></tr><tr><td><strong>Visualization</strong></td><td>matplotlib</td><td>bokeh + datashader</td><td>3-20x</td><td>Streaming reduces needs</td></tr><tr><td><strong>Machine Learning</strong></td><td>scikit-learn</td><td>rapids-cuml</td><td>10-50x</td><td>GPU utilization</td></tr></tbody></table></div>
<strong>Data Processing: pandas → polars</strong><p><strong>Key Technical Innovations:</strong></p><ul>
<li>Lazy evaluation with query optimization</li>
<li>Parallel processing across multiple cores</li>
<li>Apache Arrow memory format</li>
<li>Rust-based implementation for zero-cost abstractions</li>
</ul><p><strong>Research Applications:</strong></p><ul>
<li>Large-scale dataset preprocessing</li>
<li>Time-series analysis for financial research</li>
<li>Genomics data processing</li>
<li>Social network analysis</li>
</ul><p><strong>When to Use:</strong> Datasets &gt; 1GB, complex transformations, memory constraints</p>
<strong>Numerical Computing: numpy → numba</strong><p><strong>Key Technical Innovations:</strong></p><ul>
<li>Just-In-Time (JIT) compilation to machine code</li>
<li>Automatic parallelization of loops</li>
<li>GPU acceleration via CUDA</li>
<li>Type specialization for optimal performance</li>
</ul><p><strong>Research Applications:</strong></p><ul>
<li>Monte Carlo simulations</li>
<li>Numerical optimization algorithms</li>
<li>Signal processing research</li>
<li>Computational physics simulations</li>
</ul><p><strong>When to Use:</strong> Compute-heavy loops, numerical algorithms, custom kernels needed</p>
<strong>Deep Learning: PyTorch → JAX + XLA</strong><p><strong>Key Technical Innovations:</strong></p><ul>
<li>XLA compiler for graph optimization</li>
<li>Automatic operator fusion</li>
<li>Advanced memory layout optimization</li>
<li>Cross-device optimization</li>
</ul><p><strong>Research Applications:</strong></p><ul>
<li>Large-scale transformer training</li>
<li>Scientific machine learning</li>
<li>High-throughput inference research</li>
<li>Multi-device training optimization</li>
</ul><p><strong>When to Use:</strong> Performance-critical training, large models, production research</p>
<strong>Visualization: matplotlib → bokeh + datashader</strong><p><strong>Key Technical Innovations:</strong></p><ul>
<li>Web-based rendering with GPU acceleration</li>
<li>Automatic data aggregation for large datasets</li>
<li>Interactive exploration without memory limits</li>
<li>Streaming data visualization</li>
</ul><p><strong>Research Applications:</strong></p><ul>
<li>Large-scale data exploration</li>
<li>Real-time training monitoring</li>
<li>Interactive research presentations</li>
<li>Collaborative data analysis</li>
</ul><p><strong>When to Use:</strong> &gt;1M data points, interactive analysis, web deployment needed</p>
<strong>Machine Learning: scikit-learn → rapids-cuml</strong><p><strong>Key Technical Innovations:</strong></p><ul>
<li>GPU-accelerated implementations of ML algorithms</li>
<li>CUDA-native data structures</li>
<li>Automatic CPU-GPU memory management</li>
<li>Scikit-learn compatible API</li>
</ul><p><strong>Research Applications:</strong></p><ul>
<li>Large-scale clustering analysis</li>
<li>High-dimensional data preprocessing</li>
<li>Ensemble method research</li>
<li>Traditional ML on large datasets</li>
</ul><p><strong>When to Use:</strong> Traditional ML on large datasets, GPU resources available</p>
<h4 id="library-migration-guide">Library Migration Guide</h4>








































<div class="table-wrap"><table><thead><tr><th>Migration</th><th>Difficulty</th><th>Time</th><th>API Compatibility</th><th>Tip</th></tr></thead><tbody><tr><td>pandas → polars</td><td>Easy</td><td>1-2 hrs</td><td>High</td><td>Start with <code>.lazy()</code> operations</td></tr><tr><td>numpy → numba</td><td>Moderate</td><td>4-8 hrs</td><td>Medium</td><td>Add <code>@numba.jit</code> to heavy functions</td></tr><tr><td>matplotlib → bokeh</td><td>Moderate</td><td>6-10 hrs</td><td>Low</td><td>Start simple, add interactivity</td></tr><tr><td>scikit-learn → rapids</td><td>Easy</td><td>2-4 hrs</td><td>High</td><td>Ensure CUDA compatibility first</td></tr></tbody></table></div>
<hr>
<h2 id="summary">Summary</h2>
<p>This comprehensive guide explores the evolution of computing from traditional CPUs to modern GPU-accelerated systems, providing researchers and engineers with essential knowledge for high-performance computing.</p>
<h3 id="key-topics-covered">Key Topics Covered</h3>

































<div class="table-wrap"><table><thead><tr><th>Section</th><th>Topics</th></tr></thead><tbody><tr><td><strong>Fundamental Concepts</strong></td><td>Hardware-software duality, CPU architecture, parallel computing paradigm shift</td></tr><tr><td><strong>GPU Architecture</strong></td><td>SIMD vs SIMT, thread hierarchy, memory architecture</td></tr><tr><td><strong>Performance Analysis</strong></td><td>Arithmetic intensity, memory-bound vs compute-bound kernels, A100 modeling</td></tr><tr><td><strong>Optimization Strategies</strong></td><td>Operator fusion, tiling techniques, hierarchical optimization</td></tr><tr><td><strong>Research Hardware</strong></td><td>V100 → A100 → H100 evolution, HPC infrastructure, MIG</td></tr><tr><td><strong>Software Ecosystem</strong></td><td>PyTorch/JAX/TensorFlow comparison, performance libraries, migration strategies</td></tr></tbody></table></div>
<h3 id="research-impact">Research Impact</h3>
<p>This guide enables researchers to make informed decisions about hardware selection, software frameworks, and optimization strategies. Understanding these concepts is crucial for maximizing computational efficiency in modern research workflows, from small-scale experimentation to large-scale distributed training.</p>
<h3 id="target-audience">Target Audience</h3>
<p>Researchers, graduate students, and engineers in computational science, machine learning, and high-performance computing who need to understand and optimize GPU-accelerated workflows.</p>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>GPUs</category>
            <category>HPC</category>
            <category>Kernels</category>
            <enclosure length="53164" type="image/png" url="https://blog.ecitis.org/open-graph/understanding-gpus.png"/>
        </item>
        <item>
            <title><![CDATA[Modern Generative AI Guide]]></title>
            <link>https://blog.ecitis.org/generative-ai-guide/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/generative-ai-guide/</guid>
            <pubDate>Wed, 10 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A grounded map of models, datasets, runtimes, and provider tools for building modern generative-AI systems without cargo-cult defaults.]]></description>
            <content:encoded><![CDATA[<h2 id="why-this-guide">Why this guide</h2>
<p>Modern generative AI moves quickly. Don’t lock yourself into defaults like “BERT + FAISS + all-MiniLM” just because a quick tutorial said so. Learn the landscape, then pick the right tools for your use case. This guide covers Hugging Face (models, datasets, formats), local and server runtimes (llama.cpp, Ollama, vLLM), and multi-provider access via OpenRouter. It also includes practical patterns for building solid projects.</p>
<hr>
<h2 id="key-concepts-quick-primer">Key concepts (quick primer)</h2>
<ul>
<li>Foundation vs instruction-tuned models: base models (e.g., Llama 3.1 base) vs chat/instruct versions fine-tuned for following instructions.</li>
<li>Context window: maximum tokens per request; large contexts (e.g., 128k) change RAG and prompting strategies.</li>
<li>Tokenization: different families (SentencePiece/BPE) affect token count and latency.</li>
<li>Throughput vs latency: batch/continuous batching, quantization, and hardware determine cost/perf.</li>
</ul>
<figure><figcaption><strong>Generative AI Ecosystem</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/generative-ai-guide-0.webp" width="512" height="712" alt="Generative AI Ecosystem" loading="lazy" decoding="async"></figure>
<hr>
<h2 id="hugging-face-the-ecosystem-youll-actually-use">Hugging Face: the ecosystem you’ll actually use</h2>
<p>Hugging Face is a community hub for models, datasets, and apps (Spaces). You’ll use it to:</p>
<ul>
<li>Discover models and their licenses, sizes, and evals</li>
<li>Pull canonical weights/configs and tokenizers</li>
<li>Load and stream datasets at scale</li>
<li>Reproduce and compare baselines</li>
</ul>
<h3 id="models-on-the-hub">Models on the Hub</h3>
<p>Typical repository contents for Transformer LLMs:</p>
<ul>
<li><code>config.json</code> (architecture and hyperparameters)</li>
<li><code>tokenizer.json</code> or <code>tokenizer.model</code> (+ vocab files)</li>
<li>Weight files: <code>.safetensors</code> (preferred) or legacy <code>.bin</code></li>
<li><code>generation_config.json</code> (sampling defaults)</li>
</ul>
<p>You’ll find families like Llama, Mistral, Qwen, Phi, Gemma, OpenMistral, etc., in multiple sizes and instruction variants. Read model cards for:</p>
<ul>
<li>License (commercial use?), safety notes, and evaluation metrics</li>
<li>Context length, tokenizer details, and memory requirements</li>
<li>Recommended inference backends (e.g., vLLM, TGI, llama.cpp, TensorRT-LLM)</li>
</ul>
<h3 id="datasets-on-the-hub">Datasets on the Hub</h3>
<p>Use the <code>datasets</code> library for large and streaming loads:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> datasets </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> load_dataset</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Stream JSONL shards directly from the Hub</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">ds </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;my-org/my-dataset&quot;</span><span style="color:var(--astro-code-token-punctuation)">, split</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">, streaming</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> row </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> ds</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">take</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">    print</span><span style="color:var(--astro-code-token-punctuation)">(row)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Local JSONL</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">ds_local </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> load_dataset</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;json&quot;</span><span style="color:var(--astro-code-token-punctuation)">, data_files</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;train&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;data.jsonl&quot;</span><span style="color:var(--astro-code-token-punctuation)">})</span></span></code></pre>
<p>Datasets support map/filter/shuffle, memory-mapped Arrow, and push/pull for collaboration. Prefer canonical public datasets for benchmarking when possible.</p>
<h3 id="model-formats-youll-encounter">Model formats you’ll encounter</h3>
<ul>
<li>PyTorch weights: <code>.safetensors</code> (zero-copy, safe); the default for Transformers</li>
<li>GGUF: quantized, CPU/GPU-friendly format for <code>llama.cpp</code> and <code>Ollama</code></li>
<li>ONNX: graph format for cross-runtime acceleration (ONNX Runtime, DirectML)</li>
<li>TensorRT-LLM/MLC/AWQ/GPTQ: specialized compilers/quantization stacks; check model card support</li>
</ul>
<p>Use format-native runtimes when you want portability (GGUF), maximum throughput (TensorRT-LLM), or server features (vLLM).</p>
<hr>
<h2 id="local-inference-llamacpp-and-ollama">Local inference: llama.cpp and Ollama</h2>
<h3 id="llamacpp-cc-low-dependency-runtime">llama.cpp (C/C++ low-dependency runtime)</h3>
<p>Best for lightweight local inference and experimentation; strong CPU quantization support and GPU offload.</p>
<ol>
<li>Convert HF weights to GGUF (scripts in the <code>llama.cpp</code> repo):</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Example flow (exact script names vary by model family)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> llama.cpp/convert.py</span><span style="color:var(--astro-code-token-string)"> --model</span><span style="color:var(--astro-code-token-string)"> /path/to/hf-model</span><span style="color:var(--astro-code-token-string)"> --out</span><span style="color:var(--astro-code-token-string)"> model.gguf</span><span style="color:var(--astro-code-token-string)"> --dtype</span><span style="color:var(--astro-code-token-string)"> q4_k_m</span></span></code></pre>
<ol>
<li>Run inference:</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">./main</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> model.gguf</span><span style="color:var(--astro-code-token-string)"> -p</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Explain transformers in 3 bullet points&quot;</span><span style="color:var(--astro-code-token-string)"> -n</span><span style="color:var(--astro-code-token-constant)"> 256</span><span style="color:var(--astro-code-token-string)"> --temp</span><span style="color:var(--astro-code-token-constant)"> 0.7</span></span></code></pre>
<p>Tips:</p>
<ul>
<li>Choose quantization to fit RAM/VRAM (e.g., Q4_K_M vs Q5_K_S). Heavier quantization → smaller, faster, lower fidelity.</li>
<li>Stable prompts and stop tokens matter for repeatable results.</li>
</ul>
<h3 id="ollama-developer-friendly-local-model-runner">Ollama (developer-friendly local model runner)</h3>
<p>Ollama wraps GGUF models with a simple CLI and local REST API.</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># Install from docs, then:</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> llama3.1</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> llama3.1</span></span></code></pre>
<p>Custom <code>Modelfile</code>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span>FROM llama3.1</span></span>
<span class="line"><span>PARAMETER temperature 0.7</span></span>
<span class="line"><span>SYSTEM You are a helpful, terse assistant.</span></span></code></pre>
<p>Then:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> create</span><span style="color:var(--astro-code-token-string)"> my-assistant</span><span style="color:var(--astro-code-token-string)"> -f</span><span style="color:var(--astro-code-token-string)"> Modelfile</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> my-assistant</span></span></code></pre>
<p>Use the local HTTP API for apps. Good for prototypes and offline demos.</p>
<hr>
<h2 id="server-grade-inference-vllm-openai-compatible">Server-grade inference: vLLM (OpenAI-compatible)</h2>
<p>vLLM is a high-throughput LLM server with PagedAttention and continuous batching. It serves HF models via an OpenAI-compatible API.</p>
<p>Run a model:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> vllm</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> vllm.entrypoints.openai.api_server</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --model</span><span style="color:var(--astro-code-token-string)"> meta-llama/Meta-Llama-3.1-8B-Instruct</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --max-model-len</span><span style="color:var(--astro-code-token-constant)"> 32768</span><span style="color:var(--astro-code-foreground)"> \</span></span>
<span class="line"><span style="color:var(--astro-code-token-string)">  --tensor-parallel-size</span><span style="color:var(--astro-code-token-constant)"> 1</span></span></code></pre>
<p>Client (OpenAI SDK-style):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> OpenAI</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OpenAI</span><span style="color:var(--astro-code-token-punctuation)">(base_url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;http://localhost:8000/v1&quot;</span><span style="color:var(--astro-code-token-punctuation)">, api_key</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;sk-no-key&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">resp </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">chat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">completions</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">create</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[{</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;Write a haiku about GPUs.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(resp.choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].message.content)</span></span></code></pre>
<p>Notes:</p>
<ul>
<li>Tune <code>--gpu-memory-utilization</code>, <code>--max-model-len</code>, and tensor parallel degree to hardware.</li>
<li>vLLM supports many model families and quantization schemes; check docs for AWQ/GPTQ integration.</li>
</ul>
<hr>
<h2 id="multi-provider-access-openrouter">Multi-provider access: OpenRouter</h2>
<p>OpenRouter aggregates frontier and open models behind a single, OpenAI-compatible API. You pick the model; they route to providers (price/perf vary by model).</p>
<p>Setup:</p>
<ol>
<li>Create an API key in your dashboard.</li>
<li>Use an OpenAI-compatible client with <code>base_url</code> and your key.</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> OpenAI</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OpenAI</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    base_url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;https://openrouter.ai/api/v1&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    api_key</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">os.environ[</span><span style="color:var(--astro-code-token-string-expression)">&quot;OPENROUTER_API_KEY&quot;</span><span style="color:var(--astro-code-token-punctuation)">],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">resp </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">chat</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">completions</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">create</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;anthropic/claude-3.5-sonnet&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[{</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;Compare Llama 3.1 and Qwen 2.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(resp.choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].message.content)</span></span></code></pre>
<p>Why use it:</p>
<ul>
<li>Try multiple state-of-the-art models quickly (e.g., Claude, GPT-4o, Llama, Mistral, Qwen) without new SDKs.</li>
<li>Compare quality, latency, and price before you commit to one vendor.</li>
</ul>
<hr>
<h2 id="dont-default-your-stack-embeddings-vector-stores-and-rag">Don’t default your stack: embeddings, vector stores, and RAG</h2>
<p>Instead of always picking <code>all-MiniLM</code> + FAISS:</p>
<ul>
<li>Evaluate multiple embedding models: <code>bge-base(-en)-v1.5</code>, <code>gte-*</code>, <code>e5-*</code>, <code>voyage-*</code> (paid), domain-specific encoders. Use public evals (MTEB) and your own retrieval tests.</li>
<li>Vector DBs: try FAISS for in-process; or production stores like Qdrant, Weaviate, pgvector, Milvus, LanceDB. Consider recall, filtering, hybrid search, and operational fit.</li>
<li>Chunking: semantic-aware chunking often beats naive fixed-size. Keep overlap small; store titles/headers separately.</li>
<li>RAG pipeline: retrieval → re-ranking (e.g., cross-encoder) → structured prompts. Cache retrieved context and responses.</li>
</ul>
<p>Minimal retrieval sketch (HF embeddings + Qdrant):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> sentence_transformers </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> SentenceTransformer</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> qdrant_client </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> QdrantClient</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> qdrant_client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">models </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> Distance</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> VectorParams</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> PointStruct</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> SentenceTransformer</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;sentence-transformers/all-MiniLM-L6-v2&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-comment)">  # swap for stronger embeddings</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> QdrantClient</span><span style="color:var(--astro-code-token-punctuation)">(host</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;localhost&quot;</span><span style="color:var(--astro-code-token-punctuation)">, port</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">6333</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">recreate_collection</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    collection_name</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;docs&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    vectors_config</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)">VectorParams</span><span style="color:var(--astro-code-token-punctuation)">(size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">384</span><span style="color:var(--astro-code-token-punctuation)">, distance</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">Distance.COSINE),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">texts </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> [</span><span style="color:var(--astro-code-token-string-expression)">&quot;Doc A...&quot;</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-token-string-expression)"> &quot;Doc B...&quot;</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">vecs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">(texts, normalize_embeddings</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">upsert</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;docs&quot;</span><span style="color:var(--astro-code-token-punctuation)">, [</span><span style="color:var(--astro-code-token-function)">PointStruct</span><span style="color:var(--astro-code-token-punctuation)">(id</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">i, vector</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">vecs[i], payload</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">{</span><span style="color:var(--astro-code-token-string-expression)">&quot;text&quot;</span><span style="color:var(--astro-code-token-punctuation)">: t}) </span><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-token-punctuation)"> i, t </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(texts)])</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">query </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)"> &quot;What is in Doc B?&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">qvec </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">encode</span><span style="color:var(--astro-code-token-punctuation)">([query], normalize_embeddings</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-foreground)">[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-foreground)">]</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">hits </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> client</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">search</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&quot;docs&quot;</span><span style="color:var(--astro-code-token-punctuation)">, query_vector</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">qvec, limit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">3</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Swap the encoder, add reranking, and evaluate retrieval quality. Don’t assume defaults are best.</p>
<hr>
<h2 id="building-good-projects-patterns-that-ship">Building good projects: patterns that ship</h2>
<ul>
<li>Prompting as an interface: define schemas (JSON Mode, function/tool calling) and validate outputs.</li>
<li>Evals: build small, automatic tests early. Use task-specific metrics (e.g., exact match for QA, Rouge/BLEU for summarization) and Golden Sets. Tools like Ragas can help for RAG.</li>
<li>Guardrails: Pydantic validation, regex/JSON schema, allow lists. Consider safety filters for public apps.</li>
<li>Observability: log prompts, latencies, tokens, costs. Redact PII. Track drift.</li>
<li>Caching: local + hosted caches (e.g., KV stores). Deduplicate identical requests and retrieved contexts.</li>
<li>Cost control: use smaller models for most traffic; escalate to larger ones only when needed (router policies).</li>
</ul>
<hr>
<h2 id="choosing-the-right-runtime">Choosing the right runtime</h2>
<ul>
<li>Prototyping/offline: <code>Ollama</code> (GGUF) or <code>llama.cpp</code>; low setup, great UX.</li>
<li>Single-node serving: <code>vLLM</code>; high throughput, OpenAI-compatible API for apps.</li>
<li>Cloud APIs: <code>OpenRouter</code>; fast model switching/comparison across providers.</li>
<li>Hardware-optimized: <code>TensorRT-LLM</code>, <code>Hugging Face TGI</code>, or vendor-specific services for maximum perf.</li>
</ul>
<hr>
<h2 id="quick-starts">Quick starts</h2>
<ol>
<li>Local chat with Ollama</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> pull</span><span style="color:var(--astro-code-token-string)"> mistral</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">ollama</span><span style="color:var(--astro-code-token-string)"> run</span><span style="color:var(--astro-code-token-string)"> mistral</span></span></code></pre>
<ol>
<li>Serve Llama 3.1 with vLLM and call it with OpenAI SDK</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-string)"> vllm.entrypoints.openai.api_server</span><span style="color:var(--astro-code-token-string)"> --model</span><span style="color:var(--astro-code-token-string)"> meta-llama/Meta-Llama-3.1-8B-Instruct</span></span></code></pre>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> OpenAI</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OpenAI</span><span style="color:var(--astro-code-token-punctuation)">(base_url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;http://localhost:8000/v1&quot;</span><span style="color:var(--astro-code-token-punctuation)">, api_key</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;sk-no-key&quot;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(client.chat.completions.</span><span style="color:var(--astro-code-token-function)">create</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;meta-llama/Meta-Llama-3.1-8B-Instruct&quot;</span><span style="color:var(--astro-code-token-punctuation)">, messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[{</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;Say hi&quot;</span><span style="color:var(--astro-code-token-punctuation)">}]).choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].message.content)</span></span></code></pre>
<ol>
<li>Try multiple frontier models via OpenRouter</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> openai </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> OpenAI</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">client </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> OpenAI</span><span style="color:var(--astro-code-token-punctuation)">(base_url</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;https://openrouter.ai/api/v1&quot;</span><span style="color:var(--astro-code-token-punctuation)">, api_key</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">os.environ[</span><span style="color:var(--astro-code-token-string-expression)">&quot;OPENROUTER_API_KEY&quot;</span><span style="color:var(--astro-code-token-punctuation)">])</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(client.chat.completions.</span><span style="color:var(--astro-code-token-function)">create</span><span style="color:var(--astro-code-token-punctuation)">(model</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&quot;openai/gpt-4o&quot;</span><span style="color:var(--astro-code-token-punctuation)">, messages</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[{</span><span style="color:var(--astro-code-token-string-expression)">&quot;role&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;user&quot;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&quot;content&quot;</span><span style="color:var(--astro-code-token-punctuation)">: </span><span style="color:var(--astro-code-token-string-expression)">&quot;Give 3 project ideas with data sources.&quot;</span><span style="color:var(--astro-code-token-punctuation)">}]).choices[</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">].message.content)</span></span></code></pre>
<hr>
<h2 id="references">References</h2>
<ul>
<li>Hugging Face Hub: <a href="https://huggingface.co/">huggingface.co</a></li>
<li>Transformers: <a href="https://huggingface.co/docs/transformers">huggingface.co/docs/transformers</a></li>
<li>Datasets: <a href="https://huggingface.co/docs/datasets">huggingface.co/docs/datasets</a></li>
<li>llama.cpp: <a href="https://github.com/ggerganov/llama.cpp">github.com/ggerganov/llama.cpp</a></li>
<li>Ollama: <a href="https://ollama.com/">ollama.com</a></li>
<li>vLLM: <a href="https://github.com/vllm-project/vllm">github.com/vllm-project/vllm</a></li>
<li>OpenRouter: <a href="https://openrouter.ai/">openrouter.ai</a></li>
</ul>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Ecosystems &amp; Tooling</category>
            <category>Generative AI</category>
            <category>LLM serving</category>
            <category>Evaluation</category>
            <enclosure length="62738" type="image/png" url="https://blog.ecitis.org/open-graph/generative-ai-guide.png"/>
        </item>
        <item>
            <title><![CDATA[HPC Guide]]></title>
            <link>https://blog.ecitis.org/hpc-guide/</link>
            <guid isPermaLink="false">https://blog.ecitis.org/hpc-guide/</guid>
            <pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Build fast, reproducible Python and PyTorch workflows on GPU clusters, from environment setup to profiling and distributed training.]]></description>
            <content:encoded><![CDATA[<h2 id="overview">Overview</h2>
<p>This guide shows how to use Astral’s <code>uv</code> for fast, reproducible Python environments on HPC systems, install PyTorch with CUDA for NVIDIA A100 GPUs, tune performance (data loading, AMP/TF32, memory format, compile, CUDA Graphs, DDP), and monitor with <code>nvidia-smi</code>, <code>nvtop</code>, and NVIDIA Nsight tools.</p>
<figure><figcaption><strong>HPC Development Workflow</strong></figcaption><img src="https://blog.ecitis.org/feeds/figures/hpc-guide-0.webp" width="512" height="989" alt="HPC Development Workflow" loading="lazy" decoding="async"></figure>
<p>Links you’ll use often:</p>
<ul>
<li>uv docs: <a href="https://docs.astral.sh/uv/">docs.astral.sh/uv</a></li>
<li>PyTorch install matrix: <a href="https://pytorch.org/get-started/locally/">pytorch.org/get-started/locally</a></li>
<li>PyTorch performance tuning: <a href="https://pytorch.org/tutorials/recipes/recipes/tuning_guide.html">pytorch.org/tutorials/recipes/recipes/tuning_guide.html</a></li>
</ul>
<hr>
<h2 id="install-uv-recommended">Install uv (recommended)</h2>
<p>uv is a fast package/project manager and Python launcher. It replaces <code>pip</code>, <code>virtualenv</code>, some <code>poetry</code> tasks, and even manages Python interpreters.</p>
<p>On Linux/macOS:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> -LsSf</span><span style="color:var(--astro-code-token-string)"> https://astral.sh/uv/install.sh</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-function)"> sh</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> --version</span></span></code></pre>
<p>On Windows (PowerShell):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">powershell </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)">ExecutionPolicy ByPass </span><span style="color:var(--astro-code-token-keyword)">-</span><span style="color:var(--astro-code-foreground)">c </span><span style="color:var(--astro-code-token-string-expression)">&quot;irm https://astral.sh/uv/install.ps1 | iex&quot;</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">uv </span><span style="color:var(--astro-code-token-keyword)">--</span><span style="color:var(--astro-code-foreground)">version</span></span></code></pre>
<p>If your cluster forbids curl-based installers, you can fall back to:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> --user</span><span style="color:var(--astro-code-token-string)"> uv</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">~</span><span style="color:var(--astro-code-foreground)">/.local/bin/uv --version</span></span></code></pre>
<hr>
<h2 id="manage-python-versions-with-uv">Manage Python versions with uv</h2>
<p>Keep project-specific Python consistent to avoid ABI mismatches with CUDA.</p>
<ul>
<li>List installed Pythons:</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> list</span></span></code></pre>
<ul>
<li>Install specific versions (uses Python standalone builds):</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-constant)"> 3.11</span><span style="color:var(--astro-code-token-constant)"> 3.12</span></span></code></pre>
<ul>
<li>Pin the project to a version (writes <code>.python-version</code>):</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> pin</span><span style="color:var(--astro-code-token-constant)"> 3.11</span></span></code></pre>
<p>When running on shared clusters, ensure your job loads the same Python (via pinning) on login nodes and compute nodes.</p>
<hr>
<h2 id="create-and-use-virtual-environments-venvs">Create and use virtual environments (venvs)</h2>
<p>Recommended: put a <code>.venv/</code> in the project root and commit <code>.venv/</code> to <code>.gitignore</code>.</p>
<ul>
<li>Create venv with pinned Python:</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> venv</span><span style="color:var(--astro-code-token-string)"> --python</span><span style="color:var(--astro-code-token-constant)"> 3.11</span></span></code></pre>
<ul>
<li>Activate (Linux/macOS):</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">source</span><span style="color:var(--astro-code-token-string)"> .venv/bin/activate</span></span></code></pre>
<ul>
<li>Activate (Windows):</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">.venv\Scripts\Activate</span></span></code></pre>
<ul>
<li>Upgrade pip/setuptools/wheel inside the venv (uv provides a fast shim):</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> -U</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> setuptools</span><span style="color:var(--astro-code-token-string)"> wheel</span></span></code></pre>
<ul>
<li>Freeze or export deps if you aren’t using <code>pyproject.toml</code> yet:</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> freeze</span><span style="color:var(--astro-code-token-keyword)"> &gt;</span><span style="color:var(--astro-code-token-string)"> requirements.txt</span></span></code></pre>
<p>Tip: uv can also manage project deps via <code>pyproject.toml</code> + <code>uv.lock</code> with <code>uv add</code> and <code>uv sync</code> if you prefer lockfiles.</p>
<hr>
<h2 id="install-pytorch-with-cuda-for-a100">Install PyTorch with CUDA for A100</h2>
<ol>
<li>Confirm drivers and CUDA runtime on the node:</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">nvidia-smi</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">nvidia-smi</span><span style="color:var(--astro-code-token-string)"> topo</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-comment)">   # NUMA / PCIe fabric view</span></span></code></pre>
<p>Check the “CUDA Version” reported by <code>nvidia-smi</code> (this reflects the driver-supported CUDA runtime). On many A100 systems you’ll see CUDA 11.8 or 12.1+.</p>
<ol>
<li>In your venv, install the matching PyTorch wheels. Use the official index URL for CUDA builds (replace cu118/cu121 with what you need):</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># CUDA 11.8</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> torch</span><span style="color:var(--astro-code-token-string)"> torchvision</span><span style="color:var(--astro-code-token-string)"> torchaudio</span><span style="color:var(--astro-code-token-string)"> --index-url</span><span style="color:var(--astro-code-token-string)"> https://download.pytorch.org/whl/cu118</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># CUDA 12.1</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> torch</span><span style="color:var(--astro-code-token-string)"> torchvision</span><span style="color:var(--astro-code-token-string)"> torchaudio</span><span style="color:var(--astro-code-token-string)"> --index-url</span><span style="color:var(--astro-code-token-string)"> https://download.pytorch.org/whl/cu121</span></span></code></pre>
<p>If your cluster uses modules, load them first (example):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">module</span><span style="color:var(--astro-code-token-string)"> load</span><span style="color:var(--astro-code-token-string)"> cuda/12.1</span><span style="color:var(--astro-code-token-comment)">  # or the site-provided module that matches your drivers</span></span></code></pre>
<ol>
<li>Verify GPU visibility in Python:</li>
</ol>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(torch.</span><span style="color:var(--astro-code-token-constant)">__version__</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(torch.version.cuda)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(torch.cuda.</span><span style="color:var(--astro-code-token-function)">is_available</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(torch.cuda.</span><span style="color:var(--astro-code-token-function)">get_device_name</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">0</span><span style="color:var(--astro-code-token-punctuation)">))</span></span></code></pre>
<p>If <code>is_available()</code> is False, check that you’re on a GPU node, the right module is loaded, and your PyTorch wheels match the available CUDA runtime.</p>
<hr>
<h2 id="data-loading-keep-the-gpu-fed">Data loading: keep the GPU fed</h2>
<p>The A100 is very fast; most training slowdowns are input-bound. Key <code>DataLoader</code> knobs:</p>
<ul>
<li><code>num_workers</code>: parallel CPU workers. Start near the number of physical cores per GPU slice, then sweep.</li>
<li><code>pin_memory=True</code>: enables page-locked host buffers for faster H2D copies.</li>
<li><code>persistent_workers=True</code>: avoid worker teardown cost between epochs (when <code>num_workers&gt;0</code>).</li>
<li><code>prefetch_factor</code>: number of batches preloaded by each worker (default 2). Increase if GPU starves.</li>
<li>Use non-blocking transfers with pinned memory: <code>.to(device, non_blocking=True)</code>.</li>
</ul>
<p>Example:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">utils</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">data </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DataLoader</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">num_cpu </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> os</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">cpu_count</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-keyword)"> or</span><span style="color:var(--astro-code-token-constant)"> 8</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">loader </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DataLoader</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    dataset,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    batch_size</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">64</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    shuffle</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    num_workers</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)">min</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">8</span><span style="color:var(--astro-code-token-punctuation)">, num_cpu),</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    pin_memory</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    persistent_workers</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    prefetch_factor</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">4</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Watch <code>GPU-Util</code> vs CPU load. If GPU &lt; 90% while CPUs are busy, increase <code>prefetch_factor</code>/<code>num_workers</code>. If CPUs are idle and GPU is low-util, you may be I/O bound (optimize storage, caching, or preprocessing).</p>
<hr>
<h2 id="math-throughput-amp-tf32-memory-formats">Math throughput: AMP, TF32, memory formats</h2>
<ul>
<li>Automatic Mixed Precision (AMP): use <code>torch.cuda.amp</code> for FP16/BF16 where safe.</li>
<li>TF32 on A100: accelerates FP32 matmuls transparently; you can explicitly allow it.</li>
<li>Channels-last memory format (NCHW → NHWC) helps convolution throughput on Ampere.</li>
</ul>
<p>Minimal training loop sketch:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">amp </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> GradScaler</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(memory_format</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.channels_last)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Enable cudnn autotuner (best algos per input shapes)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">backends</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cudnn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">benchmark </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> True</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># Prefer higher TF32 throughput where numerically OK</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">backends</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">matmul</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">allow_tf32 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> True</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">backends</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cudnn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">allow_tf32 </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)"> True</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">scaler </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> GradScaler</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-token-comment)">  # use torch.amp.GradScaler for BF16 on newer PyTorch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> images</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> targets </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> loader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    images </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> images</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(device, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    images </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> images</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(memory_format</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">torch.channels_last)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    targets </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> targets</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(device, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">(set_to_none</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    with</span><span style="color:var(--astro-code-token-function)"> autocast</span><span style="color:var(--astro-code-token-punctuation)">():</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        outputs </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(images)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">        loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> criterion</span><span style="color:var(--astro-code-token-punctuation)">(outputs, targets)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">scale</span><span style="color:var(--astro-code-token-punctuation)">(loss).</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">(optimizer)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    scaler</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">update</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>Notes:</p>
<ul>
<li>Prefer BF16 on A100 when supported by your model for stability without scaling.</li>
<li>Keep batch shapes stable to maximize <code>cudnn.benchmark</code> benefit.</li>
</ul>
<hr>
<h2 id="compile-and-cuda-graphs">Compile and CUDA Graphs</h2>
<ul>
<li><code>torch.compile</code> (PyTorch 2.x) can fuse kernels and reduce Python overhead. Try safe modes first:</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-foreground)">compiled_model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">compile</span><span style="color:var(--astro-code-token-punctuation)">(model, mode</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;max-autotune&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<ul>
<li>CUDA Graphs reduce per-iteration launch overhead when your step is shape-stable:</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">static_inp </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">empty_like</span><span style="color:var(--astro-code-token-punctuation)">(example_inp, device</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">g </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">CUDAGraph</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># warm-up allocations</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">_ </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(example_inp)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">(set_to_none</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">graph</span><span style="color:var(--astro-code-token-punctuation)">(g):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    out </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(static_inp)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> criterion</span><span style="color:var(--astro-code-token-punctuation)">(out, static_target)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    loss</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">for</span><span style="color:var(--astro-code-foreground)"> inp</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> tgt </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-foreground)"> loader</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    static_inp</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">copy_</span><span style="color:var(--astro-code-token-punctuation)">(inp.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    static_target</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">copy_</span><span style="color:var(--astro-code-token-punctuation)">(tgt.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">    g</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">replay</span><span style="color:var(--astro-code-token-punctuation)">()</span></span></code></pre>
<p>Both features benefit stable shapes and control flow; avoid random control paths inside the captured region.</p>
<hr>
<h2 id="multi-gpu-on-a-single-node-ddp">Multi-GPU on a single node (DDP)</h2>
<p>Use Distributed Data Parallel (NCCL) to scale across A100s.</p>
<p>Launcher (single node, 8 GPUs):</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">torchrun</span><span style="color:var(--astro-code-token-string)"> --standalone</span><span style="color:var(--astro-code-token-string)"> --nproc_per_node=8</span><span style="color:var(--astro-code-token-string)"> train.py</span><span style="color:var(--astro-code-token-string)"> --arg1</span><span style="color:var(--astro-code-token-string)"> ...</span></span></code></pre>
<p>Inside <code>train.py</code>:</p>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">distributed </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> dist</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">nn</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">parallel </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> DistributedDataParallel </span><span style="color:var(--astro-code-token-keyword)">as</span><span style="color:var(--astro-code-foreground)"> DDP</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">dist</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">init_process_group</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;nccl&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">rank </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> dist</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">get_rank</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">cuda</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_device</span><span style="color:var(--astro-code-token-punctuation)">(rank </span><span style="color:var(--astro-code-token-keyword)">%</span><span style="color:var(--astro-code-token-punctuation)"> torch.cuda.</span><span style="color:var(--astro-code-token-function)">device_count</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">device </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">device</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">, rank </span><span style="color:var(--astro-code-token-keyword)">%</span><span style="color:var(--astro-code-token-punctuation)"> torch.cuda.</span><span style="color:var(--astro-code-token-function)">device_count</span><span style="color:var(--astro-code-token-punctuation)">())</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> model</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(device)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">model </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> DDP</span><span style="color:var(--astro-code-token-punctuation)">(model, device_ids</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[device.index])</span></span></code></pre>
<p>Use <code>DistributedSampler</code> for datasets and call <code>sampler.set_epoch(epoch)</code> each epoch.</p>
<hr>
<h2 id="cpu-affinity-threads-and-io">CPU affinity, threads, and I/O</h2>
<ul>
<li>Respect scheduler allocations: in SLURM, set <code>--cpus-per-task</code> to match <code>num_workers</code> and set threads:</li>
</ul>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> os</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> torch</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_num_threads</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">int</span><span style="color:var(--astro-code-token-punctuation)">(os.environ.</span><span style="color:var(--astro-code-token-function)">get</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;SLURM_CPUS_PER_TASK&#39;</span><span style="color:var(--astro-code-token-punctuation)">, </span><span style="color:var(--astro-code-token-string-expression)">&#39;4&#39;</span><span style="color:var(--astro-code-token-punctuation)">)))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">set_num_interop_threads</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-constant)">1</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<ul>
<li>Prefer fast local storage (NVMe, node-local scratch). If reading from network storage, increase DataLoader prefetching and use binary formats (e.g., WebDataset/TFRecord) to reduce per-file overhead.</li>
</ul>
<hr>
<h2 id="monitoring-and-profiling-on-a100">Monitoring and profiling on A100</h2>
<h3 id="quick-health-and-utilization">Quick health and utilization</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">nvidia-smi</span><span style="color:var(--astro-code-token-comment)">                 # snapshot</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">watch</span><span style="color:var(--astro-code-token-string)"> -n</span><span style="color:var(--astro-code-token-constant)"> 1</span><span style="color:var(--astro-code-token-string)"> nvidia-smi</span><span style="color:var(--astro-code-token-comment)">      # live refresh</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">nvidia-smi</span><span style="color:var(--astro-code-token-string)"> dmon</span><span style="color:var(--astro-code-token-string)"> -s</span><span style="color:var(--astro-code-token-string)"> pucm</span><span style="color:var(--astro-code-token-comment)">    # per-GPU perf counters (Power/Util/Clock/Memory)</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">nvidia-smi</span><span style="color:var(--astro-code-token-string)"> topo</span><span style="color:var(--astro-code-token-string)"> -m</span><span style="color:var(--astro-code-token-comment)">         # topology (NVLink/PCIe/NUMA)</span></span></code></pre>
<p>How to read:</p>
<ul>
<li>GPU-Util: aim for ~90%+ under steady-state training.</li>
<li>Memory-Usage: match batch size to available FB memory with headroom (fragmentation, caches).</li>
<li>Power/Thermals: A100 40GB/80GB will throttle if constrained; file an ops ticket if sustained throttling appears.</li>
</ul>
<h3 id="nvtop-interactive-gpu-top">nvtop (interactive GPU top)</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">sudo</span><span style="color:var(--astro-code-token-string)"> apt-get</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> nvtop</span><span style="color:var(--astro-code-token-comment)">  # Debian/Ubuntu</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">nvtop</span></span></code></pre>
<p>Shows per-process SM/memory utilization and graphs. Useful to identify which PID is starving or leaking memory.</p>
<h3 id="pytorch-profiler-cpucuda">PyTorch Profiler (CPU+CUDA)</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-keyword)">from</span><span style="color:var(--astro-code-foreground)"> torch</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-foreground)">profiler </span><span style="color:var(--astro-code-token-keyword)">import</span><span style="color:var(--astro-code-foreground)"> profile</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> record_function</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> ProfilerActivity</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">with</span><span style="color:var(--astro-code-token-function)"> profile</span><span style="color:var(--astro-code-token-punctuation)">(</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    activities</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-punctuation)">[ProfilerActivity.CPU, ProfilerActivity.CUDA],</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    record_shapes</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    profile_memory</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">    with_stack</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">,</span></span>
<span class="line"><span style="color:var(--astro-code-token-punctuation)">)</span><span style="color:var(--astro-code-token-keyword)"> as</span><span style="color:var(--astro-code-foreground)"> prof</span><span style="color:var(--astro-code-token-punctuation)">:</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">    for</span><span style="color:var(--astro-code-foreground)"> step</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> (x</span><span style="color:var(--astro-code-token-punctuation)">,</span><span style="color:var(--astro-code-foreground)"> y) </span><span style="color:var(--astro-code-token-keyword)">in</span><span style="color:var(--astro-code-token-function)"> enumerate</span><span style="color:var(--astro-code-token-punctuation)">(loader):</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        if</span><span style="color:var(--astro-code-foreground)"> step </span><span style="color:var(--astro-code-token-keyword)">==</span><span style="color:var(--astro-code-token-constant)"> 100</span><span style="color:var(--astro-code-token-punctuation)">:</span><span style="color:var(--astro-code-token-keyword)"> break</span></span>
<span class="line"><span style="color:var(--astro-code-token-keyword)">        with</span><span style="color:var(--astro-code-token-function)"> record_function</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;train_step&#39;</span><span style="color:var(--astro-code-token-punctuation)">):</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            x </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> x</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            y </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-foreground)"> y</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">to</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda&#39;</span><span style="color:var(--astro-code-token-punctuation)">, non_blocking</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            out </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> model</span><span style="color:var(--astro-code-token-punctuation)">(x)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            loss </span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-function)"> criterion</span><span style="color:var(--astro-code-token-punctuation)">(out, y)</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            loss</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">backward</span><span style="color:var(--astro-code-token-punctuation)">()</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">            optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">step</span><span style="color:var(--astro-code-token-punctuation)">()</span><span style="color:var(--astro-code-foreground)">; optimizer</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">zero_grad</span><span style="color:var(--astro-code-token-punctuation)">(set_to_none</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">True</span><span style="color:var(--astro-code-token-punctuation)">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-function)">print</span><span style="color:var(--astro-code-token-punctuation)">(prof.</span><span style="color:var(--astro-code-token-function)">key_averages</span><span style="color:var(--astro-code-token-punctuation)">().</span><span style="color:var(--astro-code-token-function)">table</span><span style="color:var(--astro-code-token-punctuation)">(sort_by</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-string-expression)">&#39;cuda_time_total&#39;</span><span style="color:var(--astro-code-token-punctuation)">, row_limit</span><span style="color:var(--astro-code-token-keyword)">=</span><span style="color:var(--astro-code-token-constant)">15</span><span style="color:var(--astro-code-token-punctuation)">))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">prof</span><span style="color:var(--astro-code-token-punctuation)">.</span><span style="color:var(--astro-code-token-function)">export_chrome_trace</span><span style="color:var(--astro-code-token-punctuation)">(</span><span style="color:var(--astro-code-token-string-expression)">&#39;trace.json&#39;</span><span style="color:var(--astro-code-token-punctuation)">)</span></span></code></pre>
<p>Open <code>trace.json</code> in Chrome’s tracing viewer or TensorBoard’s profiler plugin for timelines.</p>
<h3 id="nsight-systems-end-to-end-timeline">Nsight Systems (end-to-end timeline)</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">nsys</span><span style="color:var(--astro-code-token-string)"> profile</span><span style="color:var(--astro-code-token-string)"> -o</span><span style="color:var(--astro-code-token-string)"> profile_report</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> train.py</span></span></code></pre>
<p>Use the GUI to inspect kernel launches, H2D/D2H copies, CPU stalls, and NCCL collectives.</p>
<h3 id="nsight-compute-kernel-level">Nsight Compute (kernel-level)</h3>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-function)">ncu</span><span style="color:var(--astro-code-token-string)"> --set</span><span style="color:var(--astro-code-token-string)"> full</span><span style="color:var(--astro-code-token-string)"> --target-processes</span><span style="color:var(--astro-code-token-string)"> all</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> train.py</span></span></code></pre>
<p>Drill into specific kernels to see occupancy, memory throughput, and bottlenecks.</p>
<hr>
<h2 id="common-pitfalls-and-fixes">Common pitfalls and fixes</h2>
<ul>
<li>Mismatched CUDA: wheel suffix (e.g., <code>cu118</code>/<code>cu121</code>) must be compatible with your driver’s CUDA runtime. Reinstall with the right <code>--index-url</code>.</li>
<li>Data starvation: increase <code>num_workers</code>, <code>prefetch_factor</code>, enable <code>pin_memory</code>, move preprocessing to workers.</li>
<li>Unstable AMP: try BF16 first on A100, or lower LR, or disable AMP for sensitive ops.</li>
<li>Irregular shapes: pad/pack batches to stabilize shapes; enables better autotuning, compile, and graphs.</li>
<li>Multi-GPU under-utilization: check <code>nvidia-smi topo -m</code> for NVLink/PCIe topology, ensure NCCL is the backend, and avoid CPU oversubscription.</li>
</ul>
<hr>
<h2 id="minimal-end-to-end-recipe-a100-cuda-121">Minimal end-to-end recipe (A100, CUDA 12.1)</h2>
<pre class="copy-code-block"><button class="copy-button" type="button" aria-label="Copy code"></button><code><span class="line"><span style="color:var(--astro-code-token-comment)"># 0) (If needed) load site modules</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">module</span><span style="color:var(--astro-code-token-string)"> load</span><span style="color:var(--astro-code-token-string)"> cuda/12.1</span><span style="color:var(--astro-code-token-comment)">  # as required by your cluster</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 1) Install uv and create project venv</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">curl</span><span style="color:var(--astro-code-token-string)"> -LsSf</span><span style="color:var(--astro-code-token-string)"> https://astral.sh/uv/install.sh</span><span style="color:var(--astro-code-token-keyword)"> |</span><span style="color:var(--astro-code-token-function)"> sh</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-constant)"> 3.11</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> python</span><span style="color:var(--astro-code-token-string)"> pin</span><span style="color:var(--astro-code-token-constant)"> 3.11</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> venv</span><span style="color:var(--astro-code-token-string)"> --python</span><span style="color:var(--astro-code-token-constant)"> 3.11</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">source</span><span style="color:var(--astro-code-token-string)"> .venv/bin/activate</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 2) Install PyTorch CUDA wheels</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">uv</span><span style="color:var(--astro-code-token-string)"> pip</span><span style="color:var(--astro-code-token-string)"> install</span><span style="color:var(--astro-code-token-string)"> torch</span><span style="color:var(--astro-code-token-string)"> torchvision</span><span style="color:var(--astro-code-token-string)"> torchaudio</span><span style="color:var(--astro-code-token-string)"> --index-url</span><span style="color:var(--astro-code-token-string)"> https://download.pytorch.org/whl/cu121</span></span>
<span class="line"></span>
<span class="line"><span style="color:var(--astro-code-token-comment)"># 3) Sanity check</span></span>
<span class="line"><span style="color:var(--astro-code-token-function)">python</span><span style="color:var(--astro-code-token-string)"> -</span><span style="color:var(--astro-code-token-keyword)"> &lt;&lt;</span><span style="color:var(--astro-code-foreground)">&#39;PY&#39;</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">import torch</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">print(&#39;Torch:&#39;, torch.__version__, &#39;CUDA:&#39;, torch.version.cuda)</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">print(&#39;GPU available:&#39;, torch.cuda.is_available())</span></span>
<span class="line"><span style="color:var(--astro-code-token-string-expression)">print(&#39;Device:&#39;, torch.cuda.get_device_name(0))</span></span>
<span class="line"><span style="color:var(--astro-code-foreground)">PY</span></span></code></pre>
<p>Then apply the loader/AMP/TF32/compile practices above and watch <code>nvidia-smi</code>/<code>nvtop</code> for sustained high utilization.</p>
<hr>
<h2 id="references">References</h2>
<ul>
<li>uv documentation: <a href="https://docs.astral.sh/uv/">docs.astral.sh/uv</a></li>
<li>PyTorch install (CUDA wheels): <a href="https://pytorch.org/get-started/locally/">pytorch.org/get-started/locally</a></li>
<li>PyTorch performance tuning: <a href="https://pytorch.org/tutorials/recipes/recipes/tuning_guide.html">pytorch.org/tutorials/recipes/recipes/tuning_guide.html</a></li>
<li>Nsight Systems/Compute: <a href="https://developer.nvidia.com/">developer.nvidia.com</a></li>
</ul>]]></content:encoded>
            <author>Aadit Agrawal</author>
            <category>Hardware &amp; Systems</category>
            <category>HPC</category>
            <category>PyTorch</category>
            <category>GPUs</category>
        </item>
    </channel>
</rss>