Ask HN: What are some differences between x86 and ARM processors?
By &quot;ARM&quot;, I assume you mean a modern &quot;Application ARM&quot;, like the Cortex-A78 or Cortex-X1. Something like the Cortex-M0+ is so dramatically different it might be faster to say the few things that are similar...<p>---------<p>Let me start off by saying how they&#x27;re the same. Both modern x86 and modern Application-class ARM chips are pipelined, superscalar, SIMD-accelerated, out-of-order, micro-coded, multicore systems.<p>* Microcoded &#x2F; Macrocoded-- Both ARM and x86 split instructions, and combine instructions, for better efficiency. For example, ARM will combine AESE + AESMC instructions together as a singular &quot;Macrocoded&quot; instruction that will be executed once-per-clock tick under the hood. x86 combines cmp&#x2F;jmp instructions. &quot;Under the hood&quot;, both x86 and ARM are load&#x2F;store architectures.<p>* Pipelined -- Every clock-cycle, the CPU will try to start a new instruction, even if the previous instructions haven&#x27;t completed yet. Multiply is commonly 5 clock cycles for example, but both ARM and x86 can execute a multiply every clock cycle.<p>* Superscalar -- Instead of executing one instruction at a time, modern CPUs try to execute many instructions in parallel. This &quot;combos&quot; with pipelined architectures. Modern CPUs can do 4-uops, though Apple&#x27;s newest ARM chip tries to do 8-uops per clock. (A78 only does 4 IIRC).<p>* Out-of-order -- The pipeline and superscalar parts of modern processors are extremely aggressive, and are willing to execute assembly instructions out-of-order. There is a &quot;retirement&quot; unit that puts instructions back in order. This Out-of-order unit mostly works with reorder-buffer registers (ROB), &quot;secret&quot; registers that can hold values for the sole purpose of out-of-order execution.<p>* Multicore -- Both x86 and ARM have multiple cores that can each execute a pipelined &#x2F; superscalar &#x2F; out-of-order thread.<p>------------<p>What are the differences? Well, they both have different assembly languages, but that&#x27;s kinda obvious and boring.<p>IMO, the most &quot;exciting&quot; difference is the memory model. The memory model is the set of rules that the multicore part of the chip follows. In particular: when to flush the L1 cache, and when to wait on external cores.<p>Lets think of the following piece of code:<p><pre><code>    store [x], 1; &#x2F;&#x2F; Set the memory location &quot;x&quot; to 1
    &#x2F;&#x2F; What if Thread#2 sets &quot;x&quot; to 2 here??
    load R0, [x]; &#x2F;&#x2F; Load the memory location &quot;x&quot;
    printf(&quot;%d\n&quot;, R0); &#x2F;&#x2F; print the value of R0
</code></pre>
Now the printf will print &quot;1&quot; under most circumstances. But another thread may cause printf to print &quot;2&quot;, at least in theory.<p>But practice doesn&#x27;t match theory. What if &quot;x&quot; was in L1 cache? L1 cache is never shared between cores on any system I&#x27;m aware of (x86 or ARM). As such, if your core was ONLY looking at L1 cache for that &quot;load R0, [x]&quot; statement, then it will NEVER see x change between those two lines!!<p>In practice, x86 and ARM probably will &quot;never&quot; see x set to 2 in between the store-and-load. Both CPUs have &quot;decided&quot; to optimize L1 cache to keep the above lines as fast as possible. (In fact: this optimization is called store-to-load forwarding: instead of reading the value of &quot;x&quot; from memory, both x86 and ARM will read the value of &quot;x&quot; from the &quot;store [x]&quot; instruction right above it)<p>-------<p>Okay, so ARM and x86 do the same thing in this case still. But I think I&#x27;ve explained enough to finally bring up the &quot;memory model&quot; of systems. That is to say: the decisions that the multicore-designers came up with about where, and when, a core is allowed to update variables in a multithreaded context.<p>In between each assembly instruction, your CPU-cores are communicating with each other (called &quot;snooping&quot;), determining if loads or stores need to be reordered. And it turns out, x86 and ARM have made different decisions here.<p>The x86 system follows a set of rules called &quot;Total Store Ordering&quot;, which is summarized as &quot;Don&#x27;t reorder stores between threads&quot;. If Thread#1 did &quot;store [x], store[y]&quot;, then all other threads see &quot;store[x] then store[y]&quot;, in that order.<p>ARM however, is far more aggressive. ARM cores are willing to reorder stores in its memory model. That means if Thread#1 did &quot;store[x] then store[y]&quot;, then it may look like &quot;store[y] then store[x]&quot; on Thread#2.<p>But why? Why does ARM do this? Simple: because its faster. If &quot;x&quot; remains in L1 cache, but &quot;y&quot; leaves L1 cache first, then &quot;y&quot; is updated in global memory before x.<p>On x86, a snooping message needs to be sent when y is flushed out of L1 cache, telling all other cores that &quot;x&quot; is changed, and that Core#1 needs to flush &quot;x&quot; before any other core is allowed to read &quot;x&quot;.<p>------<p>ARM REQUIRES the programmer (or really, the compiler) to stick in memory barriers into the code, to &quot;reorder&quot; the stores back into correct order.<p>In practice, these memory-barriers only are used in your &quot;spinlock&quot; and &quot;mutex&quot; implementations. In all other cases, the ARM CPU is allowed to reorder reads-and-writes with each other across threads, enabling slightly faster performance across the board.<p>x86 was more conservative with its multithreaded implementation. Fewer memory-barriers are needed, fewer reorderings are allowed. As such, I expect a whole slew of ARM multithreaded bugs as x86 code is ported into ARM. In fact, Apple foresaw this, and provided a mode for total-store-ordering on their most recent ARM chips!