<?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: Sami Al-Jamal</title>
    <description>The latest articles on DEV Community by Sami Al-Jamal (@rupertjonessa).</description>
    <link>https://dev.to/rupertjonessa</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%2F3989870%2Fe74f8ac7-b15c-4a96-8a95-afeee8647811.png</url>
      <title>DEV Community: Sami Al-Jamal</title>
      <link>https://dev.to/rupertjonessa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rupertjonessa"/>
    <language>en</language>
    <item>
      <title>C++ and Microarchitecture Nuances</title>
      <dc:creator>Sami Al-Jamal</dc:creator>
      <pubDate>Wed, 17 Jun 2026 23:38:18 +0000</pubDate>
      <link>https://dev.to/rupertjonessa/c-and-microarchitecture-nuances-4a2i</link>
      <guid>https://dev.to/rupertjonessa/c-and-microarchitecture-nuances-4a2i</guid>
      <description>&lt;p&gt;C++ source code is written in order. That does not mean the processor executes it in order.&lt;/p&gt;

&lt;p&gt;This is the first correction. It is also the one many performance discussions manage to avoid.&lt;/p&gt;

&lt;p&gt;Modern high-performance cores use &lt;strong&gt;out-of-order execution&lt;/strong&gt;. They accept a sequential instruction stream, break it into internal operations, rename registers, place work into scheduling structures, execute ready operations early, and then retire the results in program order. The machine preserves the visible behavior of sequential execution. Internally, it is not taking attendance line by line.&lt;/p&gt;

&lt;p&gt;For ordinary software, this is mostly invisible. For C++ intended to run in tens of nanoseconds, it is not invisible. At that scale, performance is not just about the number of instructions. It is about whether those instructions can be scheduled in parallel or whether the program quietly built a dependency chain and then acted surprised.&lt;/p&gt;

&lt;h2&gt;
  
  
  The processor is a dependency scheduler
&lt;/h2&gt;

&lt;p&gt;Out-of-order execution exists because in-order pipelines waste time. If an older instruction stalls, an in-order processor must often wait even if later instructions are independent and ready. That is a poor use of hardware. The chip has execution units available. The instruction stream has more work. &lt;/p&gt;

&lt;p&gt;Dynamic scheduling fixes part of this problem. The processor tracks which operations have their inputs ready. When an operation is ready and an execution unit is available, it can issue. Older operations may still be waiting. Later operations may run first. The final architectural state is still committed in order, so the program behaves correctly.&lt;/p&gt;

&lt;p&gt;Tomasulo’s algorithm is the classic model for this idea. It used reservation stations and register renaming to allow instructions to execute when their operands became available rather than strictly when they appeared in the original program (Tomasulo, 1967). Later superscalar processors extended the same general approach with speculation and reorder buffers, but the central idea stayed the same: execute according to readiness, not textual order (Hennessy &amp;amp; Patterson, 2019).&lt;/p&gt;

&lt;p&gt;That is the point relevant to C++: the processor is not primarily reading the program as a list. It is resolving a graph.&lt;/p&gt;

&lt;p&gt;Nodes are operations. Edges are dependencies.&lt;/p&gt;

&lt;p&gt;No edge, possible overlap.&lt;br&gt;
Real edge, forced order.&lt;/p&gt;
&lt;h2&gt;
  
  
  True dependencies are the hard limit
&lt;/h2&gt;

&lt;p&gt;Out-of-order execution cannot violate true data dependencies.&lt;/p&gt;

&lt;p&gt;If instruction B needs a value produced by instruction A, B waits for A. No scheduler can change that. No amount of confidence in “modern CPUs are smart” changes that either.&lt;/p&gt;

&lt;p&gt;Consider this reduction:&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="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="n"&gt;s&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="k"&gt;for&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="kt"&gt;size_t&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;n&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="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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 loop looks harmless. It also creates a loop-carried dependency. Each update to &lt;code&gt;s&lt;/code&gt; depends on the previous value of &lt;code&gt;s&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s1 = s0 + data[0]
s2 = s1 + data[1]
s3 = s2 + data[2]
s4 = s3 + data[3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The processor may overlap some surrounding work, and it may have several loads in flight, but the additions themselves form a chain. The next addition needs the previous result.&lt;/p&gt;

&lt;p&gt;A better version exposes independent chains:&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="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="n"&gt;s0&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;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="n"&gt;s1&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;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="n"&gt;s2&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;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="n"&gt;s3&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="k"&gt;for&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="kt"&gt;size_t&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;n&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;+=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s0&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&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;+&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;s1&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&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;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&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;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;data&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;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&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="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now there are four shorter dependency chains instead of one long chain. The out-of-order core has more ready work. This does not make the program faster by aesthetic force. It makes it faster because the dependency graph improved.&lt;/p&gt;

&lt;p&gt;This is the useful mental model. Optimizing for out-of-order execution means reducing critical-path length and increasing available independent work.&lt;/p&gt;

&lt;p&gt;The processor cannot schedule independence that the program does not expose.&lt;/p&gt;

&lt;h2&gt;
  
  
  False dependencies are bookkeeping problems
&lt;/h2&gt;

&lt;p&gt;Not all apparent dependencies are real.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;read-after-write&lt;/strong&gt; dependency is real. If a later operation needs the result of an earlier operation, it must wait.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;write-after-read&lt;/strong&gt; or &lt;strong&gt;write-after-write&lt;/strong&gt; dependency can be false. These often come from reusing the same architectural register name, not from the actual values depending on each other.&lt;/p&gt;

&lt;p&gt;Out-of-order processors handle this with &lt;strong&gt;register renaming&lt;/strong&gt;. The instruction set exposes a limited set of architectural registers. Internally, the processor maps those architectural registers onto a larger pool of physical registers. This lets the machine separate unrelated values that happen to use the same architectural name.&lt;/p&gt;

&lt;p&gt;For example, source code may produce machine instructions that appear to reuse a register. Internally, the processor can assign different physical registers to different live values. The name is reused. The storage is not necessarily reused.&lt;/p&gt;

&lt;p&gt;That matters because it prevents false dependencies from blocking execution. The processor can see that two writes to the same architectural register do not need to serialize if no actual value relationship exists between them.&lt;/p&gt;

&lt;p&gt;This is also why reading assembly is useful but incomplete. Assembly shows the architectural instruction stream. It does not show the physical register mappings, scheduling queues, issue timing, or reorder-buffer behavior. Assembly is closer to the machine than C++. It is still not the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reorder buffer keeps the lie consistent
&lt;/h2&gt;

&lt;p&gt;If instructions can execute out of order, the processor needs a way to preserve precise program behavior. This is the job of the &lt;strong&gt;reorder buffer&lt;/strong&gt;, or ROB.&lt;/p&gt;

&lt;p&gt;Instructions may finish execution out of order. They do not usually retire out of order. The ROB holds results until each instruction is safe to commit in the original program order. If speculation was wrong, the processor can discard the speculative work and restore a correct state.&lt;/p&gt;

&lt;p&gt;This separation matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execute: may happen out of order
retire: happens in program order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is how the processor gets performance without giving up the language’s sequential behavior.&lt;/p&gt;

&lt;p&gt;For a C++ programmer, this explains a common confusion. The processor may execute later independent operations before earlier blocked operations, but the program still appears to obey the abstract machine rules, subject to the usual caveat that undefined behavior gives the compiler and hardware no meaningful obligation. That caveat is not a footnote. It is where many “low-level tricks” go to die.&lt;/p&gt;

&lt;h2&gt;
  
  
  Out-of-order execution rewards boring independence
&lt;/h2&gt;

&lt;p&gt;The easiest way to help out-of-order execution is to provide independent operations.&lt;/p&gt;

&lt;p&gt;This often means splitting state.&lt;/p&gt;

&lt;p&gt;Bad:&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="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&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;x0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&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;x1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&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;x2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&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;x3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every line depends on the previous value of &lt;code&gt;state&lt;/code&gt;. The processor sees a chain.&lt;/p&gt;

&lt;p&gt;Better, if the algorithm allows it:&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="n"&gt;s0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;s1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;s2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the processor sees several independent chains. The final combination still has to happen, but much of the work can be scheduled earlier.&lt;/p&gt;

&lt;p&gt;This pattern appears everywhere: checksums, reductions, counters, hash-like computations, parsing loops, pricing loops, and numeric kernels. The exact transformation depends on the algorithm. The principle does not.&lt;/p&gt;

&lt;p&gt;Out-of-order execution does not reward clever-looking code. It rewards code that gives the scheduler options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pointer chasing defeats the scheduler
&lt;/h2&gt;

&lt;p&gt;A dependent load chain is especially restrictive.&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="n"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;head&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;p&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&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 next address depends on the current load. The processor cannot issue the load of &lt;code&gt;p-&amp;gt;next-&amp;gt;next&lt;/code&gt; until it has loaded &lt;code&gt;p-&amp;gt;next&lt;/code&gt;. The chain is serial.&lt;/p&gt;

&lt;p&gt;This is not mainly a lecture about cache locality. Cache locality matters, but the out-of-order point is narrower: the address of future work is not available yet. The scheduler cannot issue an operation whose address has not been computed.&lt;/p&gt;

&lt;p&gt;That is why pointer-heavy structures often behave poorly in nanosecond-scale code. The processor has resources, but the program gives it one dependent step at a time. A large out-of-order window helps only when there is other independent work nearby. If the whole loop is a dependent chain, the window fills with waiting.&lt;/p&gt;

&lt;p&gt;The code is then not CPU-bound in the useful sense. It is dependency-bound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Function calls and abstraction can hide independence
&lt;/h2&gt;

&lt;p&gt;Out-of-order execution operates on the instruction stream produced by the compiler. It does not see source-level intent. If useful independence is hidden behind function calls, aliasing, virtual dispatch, or opaque control flow, the compiler may fail to expose it in the emitted code.&lt;/p&gt;

&lt;p&gt;Inlining can matter because it gives the optimizer more context. With more context, the compiler may remove redundant work, keep values in registers, reorder independent operations, or unroll a loop enough to expose multiple independent chains. Pikus emphasizes this interaction between C++ structure, compiler optimization, and CPU utilization: high-performance C++ depends on giving the compiler and hardware enough information to use available resources effectively (Pikus, 2021).&lt;/p&gt;

&lt;p&gt;This does not mean every function call is bad. That would be a convenient rule, and therefore suspicious. The actual question is whether the final instruction stream exposes enough independent work for the core to schedule.&lt;/p&gt;

&lt;p&gt;The source is not the object of measurement. The emitted machine code is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency and throughput are different questions
&lt;/h2&gt;

&lt;p&gt;Out-of-order execution also forces a distinction between latency and throughput.&lt;/p&gt;

&lt;p&gt;An operation’s &lt;strong&gt;latency&lt;/strong&gt; is how long its result takes to become available to a dependent operation.&lt;/p&gt;

&lt;p&gt;An operation’s &lt;strong&gt;throughput&lt;/strong&gt; is how many such operations can be started per cycle when enough independent work exists.&lt;/p&gt;

&lt;p&gt;This distinction matters constantly.&lt;/p&gt;

&lt;p&gt;If a multiply has a latency of several cycles but the processor can start one multiply every cycle, then one dependency chain pays the full latency repeatedly. Four independent chains can approach throughput limits instead. Same instruction. Different dependency graph. Different performance.&lt;/p&gt;

&lt;p&gt;This is why the earlier accumulator example matters. It does not make addition itself faster. It changes the loop from latency-limited toward throughput-limited.&lt;/p&gt;

&lt;p&gt;Low-latency C++ often consists of this kind of work: find the critical dependency chain, shorten it, split it, or move independent work across it. Not glamorous. Usually effective. A rare combination.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to measure
&lt;/h2&gt;

&lt;p&gt;A benchmark that reports only wall-clock time is often not enough. It may show that one version is faster, but not why.&lt;/p&gt;

&lt;p&gt;For out-of-order behavior, useful questions include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the loop limited by one dependency chain?&lt;/li&gt;
&lt;li&gt;Did unrolling expose independent operations?&lt;/li&gt;
&lt;li&gt;Did the compiler actually emit multiple accumulators?&lt;/li&gt;
&lt;li&gt;Are instructions waiting on operands or execution resources?&lt;/li&gt;
&lt;li&gt;Did a small source change alter the generated dependency graph?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools such as Compiler Explorer, &lt;code&gt;objdump&lt;/code&gt;, LLVM-MCA, &lt;code&gt;perf&lt;/code&gt;, and Intel VTune help answer these questions. They do not replace thinking. They reduce the amount of fiction in the thinking.&lt;/p&gt;

&lt;p&gt;A simple timing result can say “faster.” The assembly and counters explain whether the speed came from less work, better scheduling, fewer stalls, or a shorter critical path.&lt;/p&gt;

&lt;p&gt;For nanosecond-scale code, that difference matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical conclusion
&lt;/h2&gt;

&lt;p&gt;Out-of-order execution is not a general blessing applied to slow code. It is a specific hardware strategy for finding ready work inside an instruction stream.&lt;/p&gt;

&lt;p&gt;It can hide stalls when independent work exists.&lt;br&gt;
It can remove false register dependencies through renaming.&lt;br&gt;
It can execute operations before older stalled instructions.&lt;br&gt;
It can preserve sequential behavior through in-order retirement.&lt;br&gt;
It cannot violate true dependencies.&lt;br&gt;
It cannot issue future work whose inputs are unknown.&lt;br&gt;
It cannot rescue a program that presents one long chain and no alternatives.&lt;/p&gt;

&lt;p&gt;The C++ programmer writing low-latency code should therefore ask a different question. Not “how many lines did I write?” Not even only “how many instructions did the compiler emit?”&lt;/p&gt;

&lt;p&gt;The better question is: &lt;strong&gt;what dependency graph did I give the processor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That graph is what the out-of-order core actually schedules. The source code is merely how the problem was submitted.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;Hennessy, J. L., &amp;amp; Patterson, D. A. (2019). &lt;em&gt;Computer Architecture: A Quantitative Approach&lt;/em&gt; (6th ed.). Morgan Kaufmann.&lt;/p&gt;

&lt;p&gt;Kessler, R. E. (1999). The Alpha 21264 microprocessor. &lt;em&gt;IEEE Micro, 19&lt;/em&gt;(2), 24–36.&lt;/p&gt;

&lt;p&gt;Pikus, F. G. (2021). &lt;em&gt;The Art of Writing Efficient Programs: An Advanced Programmer’s Guide to Efficient Hardware Utilization and Compiler Optimizations Using C++ Examples&lt;/em&gt;. Packt Publishing.&lt;/p&gt;

&lt;p&gt;Smith, J. E., &amp;amp; Sohi, G. S. (1995). The microarchitecture of superscalar processors. &lt;em&gt;Proceedings of the IEEE, 83&lt;/em&gt;(12), 1609–1624.&lt;/p&gt;

&lt;p&gt;Tomasulo, R. M. (1967). An efficient algorithm for exploiting multiple arithmetic units. &lt;em&gt;IBM Journal of Research and Development, 11&lt;/em&gt;(1), 25–33.&lt;/p&gt;

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