<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Vasilios Syrakis - hardware</title>
    <subtitle>A simple blog made with Zola and Duckquill</subtitle>
    <link rel="self" type="application/atom+xml" href="https://cetanu.github.io/tags/hardware/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://cetanu.github.io"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-07-07T00:00:00+00:00</updated>
    <id>https://cetanu.github.io/tags/hardware/atom.xml</id>
    <entry xml:lang="en">
        <title>Profit or Poverty: False Sharing</title>
        <published>2026-07-07T00:00:00+00:00</published>
        <updated>2026-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/false-sharing/"/>
        <id>https://cetanu.github.io/blog/false-sharing/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/false-sharing/">&lt;p&gt;In previous blogs I talked a little bit about
&lt;a href=&quot;https:&#x2F;&#x2F;cetanu.github.io&#x2F;blog&#x2F;why-numa-is-important-in-trading&#x2F;&quot;&gt;NUMA&lt;&#x2F;a&gt; and the
&lt;a href=&quot;https:&#x2F;&#x2F;cetanu.github.io&#x2F;blog&#x2F;translation-lookaside-buffer&#x2F;&quot;&gt;TLB&lt;&#x2F;a&gt;, if you haven’t read those, I
recommend starting there before reading this.&lt;&#x2F;p&gt;
&lt;p&gt;You should already understand that accessing main memory is relatively slow for
the CPU, especially in a low-latency environment, and you may be familiar with
the fact that the CPU has small caches of varying sizes on the chip, known as
the L1, L2, and L3 caches.&lt;&#x2F;p&gt;
&lt;p&gt;As their name implies, there are levels, or a hierarchy, from fastest (and
smallest) to slowest (and largest). The L1 cache is also typically split into
two caches, one being an instruction cache and the other being a data cache.&lt;&#x2F;p&gt;
&lt;p&gt;Fun fact: these caches are usually made of SRAM and usually they are the
largest part on the chip.&lt;&#x2F;p&gt;
&lt;img class=&quot;no-hover&quot;alt=&quot;relative speed of hardware&quot;src=&quot;&amp;#x2F;img&amp;#x2F;relative-hw-speed.png&quot;&#x2F;&gt;&lt;h2 id=&quot;requesting-data-from-the-cache&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#requesting-data-from-the-cache&quot; aria-label=&quot;Anchor link for: requesting-data-from-the-cache&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Requesting data from the cache&lt;&#x2F;h2&gt;
&lt;p&gt;You may have heard of “cache lines” before (also called cache blocks), maybe on
some article about performance on hackernews.&lt;&#x2F;p&gt;
&lt;p&gt;A cache line can be thought of a single unit of data which is transferred from
RAM to the cache. Usually one cache line is 64 bytes, but this can differ
depending on your CPU architecture.&lt;&#x2F;p&gt;
&lt;p&gt;This introduces the concept of cache locality. When the CPU needs to access the
same memory several times in a row, after it’s already been pulled into the L1
cache, this is referred to as &lt;strong&gt;temporal locality&lt;&#x2F;strong&gt;. When the CPU pulls in a
cache line and all the data it needs is within that block, it’s referred to as
spatial locality. A good example of &lt;strong&gt;spatial locality&lt;&#x2F;strong&gt; is when you have an
array that fits into one cache line, resulting in traversal being extremely
fast. An example of potentially terrible cache performance might be something
like a linked-list.&lt;&#x2F;p&gt;
&lt;p&gt;On systems with multiple CPU cores, they might share the same chunk of memory
in their local caches. Most processors use what is known as the MESI protocol
to prevent overwriting each others data. Jon Gjengset has a &lt;a href=&quot;https:&#x2F;&#x2F;cetanu.github.io&#x2F;blog&#x2F;false-sharing&#x2F;www.youtube.com&#x2F;watch?v=tND-wBBZ8RY&quot;&gt;great
talk&lt;&#x2F;a&gt; about Mutexes which also covers the
MESI protocol.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-it-can-go-wrong&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-it-can-go-wrong&quot; aria-label=&quot;Anchor link for: where-it-can-go-wrong&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Where it can go wrong&lt;&#x2F;h2&gt;
&lt;p&gt;CPUs can enter what is called a coherence livelock or cache-line pingponging,
where multiple cores fight over the same memory, or the same cache line.&lt;&#x2F;p&gt;
&lt;p&gt;The first way this can happen is called &lt;strong&gt;False Sharing&lt;&#x2F;strong&gt;, where your data may
reside in a cache line, but it may not be occupying that entire cache line by
itself. If your data sits in the first half but some other data sits in the
latter half, it’s possible that two CPUs may continuously write to the same
cache line, requiring them to communicate with each other to gain ownership in
order to modify the cache line. If you watched Jon’s video above you would know
that this can eat up dozens of precious nanoseconds, just for the comms between
CPUs. As you increase the number of cores on a system this problem can be
exacerbated.&lt;&#x2F;p&gt;
&lt;img class=&quot;no-hover&quot;alt=&quot;diagram of false sharing&quot;src=&quot;&amp;#x2F;img&amp;#x2F;false-sharing.png&quot;&#x2F;&gt;
&lt;p&gt;The second way is &lt;strong&gt;True Sharing&lt;&#x2F;strong&gt;, where cores may write to the same variable or
data structure in memory. This could happen during atomic operations or when
using synchronization primitives such as mutexes, semaphores, barriers,
spinlocks, and so on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-protect-against-it&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-protect-against-it&quot; aria-label=&quot;Anchor link for: how-to-protect-against-it&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
How to protect against it&lt;&#x2F;h2&gt;
&lt;p&gt;At least for false sharing, the solution seems simple: just pad your data
structure or variable so that it occupies an entire cache line. Obviously it
would be silly to do this with every variable in your program, so you have to
pick the time-critical parts of your application here.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;atomic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span&gt;repr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;align&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;))]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; PaddedAtomic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; AtomicU64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For true sharing, it seems quite a bit more complicated to get right. Sometimes
you really do need atomic operations on global memory.&lt;&#x2F;p&gt;
&lt;p&gt;You could pull data into thread local storage, only merging back at the end of
expensive or time critical execution.&lt;&#x2F;p&gt;
&lt;p&gt;You could reorganize your program to do batching to reduce how often it needs
to write back to the shared memory.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe a lock-free data structure could be used.&lt;&#x2F;p&gt;
&lt;p&gt;The solution most likely depends on exactly what you are willing to accept in
terms of trade-offs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Like I usually say, for the most part things like this are unnoticeable and
work just fine for the majority of situations. If you’re running a flask webapp
you probably don’t and should not care about this.&lt;&#x2F;p&gt;
&lt;p&gt;When you require low-latency, then you need to understand how to write your
programs such that they exploit cache-lines for your architecture because it
makes the difference between a 1ns L1 cache hit, or falling off a cliff with a
100x slower execution.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
