<?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: David Álvarez Rosa</title>
    <description>The latest articles on DEV Community by David Álvarez Rosa (@david-alvarez-rosa).</description>
    <link>https://dev.to/david-alvarez-rosa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3948273%2F27a7e366-2a82-4249-9bda-ef7bcbb03af7.jpg</url>
      <title>DEV Community: David Álvarez Rosa</title>
      <link>https://dev.to/david-alvarez-rosa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/david-alvarez-rosa"/>
    <language>en</language>
    <item>
      <title>Self-Hosting Behind CGNAT</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Tue, 22 Sep 2026 16:19:18 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/self-hosting-behind-cgnat-2ono</link>
      <guid>https://dev.to/david-alvarez-rosa/self-hosting-behind-cgnat-2ono</guid>
      <description>&lt;p&gt;There is nothing more satisfying than owning, end to end, the software and the hardware you use without relying on abusive cloud corporations. Internet is us, not them.  Break free from censorship.  Learn how to self-host at home, and be truly &lt;em&gt;libre&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the past, self-hosting was easier.  You just had to open a port on your router and forward it to any machine at home.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;  Nowadays, the shortage of IPv4 addresses means routers share the same IP across your neighborhood.  Requests are routed using carrier-grade NAT (CGNAT), a second-layer NAT inside the carrier's network, where your router's address is private and translated by the carrier on the way out.  The public address is the carrier's, so port forwarding no longer works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topology
&lt;/h2&gt;

&lt;p&gt;My services run on a mid-range machine in my mother's basement in northern Spain, and are exposed to the Internet through a cheap VPS bridge in a French data center.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  +-------------------------------------+
  |           public Internet           |
  +-------------------------------------+
        ^                       ^
        | inbound               |
        v                       |
  +------------+                |
  |   bridge   |                | egress
  +------------+                |
        ^^                      |
        || WireGuard            |
        vv                      |
  +-------------------------------------+
  |               homelab               |
  +-------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;b&gt;Topology diagram.&lt;/b&gt;  The homelab is exposed to the Internet through a WireGuard tunnel to a VPS bridge.&lt;/p&gt;

&lt;p&gt;A bidirectional WireGuard tunnel&lt;sup id="fnref2"&gt;2&lt;/sup&gt; forwards all packets in all ports from the bridge to the homelab box, and vice versa. The beauty of this is that the tunnel is initiated by the homelab, so you don't need a static dedicated IP at home.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;  The penalty of the bridge is 39 ms of RTT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tunnel configuration
&lt;/h2&gt;

&lt;p&gt;Bridge's &lt;code&gt;wg0.conf&lt;/code&gt;.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Interface]
Address = 10.0.0.1/24
PrivateKey = &amp;lt;bridge-private-key&amp;gt;
ListenPort = 51820
PostUp = ...
PostDown = ...

[Peer]
PublicKey = &amp;lt;homelab-public-key&amp;gt;
AllowedIPs = 10.0.0.2/32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PostUp&lt;/code&gt; sets up NAT and forwarding rules at the kernel level.&lt;sup id="fnref5"&gt;5&lt;/sup&gt;  The first two exclude ports 2222 for SSH, and 51820 for the VPN tunnel itself.  The last three forward all traffic in all ports to the homelab.  The destination is rewritten but not the source, so the homelab sees the real client IPs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-i&lt;/span&gt; ens3 &lt;span class="nt"&gt;-p&lt;/span&gt; udp &lt;span class="nt"&gt;--dport&lt;/span&gt; 51820 &lt;span class="nt"&gt;-j&lt;/span&gt; RETURN
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-i&lt;/span&gt; ens3 &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 2222 &lt;span class="nt"&gt;-j&lt;/span&gt; RETURN
iptables &lt;span class="nt"&gt;-t&lt;/span&gt; nat &lt;span class="nt"&gt;-A&lt;/span&gt; PREROUTING &lt;span class="nt"&gt;-i&lt;/span&gt; ens3 &lt;span class="nt"&gt;-j&lt;/span&gt; DNAT &lt;span class="nt"&gt;--to-destination&lt;/span&gt; 10.0.0.2
iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-i&lt;/span&gt; wg0 &lt;span class="nt"&gt;-o&lt;/span&gt; ens3 &lt;span class="nt"&gt;-s&lt;/span&gt; 10.0.0.2 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
iptables &lt;span class="nt"&gt;-A&lt;/span&gt; FORWARD &lt;span class="nt"&gt;-i&lt;/span&gt; ens3 &lt;span class="nt"&gt;-o&lt;/span&gt; wg0 &lt;span class="nt"&gt;-d&lt;/span&gt; 10.0.0.2 &lt;span class="nt"&gt;-j&lt;/span&gt; ACCEPT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Homelab's &lt;code&gt;wg0.conf&lt;/code&gt;.&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Interface]
Address = 10.0.0.2/24
PrivateKey = &amp;lt;homelab-private-key&amp;gt;
Table = off
PostUp = ip route add default dev wg0 table 200
PostUp = ip rule add from 10.0.0.2 table 200
PostDown = ...

[Peer]
PublicKey = &amp;lt;bridge-public-key&amp;gt;
Endpoint = 213.32.19.229:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replies from the homelab have to go back down the tunnel.  That is what the config is for, sending those replies through the bridge, while leaving the homelab's own traffic on the home router.&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resilience
&lt;/h2&gt;

&lt;p&gt;Three pieces can fail.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;Homelab.&lt;/em&gt; A cronjob in the homelab checks whether SSH is still
working and, if it is not, reboots the box.&lt;/li&gt;
&lt;li&gt;  &lt;em&gt;Bridge.&lt;/em&gt; In case it fails, I recommend a backup entry point like a
Cloudflare tunnel or Tailscale directly to the homelab.&lt;/li&gt;
&lt;li&gt;  &lt;em&gt;Tunnel.&lt;/em&gt; A short drop re-handshakes on its own.  A longer one is
covered by the two cases above.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Own your services.  Be &lt;em&gt;libre&lt;/em&gt; and have fun!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;A dynamic DNS service kept your domain pointing at the right public IP whenever your ISP rotated it.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://www.wireguard.com/" rel="noopener noreferrer"&gt;WireGuard&lt;/a&gt; is a fast, modern and secure VPN tunnel that lives inside the Linux kernel.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Buying a static IP from your ISP is a valid alternative, at around 20 euros a month in Spain.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;See &lt;a href="https://dev.to/posts/first-steps-on-a-new-server/"&gt;First Steps on a New Server&lt;/a&gt; for how I set up a fresh machine.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;And &lt;code&gt;PostDown&lt;/code&gt; removes them when the tunnel goes down.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Its full configuration lives in my &lt;a href="https://github.com/david-alvarez-rosa/homelab" rel="noopener noreferrer"&gt;homelab&lt;/a&gt; repository.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;From then on, SSH to &lt;code&gt;ssh.alvarezrosa.com&lt;/code&gt; at port 22 lands on the homelab, and port 2222 on the bridge.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>selfhosting</category>
    </item>
    <item>
      <title>Optimizing a Spin-Lock</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:16:31 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/optimizing-a-spin-lock-475c</link>
      <guid>https://dev.to/david-alvarez-rosa/optimizing-a-spin-lock-475c</guid>
      <description>&lt;p&gt;A spin-lock is a lock that never sleeps.  Instead of yielding to the scheduler, the thread stays on the CPU and &lt;em&gt;spins&lt;/em&gt;.  No syscalls.  No context switches.  In this post, we'll build a version, step by step, that is 5.7x faster while drawing 5.4x less energy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark
&lt;/h2&gt;

&lt;p&gt;Threads increment a shared counter under the lock.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;Lockable&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;BM_SpinLock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;benchmark&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;alignas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hardware_destructive_interference_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;lockable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;Lockable&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;alignas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hardware_destructive_interference_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="n"&gt;pinThread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thread_index&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lockable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;lockable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;benchmark&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DoNotOptimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lock and the counter get a cache line each.  Threads are pinned.&lt;/p&gt;

&lt;h2&gt;
  
  
  A naive spin-lock
&lt;/h2&gt;

&lt;p&gt;An atomic bool and an exchange loop.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpinLockV1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic_bool&lt;/span&gt; &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncontended it takes 3.14 ns.  Two threads take 61.5 ns, twenty times as long.  Four take &lt;strong&gt;246 ns&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V1&amp;gt;'&lt;/span&gt;
BM_SpinLock&amp;lt;SpinLockV1&amp;gt;/real_time/threads:1      3.14 ns
BM_SpinLock&amp;lt;SpinLockV1&amp;gt;/real_time/threads:2      61.5 ns
BM_SpinLock&amp;lt;SpinLockV1&amp;gt;/real_time/threads:4       246 ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A core must own the line exclusively to write it, so waiters take it from each other.  L1-d misses go from 1.27% at one thread to 61.73% at four, and one branch in eight is mispredicted.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V1&amp;gt;.*threads:1'&lt;/span&gt;
1,638,619,370      instructions           &lt;span class="c"&gt;# 0.51  insn per cycle&lt;/span&gt;
      244,253      branch-misses          &lt;span class="c"&gt;# 0.11% of all branches&lt;/span&gt;
       75,519      L1-dcache-load-misses  &lt;span class="c"&gt;# 1.27% of all L1-dcache accesses&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V1&amp;gt;.*threads:4'&lt;/span&gt;
1,231,495,723      instructions           &lt;span class="c"&gt;# 0.02  insn per cycle&lt;/span&gt;
   33,824,516      branch-misses          &lt;span class="c"&gt;# 12.52% of all branches&lt;/span&gt;
  208,756,315      L1-dcache-load-misses  &lt;span class="c"&gt;# 61.73% of all L1-dcache accesses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spinning costs energy.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;  At four threads it draws &lt;strong&gt;64.92 J&lt;/strong&gt;.&lt;sup id="fnref5"&gt;5&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; power/energy-pkg/ ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V1&amp;gt;.*threads:4'&lt;/span&gt;
          64.92 Joules power/energy-pkg/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Memory ordering
&lt;/h2&gt;

&lt;p&gt;The default is &lt;code&gt;seq_cst&lt;/code&gt;, stronger than a lock needs.  It only has to &lt;code&gt;acquire&lt;/code&gt; on the way in and &lt;code&gt;release&lt;/code&gt; on the way out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpinLockV2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic_bool&lt;/span&gt; &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_acquire&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_release&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On x86 &lt;code&gt;lock&lt;/code&gt; is unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SpinLockV2::lock():
        mov     al, 1
        xchg    byte ptr [rdi], al  // Locked exchange, both orderings
        test    al, 1
        jne     .LBB0_1
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is in &lt;code&gt;unlock&lt;/code&gt;.  The default ordering adds a second locked read-modify-write, on top of the one in &lt;code&gt;lock&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;SpinLockV1::unlock():
        xor     eax, eax
        xchg    byte ptr [rdi], al  // Locked read-modify-write
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;memory_order_release&lt;/code&gt;, &lt;code&gt;unlock&lt;/code&gt; is a plain store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SpinLockV2::unlock():
        mov     byte ptr [rdi], 0   // Plain store
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One atomic instead of two.  3.14 ns to 1.57 ns uncontended, 246 ns to &lt;strong&gt;131 ns&lt;/strong&gt; at four threads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V2&amp;gt;'&lt;/span&gt;
BM_SpinLock&amp;lt;SpinLockV2&amp;gt;/real_time/threads:1      1.57 ns
BM_SpinLock&amp;lt;SpinLockV2&amp;gt;/real_time/threads:2      32.5 ns
BM_SpinLock&amp;lt;SpinLockV2&amp;gt;/real_time/threads:4       131 ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Miss rates fall too.  L1-d 61.73% to 21.16%, branches 12.52% to 7.43%. Energy drops to &lt;strong&gt;34.45 J&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V2&amp;gt;.*threads:4'&lt;/span&gt;
773,887,322      instructions           &lt;span class="c"&gt;# 0.03  insn per cycle&lt;/span&gt;
 12,348,239      branch-misses          &lt;span class="c"&gt;# 7.43% of all branches&lt;/span&gt;
 99,804,390      L1-dcache-load-misses  &lt;span class="c"&gt;# 21.16% of all L1-dcache accesses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exchange writes the line even when it fails.  Waiters must stop writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test and test-and-set
&lt;/h2&gt;

&lt;p&gt;Exchange once, then wait on a read-only load.  The &lt;code&gt;_mm_pause&lt;/code&gt; instruction marks the loop as a spin-wait, so the core idles.&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpinLockV3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic_bool&lt;/span&gt; &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_acquire&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_relaxed&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// Read-only spin&lt;/span&gt;
        &lt;span class="n"&gt;_mm_pause&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                                     &lt;span class="c1"&gt;// Backoff&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_release&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two threads drop by a third, 32.5 ns to 21.3 ns.  Four threads gain 8%, 131 ns to &lt;strong&gt;120 ns&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V3&amp;gt;'&lt;/span&gt;
BM_SpinLock&amp;lt;SpinLockV3&amp;gt;/real_time/threads:1      1.58 ns
BM_SpinLock&amp;lt;SpinLockV3&amp;gt;/real_time/threads:2      21.3 ns
BM_SpinLock&amp;lt;SpinLockV3&amp;gt;/real_time/threads:4       120 ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L1-d misses fall from 21.16% to 17.31%, branches from 7.43% to 3.72%.  A read-only spin is predictable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V3&amp;gt;.*threads:4'&lt;/span&gt;
1,290,214,448      instructions           &lt;span class="c"&gt;# 0.05  insn per cycle&lt;/span&gt;
   12,089,906      branch-misses          &lt;span class="c"&gt;# 3.72% of all branches&lt;/span&gt;
   83,836,255      L1-dcache-load-misses  &lt;span class="c"&gt;# 17.31% of all L1-dcache accesses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Energy falls 10%, from 34.45 J to &lt;strong&gt;30.97 J&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; power/energy-pkg/ ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V3&amp;gt;.*threads:4'&lt;/span&gt;
          30.97 Joules power/energy-pkg/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every waiter pauses for the same length of time, so they all wake together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exponential backoff
&lt;/h2&gt;

&lt;p&gt;Intel documents the fix.  Wait longer each round, doubling up to a cap.&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SpinLockV4&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic_bool&lt;/span&gt; &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;backoff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_acquire&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;_mm_pause&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// Backoff&lt;/span&gt;
        &lt;span class="n"&gt;backoff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backoff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;backoff&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// Exp. growth&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_relaxed&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// Read-only spin&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;noexcept&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;locked_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;memory_order_release&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Waiters back off by different amounts and stop waking together.  Four threads drop from 120 ns to &lt;strong&gt;43.0 ns&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V4&amp;gt;'&lt;/span&gt;
BM_SpinLock&amp;lt;SpinLockV4&amp;gt;/real_time/threads:1      1.58 ns
BM_SpinLock&amp;lt;SpinLockV4&amp;gt;/real_time/threads:2      18.3 ns
BM_SpinLock&amp;lt;SpinLockV4&amp;gt;/real_time/threads:4      43.0 ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L1-d misses fall from 17.31% to 12.88%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V4&amp;gt;.*threads:4'&lt;/span&gt;
600,071,010      instructions           &lt;span class="c"&gt;# 0.07  insn per cycle&lt;/span&gt;
  8,296,063      branch-misses          &lt;span class="c"&gt;# 6.17% of all branches&lt;/span&gt;
 33,717,087      L1-dcache-load-misses  &lt;span class="c"&gt;# 12.88% of all L1-dcache accesses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Energy falls to &lt;strong&gt;11.92 J&lt;/strong&gt;, 5.4x less than the naive version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; power/energy-pkg/ ./benchmark &lt;span class="nt"&gt;--benchmark_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'V4&amp;gt;.*threads:4'&lt;/span&gt;
          11.92 Joules power/energy-pkg/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Reproduce it with the &lt;a href="https://github.com/david-alvarez-rosa/CppPlayground/blob/main/dsa/spin_lock.cpp" rel="noopener noreferrer"&gt;benchmark&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;1 thread&lt;/th&gt;
&lt;th&gt;2 threads&lt;/th&gt;
&lt;th&gt;4 threads&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;V1&lt;/td&gt;
&lt;td&gt;3.14 ns&lt;/td&gt;
&lt;td&gt;61.5 ns&lt;/td&gt;
&lt;td&gt;246 ns / 64.92 J&lt;/td&gt;
&lt;td&gt;Naive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V2&lt;/td&gt;
&lt;td&gt;1.57 ns&lt;/td&gt;
&lt;td&gt;32.5 ns&lt;/td&gt;
&lt;td&gt;131 ns / 34.45 J&lt;/td&gt;
&lt;td&gt;Memory ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V3&lt;/td&gt;
&lt;td&gt;1.58 ns&lt;/td&gt;
&lt;td&gt;21.3 ns&lt;/td&gt;
&lt;td&gt;120 ns / 30.97 J&lt;/td&gt;
&lt;td&gt;Test and test-and-set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;V4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.58 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18.3 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;43.0 ns&lt;/strong&gt; / &lt;strong&gt;11.92 J&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Exponential backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In most code, &lt;code&gt;std::mutex&lt;/code&gt; is still the right default.  Consider a spin-lock when the threads are pinned to dedicated cores, and only after measuring.&lt;sup id="fnref8"&gt;8&lt;/sup&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Run on a box &lt;a href="https://dev.to/posts/tuning-a-server-for-benchmarking/"&gt;tuned for benchmarking&lt;/a&gt;.  Built with &lt;code&gt;clang&lt;/code&gt;.  All optimizations enabled.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;code&gt;exchange&lt;/code&gt; atomically writes &lt;code&gt;true&lt;/code&gt; and returns the previous value.  &lt;code&gt;false&lt;/code&gt; means the lock was free and is now ours.  &lt;code&gt;true&lt;/code&gt; means someone else holds it, so we retry.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Whether the exchange succeeds is decided by the other cores, so the branch predictor has nothing to learn.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;High-frequency trading shops care about it. &lt;a href="https://www.nyse.com/technology/colo" rel="noopener noreferrer"&gt;Exchange colocation services&lt;/a&gt; charge for power, and NYSE &lt;a href="https://www.federalregister.gov/documents/2023/11/20/2023-25548/self-regulatory-organizations-new-york-stock-exchange-llc-nyse-american-llc-nysearca-inc-nyse" rel="noopener noreferrer"&gt;caps&lt;/a&gt; at 32 kW.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;Reading the RAPL counters requires system-wide mode (&lt;code&gt;-a&lt;/code&gt;) and root, so the figure covers the whole package, idle cores included.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;The load can be &lt;code&gt;relaxed&lt;/code&gt;.  What orders the critical section is the &lt;code&gt;exchange&lt;/code&gt; that succeeds, not the reads that fail.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;Example 2-10, &lt;em&gt;Contended Locks with Increasing Back-off&lt;/em&gt;, in the &lt;a href="https://cdrdv2.intel.com/v1/dl/getContent/671488" rel="noopener noreferrer"&gt;Intel Optimization Reference Manual&lt;/a&gt; (PDF, 248966-050US).&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;With one writer and many readers, consider a &lt;code&gt;seqlock&lt;/code&gt; instead.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>cpp</category>
      <category>performance</category>
    </item>
    <item>
      <title>One Hundred Thousand Reads</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Sat, 18 Jul 2026 10:53:50 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/one-hundred-thousand-reads-1mej</link>
      <guid>https://dev.to/david-alvarez-rosa/one-hundred-thousand-reads-1mej</guid>
      <description>&lt;p&gt;This site just passed one hundred thousand reads.  It started as a public notebook that almost no one read.  The plan hasn't changed: one post a month, no quick takes, only deep dives into things I care about. If you have read even one, thank you.  That number is you.&lt;/p&gt;

&lt;p&gt;Visitors stay five and a half minutes on average.  Reddit and Hacker News send almost nine in ten of them; every search engine put together sends fewer than one in twenty.&lt;/p&gt;

&lt;p&gt;The traffic comes in spikes: a post hits a front page, pulls a few thousand reads in a day, then goes quiet.  The three most-read posts owe over 41,000 reads to a few such days.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Thank you, again, for reading.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://dev.to/posts/optimizing-a-lock-free-ring-buffer/"&gt;Optimizing a Lock-Free Ring Buffer&lt;/a&gt; leads with 17,169 reads, followed by the &lt;a href="https://dev.to/posts/fundamental-theorem-of-calculus/"&gt;Fundamental Theorem of Calculus&lt;/a&gt; (12,575) and &lt;a href="https://dev.to/posts/devirtualization-and-static-polymorphism/"&gt;Devirtualization and Static Polymorphism&lt;/a&gt; (11,961).&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>meta</category>
    </item>
    <item>
      <title>Tuning a Server for Benchmarking</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Fri, 26 Jun 2026 16:50:06 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/tuning-a-server-for-benchmarking-2b97</link>
      <guid>https://dev.to/david-alvarez-rosa/tuning-a-server-for-benchmarking-2b97</guid>
      <description>&lt;p&gt;Optimizing code starts with measuring it, and a measurement is only useful if it is repeatable: a 2% improvement is invisible under 5% of noise. Yet on an untuned machine the same binary can easily run several percent faster or slower between runs. In this post we take a tiny benchmark and tune the machine step by step, re-measuring after every change, until runs become deterministic.&lt;/p&gt;

&lt;p&gt;Continue reading---&lt;a href="https://david.alvarezrosa.com/posts/tuning-a-server-for-benchmarking/" rel="noopener noreferrer"&gt;Tuning a Server for Benchmarking&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>performance</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Self-Hosting on the Dark Web</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Mon, 01 Jun 2026 10:56:24 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/self-hosting-on-the-dark-web-53p2</link>
      <guid>https://dev.to/david-alvarez-rosa/self-hosting-on-the-dark-web-53p2</guid>
      <description>&lt;p&gt;This site is now reachable over Tor as a hidden service, at a &lt;code&gt;.onion&lt;/code&gt; address that resolves only inside the Tor network.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; &lt;a href="https://www.torproject.org/" rel="noopener noreferrer"&gt;Tor&lt;/a&gt; relays and encrypts your traffic as it passes through thousands of volunteer-run servers, so that no single party can link who you are to what you are doing; a hidden service extends that anonymity to the server itself.&lt;/p&gt;

&lt;p&gt;It's built by the nonprofit &lt;a href="https://www.torproject.org/" rel="noopener noreferrer"&gt;Tor Project&lt;/a&gt;, which advances human rights and freedoms through free software and open networks, so that anyone can use the internet free from tracking, surveillance, and censorship.  The network only works because people use it, so consider &lt;a href="https://donate.torproject.org/" rel="noopener noreferrer"&gt;supporting them&lt;/a&gt; or running a relay---your contribution helps millions stay safe and private online every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hidden service
&lt;/h2&gt;

&lt;p&gt;Install Tor and point a hidden service at a local port.  Edit &lt;code&gt;/etc/tor/torrc&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;HiddenServiceDir /var/lib/tor/blog/
HiddenServicePort 80 127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The directory must be a dedicated, Tor-owned path---not your web root.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; Restart Tor and read the address it generates&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart tor@default
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /var/lib/tor/blog/hostname
dhevt6e4rtgbtr3jh53xrpwmgtilkah6nyjujocsspssrsexc7omxhid.onion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Serving the site
&lt;/h2&gt;

&lt;p&gt;Tor forwards the onion's port 80 to &lt;code&gt;127.0.0.1:8080&lt;/code&gt;, so the web server just needs to listen there.  Add an nginx server block for it---no TLS, no HTTP/2, no QUIC, since Tor speaks plain TCP and provides its own encryption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="nf"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;dhevt6e4rtgbtr3jh53xrpwmgtilkah6nyjujocsspssrsexc7omxhid.onion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/srv/tor.david.alvarezrosa.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;error_page&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="n"&gt;/404/index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload nginx and the site is live on Tor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building for the onion
&lt;/h2&gt;

&lt;p&gt;A static site bakes its base URL into absolute links, so a clearnet build would point visitors back to the clearnet domain even when served over Tor.  The fix is to build a second copy with the onion as its base URL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hugo &lt;span class="nt"&gt;--minify&lt;/span&gt; &lt;span class="nt"&gt;--baseURL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://dhevt6e4rtgbtr3jh53xrpwmgtilkah6nyjujocsspssrsexc7omxhid.onion/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deploy pipeline does this automatically: every push builds the site once per target---clearnet and Tor---and rsyncs each to its own web root, so the two stay in sync without any manual work.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;



&lt;p&gt;That's it.  Read this site over Tor at &lt;code&gt;dhevt6e4rtgbtr3jh53xrpwmgtilkah6nyjujocsspssrsexc7omxhid.onion&lt;/code&gt;.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Open it in the &lt;a href="https://www.torproject.org/" rel="noopener noreferrer"&gt;Tor Browser&lt;/a&gt;.  There is no certificate authority, no DNS, and no exposed IP---the address is derived directly from a public key, and the connection is end-to-end encrypted by Tor itself.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Tor stores the service's private key and &lt;code&gt;hostname&lt;/code&gt; file here and insists on owning it (&lt;code&gt;chmod 700&lt;/code&gt;, user &lt;code&gt;debian-tor&lt;/code&gt;).  Point it at your site files and Tor refuses to start.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;See &lt;a href="https://dev.to/posts/first-steps-on-a-new-server/"&gt;First Steps on a New Server&lt;/a&gt; for the underlying machine; the full configuration lives in my &lt;a href="https://github.com/david-alvarez-rosa/homelab" rel="noopener noreferrer"&gt;homelab&lt;/a&gt; repository, and the &lt;a href="https://github.com/david-alvarez-rosa/personal-website" rel="noopener noreferrer"&gt;site's own repository&lt;/a&gt; holds the GitHub Actions workflow that builds and deploys the Tor copy.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>infrastructure</category>
      <category>networking</category>
      <category>privacy</category>
      <category>security</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Sun, 24 May 2026 09:58:02 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/-1jjo</link>
      <guid>https://dev.to/david-alvarez-rosa/-1jjo</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg" class="crayons-story__hidden-navigation-link"&gt;Devirtualization and Static Polymorphism&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/david-alvarez-rosa" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3948273%2F27a7e366-2a82-4249-9bda-ef7bcbb03af7.jpg" alt="david-alvarez-rosa profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/david-alvarez-rosa" class="crayons-story__secondary fw-medium m:hidden"&gt;
              David Álvarez Rosa
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                David Álvarez Rosa
                
              
              &lt;div id="story-author-preview-content-3735845" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/david-alvarez-rosa" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3948273%2F27a7e366-2a82-4249-9bda-ef7bcbb03af7.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;David Álvarez Rosa&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 23&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg" id="article-link-3735845"&gt;
          Devirtualization and Static Polymorphism
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/computerscience"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;computerscience&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cpp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cpp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/performance"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;performance&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Devirtualization and Static Polymorphism</title>
      <dc:creator>David Álvarez Rosa</dc:creator>
      <pubDate>Sat, 23 May 2026 21:36:21 +0000</pubDate>
      <link>https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg</link>
      <guid>https://dev.to/david-alvarez-rosa/devirtualization-and-static-polymorphism-3mmg</guid>
      <description>&lt;p&gt;Ever wondered why your "clean" polymorphic design underperforms in benchmarks?  Virtual dispatch enables polymorphism, but it comes with hidden overhead: pointer indirection, larger object layouts, and fewer inlining opportunities.&lt;/p&gt;

&lt;p&gt;Compilers do their best to &lt;em&gt;devirtualize&lt;/em&gt; these calls, but it isn't always possible.  On latency-sensitive paths, it's beneficial to manually replace dynamic dispatch with &lt;em&gt;static polymorphism&lt;/em&gt;, so calls are resolved at compile time and the abstraction has effectively zero runtime cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtual dispatch
&lt;/h2&gt;

&lt;p&gt;Runtime polymorphism occurs when a base interface exposes a virtual method that derived classes override.  Calls made through a &lt;code&gt;Base&amp;amp;&lt;/code&gt; are then dispatched to the appropriate override at runtime.  Under the hood, a virtual table (&lt;code&gt;vtable&lt;/code&gt;) is created &lt;em&gt;for each class&lt;/em&gt;, and a pointer (&lt;code&gt;vptr&lt;/code&gt;) to the &lt;code&gt;vtable&lt;/code&gt; is added &lt;em&gt;to each instance&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;On a virtual call, the compiler loads the &lt;code&gt;vptr&lt;/code&gt;, selects the right slot in the &lt;code&gt;vtable&lt;/code&gt;, and performs an indirect call through that function pointer.  The drawback is that the extra &lt;code&gt;vptr&lt;/code&gt; increases object size, and the indirection through the &lt;code&gt;vtable&lt;/code&gt; makes the call hard to predict.  This prevents inlining, increases branch mispredictions, and reduces cache efficiency.&lt;/p&gt;

&lt;p&gt;The best way to observe this phenomenon is by inspecting the assembly&lt;sup id="fnref1"&gt;1&lt;/sup&gt; code emitted by the compiler for a minimal example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a non-virtual member function &lt;code&gt;foo&lt;/code&gt; like in the example above, the free function &lt;code&gt;bar&lt;/code&gt; issues a direct call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar(Base*):
        sub     rsp, 8
        call    Base::foo()  // Direct call
        add     rsp, 8
        add     eax, 77
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, declaring &lt;code&gt;foo&lt;/code&gt; as &lt;code&gt;virtual&lt;/code&gt; changes &lt;code&gt;bar&lt;/code&gt;'s assembly into an indirect, vtable-based call&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bar(Base*):
        sub     rsp, 8
        mov     rax, QWORD PTR [rdi]  // vptr (pointer to vtable)
        call    [QWORD PTR [rax]]     // Virtual call
        add     rsp, 8
        add     eax, 77
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Devirtualization
&lt;/h2&gt;

&lt;p&gt;Sometimes the compiler can statically deduce which override a virtual call will hit.  In those cases, it &lt;em&gt;devirtualizes&lt;/em&gt; the call and emits a direct call instead (skipping the &lt;code&gt;vtable&lt;/code&gt;).  For example, devirtualization is straightforward&lt;sup id="fnref2"&gt;2&lt;/sup&gt; when the runtime type is clearly fixed&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Derived&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// compiler knows this is Derived::foo&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler is able to devirtualize even through a base pointer, as long as it can track the allocation and prove there is only one possible concrete type.  The problem is that with traditional compilation, object files are created per translation unit (TU)---compiled and optimized in isolation.  The linker simply stitches those objects together, so cross-TU optimizations are inherently limited.  That's where compiler flags are useful.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-fwhole-program&lt;/code&gt;&lt;br&gt;
: tells the compiler "this translation unit is the entire program."  If no class derives from &lt;code&gt;Base&lt;/code&gt; in this TU, the compiler is free to assume nothing ever does, and can devirtualize calls on &lt;code&gt;Base&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-flto&lt;/code&gt;&lt;br&gt;
: link-time optimization.  Keeps an intermediate representation in the object files and optimizes across all of them at link time, effectively treating multiple source files as a single TU.&lt;/p&gt;

&lt;p&gt;On the language side, &lt;code&gt;final&lt;/code&gt; is a lightweight way to give the compiler the same guarantee for specific methods&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// override&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;final&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// final&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Derived&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;foo()&lt;/code&gt; can still be overridden, so &lt;code&gt;derived-&amp;gt;foo()&lt;/code&gt; remains a virtual call.  However, &lt;code&gt;bar()&lt;/code&gt; is marked as &lt;code&gt;final&lt;/code&gt;, so the compiler emits a direct call even though it's declared &lt;code&gt;virtual&lt;/code&gt; in the base&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test(Derived*):
        push    rbx
        sub     rsp, 16
        mov     rax, QWORD PTR [rdi]
        mov     QWORD PTR [rsp+8], rdi
        call    [QWORD PTR [rax]]       // Virtual call
        mov     rdi, QWORD PTR [rsp+8]
        mov     ebx, eax
        call    Derived::bar()          // Direct call
        add     rsp, 16
        add     eax, ebx
        pop     rbx
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Static polymorphism
&lt;/h2&gt;

&lt;p&gt;When the compiler can't devirtualize, one option is to use static polymorphism instead.  The canonical tool for this is the Curiously Recurring Template Pattern&lt;sup id="fnref3"&gt;3&lt;/sup&gt; (CRTP).  With CRTP, the base class is templated on the derived class, and invokes methods on it via &lt;code&gt;static_cast&lt;/code&gt;---no virtual keyword involved&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Derived&lt;/span&gt;&lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Derived&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Derived&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;derived&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;-O3&lt;/code&gt; optimization, the compiler inlines everything and constant-folds the result.  No &lt;code&gt;vtable&lt;/code&gt;, no &lt;code&gt;vptr&lt;/code&gt;, no indirection.  Fully optimized&lt;sup id="fnref4"&gt;4&lt;/sup&gt; call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test():
        mov     eax, 165  // 77 + 88
        ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deducing this.&lt;/strong&gt; C++23's &lt;em&gt;deducing this&lt;/em&gt; keeps the same static-dispatch model but makes it easier to write.  Instead of templating the entire class (and writing &lt;code&gt;Base&amp;lt;Derived&amp;gt;&lt;/code&gt; everywhere), you template only the member function that needs access to the derived type, and let the compiler deduce &lt;code&gt;self&lt;/code&gt; from &lt;code&gt;*this&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Derived&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
  &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This yields identical optimized code: &lt;code&gt;foo&lt;/code&gt; is instantiated as &lt;code&gt;foo&amp;lt;Derived&amp;gt;&lt;/code&gt;, and the call to &lt;code&gt;bar&lt;/code&gt; is resolved statically and inlined.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Assembly generated with &lt;code&gt;gcc&lt;/code&gt; at &lt;code&gt;-O3&lt;/code&gt; on x86-64.  Similar results were observed with &lt;code&gt;clang&lt;/code&gt; on the same platform.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The compiler emits a direct call to &lt;code&gt;Derived::foo&lt;/code&gt; (or inlines it), because &lt;code&gt;derived&lt;/code&gt; cannot have any other dynamic type.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;The curiously recurring template pattern is an idiom where a class X derives from a class template instantiated with X itself as a template argument.  More generally, this is known as F-bound polymorphism, a form of F-bounded quantification.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;The trade-off is that each &lt;code&gt;Base&amp;lt;Derived&amp;gt;&lt;/code&gt; instantiation is a distinct, unrelated type, so there's no common runtime base to upcast to.  Any shared functionality that operates across different derived types must itself be templated.&amp;nbsp;↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>computerscience</category>
      <category>cpp</category>
      <category>performance</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
