<?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: Blake Pelton</title>
    <description>The latest articles on DEV Community by Blake Pelton (@dangling_pointers_0bfce7ce6993).</description>
    <link>https://dev.to/dangling_pointers_0bfce7ce6993</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3529991%2F2f02bea6-42b4-43ee-9e0a-efa6195b45b7.png</url>
      <title>DEV Community: Blake Pelton</title>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dangling_pointers_0bfce7ce6993"/>
    <language>en</language>
    <item>
      <title>Gigaflow: Pipeline-Aware Sub-Traversal Caching for Modern SmartNICs</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Wed, 15 Oct 2025 12:01:25 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/gigaflow-pipeline-aware-sub-traversal-caching-for-modern-smartnics-3al6</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/gigaflow-pipeline-aware-sub-traversal-caching-for-modern-smartnics-3al6</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716000" rel="noopener noreferrer"&gt;Gigaflow: Pipeline-Aware Sub-Traversal Caching for Modern SmartNICs&lt;/a&gt; Annus Zulfiqar, Ali Imran, Venkat Kunaparaju, Ben Pfaff, Gianni Antichi, and Muhammad Shahbaz &lt;em&gt;ASPLOS'25&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtual Switch
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;virtual switch&lt;/em&gt; (vSwitch) routes network traffic to and from virtual machines. Section 2.1 of the paper describes the historical development of vSwitch technology, ending with a pipeline of &lt;em&gt;match-action tables&lt;/em&gt; (MATs). A match-action table is a data-driven way to configuration a vSwitch, comprising matching rules, and associated actions to take when a matching packet is encountered. When a packet arrives at the vSwitch, it traverses the full pipeline of match-action tables. At each pipeline stage, header fields from the packet are used to perform a lookup into a match action table. If a match is found, then the packet is modified according to the actions found in the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Megaflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/system/files/conference/nsdi15/nsdi15-paper-pfaff.pdf" rel="noopener noreferrer"&gt;Megaflow&lt;/a&gt; is prior work which memoizes the full pipeline of MATs. The memoization data structure is treated like a cache. When a packet arrives, a cache lookup occurs. On a miss, the regular vSwitch implementation is called to transform the packet. Subsequent packets which hit in the cache avoid executing the vSwitch code entirely. Megaflow supports keys with wildcards, to allow one cache entry to serve multiple flows.&lt;/p&gt;

&lt;p&gt;A problem with Megaflow is that even with wildcards, a large cache is needed to achieve a high hit rate. For throughput reasons, one may wish to place a Megaflow cache in on-chip memory on a SmartNIC. However, if the SmartNIC does not have enough on-chip memory to achieve a high hit rate, then throughput suffers. See &lt;a href="https://danglingpointers.substack.com/p/scaling-ip-lookup-to-large-databases" rel="noopener noreferrer"&gt;this post&lt;/a&gt;, and &lt;a href="https://danglingpointers.substack.com/p/enabling-portable-and-high-performance" rel="noopener noreferrer"&gt;this post&lt;/a&gt; for a description of SmartNIC architectures and their on-chip memories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gigaflow
&lt;/h2&gt;

&lt;p&gt;This paper introduces a different memoization scheme (Gigaflow) to make better use of SmartNIC memory. Rather than memoizing the entire vSwitch pipeline for a packet, Gigaflow divides the vSwitch pipeline into multiple smaller pipelines, and memoizes each one separately. Fig. 1 illustrates this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk084b7znmuun5e8iknlk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk084b7znmuun5e8iknlk.png" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716000" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716000&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Gigaflow takes advantage of SmartNICs’ ability to perform many table lookups per packet while maintaining high throughput. The total working set in a typical workload is reduced, because many flows can share some table entries (rather than all-or-nothing sharing).&lt;/p&gt;

&lt;p&gt;Another way to think about this is that Megaflow combines all of the MATs in a vSwitch pipeline into one very large table, whereas Gigaflow partitions the MAT vSwitch pipeline into a handful of sub-pipelines, and combines each sub-pipeline into a medium-sized table.&lt;/p&gt;

&lt;p&gt;Sections 4.1.1 and 4.2.2 of the paper have the nitty-gritty details of how Gigaflow decides how to correctly assign subsets of the vSwitch MAT pipeline into a set of tables.&lt;/p&gt;

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

&lt;p&gt;Fig. 9 shows cache misses for Megaflow and Gigaflow for a variety of benchmarks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplx80je6gy34wnow9pbv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplx80je6gy34wnow9pbv.png" width="533" height="247"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716000" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716000&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;Memoization is useful in settings outside of networking. It would be interesting to see if the idea of separable memoization could be applied to other applications.&lt;/p&gt;

&lt;p&gt;Like I mentioned &lt;a href="https://danglingpointers.substack.com/p/scaling-ip-lookup-to-large-databases" rel="noopener noreferrer"&gt;here&lt;/a&gt;, hardware support for memoization in general purpose CPUs seems compelling.&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
    <item>
      <title>Optimizing Datalog for the GPU</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Mon, 13 Oct 2025 12:02:25 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/optimizing-datalog-for-the-gpu-15p1</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/optimizing-datalog-for-the-gpu-15p1</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3669940.3707274" rel="noopener noreferrer"&gt;Optimizing Datalog for the GPU&lt;/a&gt; Yihao Sun, Ahmedur Rahman Shovon, Thomas Gilray, Sidharth Kumar, and Kristopher Micinski &lt;em&gt;ASPLOS'25&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Datalog Primer
&lt;/h2&gt;

&lt;p&gt;Datalog source code comprises a set of relations, and a set of rules.&lt;/p&gt;

&lt;p&gt;A relation can be explicitly defined with a set of tuples. A running example in the paper is to define a graph with a relation named &lt;code&gt;edge&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Edge(0, 1)
Edge(1, 3)
Edge(0, 2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A relation can also be implicitly defined with a set of rules. The paper uses the &lt;code&gt;Same Generation (SG)&lt;/code&gt; relation as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1: SG(x, y) &amp;lt;- Edge(p, x), Edge(p, y), x != y
2: SG(x, y) &amp;lt;- Edge(a, x), SG(a, b), Edge(b, y), x != y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rule 1 states that two vertices (&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;) are part of the same generation if they both share a common ancestor (&lt;code&gt;p&lt;/code&gt;), and they are not actually the same vertex (&lt;code&gt;x != y&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Rule 2 states that two vertices (&lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;) are part of the same generation if they have ancestors (&lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;) from the same generation.&lt;/p&gt;

&lt;p&gt;“Running a Datalog program” entails evaluating all rules until a fixed point is reached (no more tuples are added).&lt;/p&gt;

&lt;h2&gt;
  
  
  Semi-naïve Evaluation
&lt;/h2&gt;

&lt;p&gt;One key idea to internalize is that evaluating a Datalog rule is equivalent to performing a SQL join. For example, rule 1 is equivalent to joining the &lt;code&gt;Edge&lt;/code&gt; relation with itself, using &lt;code&gt;p&lt;/code&gt; as the join key, and &lt;code&gt;(x != y)&lt;/code&gt; as a filter.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Semi-naïve Evaluation&lt;/em&gt; is an algorithm for performing these joins until convergence, while not wasting too much effort on redundant work. The tuples in a relation are put into three buckets:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;new&lt;/code&gt;: holds tuples that were discovered on the current iteration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;delta:&lt;/code&gt; holds tuples which were added in the previous iteration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;full&lt;/code&gt;: holds all tuples that have been found in any iteration&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a join involving two relations (&lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;), &lt;code&gt;new&lt;/code&gt; is computed as the union of the result of 3 joins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;delta(A)&lt;/code&gt; joined with &lt;code&gt;full(B)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;full(A)&lt;/code&gt; joined with &lt;code&gt;delta(B)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;delta(A)&lt;/code&gt; joined with &lt;code&gt;delta(B)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key fact for performance is that &lt;code&gt;full(A)&lt;/code&gt; is never joined with &lt;code&gt;full(B)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;More details on Semi-naïve Evaluation can be found in &lt;a href="https://pages.cs.wisc.edu/~paris/cs838-s16/lecture-notes/lecture8.pdf" rel="noopener noreferrer"&gt;these notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Hash-Indexed Sorted Array&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This paper introduces the &lt;em&gt;hash-indexed sorted array&lt;/em&gt; for storing relations while executing Semi-naïve Evaluation on a GPU. It seems to me like this data structure would work well on other chips too. Fig. 2 illustrates the data structure (join keys are in red):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcfzhzt451leq7n31kss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcfzhzt451leq7n31kss.png" width="800" height="550"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3669940.3707274" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3669940.3707274&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;data array&lt;/em&gt; holds the actual tuple data. It is densely packed in row-major order.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;sorted index array&lt;/em&gt; holds pointers into the data array (one pointer per tuple). These pointers are lexicographically sorted (join keys take higher priority in the sort).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;hash table&lt;/em&gt; is an open-addressed hash table which maps a hash of the join keys to the first element in the sorted index array that contains those join keys.&lt;/p&gt;

&lt;p&gt;A join of relations A and &lt;code&gt;B&lt;/code&gt;, can be implemented with the following pseudo-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for each tuple 'a' in the sorted index array of A:

  lookup (hash table) the first tuple in B which has matching join keys to 'a'

  iterate over all tuples in the sorted index array of B with matching keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Memory accesses when probing through the sorted index array are coherent. Memory accesses when accessing the data array are coherent up to the number of elements in a tuple.&lt;/p&gt;

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

&lt;p&gt;Table 3 compares the results from this paper (GPULog) against a state-of-the-art CPU implementation (Soufflé). HIP represents GPULog ported to AMD’s &lt;a href="https://github.com/ROCm/rocm-systems/tree/develop/projects/hip" rel="noopener noreferrer"&gt;HIP&lt;/a&gt; runtime and then run on the same Nvidia GPU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hwhwbdccjuzxftleb9t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8hwhwbdccjuzxftleb9t.png" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3669940.3707274" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3669940.3707274&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;The data structure and algorithms described by this paper seem generic, it would be interesting to see them run on other chips (FPGA, DPU, CPU, HPC cluster).&lt;/p&gt;

&lt;p&gt;I would guess most of GPULog is bound by memory bandwidth, not compute. I wonder if there are Datalog-specific algorithms to reduce the bandwidth/compute ratio.&lt;/p&gt;

</description>
      <category>database</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>No Cap, This Memory Slaps: Breaking Through the Memory Wall of Transactional Database Systems with Processing-in-Memory</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Wed, 08 Oct 2025 12:00:59 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/no-cap-this-memory-slaps-breaking-through-the-memory-wall-of-transactional-database-systems-with-3fh4</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/no-cap-this-memory-slaps-breaking-through-the-memory-wall-of-transactional-database-systems-with-3fh4</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vldb.org/pvldb/volumes/18/paper/No%20Cap%2C%20This%20Memory%20Slaps%3A%20Breaking%20Through%20the%20Memory%20Wall%20of%20Transactional%20Database%20Systems%20with%20Processing-in-Memory" rel="noopener noreferrer"&gt;No Cap, This Memory Slaps: Breaking Through the Memory Wall of Transactional Database Systems with Processing-in-Memory&lt;/a&gt; Hyoungjoo Kim, Yiwei Zhao, Andrew Pavlo, Phillip B. Gibbons &lt;em&gt;VLDB'25&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This paper describes how processing-in-memory (PIM) hardware can be used to improve OLTP performance. &lt;a href="https://danglingpointers.substack.com/p/spid-join" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is a prior paper summary from me on a similar topic, but that one is focused on OLAP rather than OLTP.&lt;/p&gt;

&lt;h2&gt;
  
  
  UPMEM
&lt;/h2&gt;

&lt;p&gt;UPMEM is specific PIM product (also used in the &lt;a href="https://danglingpointers.substack.com/p/spid-join" rel="noopener noreferrer"&gt;prior paper&lt;/a&gt;) on this blog. A UPMEM DIMM is like a DRAM DIMM, but each DRAM bank is extended with a simple processor which can run user code. That processor has access to a small local memory and the DRAM associated with the bank. This paper calls each processor a &lt;em&gt;PIM Module&lt;/em&gt;. There is no direct communication between PIM modules.&lt;/p&gt;

&lt;p&gt;Fig. 2 illustrates the system architecture used by this paper. A traditional CPU is connected to a set of boring old DRAM DIMMs and is also connected to a set of UPMEM DIMMs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04mtxznbe62fapmmiogs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04mtxznbe62fapmmiogs.png" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;a href="https://vldb.org/pvldb/volumes/18/paper/No%20Cap%2C%20This%20Memory%20Slaps%3A%20Breaking%20Through%20the%20Memory%20Wall%20of%20Transactional%20Database%20Systems%20with%20Processing-in-Memory" rel="noopener noreferrer"&gt;https://vldb.org/pvldb/volumes/18/paper/No%20Cap%2C%20This%20Memory%20Slaps%3A%20Breaking%20Through%20the%20Memory%20Wall%20of%20Transactional%20Database%20Systems%20with%20Processing-in-Memory&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Four Challenges
&lt;/h2&gt;

&lt;p&gt;The paper identifies the following difficulties associated with using UPMEM to accelerate an OLTP workload:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;PIM modules can only access their local memory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PIM modules do not have typical niceties associated with x64 CPUs (high clock frequency, caches, SIMD)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a non-trivial cost for the CPU to send data to UPMEM DIMMs (similar to the CPU writing data to regular DRAM)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OLTP workloads have tight latency constraints&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Near Memory Affinity
&lt;/h2&gt;

&lt;p&gt;The authors arrived at a solution that both provides a good speedup and doesn’t require boiling the ocean. The database code and architecture remain largely unchanged. Much of the data remains in standard DRAM DIMMs, and the database operates on it as it always has.&lt;/p&gt;

&lt;p&gt;In section 3.2 the authors identify a handful of data structures and operations with &lt;em&gt;near-memory affinity&lt;/em&gt; which are offloaded. These data structures are stored in UPMEM DIMMs, and the algorithms which access them are offloaded to the PIM modules.&lt;/p&gt;

&lt;p&gt;The key feature that these algorithms have in common is &lt;em&gt;pointer chasing&lt;/em&gt;. The sweet spots the authors identify involve a small number of parameters sent from the CPU to a PIM module, then the PIM module performing multiple roundtrips to its local DRAM bank, followed by the CPU reading back a small amount of response data. The roundtrips to PIM-local DRAM have lower latency than accesses from a traditional CPU core.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hash-Partitioned Index
&lt;/h2&gt;

&lt;p&gt;One data structure which involves a lot of pointer chasing is &lt;a href="https://en.wikipedia.org/wiki/B%2B_tree" rel="noopener noreferrer"&gt;B+ tree&lt;/a&gt; traversal. Thus, the system described in this paper moves B+ tree indexes into UPMEM DIMMs and uses PIM modules to search for values in an index. Note that the actual tuples that hold row data stay in plain-old DRAM.&lt;/p&gt;

&lt;p&gt;The tricky part is handling range queries while distributing an index across many banks. The solution described in this paper is to partition the set of keys into 2&lt;sup&gt;R&lt;/sup&gt; partitions (the lower &lt;code&gt;R&lt;/code&gt; bits of a key define the index the partition which holds that key). Each partition is thus responsible for a contiguous array of keys. For a range query, the lower &lt;code&gt;R&lt;/code&gt; bits of the lower and upper bounds of the range can be used to determine which partitions must be searched. Each PIM module is responsible for multiple partitions, and a hash function is used to convert a partition index into a PIM module index.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVCC Chain Traversal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Multiversion_concurrency_control" rel="noopener noreferrer"&gt;MVCC&lt;/a&gt; is a concurrency control method which requires the database to keep around old versions of a given row (to allow older in-flight queries to access them). The set of versions associated with a row are typically stored in a linked list (yet another pointer traversal). Again, the actual tuple contents are stored in regular DRAM, but the list &lt;em&gt;links&lt;/em&gt; are stored in UPMEM DIMMs, with the PIM modules traversing the links. Section 4.3 has more information about how old versions are eventually reclaimed with garbage collection.&lt;/p&gt;

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

&lt;p&gt;Fig. 7 has the headline results. &lt;code&gt;MosaicDB&lt;/code&gt; is the baseline, &lt;code&gt;OLTPim&lt;/code&gt; is the work described by this paper. It is interesting that &lt;code&gt;OLTPim&lt;/code&gt; only beats &lt;code&gt;MosaicDB&lt;/code&gt; on &lt;code&gt;TPC-C&lt;/code&gt; for read-only workloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firlbebmsopyha114jj0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Firlbebmsopyha114jj0d.png" width="800" height="299"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
_Source: &lt;a href="https://vldb.org/pvldb/volumes/18/paper/No%20Cap%2C%20This%20Memory%20Slaps%3A%20Breaking%20Through%20the%20Memory%20Wall%20of%20Transactional%20Database%20Systems%20with%20Processing-in-Memory" rel="noopener noreferrer"&gt;https://vldb.org/pvldb/volumes/18/paper/No%20Cap%2C%20This%20Memory%20Slaps%3A%20Breaking%20Through%20the%20Memory%20Wall%20of%20Transactional%20Database%20Systems%20with%20Processing-in-Memory&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;Processing-in-memory can help with memory bandwidth and memory latency. It seems like this work is primarily focused on memory latency. I suppose this indicates that OLTP workloads are fundamentally latency-bound, because there is not enough potential concurrency between transactions to hide that latency. Is there no way to structure a database such that OLTP workloads are not bound by memory latency?&lt;/p&gt;

&lt;p&gt;It would be interesting to see if these tricks could work in a distributed system, where the PIM modules are replaced by separate nodes in the system.&lt;/p&gt;

</description>
      <category>database</category>
    </item>
    <item>
      <title>Parendi: Thousand-Way Parallel RTL Simulation</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Mon, 06 Oct 2025 11:02:09 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/parendi-thousand-way-parallel-rtl-simulation-f7f</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/parendi-thousand-way-parallel-rtl-simulation-f7f</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716010" rel="noopener noreferrer"&gt;Parendi: Thousand-Way Parallel RTL Simulation&lt;/a&gt; Mahyar Emami, Thomas Bourgeat, and James R. Larus &lt;em&gt;ASPLOS'25&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This paper describes an RTL simulator running on (one or more) &lt;a href="https://www.graphcore.ai/" rel="noopener noreferrer"&gt;Graphcore&lt;/a&gt; IPUs. One nice side benefit of this paper is the quantitative comparisons of IPU synchronization performance vs traditional CPUs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://danglingpointers.substack.com/p/dont-repeat-yourself-coarse-grained" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is another paper summary which describes some challenges with RTL simulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphcore IPU
&lt;/h2&gt;

&lt;p&gt;The Graphcore IPU used in this paper is a chip with 1472 cores, operating with a MIMD architecture. A 1U server can contain 4 IPUs. It is interesting to see a chip that was designed for DNN workloads adapted to the domain of RTL simulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partitioning and Scheduling
&lt;/h2&gt;

&lt;p&gt;Similar to other papers on RTL simulation, a fundamental step of the Parendi simulator is partitioning the circuit to be simulated. Parendi partitions the circuit into &lt;em&gt;fibers&lt;/em&gt;. A fiber comprises a single (word-wide) register, and all of the combinational logic which feeds it. Note that some combinational logic may be present in multiple fibers. Fig. 3 contains an example, node a3 is present in multiple fibers. As far as I can tell, Parendi does not try to deduplicate this work (extra computation to save synchronization).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxlyfgm6s3rkvq8cr11s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsxlyfgm6s3rkvq8cr11s.png" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716010" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716010&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The driving factor in the design of this fiber-specific partitioning system is &lt;em&gt;scalability&lt;/em&gt;. Each register has storage to hold the value of the register at the beginning and end of the current clock cycle (i.e., the &lt;code&gt;current&lt;/code&gt; and &lt;code&gt;next&lt;/code&gt; values).&lt;/p&gt;

&lt;p&gt;I think of the logic to simulate a single clock cycle with the following pseudo-code (&lt;code&gt;f.root&lt;/code&gt; is the register rooted at fiber &lt;code&gt;f)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parallel for each fiber : f
  f.root.next = evaluate f

barrier

parallel for each fiber : f
    f.root.current = f.root.next

barrier
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scalability comes from the fact that there are only two barriers per simulated clock cycle. This is an instance of the &lt;em&gt;bulk synchronous parallel&lt;/em&gt; (BSP) model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Partitioning
&lt;/h2&gt;

&lt;p&gt;In many cases, there are more fibers than CPU/IPU cores. Parendi addresses this by distributing the simulation across chips and scheduling multiple fibers to run on the same core.&lt;/p&gt;

&lt;p&gt;If the simulation is distributed across multiple chips, then a min-cut algorithm is used to partition the fibers across chips while minimizing communication.&lt;/p&gt;

&lt;p&gt;The Parendi compiler statically groups multiple fibers together into a single &lt;em&gt;process&lt;/em&gt;. A core simulates all fibers within a process. The merging process primarily seeks to minimize inter-core communication. First, a special case merging algorithm merges multiple fibers which reference the same large array. This is to avoid communicating the contents of such an array across cores. I imagine this is primarily for simulation of on-chip memories. Secondly, a general-purpose merging algorithm merges fibers which each have low compute cost, and high data sharing with each other.&lt;/p&gt;

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

&lt;p&gt;Fig. 7 compares Parendi vs Verilator simulation. &lt;code&gt;x64_ix3&lt;/code&gt; is a 2-socket server with 28 Intel cores per socket. &lt;code&gt;x64_ae4&lt;/code&gt; is a 2-socket server with 64 AMD cores per socket:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxrxffe2yhtfo970q0pl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxrxffe2yhtfo970q0pl.png" width="800" height="152"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716010" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716010&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Section 6.4 claims a roughly 2x improvement in cost per simulation using cloud pricing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;As far as I can tell, this system doesn’t have optimizations for the case where some or all of a fiber’s inputs do not change between clock cycles. It seems tricky to optimize for this case while maintaining a static assignment of fibers to cores.&lt;/p&gt;

&lt;p&gt;Fig. 4 has a fascinating comparison of synchronization costs between an IPU and a traditional x64 CPU. This microbenchmark loads up the system with simple fibers (roughly 6 instructions per fiber). Note that the curves represent different fiber counts (e.g., the red dotted line represents 7 fibers on the IPU graph, vs 736 fibers on the x64 graph). The paper claims that a barrier between 56 x64 threads implemented with atomic memory accesses consumes thousands of cycles, whereas the IPU has dedicated hardware barrier support.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0s9l6pdrniv2w9p2h5eg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0s9l6pdrniv2w9p2h5eg.png" width="577" height="276"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716010" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716010&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This seems to be one of many examples of how generic multi-core CPUs do not perform well with fine-grained multi-threading. We’ve seen it with pipeline parallelism, and now with the BSP model. Interestingly, both cases seem to work better with specialized multi-core chips (pipeline parallelism works with CPU-based SmartNICs, BSP works with IPUs). I’m not convinced this is a fundamental hardware problem that cannot be addressed with better software.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>performance</category>
    </item>
    <item>
      <title>Skia: Exposing Shadow Branches</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Fri, 03 Oct 2025 12:02:09 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/skia-exposing-shadow-branches-3n0j</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/skia-exposing-shadow-branches-3n0j</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716273" rel="noopener noreferrer"&gt;Skia: Exposing Shadow Branches&lt;/a&gt; Chrysanthos Pepi, Bhargav Reddy Godala, Krishnam Tibrewala, Gino A. Chacon, Paul V. Gratz, Daniel A. Jiménez, Gilles A. Pokam, and David I. August &lt;em&gt;ASPLOS'25&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This paper starts with your yearly reminder of the high cost of the &lt;a href="https://www.doc.ic.ac.uk/~phjk/AdvancedCompArchitecture/Lectures/pdfs/Ch01-part4-TuringTaxDiscussion.pdf" rel="noopener noreferrer"&gt;Turing Tax&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Recent works demonstrate that the front-end is a considerable source of performance loss [16], with upwards of 53% of performance [23] bounded by the front-end.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Fetch Directed Instruction Prefetching
&lt;/h2&gt;

&lt;p&gt;Everyone knows that the front-end runs ahead of the back-end of a processor. If you want to think of it in AI terms, imagine a model that is told about the current value of and recent history of the program counter, and asked to predict future values of the program counter. The accuracy of these predictions determines how utilized the processor pipeline is.&lt;/p&gt;

&lt;p&gt;What I did not know is that in a modern processor, the front-end &lt;em&gt;itself&lt;/em&gt; is divided into two decoupled components, one of which runs ahead of the other. Fig. 4 illustrates this &lt;em&gt;Fetch Direction Instruction Processing&lt;/em&gt; (FDIP) microarchitecture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo51462211q5ky8wbhhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo51462211q5ky8wbhhh.png" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716273" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716273&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Instruction Address Generator&lt;/em&gt; (IAG) runs the furthest ahead and uses tables (e.g., the &lt;em&gt;Branch Target Buffer&lt;/em&gt; (BTB)) in the &lt;em&gt;Branch Prediction Unit&lt;/em&gt; (BPU) to predict the sequence of basic blocks which will be executed. Information about each predicted basic block is stored in the &lt;em&gt;Fetch Target Queue&lt;/em&gt; (FTQ).&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;Instruction Fetch Unit&lt;/em&gt; (IFU) uses the control flow predictions from the FTQ to actually read instructions from the instruction cache. Some mispredictions can be detected after an instruction has been read and decoded. These result in an &lt;em&gt;early re-steer&lt;/em&gt; (i.e., informing the IAG about the misprediction immediately after decode).&lt;/p&gt;

&lt;p&gt;When a basic block is placed into the FTQ, the associated instructions are prefetched into the IFU (to reduce the impact of instruction cache misses).&lt;/p&gt;

&lt;h2&gt;
  
  
  Shadow Branches
&lt;/h2&gt;

&lt;p&gt;This paper introduces the term “shadow branch”. A shadow branch is a (static) branch instruction which is currently stored in the instruction cache but is not present in any BPU tables.&lt;/p&gt;

&lt;p&gt;The top of fig. 5 illustrates a &lt;em&gt;head&lt;/em&gt; shadow branch. A branch instruction caused execution to jump to byte 24 and execute the non-shaded instructions. This causes an entire cache line to be pulled into the instruction cache, including the branch instruction starting at byte 19.&lt;/p&gt;

&lt;p&gt;The bottom of fig. 5 shows a &lt;em&gt;tail&lt;/em&gt; shadow branch. In this case, the instruction at byte 12 jumped away from the cache line, causing the red branch instruction at byte 16 to not be executed (even though it is present in the instruction cache).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wyl4vxqqmsbarb7hi89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wyl4vxqqmsbarb7hi89.png" width="674" height="431"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716273" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716273&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Skia
&lt;/h2&gt;

&lt;p&gt;The proposed design (Skia) allows the IAG to make accurate predictions for a subset of shadow branches, thus improving pipeline utilization and reducing instruction cache misses. The types of shadow branches which Skia supports are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Direct unconditional branches (target PC can be determined without looking at backend state)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Function calls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Returns&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As shown in Fig. 6, these three categories of branches (purple, red, orange) account for a significant fraction of all BTB misses:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1z2yf3k5alug34jibre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1z2yf3k5alug34jibre.png" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716273" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716273&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When a cache line enters the instruction cache, the &lt;em&gt;Shadow Branch Decoder&lt;/em&gt; (SBD) decodes just enough information to locate shadow branches in the cache line and determine the target PC (for direct unconditional branches and function calls). Metadata from the SBD is placed into two new branch prediction tables in the BPU:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The U-SBB holds information about direct unconditional branches and function calls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The R-SBB holds information about returns&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the BPU encounters a BTB miss, it can fall back to the U-SBB or R-SBB for a prediction.&lt;/p&gt;

&lt;p&gt;Fig. 11 illustrates the microarchitectural changes proposed by Skia:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ko8148h68m9ljqav18f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ko8148h68m9ljqav18f.png" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716273" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716273&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Section 4 goes into more details about these structures including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Replacement policy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How a shadow branch is upgraded into a first-class branch in the BTB&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handling variable length instructions&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Fig. 14 has (simulated) IPC improvements across a variety of benchmarks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3450ef5ww446ax1e5dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3450ef5ww446ax1e5dp.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3676641.3716273" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3676641.3716273&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;A common problem that HW and SW architects solve is getting teams out of a local minimum caused by fixed interfaces. The failure mode is when two groups of engineers agree on a static interface, and then each optimize their component as best they can without changing the interface.&lt;/p&gt;

&lt;p&gt;In this paper, the interface is the ISA, and Skia is a clever optimization inside of the CPU front-end. Skia shows that there is fruit to be picked here. It would be interesting to examine potential performance gains from architectural (i.e., ISA) changes to pick the same fruit.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>performance</category>
    </item>
    <item>
      <title>Accelerate Distributed Joins with Predicate Transfer</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Mon, 29 Sep 2025 12:02:31 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/accelerate-distributed-joins-with-predicate-transfer-3fn7</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/accelerate-distributed-joins-with-predicate-transfer-3fn7</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3725259" rel="noopener noreferrer"&gt;Accelerate Distributed Joins with Predicate Transfer&lt;/a&gt; Yifei Yang and Xiangyao Yu &lt;em&gt;SIGMOD'25&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Thanks to for “dereferencing” this dangling pointer from the &lt;a href="https://danglingpointers.substack.com/p/predicate-transfer-efficient-pre" rel="noopener noreferrer"&gt;prior post&lt;/a&gt; on predicate transfer. This paper extends prior work on predicate transfer to apply to distributed joins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Predicate Transfer Refresh
&lt;/h2&gt;

&lt;p&gt;If you have time, check out my post on &lt;a href="https://danglingpointers.substack.com/p/predicate-transfer-efficient-pre" rel="noopener noreferrer"&gt;predicate transfer&lt;/a&gt;. If not, an executive can derive a summary from Fig. 1:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cu1s05csp8xx87jv7fl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cu1s05csp8xx87jv7fl.png" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3725259" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3725259&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The idea is to pre-filter the tables involved in a query, so as to reduce total query time by joining smaller tables. Fig. 1(a) shows two tables which will be joined during query execution: &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt;. &lt;code&gt;S'&lt;/code&gt; is the pre-filtered version of S. &lt;code&gt;S'&lt;/code&gt; is constructed with the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Iterate through all join keys in &lt;code&gt;R&lt;/code&gt;, inserting each key into a bloom filter (&lt;code&gt;BF.R&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Iterate through all rows in &lt;code&gt;S&lt;/code&gt;, probing &lt;code&gt;BF.R&lt;/code&gt; for each row, insert rows that pass the bloom filter into &lt;code&gt;S'&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that &lt;code&gt;S'&lt;/code&gt; is constructed, the algorithm takes another step in the join graph (illustrated in Fig. 1(b)). In this next step, the &lt;code&gt;S'&lt;/code&gt; computed in a previous iteration performs the job of &lt;code&gt;R&lt;/code&gt; (a different join key is used in this step). The algorithm starts at tables with pushed-down filters, propagates predicate information forward through the join graph, and then reverses and propagates predicate information backward.&lt;/p&gt;

&lt;p&gt;Now that you remember the basics of predicate transfer, it’s time to deal with distributed joins. In such an environment, each node in the system holds a subset of each table (e.g., &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Broadcast
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;R&lt;/code&gt; is small relative to &lt;code&gt;S&lt;/code&gt;, then it makes sense to broadcast &lt;code&gt;BF.R&lt;/code&gt; to each node. Fig. 3 illustrates three ways to do this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6w15g7zqmjiecllnl5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6w15g7zqmjiecllnl5d.png" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3725259" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3725259&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;(a) Design 1 is the simplest, let’s start there. It is a two-step process to compute the pre-filtered version of &lt;code&gt;S&lt;/code&gt; (i.e., &lt;code&gt;S').&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Node &lt;code&gt;i&lt;/code&gt; iterates through all rows in its local subset of &lt;code&gt;R&lt;/code&gt;, and inserts each join key into a local bloom filter &lt;code&gt;BF.Ri&lt;/code&gt;. Each of these small bloom filters is broadcast to every other node (which isn’t too expensive because &lt;code&gt;R&lt;/code&gt; is assumed to be small). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Node &lt;code&gt;i&lt;/code&gt; iterates through all rows in its local subset of &lt;code&gt;S&lt;/code&gt;, and probes all of the small bloom filters (&lt;code&gt;BF.R1&lt;/code&gt;, &lt;code&gt;BF.R2&lt;/code&gt;, …). If any probe operation results in a hit, then the row is inserted into the local subset of &lt;code&gt;S'&lt;/code&gt;. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Design 2 merges all of the small bloom filters together to avoid multiple probes, and design 3 parallelizes the merging process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shuffle
&lt;/h2&gt;

&lt;p&gt;If both tables are roughly the same size, then shuffling is likely more efficient. Shuffling is based on the following property of relational algebra (this is the 3rd post with this same formula):&lt;/p&gt;

&lt;p&gt;In English: partition &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt; into two partitions (based on hashing the join key) and then perform partition-wise joins.&lt;/p&gt;

&lt;p&gt;In the distributed setting, the number of partitions can equal the number of nodes.&lt;/p&gt;

&lt;p&gt;Fig. 4(b) illustrates shuffle-based predicate transfer with (&lt;code&gt;N&lt;/code&gt;=2) nodes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx4wjeetm64iffnrr134.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx4wjeetm64iffnrr134.png" width="545" height="665"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3725259" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3725259&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Node &lt;code&gt;i&lt;/code&gt; partitions its local subset of &lt;code&gt;R&lt;/code&gt; and &lt;code&gt;S&lt;/code&gt; into &lt;code&gt;N&lt;/code&gt; partitions (&lt;code&gt;Ri.JK1&lt;/code&gt;, &lt;code&gt;Ri.JK2&lt;/code&gt;, …), using a hash of the join key to assign each row to a partition. Partitions of &lt;code&gt;R&lt;/code&gt; are only used to compute local bloom filters (one per partition). The resulting bloom filters and partitions of S are sent across the network. For example, &lt;code&gt;Si.JK2&lt;/code&gt; is sent to node 2, and similarly the bloom filter derived from &lt;code&gt;Ri.JK2&lt;/code&gt; is sent to node 2.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each node iterates through all join keys in the partition of S that it just received, probing all bloom filters. If there is a hit in any bloom filter, then the join key is inserted into one of &lt;code&gt;N&lt;/code&gt; bloom filters (the bloom filter index depends on &lt;strong&gt;which node the row originally came from&lt;/strong&gt; ). These bloom filters are sent back to the associated nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each node iterates through its local subset of &lt;code&gt;S&lt;/code&gt;. For each row, the join key is used to determine which node computed the corresponding bloom filter. That bloom filter is used to check to see if the row should be inserted into the local subset of &lt;code&gt;S'&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In step 2, each node acts like an RPC server: it handles requests and sends responses. The request payload is a subset of &lt;code&gt;S&lt;/code&gt;. The response payload is a bloom filter which represents the subset of that subset which should be included in &lt;code&gt;S'&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Fig. 6 has results for both end-to-end time, and the amount of data sent over the network. &lt;code&gt;NoPT&lt;/code&gt; is the vanilla baseline, &lt;code&gt;QS&lt;/code&gt; is prior work that tries to achieve a similar goal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4h9t6k0fouessg96qcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg4h9t6k0fouessg96qcd.png" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://dl.acm.org/doi/10.1145/3725259" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3725259&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;I’ve added the &lt;code&gt;SlowRandomAccess&lt;/code&gt; tag to this one, Bloom filter insertion and probe operations require a small amount of compute, and then at least one random read/write. It would be amazing if there was another &lt;a href="https://dl.acm.org/doi/10.1145/800133.804332" rel="noopener noreferrer"&gt;approximate membership testing&lt;/a&gt; algorithm that was more friendly to the memory hierarchy. In this paper, it seems like this could be a poor point for scalability, because at most steps there are multiple bloom filters at play, so the total working set for all bloom filters accessed by a single node in a single step is large.&lt;/p&gt;

&lt;p&gt;In the shuffling case, bloom filter representations of subsets of &lt;code&gt;R&lt;/code&gt; are sent across the network (nice for reducing networking bandwidth), but the actual contents of &lt;code&gt;S&lt;/code&gt; must be sent. I believe this is because there is no efficient way to compute the intersection of two sets represented by two bloom filters.&lt;/p&gt;

</description>
      <category>database</category>
    </item>
    <item>
      <title>To PRI or Not To PRI, That's the question</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Fri, 26 Sep 2025 12:02:41 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/to-pri-or-not-to-pri-thats-the-question-4834</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/to-pri-or-not-to-pri-thats-the-question-4834</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/conference/osdi25/presentation/wang-yun" rel="noopener noreferrer"&gt;To PRI or Not To PRI, That's the question&lt;/a&gt; Yun Wang, Liang Chen, Jie Ji, Xianting Tian, and Ben Luo, Zhixiang Wei, Zhibai Huang, and Kailiang Xu, Kaihuan Peng, Kaijie Guo, Ning Luo, Guangjian Wang, Shengdong Dai, Yibin Shen, Jiesheng Wu, and Zhengwei Qi &lt;em&gt;OSDI'25&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast IO and Oversubscription
&lt;/h2&gt;

&lt;p&gt;The problem this paper addresses comes from the tension between two requirements in cloud environments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fast, Virtualized, IO&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DRAM oversubscription&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PCIe has bells and whistles to enable fast, virtualized, IO. With &lt;em&gt;Single Root I/O Virtualization&lt;/em&gt; (SR-IOV) a device (e.g., a NIC) can advertise many virtual functions (VFs). Each virtual function can be mapped directly into a VM. Each VF appears to a VM as a dedicated NIC which the VM can directly access. For example, a VM can send network packets without a costly hypervisor switch on each packet.&lt;/p&gt;

&lt;p&gt;Oversubscription allows more VMs to be packed onto a single server, taking advantage of the fact that it is rare that all VMs actually need all of the memory they have been allocated. The hypervisor can sneakily move rarely used pages out to disk, even though the guest OS still thinks those pages are resident.&lt;/p&gt;

&lt;p&gt;The trouble with putting these two (SR-IOV and DRAM oversubscription) together is that devices typically require that any page they access in host memory is pinned. In other words, the device cannot handle a page fault when doing a DMA read or write. This stops the hypervisor for paging out any page which may be accessed by an I/O device.&lt;/p&gt;

&lt;p&gt;This paper describes the &lt;em&gt;Page Request Interface&lt;/em&gt; (PRI) of PCIe, which enables devices to handle page faults during DMA. The trouble with this interface is that the end-to-end latency of handling a page fault is high:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mellanox [20] and VPRI focus on optimizing the latency of the IOPF process, claiming that the entire IOPF handling cycle introduces a latency of a few hundred milliseconds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A NIC cannot hide this latency, and thus a page fault causes packets to be dropped. Additionally, PRI is relatively new and does not have OS support in many VMs that are running in the cloud today.&lt;/p&gt;

&lt;p&gt;Fig. 1 shows data from a production environment which indicates that 30 additional VMs could be packed into the environment if SR-IOV could be made to not prevent DRAM oversubscription:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5g3664wdir53o579pl9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5g3664wdir53o579pl9c.png" width="766" height="779"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/osdi25/presentation/wang-yun" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/osdi25/presentation/wang-yun&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  VM Categorization
&lt;/h2&gt;

&lt;p&gt;Fig. 4 argues for a two-pronged approach (“there are two types of VMs in this world”):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xgk2geyrunsn2e4k8wa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xgk2geyrunsn2e4k8wa.png" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/osdi25/presentation/wang-yun" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/osdi25/presentation/wang-yun&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The key observation is that most VMs don’t do high frequency IO. The paper proposes to dynamically classify VMs into two IO frequency buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For VMs with low frequency IO, the hypervisor is in the loop for each IO operation and moves pages between disk/DRAM to allow oversubscription. The paper calls this mode &lt;em&gt;IOPA-Snoop&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For VMs with high frequency IO, the hypervisor ensures that all pages stay resident, and the hypervisor gets out of the way as much as possible. The paper calls this mode &lt;em&gt;Passthrough&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At any point in time, most VMs are in IOPA-Snoop mode, and the hypervisor benefits from DRAM oversubscription for these VMs.&lt;/p&gt;

&lt;p&gt;The engineering marvel here is that this system can be done without any changes to the guest OS. The fine print on that point is this system requires the guest OS to use &lt;a href="https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html" rel="noopener noreferrer"&gt;VirtIO&lt;/a&gt; drivers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The system described in this paper leverages &lt;a href="https://en.wikipedia.org/wiki/Second_Level_Address_Translation" rel="noopener noreferrer"&gt;nested paging&lt;/a&gt;. Intel’s implementation is called &lt;em&gt;Extended Paged Table&lt;/em&gt; (EPT). Each ring buffer used to communicate with the device (i.e., &lt;em&gt;Native Ring&lt;/em&gt;) has a &lt;em&gt;Shadow Ring&lt;/em&gt; buffer associated with it. EPT is used to atomically switch the guest VM between the two rings. Similarly, the &lt;em&gt;I/O page table&lt;/em&gt; (IOPT) in the IOMMU is used to atomically switch the device between the two rings.&lt;/p&gt;

&lt;p&gt;When operating in IOPA-Snoop mode, the guest OS writes packet descriptors into the shadow ring. A module in the hypervisor detects a change to the shadow ring, moves pages to/from disk as necessary, and then updates the native ring, thus triggering the device to perform the IO. When operating in elastic passthrough mode, the guest OS writes packet descriptors directly into the native ring, and the hardware processes them immediately.&lt;/p&gt;

&lt;p&gt;Before transitioning from snoop to passthrough mode, the hypervisor disables DRAM oversubscription (and pages in all pages the device could possibly access). Section 3.3 has more details on how the transition is implemented.&lt;/p&gt;

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

&lt;p&gt;Fig. 11 shows throughput for IOPA-Snoop, Passthrough, and hardware-based page fault handling (i.e., VPRI). Results are normalized to passthrough throughput (i.e., 100 is the speed at which passthrough mode operates). The right-hand side shows the significant cost of hardware page fault handling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyawvi2uw98a88g4q4u8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyawvi2uw98a88g4q4u8.png" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/osdi25/presentation/wang-yun" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/osdi25/presentation/wang-yun&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;I wonder how much there is to be gained by a NIC-specific vertical solution. &lt;a href="https://danglingpointers.substack.com/p/disentangling-the-dual-role-of-nic" rel="noopener noreferrer"&gt;Disentangling the Dual Role of NIC Receive Rings&lt;/a&gt; indicates that there could be significant performance gained by cooperation between the NIC, hypervisor, and guest OS. For example, there could be a portion of host memory dedicated to hold packets received by the NIC, and this host memory could be dynamically shared between all guest VMs.&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
    <item>
      <title>Pushing the Limits of In-Network Caching for Key-Value Stores</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Wed, 24 Sep 2025 15:01:26 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/pushing-the-limits-of-in-network-caching-for-key-value-stores-2ak5</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/pushing-the-limits-of-in-network-caching-for-key-value-stores-2ak5</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/conference/nsdi25/presentation/kim" rel="noopener noreferrer"&gt;Pushing the Limits of In-Network Caching for Key-Value Stores&lt;/a&gt; Gyuyeong Kim &lt;em&gt;NSDI'25&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Load Balancing Cache
&lt;/h2&gt;

&lt;p&gt;I generally think of a cache as serving the purpose of load reduction, (e.g., reducing the total number of DRAM accesses or backend server requests). The purpose here is different: &lt;strong&gt;load balancing&lt;/strong&gt;. This paper builds on &lt;a href="https://dl.acm.org/doi/10.1145/2038916.2038939" rel="noopener noreferrer"&gt;prior work&lt;/a&gt; which defined the &lt;em&gt;small cache effect&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;small cache effect: we can balance loads for &lt;em&gt;N&lt;/em&gt; servers (or partitions) by caching the O(&lt;em&gt;N&lt;/em&gt; log &lt;em&gt;N&lt;/em&gt;) hottest items, regardless of the number of items&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine a key-value store with keys sharded across multiple backend servers. By configuring the network switches (which are already present in the network) correctly, read requests for hot keys can be handled entirely by the switch, which balances the load across the backend servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything Looks Like a Nail
&lt;/h2&gt;

&lt;p&gt;The core of this paper is a creative approach to configuring a &lt;em&gt;reconfigurable match table&lt;/em&gt; (RMT) switch to act as a load balancing cache. The RMT architecture contains a pipeline of stages, where each stage has access to dedicated SRAM and TCAM memories. The specific switch used in this paper is an &lt;a href="https://www.intel.com/content/www/us/en/products/details/network-io/intelligent-fabric-processors/tofino.html" rel="noopener noreferrer"&gt;Intel Tofino&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The natural thing to try is to store the hot items in the SRAM/TCAM associated with each stage. When a cache read request packet arrives at the switch, the packet can flow through the RMT pipeline searching for matches (TCAM should be very helpful). That is &lt;strong&gt;not&lt;/strong&gt; the approach taken in this work. Section 2 of the paper goes into depth about the drawbacks of this approach (e.g., handling variable length keys and values).&lt;/p&gt;

&lt;p&gt;Here is the punchline: &lt;em&gt;store read requests in switch memory&lt;/em&gt;. When a read request arrives at the switch, the switch determines if the request is for a hot item. If so, it searches for an empty spot in on-chip memory and stores the request there. Hot items are not stored in switch memory, rather &lt;em&gt;they are continuously recycled through the switch&lt;/em&gt;. It is like there is another component in the network which is continuously sending packets to the switch which represent the hot (key, value) pairs. No such component is necessary however, because switches have the ability to recycle packets back through themselves.&lt;/p&gt;

&lt;p&gt;This cache is called &lt;em&gt;OrbitCache&lt;/em&gt;, because you can think of the recycled packets as moons orbiting a planet (the switch).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When a new (key, value) pair is cached, a (fixed length) hash of the key is stored in the &lt;em&gt;lookup table&lt;/em&gt; (in switch memory), and the (key, value) pair is continuously recycled through the switch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When a read request arrives at the switch, a hash of the key is used to check the lookup table for a match. If there is a match (cache hit), then the read request is stored in switch memory. If there is no match (cache miss), then the read request is forwarded to the appropriate backend server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When a cached (key, value) pair is recycled through the switch, a hash of the key is used to check if there are pending read requests in cache memory. If so, for each pending read request, a response packet is generated and sent back to the client.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Variable length keys are handled by hashing all key bytes down to a fixed length. Hash collisions are detected and handled by the client (this assumes that collisions are rare).&lt;/p&gt;

&lt;p&gt;Variable length values are naturally handled by all of the networking protocol support for variable length packets (up to an MTU).&lt;/p&gt;

&lt;p&gt;Section 3 of the paper goes into more details (e.g., how cache coherence is maintained).&lt;/p&gt;

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

&lt;p&gt;Fig. 13 has results for Twitter workloads. NetCache is prior work that stores cached data in switch memory. NetCache doesn’t perform as well because it cannot cache all items due to key/value size limits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6adm8zi2mxijqodksk1h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6adm8zi2mxijqodksk1h.png" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/system/files/nsdi25-kim.pdf" rel="noopener noreferrer"&gt;https://www.usenix.org/system/files/nsdi25-kim.pdf&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;It seems like &lt;a href="https://www.intel.com/content/www/us/en/products/details/network-io/intelligent-fabric-processors/tofino.html" rel="noopener noreferrer"&gt;Tofino&lt;/a&gt; is a great networking research platform. I imagine many academics were saddened when Intel &lt;a href="https://www.tomshardware.com/news/intel-sunsets-network-switch-biz-kills-risc-v-pathfinder-program" rel="noopener noreferrer"&gt;exited the network switch business&lt;/a&gt;. It seems like the world could use a research platform for switches ala &lt;a href="https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-158.html" rel="noopener noreferrer"&gt;RAMP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Switches can contain DRAM, I wonder how well it could be used as a larger (and slower) cache.&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
    <item>
      <title>State-Compute Replication: Parallelizing High-Speed Stateful Packet Processing</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Mon, 22 Sep 2025 15:01:09 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/state-compute-replication-parallelizing-high-speed-stateful-packet-processing-26b3</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/state-compute-replication-parallelizing-high-speed-stateful-packet-processing-26b3</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/conference/nsdi25/presentation/xu-qiongwen" rel="noopener noreferrer"&gt;State-Compute Replication: Parallelizing High-Speed Stateful Packet Processing&lt;/a&gt; Qiongwen Xu et al. &lt;em&gt;NSDI'25&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is difficult for me to say if this idea is brilliant or crazy. I suspect it will force you to change some intuitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  State, Skew, Parallelism
&lt;/h2&gt;

&lt;p&gt;The goal is laudable: a framework to support generic packet processing on CPUs with the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Shared state between packets (this state can be read and written when processing each packet)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High performance with skewed distributions of flows (some flows may dominate the total amount of work, i.e., receive side scaling won’t work)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Throughput increases with the number of CPU cores used &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I see it, there are two options for parallel processing of network packets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Distribute packets across cores&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pipeline parallelism&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both pose different problems for handling shared state. This paper advocates for distributing packets among cores and has a fascinating technique for solving the shared state problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast Forward State Replication
&lt;/h2&gt;

&lt;p&gt;In this model, each core has a private replica of shared state. Incoming packets are sent to each core in a round-robin fashion. And now you are totally confused, how can this ever work? Enter &lt;em&gt;the sequencer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The sequencer is a hardware component (it could be a feature of the NIC or switch) which processes all incoming packets sequentially and appends a “packet history” to each packet immediately before it is sent to be processed by a core.&lt;/p&gt;

&lt;p&gt;Say that four cores are used to implement a DDoS mitigator, and the crux of the DDoS mitigator state update depends on IP addresses from incoming packets. Each core receives a full copy of every fourth packet. Along with every fourth packet, each core also receives the IP addresses of the previous three packets (the packets that were sent to the other cores).&lt;/p&gt;

&lt;p&gt;The per-core DDoS mitigator first updates its local state replica using the IP addresses of the previous three packets from the packet history. The paper calls this &lt;em&gt;fast-forwarding&lt;/em&gt; the local state replica. At this point, the core has a fresh view of the state and can process the incoming packet.&lt;/p&gt;

&lt;p&gt;Another way of thinking about this is that network functions can be decomposed into two steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared state update&lt;/strong&gt; , which is relatively inexpensive, and only depends on a few packet fields&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Packet processing&lt;/strong&gt; , which is relatively expensive, and consumes both the shared state and a full packet as input&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;The shared state update computation is performed redundantly by all cores; there is no parallel speedup associated with it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The authors argue that many network functions can be decomposed in such a manner and also argue that packet processing is fundamentally expensive because it involves the CPU overhead of programming a NIC to send an outgoing packet.&lt;/p&gt;

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

&lt;p&gt;Fig. 6 has results. The baseline (orange) implementation suffers in many cases because of cross-core synchronization. The sharding implementations suffer in cases of flow skew (a few flows dominating the total cost).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijwj9fjgoixw1cczqr79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijwj9fjgoixw1cczqr79.png" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/system/files/nsdi25-xu-qiongwen.pdf" rel="noopener noreferrer"&gt;https://www.usenix.org/system/files/nsdi25-xu-qiongwen.pdf&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;There are heterodox and orthodox assumptions underpinning this paper.&lt;/p&gt;

&lt;p&gt;An orthodox assumption is that the packet sequencer must run in hardware because it must process packets in order, and that is the sort of thing that dedicated hardware is much better at than a multi-core CPU.&lt;/p&gt;

&lt;p&gt;A heterodox assumption is that many network functions can be expressed with the fast-forward idiom.&lt;/p&gt;

&lt;p&gt;I wonder about the orthodox assumption. Hardware isn’t magic, is there a fundamental reason a multi-core CPU cannot handle 100Gbps networking processing with only pipeline parallelism?&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
    <item>
      <title>Scaling IP Lookup to Large Databases using the CRAM Lens</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Wed, 17 Sep 2025 14:03:10 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/scaling-ip-lookup-to-large-databases-using-the-cram-lens-an0</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/scaling-ip-lookup-to-large-databases-using-the-cram-lens-an0</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/conference/nsdi25/presentation/chang" rel="noopener noreferrer"&gt;Scaling IP Lookup to Large Databases using the CRAM Lens&lt;/a&gt; Robert Chang, Pradeep Dogga, Andy Fingerhut, Victor Rios, and George Varghese &lt;em&gt;NSDI'25&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  IP Lookup
&lt;/h1&gt;

&lt;p&gt;This paper introduces a new computational model (&lt;em&gt;CRAM&lt;/em&gt;) and then applies that model to the problem of &lt;em&gt;IP lookup&lt;/em&gt;. IP lookup is simply a mapping of an IP address to arbitrary data (e.g., the address of the next hop that a packet should be routed to). The mapping is described by (relatively static) routing tables.&lt;/p&gt;

&lt;p&gt;The tricky part is that the keys in a routing table can contain wildcards. Table 1 contains a simple example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe80cxiqrw62o1o6yt9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbe80cxiqrw62o1o6yt9p.png" width="800" height="658"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/chang" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/chang&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Content Addressable Memory
&lt;/h1&gt;

&lt;p&gt;Here is a refresher if you’ve forgotten the difference between SRAM, CAM, and TCAM. All three are types of on-chip memories.&lt;/p&gt;

&lt;p&gt;SRAM behaves like an array: it maps integer keys to values. SRAM is dense: if the key width is 10 bits, then the SRAM contains 1024 entries.&lt;/p&gt;

&lt;p&gt;CAM behaves like a hash table. It maps integer keys to values, but it contains sparse storage. For example, a CAM could have an input key width of 10 bits but only have storage for 64 entries.&lt;/p&gt;

&lt;p&gt;A TCAM is like a CAM but supports wildcards in the key bits. For example, a CAM could have 10-bit input keys and contain a key→value mapping like ( &lt;strong&gt;1010&lt;/strong&gt; ***\ &lt;em&gt;**11&lt;/em&gt;* → 35). The input key &lt;strong&gt;1010&lt;/strong&gt; 0000 &lt;strong&gt;11&lt;/strong&gt; would produce a value of 35, and an input key &lt;strong&gt;1010&lt;/strong&gt; 1101 &lt;strong&gt;11&lt;/strong&gt; would also.&lt;/p&gt;

&lt;h1&gt;
  
  
  RMT and dRMT
&lt;/h1&gt;

&lt;p&gt;The CRAM model introduced by this paper is a simplified computational model for hardware which works well for this type of application. The elevator pitch for this model is that it is simple, and yet accurately predicts performance for two widely used network processing hardware architectures: &lt;em&gt;RMT&lt;/em&gt; and &lt;em&gt;dRMT&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The idea is that a hardware architect, or network engineer could use the CRAM model to do performance analysis before diving into the weeds.&lt;/p&gt;

&lt;p&gt;Fig. 11 illustrates the RMT and dRMT hardware architectures:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2vf1zi6jiqvjyndzxin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff2vf1zi6jiqvjyndzxin.png" width="677" height="571"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/chang" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/chang&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the context of IP lookup, the four hardware blocks illustrated in Fig. 11 perform the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Match compares packet addresses against addresses in the routing table (key comparison)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Action forwards packets based on the data read from the routing table&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CAM represents CAM or TCAM memories&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RAM represents SRAM&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RMT is a more rigid pipeline, whereas dRMT is more similar to a multi-core processor where each core has access to a shared pool of CAM and RAM.&lt;/p&gt;

&lt;h1&gt;
  
  
  CRAM Model
&lt;/h1&gt;

&lt;p&gt;The core of the CRAM model is a DAG. Each vertex represents a &lt;em&gt;step&lt;/em&gt; of the computation. Data flows between steps in a fixed-size array of fixed-sized registers.&lt;/p&gt;

&lt;p&gt;A step comprises an optional table lookup, followed by a sequence of statements.&lt;/p&gt;

&lt;p&gt;The lookup table can be SRAM, CAM or TCAM. Table lookup keys come from registers, and table lookup results are stored in registers. The lookup table itself is coupled with the step. There is no way for step &lt;code&gt;N&lt;/code&gt; to access the lookup table associated with step &lt;code&gt;M&lt;/code&gt;. Note that in the strict CRAM model, tables cannot be stored off-chip (e.g., DRAM).&lt;/p&gt;

&lt;p&gt;Each statement looks more or less like a typical RISC instruction (e.g., &lt;code&gt;R1 = R2 ^ R3&lt;/code&gt;). All statements within a step run in parallel (after the table lookup).&lt;/p&gt;

&lt;h1&gt;
  
  
  Design Space Exploration
&lt;/h1&gt;

&lt;p&gt;Section 2.2 lists eight idioms which can be used to explore the design space of CRAM graphs (I think of them as CRAM programs). Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Converting a TCAM to an SRAM by duplicating entries, or visa-versa&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using a large SRAM to handle common (and simple) cases, and a small TCAM to handle uncommon (and complex) cases&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Breaking a single large lookup table into multiple small lookup tables, with each small lookup table handling a subset of the key bits &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;There are two types of results in this paper:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;New algorithms discovered by the authors using design-space exploration with the CRAM model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The predictive accuracy of the CRAM model vs the real world&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  RESAIL
&lt;/h2&gt;

&lt;p&gt;The paper describes three new algorithms, &lt;em&gt;RESAIL&lt;/em&gt; is one of them. RESAIL is a incremental improvement to &lt;a href="https://doi.org/10.1145/2740070.2626297" rel="noopener noreferrer"&gt;SAIL&lt;/a&gt; (a state-of-the-art IPv4 lookup algorithm which requires DRAM). RESAIL is based on the fact that most IPv4 addresses (keys) in real-world routing tables have a suffix of wildcards that is at least 8 bits wide (e.g., 54.135.54.*). The &lt;em&gt;prefix length&lt;/em&gt; of an address in the routing table is number of bits before the wildcard suffix.&lt;/p&gt;

&lt;p&gt;RESAIL uses a small (~3KiB) TCAM to handle routing table entries with a prefix length greater than 24. The set of routing table entries with long prefix lengths is a good fit for TCAM because it is sparse.&lt;/p&gt;

&lt;p&gt;For narrower prefix lengths, RESAIL uses two data structures in SRAM. The first is a set of 24 bitmaps. Bitmap &lt;code&gt;i&lt;/code&gt; has length &lt;code&gt;2^i&lt;/code&gt;. &lt;code&gt;i&lt;/code&gt; prefix bits from an IPv4 address are used to check if that prefix is present in the routing table. All bitmaps can be checked in parallel. If there are multiple matches, then the longest prefix is chosen.&lt;/p&gt;

&lt;p&gt;These bitmaps are also sparse (a whole lot of zeros), but it is still better to use SRAM vs TCAM because each entry is only a single bit wide.&lt;/p&gt;

&lt;p&gt;If the bitmap lookup results in a hit, then a hash table (also stored in SRAM) is used to lookup the value associated with the IPv4 address. The prefix length from the bitmap lookup is used when constructing the key used for the hash table lookup. This is how wildcard support is added to a traditional hash table. The paper mentions that a hash table with low probability of collisions is used, but does not go into more detail about how collisions are handled.&lt;/p&gt;

&lt;p&gt;Appendix A.5 contains pseudocode for RESAIL:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcktbfxspwqm312cx0nq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcktbfxspwqm312cx0nq6.png" width="727" height="556"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/chang" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/chang&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Predictive Accuracy
&lt;/h2&gt;

&lt;p&gt;Table 10 compares TCAM and SRAM usage predicted by the CRAM model vs an ideal and actual RMT architecture. I suppose the unspoken assumption on all of this is that networking is all about on-chip memory, logic isn’t significant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdirm8r4pc9puw9qskmpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdirm8r4pc9puw9qskmpl.png" width="726" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/chang" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/chang&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Dangling Pointers
&lt;/h1&gt;

&lt;p&gt;It is a bit odd that CAM and TCAM are so important for networking hardware, but not other chips. Maps/Dictionaries are extremely common in general software, so why don’t general purpose processors have dedicated support? There is probably a lot of work to design an architecture which allows for virtualization and composition of libraries.&lt;/p&gt;

&lt;p&gt;In some sense, a CPU cache acts a bit like a CAM. I wonder if the same hardware could be reused to support an architecture which had explicit CAM support.&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
    <item>
      <title>Enabling Silent Telemetry Data Transmission with InvisiFlow</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Mon, 15 Sep 2025 15:02:41 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/enabling-silent-telemetry-data-transmission-with-invisiflow-10pk</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/enabling-silent-telemetry-data-transmission-with-invisiflow-10pk</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/conference/nsdi25/presentation/zhang-yinda" rel="noopener noreferrer"&gt;Enabling Silent Telemetry Data Transmission with InvisiFlow&lt;/a&gt; Yinda Zhang, Liangcheng Yu, Gianni Antichi, Ran Ben Basat, and Vincent Liu &lt;em&gt;NSDI'25&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Watch out for symptoms of techno-Eeyore syndrome (&lt;em&gt;TES&lt;/em&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You frequently feel like all of the low hanging fruit has been picked&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Someone has recently asked you: “if this is such a good idea, why aren’t other companies doing it?”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You believe that the only remaining path for innovation requires spending a significant fraction of global GDP on new datacenters&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are experiencing TES, then this paper will snap you out of it. Herein lies a wonderfully elegant solution to a real-world problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schrödinger's Network Telemetry
&lt;/h2&gt;

&lt;p&gt;The task at hand is collecting comprehensive network telemetry without disrupting the network itself. Services, operating systems, NICs, and switches are all great sources of telemetry. A global view of network telemetry could provide valuable insight into the behavior of a network.&lt;/p&gt;

&lt;p&gt;Collecting information about network activity requires sending telemetry data over the network in question (unless one wants to build a separate network just for telemetry, which would be expensive). The problem this paper addresses is: how to collect such telemetry without altering the behavior of the network itself? The paper cites prior work which claims that enabling network telemetry can degrade application-level network throughput by &lt;strong&gt;20%&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Telemetry Flows Like Water
&lt;/h2&gt;

&lt;p&gt;Here is the refreshingly elegant solution. Designate one or more servers in the network as telemetry &lt;em&gt;collector sinks&lt;/em&gt;. These sinks are the ultimate destination for any packet containing telemetry data. Any device which produces telemetry data is called a &lt;em&gt;source&lt;/em&gt;. Sources produce network packets which contain telemetry information, and those packets make their way through the network until they reach a sink which consumes them.&lt;/p&gt;

&lt;p&gt;The magic of this system is that when a source produces a telemetry packet, the address of the sink &lt;strong&gt;is not known&lt;/strong&gt;. The packet meanders through the network (on uncongested links) until it arrives at a sink. The paper makes a great analogy: telemetry packets flowing through the network are like drops of water flowing down a mountain toward the ocean.&lt;/p&gt;

&lt;p&gt;Each component in the network does its part via the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Maintaining a dedicated buffer (i.e., a &lt;em&gt;telemetry buffer)&lt;/em&gt; for telemetry packets which are waiting to continue their journey through the network&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Periodically sending &lt;em&gt;pull requests&lt;/em&gt; to neighbors (networking equipment physically connected to the component in question). This is somewhat like PFC, where a NIC can send flow-control data to the switch it is connected to (and vice versa). This kind of pull request has nothing to do with a Git pull request. A pull request informs neighboring network components how full the telemetry buffer is. In the hydrodynamic analogy: &lt;strong&gt;the fullness of a telemetry buffer represents how high a networking component is above sea level.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Responding to pull requests from neighbors. When a network component receives a pull request, it checks to see if its own telemetry buffer is more or less full than the neighbor’s buffer. If the neighbor’s buffer is less full, then telemetry packets are moved to the neighbor. Telemetry packets are only sent in windows of time where there are no application-level packets to send on a link (i.e., telemetry packets fill in the empty space on each link).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Telemetry sinks send pull requests when they are ready to ingest more telemetry data. When a telemetry packet arrives at a sink, the sink processes the packet, and the packet disappears from the network.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The author’s call this “gradient-based transmission”; the differences in telemetry buffer utilization create a gradient, and packets descend that gradient. If there are many paths between a particular switch and a sink, this gradient information will cause the switch to choose the least-congested path.&lt;/p&gt;

&lt;p&gt;The only hiccup here occurs when most telemetry buffers are empty most of the time. In this case, the gradient is near zero everywhere, and telemetry packets can oscillate rather than make forward progress to a sink. The fix for this is to determine the distance (in network hops) between each network component and the nearest sink. This distance is used to bias the gradient toward sinks.&lt;/p&gt;

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

&lt;p&gt;The authors were able to use P4 to implement this algorithm on Wedge100BF-32X switches.&lt;/p&gt;

&lt;p&gt;Fig. 7 contains some great results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnampqcgznrqlcf6qo588.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnampqcgznrqlcf6qo588.png" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/zhang-yinda" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/zhang-yinda&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;It seems like this could be generalized to more than just telemetry. If there is ever a situation where a network packet could be sent to one of a set of destinations, it may be beneficial for the sender to not pick a destination and instead organize the behavior of each network component such that the network has an emergent property that causes packets to take an uncongested path toward one of the destinations.&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
    <item>
      <title>Enabling Portable and High-Performance SmartNIC Programs with Alkali</title>
      <dc:creator>Blake Pelton</dc:creator>
      <pubDate>Fri, 12 Sep 2025 15:02:21 +0000</pubDate>
      <link>https://dev.to/dangling_pointers_0bfce7ce6993/enabling-portable-and-high-performance-smartnic-programs-with-alkali-4ieh</link>
      <guid>https://dev.to/dangling_pointers_0bfce7ce6993/enabling-portable-and-high-performance-smartnic-programs-with-alkali-4ieh</guid>
      <description>&lt;p&gt;&lt;em&gt;This was originally posted on &lt;a href="//danglingpointers.substack.com"&gt;Dangling Pointers&lt;/a&gt;.  My goal is to help busy people stay current with recent academic developments.  Head there to subscribe for regular summaries of computer science research.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.usenix.org/conference/nsdi25/presentation/lin-jiaxin" rel="noopener noreferrer"&gt;Enabling Portable and High-Performance SmartNIC Programs with Alkali&lt;/a&gt; Jiaxin Lin, Zhiyuan Guo, Mihir Shah, Tao Ji, Yiying Zhang, Daehyeok Kim and Aditya Akella &lt;em&gt;NSDI'25&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SmartNIC HodgePodge
&lt;/h2&gt;

&lt;p&gt;Fig. 1 illustrates four modern SmartNIC hardware architectures. Note the diversity of implementation options:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62i58cjx9ajert1gaj3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F62i58cjx9ajert1gaj3r.png" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/lin-jiaxin" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/lin-jiaxin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Woe to the engineer who must write high performance SmartNIC programs which are portable across these architectures. &lt;em&gt;Alkali&lt;/em&gt; is a SmartNIC programming framework which enables high-performance SmartNIC programs to be written in a concise, portable way.&lt;/p&gt;

&lt;p&gt;This paper classifies a SmartNIC based on the presence and configuration of three types of resources:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Compute&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Memory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reconfigurable match-action pipelines&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The authors programming view #3 as a solved problem, and thus primarily focus on the first two resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handler Graph
&lt;/h2&gt;

&lt;p&gt;The Alkali framework is based on a compiler. It represents a SmartNIC program as a &lt;em&gt;handler graph&lt;/em&gt;, where each edge in the graph runs inside of a single compute unit. A (single-threaded) user-written program is initially transformed into a simple handler graph. Then compiler optimization passes transform the graph so as to improve performance.&lt;/p&gt;

&lt;p&gt;There are two primary ways that a handler graph can be optimized: replication and pipelining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replication
&lt;/h2&gt;

&lt;p&gt;Replication allows multiple packets to be processed in parallel (across multiple compute units) by replicating handlers. An SMT solver is used to determine how many replicas of each handler should exist and assigns handlers to specific compute units. One input to the SMT solver is a performance model which is used to predict the amount of time a handler will spend processing each packet.&lt;/p&gt;

&lt;p&gt;This transformation converts a single-threaded input program into a parallel one with shared-memory multi-threading. So, the question you should be asking yourself is: how can this be done correctly? Parallel access to shared data structures is only supported in two cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Accesses to read-only tables can be parallelized&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accesses to shardable tables can be parallelized (i.e., a table can be sharded across compute units)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Receive side scaling is an example of #2. Fields from a packet header are hashed together to produce an index, and that index is used to access a table. The table can be spread across compute units (e.g., compute unit 0 accesses half of the table while compute unit 1 accesses the other half). Packets are directed to the appropriate handler based on the computed index.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pipelining
&lt;/h2&gt;

&lt;p&gt;After replication, the compiler has an estimate of which handler is the bottleneck in the system. The compiler then attempts to split the handler into two, thus adding pipeline parallelism. A min-cut algorithm is used, which attempts to find a cut point that minimizes the amount of data that must flow between pipeline stages.&lt;/p&gt;

&lt;p&gt;Just like replication, the pipelining process must be careful to not introduce correctness problems (i.e., concurrent access to a shared data structure from multiple pipeline stages). Before the min-cut algorithm is run, the compiler inserts synthetic vertices and edges into the graph corresponding to accesses to shared tables. For example, if a handler both reads and writes a table, then a vertex is added to the graph corresponding to the table, with bidirectional edges connecting the synthetic vertex to handlers which access the table. The edges have infinite weights, which prevents them from being cut.&lt;/p&gt;

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

&lt;p&gt;Fig. 11 has a performance comparison against an open-source implementation of a network transport. The pitch here is that Alkali offers abstraction and portability without sacrificing performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3vexnx1wxve79v560ww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3vexnx1wxve79v560ww.png" width="540" height="228"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Source: &lt;a href="https://www.usenix.org/conference/nsdi25/presentation/lin-jiaxin" rel="noopener noreferrer"&gt;https://www.usenix.org/conference/nsdi25/presentation/lin-jiaxin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dangling Pointers
&lt;/h2&gt;

&lt;p&gt;The replication and pipelining process assumes that performance is predictable at compile time. Support for dynamic loops would invalidate this assumption, requiring profile-guided optimization or dynamic compilation.&lt;/p&gt;

&lt;p&gt;The two supported shared-memory access patterns seem restrictive, it seems worthy to understand what other memory access patterns can be parallelized.&lt;/p&gt;

&lt;p&gt;Section 9 of the paper mentions both of these limitations.&lt;/p&gt;

&lt;p&gt;It is interesting that this framework is cast as SmartNIC-specific. Is there something special about networking that makes pipeline parallelism so attractive? I would think other domains could benefit from a similar framework.&lt;/p&gt;

&lt;p&gt;Rather than a technical solution to SmartNIC programming, there might be an economic one. The early days of 3D graphics was a bit of a zoo, with separate programming frameworks for each hardware vendor. One thing that enabled Direct3D and OpenGL to succeed was industry consolidation. Over time, the market figured out which hardware approaches were best, and the industry converged. Once that convergence reached a certain point, then a portable platform became much easier to create. I wonder if SmartNIC programming simply needs time for the market to do its thing and create an ecosystem which is ripe for standardization.&lt;/p&gt;

</description>
      <category>networking</category>
    </item>
  </channel>
</rss>
