# C++11's atomic and volatile, under the hood on x86

DevFeed: [C++11's atomic and volatile, under the hood on x86](<https://devfeed.tech/articles/c-11-s-atomic-and-volatile-under-the-hood-on-x86-12446.md>)

Original publisher: [Read original article](<http://brooker.co.za/blog/2013/01/06/volatile.html>)

Author: Marc Brooker

Published: 2013-01-06T00:00:00Z

Content type: article

Language: en

Sources: [Marc Brooker's Blog](<https://devfeed.tech/sources/marc-brooker-s-blog.md>), [Marc Brooker's Blog](<https://devfeed.tech/sources/marc-brooker-s-blog-2.md>)

Topics: [C++](<https://devfeed.tech/topics/c-plus-plus.md>), [Compiler](<https://devfeed.tech/topics/compiler.md>), [gcc](<https://devfeed.tech/topics/gcc.md>), [Assembly](<https://devfeed.tech/topics/assembly.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [assembly](<https://devfeed.tech/tags/assembly.md>), [c-plus-plus](<https://devfeed.tech/tags/c-plus-plus.md>), [code](<https://devfeed.tech/tags/code.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [gcc](<https://devfeed.tech/tags/gcc.md>), [x86](<https://devfeed.tech/tags/x86.md>)

## AI overview

The article examines how C++11 std::atomic and volatile affect generated x86 assembly and runtime performance. It compares ordinary, volatile, and atomic variables, showing how compiler optimizations, memory accesses, and lock-prefixed instructions produce substantially different execution costs.

## Source excerpt

C++11's atomic and volatile, under the hood on x86 How do C++11's atomic and volatile work? In my previous post Java's Atomic and volatile, under the hood on x86 I look at Atomic and volatile in Java, and how they affect the generated assembly. In this post, I'm looking at std::atomic and volatile in C++. Like in Java, it's well known that std::atomic and volatile have different meanings in C++, but it's still interesting to take a look at how that translates to what actually gets run. Let's start with a very simple program: for (int i = 0; i < 500000000; i++) { x += 0x3; } Then define x in one of three ways: long x; volatile long x; std::atomic_long x; Before digging directly into the assembly, we can compare the run-time of the three programs (on a Core2 Q6600 compiled with gcc4.6 -O2): long took 0.0018s volatile took 1.9s atomic_long took 8.5s It's clear from the difference in run times that these three programs do produce significantly different code. The step up from long to volatile long is 100x, and another 4x up to atomic_long. Starting the with assembly for the long version, we can see why it's so fast: addq $1500000000, %rsi Oh gcc, you're sneaky. The compiler has completely discarded the loop, and calculated the result into a constant. Without the guarantees of atomic or volatile, it's free to make optimizations like this. Next, the volatile version: movl $500000000, %eax .L2: movq x(%rip), %rdx addq $3, %rdx subl $1, %eax movq %rdx, x(%rip) jne .L2 The inclusion of volatile has forced the compiler to not only run the loop, but also load and store the variable from memory on every run (the two movq instructions). The overhead of this is clearly significant, but it's hard to seperate the effects of the two. Moving the load and store out of the loop breaks the volatile guarantee, but keeps the loop: movl $500000000, %eax movq x(%rip), %rdx .L2: addq $3, %rdx subl $1, %eax jne .L2 movq %rdx, x(%rip) That version takes about 0.3s to run, so the it's clear tha