<p>Regardless of the memory order setting, you are requiring an atomic operation in both loops. It turns out that, with x86 processors, which are inherently strongly ordered in most situations, this results in using the same asm codes for each fetch_add: <code>lock xadd</code>. This atomic operation on x86 processors is always sequentially consistent, so there are no optimization opportunities here when specifying relaxed memory order.</p>

<p>Using relaxed memory order allows further optimizations of surrounding operations, but your code doesn't provide any further optimization opportunities, so the emitted code is the same. Note that the results may have been different with a weakly-ordered processor (e.g., ARM) or with more data manipulation within the loop (which could offer more reordering opportunities).</p>

<p>From <a href="https://en.cppreference.com/w/cpp/atomic/memory_order" rel="noreferrer">cppreference</a> (my italics):</p>

<blockquote>
  <p>std::memory_order specifies how regular, non-atomic memory accesses are to be ordered <em>around</em> an atomic operation. </p>
</blockquote>

<p>The paper <a href="https://arxiv.org/pdf/1803.04432.pdf" rel="noreferrer">Memory Models for C/C++ Programmers</a> provides <em>much</em> greater detail on this.</p>

<p>As a side note, repeatedly running atomic benchmarks or running them on different x86 processors (even by the same manufacturer) may result in dramatically different results, as the threads might not be distributed across all the cores equally, and cache latencies are affected by whether it is a local core, another core on the same chip, or on another chip. It's also affected by how the particular processor handles potential consistency conflicts. Furthermore, level 1, 2 and 3 caches behave differently, as does ram, so total size of the data set also has significant effects. See <a href="https://spcl.inf.ethz.ch/Publications/.pdf/atomic-bench.pdf" rel="noreferrer">Evaluating the Cost of Atomic Operations on
Modern Architectures</a>.</p>
why memory_order_relaxed performance is the same as memory_order_seq_cst
<p>I've created a simple test to check how <code>std::memory_order_relaxed</code> is faster than <code>std::memory_order_seq_cst</code> value for <code>atomic&lt;int&gt;</code> increment. However the performance was the same for both cases.<br>
My compiler: gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)<br>
Build arguments: g++ -m64 -O3 main.cpp -std=c++17 -lpthread<br>
CPU: Intel(R) Core(TM) i7-2670QM CPU @ 2.20GHz, 4 core, 2 thread per core<br>
Test code:</p>

<pre><code>#include &lt;vector&gt;
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;atomic&gt;
#include &lt;chrono&gt;
#include &lt;functional&gt;

std::atomic&lt;int&gt; cnt = {0};

void run_test_order_relaxed()
{
    std::vector&lt;std::thread&gt; v;
    for (int n = 0; n &lt; 4; ++n) {
        v.emplace_back([]() {
            for (int n = 0; n &lt; 30000000; ++n) {
                cnt.fetch_add(1, std::memory_order_relaxed);
            }
        });
    }
    std::cout &lt;&lt; "rel: " &lt;&lt; cnt.load(std::memory_order_relaxed);
    for (auto&amp; t : v)
        t.join();
    }

void run_test_order_cst()
{
    std::vector&lt;std::thread&gt; v;
    for (int n = 0; n &lt; 4; ++n) {
        v.emplace_back([]() {
            for (int n = 0; n &lt; 30000000; ++n) {
                cnt.fetch_add(1, std::memory_order_seq_cst);
            }
        });
    }
    std::cout &lt;&lt; "cst: " &lt;&lt; cnt.load(std::memory_order_seq_cst);
    for (auto&amp; t : v)
        t.join();
}

void measure_duration(const std::function&lt;void()&gt;&amp; func)
{
    using namespace std::chrono;
    high_resolution_clock::time_point t1 = high_resolution_clock::now();
    func();
    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    auto duration = duration_cast&lt;milliseconds&gt;( t2 - t1 ).count();
    std::cout &lt;&lt; " duration: " &lt;&lt; duration &lt;&lt; "ms" &lt;&lt; std::endl;
}

int main()
{
    measure_duration(&amp;run_test_order_relaxed);
    measure_duration(&amp;run_test_order_cst); 
    return 0;
}
</code></pre>

<p>Why does <code>std::memory_order_relaxed</code> and <code>std::memory_order_seq_cst</code> always produce almost the same results?<br>
Result:<br>
rel: 2411 duration: 4440ms<br>
cst: 120000164 duration: 4443ms</p>