<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anushka S Raghunandan</title>
    <description>The latest articles on DEV Community by Anushka S Raghunandan (@siriscent7).</description>
    <link>https://dev.to/siriscent7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4114499%2F79c89266-157e-452a-8b47-ed16c7589a66.jpg</url>
      <title>DEV Community: Anushka S Raghunandan</title>
      <link>https://dev.to/siriscent7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/siriscent7"/>
    <language>en</language>
    <item>
      <title>What I Learned Building a Mini TensorRT</title>
      <dc:creator>Anushka S Raghunandan</dc:creator>
      <pubDate>Mon, 07 Sep 2026 19:15:23 +0000</pubDate>
      <link>https://dev.to/siriscent7/what-i-learned-building-a-mini-tensorrt-5g32</link>
      <guid>https://dev.to/siriscent7/what-i-learned-building-a-mini-tensorrt-5g32</guid>
      <description>&lt;p&gt;I wanted to understand how inference compilers like NVIDIA's TensorRT actually make models faster — not the marketing explanation, the mechanical one. So instead of reading the docs and calling it a day, I built a small version myself: &lt;strong&gt;OptiFlow&lt;/strong&gt;, a graph compiler that takes an ONNX model and optimizes it the way a production inference engine would.&lt;/p&gt;

&lt;p&gt;Here's what it does, why it works, and the bug that taught me the most.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea: models are graphs, and graphs can be rewritten
&lt;/h2&gt;

&lt;p&gt;An ONNX model is just a computation graph — nodes are operators (&lt;code&gt;Conv&lt;/code&gt;, &lt;code&gt;Add&lt;/code&gt;, &lt;code&gt;Relu&lt;/code&gt;, etc.), edges are tensors flowing between them. Most people treat that graph as fixed once training is done. It isn't. OptiFlow lifts the ONNX graph into a custom intermediate representation (IR) and runs a series of optimization passes over it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Constant folding&lt;/strong&gt; — if a subgraph's inputs are all constants, compute it once at compile time instead of on every inference call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead-node elimination&lt;/strong&gt; — remove nodes whose outputs are never consumed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operator fusion&lt;/strong&gt; — merge adjacent operators (&lt;code&gt;Conv → Relu&lt;/code&gt;, &lt;code&gt;Add → Relu&lt;/code&gt;) into a single fused op.
The first two are intuitive. Fusion is where the interesting engineering is.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why fusion actually matters
&lt;/h2&gt;

&lt;p&gt;Naively, &lt;code&gt;Conv → Relu&lt;/code&gt; looks like it should cost roughly the same whether you run it as two operators or one. It doesn't, and the reason is memory, not compute.&lt;/p&gt;

&lt;p&gt;Run them separately, and the hardware has to: compute the &lt;code&gt;Conv&lt;/code&gt; output, write the full tensor back to memory, then read that same tensor back in for &lt;code&gt;Relu&lt;/code&gt;, before writing the final result out again. That's two extra memory round-trips for a value that's only ever used once. On modern accelerators, moving data is frequently more expensive than the arithmetic itself — kernels are often memory-bandwidth-bound, not compute-bound. Fusing the two operators into one means the intermediate &lt;code&gt;Conv&lt;/code&gt; output never leaves fast on-chip memory before &lt;code&gt;Relu&lt;/code&gt; consumes it — it disappears entirely.&lt;/p&gt;

&lt;p&gt;This is also why fusion passes are a core part of what real inference compilers do: on GPUs, avoiding redundant memory traffic tends to matter more than shaving arithmetic.&lt;/p&gt;

&lt;p&gt;On a ResNet-18 graph, OptiFlow's fusion pass collapsed 51 nodes down to 34 (17 fused operator pairs), which brought estimated memory traffic down about 19% and modeled p95 tail latency down about 27%.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that scared me: how do I know it's still correct?
&lt;/h2&gt;

&lt;p&gt;This is the part nobody warns you about when you start writing a compiler pass: it's trivially easy to write a fusion rule that's &lt;em&gt;structurally&lt;/em&gt; elegant and &lt;em&gt;semantically&lt;/em&gt; wrong. A graph rewrite that looks correct on paper can silently change the model's output, and you won't know until inference numbers quietly drift.&lt;/p&gt;

&lt;p&gt;So before trusting any transformation, OptiFlow runs it through a round-trip harness: after each optimization pass, the modified IR is serialized back into a valid ONNX graph, and both the &lt;em&gt;original&lt;/em&gt; and the &lt;em&gt;optimized&lt;/em&gt; graphs are run through &lt;code&gt;onnxruntime&lt;/code&gt; on identical inputs. The outputs are diffed within floating-point tolerance. If they don't match, the pass is rejected — full stop, no partial credit.&lt;/p&gt;

&lt;p&gt;This caught more bugs than I expected. The most instructive one: a fusion rule that merged an &lt;code&gt;Add → Relu&lt;/code&gt; pair without checking whether the intermediate &lt;code&gt;Add&lt;/code&gt; output had more than one consumer downstream. Most of the time it only fed into &lt;code&gt;Relu&lt;/code&gt;, so fusing was safe. But in a branch of the graph where the &lt;code&gt;Add&lt;/code&gt; output also fed a skip connection, fusing silently dropped that second consumer — the fused node produced the right final tensor for the &lt;code&gt;Relu&lt;/code&gt; path, but the skip connection lost its input entirely. Structurally the graph still "looked" valid; numerically it was wrong. The round-trip harness caught the mismatch immediately, which is exactly the point of building it before optimizing anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd take into a real inference-compiler team
&lt;/h2&gt;

&lt;p&gt;The lesson wasn't really about ONNX or fusion rules specifically — it was that &lt;strong&gt;optimization and correctness have to be verified together, not sequentially.&lt;/strong&gt; It's tempting to write a rewrite pass, eyeball a few examples, and assume it generalizes. It's much harder — and much more valuable — to build the harness that proves it, before you trust the speedup.&lt;/p&gt;

&lt;p&gt;If you're curious, the code is on GitHub — feedback and PRs welcome.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>deeplearning</category>
      <category>machinelearning</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
