title: Copying from AoS to SoA or AoS leads to the same performance
body: <p>Let's assume the following input data</p>
  <pre><code>struct Bar {
      std::vector&lt;size_t&gt; i1s;
      std::vector&lt;size_t&gt; i2s;
      std::vector&lt;size_t&gt; i3s;
      std::vector&lt;size_t&gt; i4s;
      std::vector&lt;size_t&gt; i5s;
  };
  
  struct Foo_tuple {
      size_t i1;
      size_t i2;
      size_t i3;
      size_t i4;
      size_t i5;
      Foo_tuple() = default;
  
      Foo_tuple(size_t i1 ,size_t i2 ,size_t i3 ,size_t i4 ,size_t i5) : i1(i1) ,i2(i2) ,i3(i3) ,i4(i4) ,i5(i5) {}
  };
  
  struct Foo {
      std::vector&lt;Foo_tuple&gt; tuples;
  
      void to_bar(Bar *bar);
      void to_foo(Foo *foo);
  };
  </code></pre>
  <p>with the following implementations</p>
  <pre><code>void Foo::to_foo(Foo *foo) {
      size_t tuplesToProcess = tuples.size();
      
      auto inputBegin = tuples.begin();
      auto outputBegin = foo-&gt;tuples.begin();
  
      std::copy_n(inputBegin, tuplesToProcess, outputBegin);
  };
  
  void Foo::to_bar(Bar *bar) {
      size_t tuplesToProcess = tuples.size();
      
      for (size_t tuple = 0; tuple &lt; tuplesToProcess; ++tuple) {
          bar-&gt;i1s[tuple] = tuples[tuple].i1;
          bar-&gt;i2s[tuple] = tuples[tuple].i2;
          bar-&gt;i3s[tuple] = tuples[tuple].i3;
          bar-&gt;i4s[tuple] = tuples[tuple].i4;
          bar-&gt;i5s[tuple] = tuples[tuple].i5;
      }
  };
  </code></pre>
  <p>Now, I observe that both <code>to_foo()</code> and <code>to_bar()</code> calls in</p>
  <pre><code>Foo inputTuples;
  Foo outputTuples;
  // do proper population of the inputTuple's and resizing of the outputTuple's here
  inputTuples.to_foo(&amp;outputTuples);
  </code></pre>
  <p>as well as</p>
  <pre><code>Foo inputTuples;
  Bar outputTuples;
  // do proper population of the inputTuple's and resizing of the outputTuple's here
  inputTuples.to_bar(&amp;outputTuples);
  </code></pre>
  <p>lead to exactly the same performance (measured with google benchmark).</p>
  <p>This is somehow surprising to me, since the foo to foo transformation can be performed by a sequential copy of memory (I also confirmed that it results in a <code>memcpy</code>) while the bar to foo transformation has to read one tuple from one location and then has to write five different locations, which should result in way more random memory access.</p>
  <p>Additionally I observed, that this behavior only holds as long as</p>
  <ul>
  <li><p>The number of fields in the struct is less or equal than 8. With more fields, the foo to bar transformation starts to get significantly slower the more fields I add.
  This plot has a fixed tuple count of 8'192 and varies the field count
  <a href="https://i.stack.imgur.com/ifpL9.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ifpL9.png" alt="enter image description here" /></a></p>
  </li>
  <li><p>The struct has at least 16 tuples. When having less than 16 tuples, the foo to bar transformation starts to get significantly slower the less tuples are in the input.
  This plot has a fixed field count of 20 and varies the tuple count
  <a href="https://i.stack.imgur.com/8rgnH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/8rgnH.png" alt="enter image description here" /></a></p>
  </li>
  </ul>
  <p>Can anyone explain to me where these three effects come from?</p>
  <p>Edit:
  The code was compiled with gcc 12.0.1 and <code>-O3</code> on a 64 Bit platofrm.</p>