<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>AoS Archives - Fluent C++</title>
	<atom:link href="https://www.fluentcpp.com/tag/aos/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.fluentcpp.com/tag/aos/</link>
	<description>Jonathan Boccara&#039;s blog</description>
	<lastBuildDate>Fri, 03 May 2019 04:03:44 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.4</generator>
<site xmlns="com-wordpress:feed-additions:1">214950964</site>	<item>
		<title>The SoA Vector &#8211; Part 2: Implementation in C++</title>
		<link>https://www.fluentcpp.com/2018/12/21/an-soa-vector-with-an-stl-container-interface-in-cpp/</link>
		
		<dc:creator><![CDATA[Jonathan Boccara]]></dc:creator>
		<pubDate>Fri, 21 Dec 2018 01:00:52 +0000</pubDate>
				<category><![CDATA[STL]]></category>
		<category><![CDATA[AoS]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[SoA]]></category>
		<category><![CDATA[vector]]></category>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4548</guid>

					<description><![CDATA[<p>Today’s guest post is the second part of a two-posts series written by Sidney Congard. Sidney is an almost graduated student and an intern at QuasarDB, a company writing it’s own database in C++17. He has been doing C++ in his free time regularly for two years. Also interested in writing on Fluent C++? Check [&#8230;]</p>
<p>The post <a href="https://www.fluentcpp.com/2018/12/21/an-soa-vector-with-an-stl-container-interface-in-cpp/">The SoA Vector &#8211; Part 2: Implementation in C++</a> appeared first on <a href="https://www.fluentcpp.com">Fluent C++</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Today’s guest post is the second part of a two-posts series written by <b>Sidney Congard</b>. Sidney is an almost graduated student and an intern at QuasarDB, a company writing it’s own database in C++17. He has been doing C++ in his free time regularly for two years.</em></p>
<p><em>Also interested in writing on Fluent C++? Check out the <a href="https://www.fluentcpp.com/write-for-us/">guest posting area</a>!</em></p>
<p>Like we saw in the <a href="https://www.fluentcpp.com/2018/12/18/the-soa-vector-part-1-optimizing-the-traversal-of-a-collection/">first part</a> of this series on SoA, the SoA is a way to organise the data of a collection of objects to optimise the performance of certain use cases: traversing the collection by accessing the same data member of all the objects:</p>
<pre class="lang:c++ decode:true ">struct person {
   std::string name;
   int age;
};

std::vector&lt;person&gt; persons = ...

for (auto&amp; person : persons)
{
   ++person.age;
}</pre>
<p>The SoA in its barest expression is this:</p>
<pre class="lang:c++ decode:true ">struct persons {
    std::vector&lt;std::string&gt; names;
    std::vector&lt;int&gt; ages;
};</pre>
<p>By putting all the <code>ages</code> next to each other in memory, we optimise the performance of the traversal. But such a structure is not a container in itself, and is in particular not <a href="https://www.fluentcpp.com/2018/04/24/following-conventions-stl/">compatible with the STL</a>.</p>
<p>Let&#8217;s design an SoA collection with an interface as close as possible to <code>std::vector&lt;persons&gt;</code>, but with the SoA structure of components stored in separate arrays.</p>
<h3><span style="color: #ff6600;">Proxy types</span></h3>
<p>Here are the most basic expressions that we want to support:</p>
<pre class="lang:c++ decode:true ">auto persons = soa::vector&lt;person&gt;{};
persons.push_back({ “Julie”, 46 });
persons[0].age += 1;</pre>
<p>The <code>operator[]</code> allows to modify the components through their names. So we need to implement a proxy class which holds references to each component with the same names, which will be created by our <code>soa::vector</code> iterator.</p>
<p>It means that we can’t avoid to use of a macro to create these proxy types, unless we let the user write the proxy type explicitly. This macro then allow us to generate another proxy (for const references).</p>
<p>This macro can be tedious to write: The <a href="https://www.boost.org/doc/libs/1_68_0/libs/preprocessor/doc/index.html">Boost.Preprocessor</a> library can help by providing high-level macro functions to generate our code. <a href="https://www.fluentcpp.com/2017/08/04/metaclasses-cpp-summary/">Metaclasses</a> will surely allow us to avoid this once they are available!</p>
<p>On the implementation side, we will have a tuple of vectors. We can then later improve this by having a single allocation and a tuple of indexes and accepting a custom allocator as a template parameter, but it won&#8217;t affect much its use.</p>
<pre class="lang:c++ decode:true">namespace soa {

template &lt;class T&gt;
class vector {
    // We will see how we get these '???' types later.
    std::tuple&lt;std::vector&lt;???&gt;, ...&gt; vectors_;
};

}</pre>
<h4><span style="color: #ff6600;">Creating the proxy types with macros</span></h4>
<p>Let’s start by creating the proxy types, that will be what we get when we dereference an iterator coming from our SoA vector:</p>
<pre class="lang:c++ decode:true">#define SOA_PP_REF(type, member) \
decltype(std::declval&lt;type&gt;().member) &amp; member;

#define SOA_PP_CREF(type, member) \
decltype(std::declval&lt;type&gt;().member) const&amp; member;

#define SOA_DEFINE_TYPE(type, ...) \
namespace soa { \

    template &lt;&gt; \
    struct ref_proxy&lt;::type&gt; { \
        SOA_PP_MAP(SOA_PP_REF, ::type, __VA_ARGS__) \
    }; \
    template &lt;&gt; \
    struct cref_proxy&lt;::type&gt; { \
        SOA_PP_MAP(SOA_PP_CREF, ::type, __VA_ARGS__) \
    }; \
}</pre>
<p>The above code relies on the macro <code>SOA_PP_MAP(macro, type, args...)</code> which will expands to <code>macro(type, arg)</code> for each <code>arg</code> in <code>args</code>. We will skip the implementation of the implementation of the <code>SOA_PP_MAP</code> here. If you’re interested to see its code you can check it out <a href="https://github.com/Dwarfobserver/soa_vector/blob/master/soa_vector.hpp#L937">here</a>.</p>
<p>To instantiate the proxy types corresponding to the following type:</p>
<pre class="lang:c++ decode:true ">struct person {
    std::string name;
    int age;
};</pre>
<p>We would invoke the macro this way:</p>
<pre class="lang:c++ decode:true ">SOA_DEFINE_TYPE(person, name, age);</pre>
<p>The code generated by the macro would look like this:</p>
<pre class="lang:c++ decode:true">namespace soa {

template &lt;&gt;
struct ref_proxy&lt;person&gt; {
    std::string&amp; name;
    int&amp; age;
};
template &lt;&gt;
struct cref_proxy&lt;person&gt; {
    std::string const&amp; name;
    int const&amp; age;
};
}</pre>
<h3><span style="color: #ff6600;">The iterator class</span></h3>
<p>Now we can make iterators that creates our proxy when they are dereferenced. I didn&#8217;t find if there is a way to make them work with the arrow operator as well, so please tell me if you know how !</p>
<pre class="lang:c++ decode:true">namespace soa {

template &lt;class Vector&gt;
class iterator {
    Vector* vec_;
    int index_;
    // This is used to write functions using every vector at the same time.
    // We assume the iterator is a friend of our vector.
    using sequence_type = std::index_sequence&lt;std::tuple_size_v&lt;decltype(vec_-&gt;vectors_)&gt;&gt;;

    // We create a proxy object from the components at 'index_' of each 'vec_' vector.
    template &lt;size_t...Is&gt;
    ref_proxy&lt;typename Vector::value_type&gt; make_proxy(std::index_sequence&lt;Is...&gt;) const noexcept {
        return { std::get&lt;Is&gt;(vec_-&gt;vectors_)[index_] ... };
    }
public:
    iterator&amp; operator++() noexcept { return ++index_, *this; }
    // I don't put the rest of our random-access iterator implementation (based on index_).
    // The full code is available on GitHub as explained at the end of the article.

    // The dereferencing operator simply returns a new proxy object.
    auto operator*() const noexcept {
        return make_proxy(sequence_type{});
    }
};
}</pre>
<p>Once we have this iterator, the <code>soa::vector::operator[]</code> is now easy to write:</p>
<pre class="lang:c++ decode:true">template &lt;class T&gt;
auto soa::vector&lt;T&gt;::operator[](int i) {
    return *soa::iterator&lt;vector&lt;T&gt;&gt;{ this, i };
}</pre>
<h3><span style="color: #ff6600;">Implementing <code>push_back</code></span></h3>
<p>The <code>push_back</code> method needs to deconstruct the given object into its components:</p>
<pre class="lang:c++ decode:true">template &lt;class T&gt;
void soa::vector&lt;T&gt;::push_back(T const&amp; val) {
    auto elements = detail::as_tuple(val);
    detail::for_each(vectors_, elements, [] (auto&amp; vec, auto const&amp; elt) {
        vec.push_back(elt);
    });
}</pre>
<p>To implement the helper functions used in this code, we can use C++17 structured bindings with aggregates to have a tuple of references on its members. Then, we can iterate over the tuple elements and put them into our tuple of vectors (that can be deduced from aggregate tuple).</p>
<pre class="lang:c++ decode:true">namespace detail {

// Arity gives us the number of components of an aggregate by counting the number of references in it’s proxy.
template &lt;class T&gt;
constexpr int aggregate_arity = sizeof(soa::ref_proxy&lt;T&gt;) / sizeof(void*);

// as_tuple returns a tuple of references on the given aggregate’s components.
// Currently, we cannot make this function variadic, so we must recopy come code, manually or with a macro.
// We skip this here for simplicity but you can find the details in the Github library
// As an example, this is the function implementation when our aggregate has three components :
template &lt;class T&gt;
auto as_tuple_impl(T&amp; agg, std::integral_constant&lt;int, 3&gt;) {
    auto&amp; [v1, v2, v3] = agg;
    return std::forward_as_tuple(v1, v2, v3);
}

// This function dispatches the call on the 'as_tuple_impl' function which takes the right number of components.
template &lt;class T&gt;
auto as_tuple(T&amp;&amp; agg) {
    using arity = std::integral_constant&lt;int, aggregate_arity&lt;T&gt;&gt;;
    return as_tuple_impl(agg, arity{});
}

// for_each applies a function on two tuple elements.
template &lt;class T1, class T2, class BinaryOp, size_t...Is&gt;
void for_each_impl(T1&amp; t1, T2&amp; t2, BinaryOp&amp; f, std::index_sequence&lt;Is…&gt;) {
    (f(std::get&lt;Is&gt;(t1, t2)), ...);
}
template &lt;class T1, class T2, class BinaryOp&gt;
void for_each(T1&amp;&amp; t1, T2&amp;&amp; t2, BinaryOp&amp;&amp; f) {
    static_assert(std::tuple_size_v&lt;T1&gt; == std::tuple_size_v&lt;T2&gt;);
    using seq = std::make_index_sequence&lt;std::tuple_size_v&lt;T1&gt;&gt;;
    for_each_impl(t1, t2, f, seq{});
}

}
</pre>
<p>We now have our core functionalities: a way to add elements and to access them (through iterators). Then, we can copy most of <code>std::vector</code>&#8216;s interface (<code>begin()</code> and <code>end()</code>, <code>back()</code> and <code>front()</code>, <code>emplace_back(components...)</code>, vector moves and copies, &#8230;) by using the same tools.</p>
<p>In particular, this makes our SoA vector <a href="https://www.fluentcpp.com/2017/04/18/the-design-of-the-stl/">compatible with STL algorithms</a>:</p>
<pre class="lang:c++ decode:true">std::string get_name_by_age(soa::vector&lt;person&gt; const&amp; persons, int required_age) {
    auto const it = std::find_if(persons.begin(), persons.end(), [=] (auto&amp;&amp; p) {
        return p.age == required_age;
    });
    return (*it).name;
}</pre>
<h3><span style="color: #ff6600;">Performance of traversing the collection</span></h3>
<p>Since we’re having a proxy in the iterator, iterating in the collection could be harder to optimize for the compiler.</p>
<p>Let’s consider a simple traversal in the standard case of an AoS simple collection such as an array:</p>
<pre class="lang:c++ decode:true">void copy_ages(int const* src, int* __restrict dst) {
    for (int i = 0; i &lt; persons.size(); ++i) {
        dst[i] = src[i];
    }
}</pre>
<p>With the right optimization flags (-O3 for Clang and gcc and /Ox for MSVC), the compiler generates a memcpy to haul the entire collection.</p>
<p>Let’s now consider the same traversal with our SoA collection, that uses a proxy with its iterator:</p>
<pre class="lang:c++ decode:true ">void copy_ages_with_proxy(soa::vector&lt;user::person&gt; const&amp; persons, int* __restrict dst) {
    for (int i = 0; i &lt; persons.size(); ++i) {
        dst[i] = persons[i].age;
    }
}</pre>
<p>With -O3, GCC and Clang compiles this function with memcpy, but not MSVC with /Ox. MSVC generates a less efficient loop that copies the elements one by one.</p>
<p>For more complex use cases, there is a good chance that we miss this kind of optimizations on every compiler.</p>
<p>The whole point of SoA was an optimal performance, so can we do something to have a optimized traversal, regardless of the compiler?</p>
<p>One way to do this is to give to the user a way to access directly one of our array of components.</p>
<pre class="lang:c++ decode:true">namespace soa {

template &lt;class T&gt;
struct vector_span {
    T* begin_;
    T* end_;
    T* begin() const { return begin_ };
    T* end()   const { return end_ };
};

template &lt;class T&gt;
template &lt;size_t I&gt;
auto vector&lt;T&gt;::get_span() {
    auto&amp; vec = std::get&lt;I&gt;(vectors_);
    return vector_span{ vec.data(), vec.data() + vec.size() };
}

}</pre>
<p>The above code uses an numeric index (<code>size_t I</code>) to identify the member in the data object.</p>
<p>But now that we know the component&#8217;s names, we can allow the user to access to these arrays through these names! To achieve this, we can inherit those spans from our <code>soa::vector</code>. To do so, we will have a third class created with our macro:</p>
<pre class="lang:c++ decode:true ">SOA_DEFINE_TYPE(person, name, age);</pre>
<p>This macro generates this code:</p>
<pre class="lang:c++ decode:true">namespace soa {

template &lt;&gt;
struct members&lt;person&gt; {
    vector_span&lt;decltype(std::declval&lt;person&gt;().name)&gt; name;
    vector_span&lt;decltype(std::declval&lt;person&gt;().age)&gt; age;
};

}</pre>
<p>We then make our <code>soa::vector</code> inherits from this structure:</p>
<pre class="lang:c++ decode:true">namespace soa {

template &lt;class T&gt;
class vector : public members&lt;T&gt; {
    // ...
};
}</pre>
<p>Now we can access our components without the proxy:</p>
<pre class="lang:c++ decode:true ">int sum_ages(soa::vector&lt;person&gt;&amp; persons) {
    return std::reduce(persons.age.begin(), persons.age.end());
}</pre>
<p>These spans can be painful to maintain when the vector is modified, but our functionality is here. In my implementation, I improved this by storing one pointer per span and removing the tuple of vectors. As a result, I have only one allocation and no information is copied (the size is stored once and can be retrieved by the custom spans).</p>
<h3><span style="color: #ff6600;">Polishing off the interface</span></h3>
<p>Finally, we can improve our proxies by adding them operators:</p>
<ul>
<li><code>ref_proxy&lt;T&gt;::operator T()</code> to construct a <code>T</code> by copying the proxy elements. It requires <code>T</code> to be copy constructible.</li>
<li><code>ref_proxy&lt;T&gt;::operator=(T const&amp;)</code> to assign by copy <code>T</code> elements to the proxy&#8217;s elements. It also requires <code>T</code> to be copy constructible.</li>
<li><code>ref_proxy&lt;T&gt;::operator=(T&amp;&amp;)</code> to assign by move <code>T</code> elements to the proxy&#8217;s elements.</li>
</ul>
<p>Here are the new expressions this allows us to write:</p>
<pre class="lang:c++ decode:true">person change_last_person(soa::vector&lt;person&gt;&amp; persons) {
    // Move assignment operator
    persons.back() = { "Abbie", 26 };

    // Cast operator
    return persons.back();
}</pre>
<p>Unfortunately, I don&#8217;t know a way to construct a T by moving the proxy elements. We can continue to extend our interface but I think we covered most things here. My final implementation can be found on the <a href="https://github.com/Dwarfobserver/soa_vector">GitHub repository</a> . I&#8217;ll be glad to know any alternative design or insights about it !</p>
<p>There is also <a href="https://github.com/electronicarts/EASTL/blob/master/doc/Bonus/tuple_vector_readme.md">eastl::tuple_vector&lt;Ts&#8230;&gt;</a> that I discovered after creating soa::vector: it has the same goal as <code>soa::vector</code>, although it targets tuples.</p>
Don't want to miss out ? <strong>Follow:</strong> &nbsp&nbsp<a class="synved-social-button synved-social-button-follow synved-social-size-48 synved-social-resolution-single synved-social-provider-twitter nolightbox" data-provider="twitter" target="_blank" rel="nofollow" title="Follow me on twitter" href="https://twitter.com/joboccara" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img decoding="async" alt="twitter" title="Follow me on twitter" class="synved-share-image synved-social-image synved-social-image-follow" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/twitter.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-follow synved-social-size-48 synved-social-resolution-single synved-social-provider-linkedin nolightbox" data-provider="linkedin" target="_blank" rel="nofollow" title="Find us on Linkedin" href="https://www.linkedin.com/in/jonathan-boccara-23826921/" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img decoding="async" alt="linkedin" title="Find us on Linkedin" class="synved-share-image synved-social-image synved-social-image-follow" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/linkedin.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-follow synved-social-size-48 synved-social-resolution-single synved-social-provider-rss nolightbox" data-provider="rss" target="_blank" rel="nofollow" title="Subscribe to our RSS Feed" href="https://www.fluentcpp.com/feed/" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px"><img decoding="async" alt="rss" title="Subscribe to our RSS Feed" class="synved-share-image synved-social-image synved-social-image-follow" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/rss.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><br/>Share this post!<a class="synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-facebook nolightbox" data-provider="facebook" target="_blank" rel="nofollow" title="Check out this post from Fluent C++" href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F21%2Fan-soa-vector-with-an-stl-container-interface-in-cpp%2F&#038;t=The%20SoA%20Vector%20%E2%80%93%20Part%202%3A%20Implementation%20in%20C%2B%2B&#038;s=100&#038;p&#091;url&#093;=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F21%2Fan-soa-vector-with-an-stl-container-interface-in-cpp%2F&#038;p&#091;images&#093;&#091;0&#093;=&#038;p&#091;title&#093;=The%20SoA%20Vector%20%E2%80%93%20Part%202%3A%20Implementation%20in%20C%2B%2B" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="Facebook" title="Check out this post from Fluent C++" class="synved-share-image synved-social-image synved-social-image-share" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/facebook.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-twitter nolightbox" data-provider="twitter" target="_blank" rel="nofollow" title="Tweet about this" href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F21%2Fan-soa-vector-with-an-stl-container-interface-in-cpp%2F&#038;text=Check%20out%20this%20post%20from%20Fluent%20C%2B%2B" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="twitter" title="Tweet about this" class="synved-share-image synved-social-image synved-social-image-share" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/twitter.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-linkedin nolightbox" data-provider="linkedin" target="_blank" rel="nofollow" title="Share on Linkedin" href="https://www.linkedin.com/shareArticle?mini=true&#038;url=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F21%2Fan-soa-vector-with-an-stl-container-interface-in-cpp%2F&#038;title=The%20SoA%20Vector%20%E2%80%93%20Part%202%3A%20Implementation%20in%20C%2B%2B" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px"><img loading="lazy" decoding="async" alt="linkedin" title="Share on Linkedin" class="synved-share-image synved-social-image synved-social-image-share" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/linkedin.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><p>The post <a href="https://www.fluentcpp.com/2018/12/21/an-soa-vector-with-an-stl-container-interface-in-cpp/">The SoA Vector &#8211; Part 2: Implementation in C++</a> appeared first on <a href="https://www.fluentcpp.com">Fluent C++</a>.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4548</post-id>	</item>
		<item>
		<title>The SoA Vector &#8211; Part 1: Optimizing the Traversal of a Collection</title>
		<link>https://www.fluentcpp.com/2018/12/18/the-soa-vector-part-1-optimizing-the-traversal-of-a-collection/</link>
					<comments>https://www.fluentcpp.com/2018/12/18/the-soa-vector-part-1-optimizing-the-traversal-of-a-collection/#comments</comments>
		
		<dc:creator><![CDATA[Jonathan Boccara]]></dc:creator>
		<pubDate>Tue, 18 Dec 2018 01:00:51 +0000</pubDate>
				<category><![CDATA[STL]]></category>
		<category><![CDATA[AoS]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[SoA]]></category>
		<category><![CDATA[vector]]></category>
		<guid isPermaLink="false">https://www.fluentcpp.com/?p=4478</guid>

					<description><![CDATA[<p>Today’s guest post is the first part of a two-posts series written by Sidney Congard. Sidney is an almost graduated student and an intern at QuasarDB, a company writing it’s own database in C++17. He has been doing C++ in his free time regularly for two years. Also interested in writing on Fluent C++? Submit [&#8230;]</p>
<p>The post <a href="https://www.fluentcpp.com/2018/12/18/the-soa-vector-part-1-optimizing-the-traversal-of-a-collection/">The SoA Vector &#8211; Part 1: Optimizing the Traversal of a Collection</a> appeared first on <a href="https://www.fluentcpp.com">Fluent C++</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Today’s guest post is the first part of a two-posts series written by <b>Sidney Congard</b>. Sidney is an almost graduated student and an intern at QuasarDB, a company writing it’s own database in C++17. He has been doing C++ in his free time regularly for two years.</em></p>
<p><i>Also interested in writing on Fluent C++? </i><a style="font-style: italic;" href="https://www.fluentcpp.com/write-for-us/">Submit your guest post</a><i>!</i></p>
<p>I like C++ because it offers a good compromise between writing expressive and fast code. But, I discovered a problem where I didn’t know any way to hide the implementation detail away from its use: The “Structure of Arrays” (SoA) versus the “Array of Structures” (AoS) problem.</p>
<p>This is the first part of a series of two articles:</p>
<ul>
<li>what &#8216;SoA&#8217; is about and what benefits it brings (part 1)</li>
<li>how to implement an SoA vector in C++ (part 2)</li>
</ul>
<p>So let’s see what those SoA and AoS are all about.</p>
<h3><span style="color: #ff6600;">SoA and AoS</span></h3>
<p>These terms designates two ways to lay out objects contiguously in memory. The AoS is the standard way to dot it. For example, with a class <code>Person</code>:</p>
<pre class="lang:c++ decode:true">struct person {
   std::string name;
   int age;
};</pre>
<p>If we use a standard vector:</p>
<pre class="lang:c++ decode:true">std::vector&lt;person&gt; persons;</pre>
<p>Then the layout of the objects in memory will look like this:</p>
<p><code>[name1, age1, name2, age2, ...]</code></p>
<p>This is the standard way. But there would be another way to store them: first all the names, then all the ages:</p>
<p><code>[name1, name2, ...], [age1, age2, ...]</code></p>
<p>This is SoA (Structure of Arrays.) This is no longer the layout of an <code>std::vector</code>. Rather, it would be the layout of a structure like this:</p>
<pre class="lang:c++ decode:true">struct persons {
    std::vector&lt;std::string&gt; names;
    std::vector&lt;int&gt; ages;
};</pre>
<p>The AoS is more conventional and more straightforward than the SoA. So what’s the point of the SoA?</p>
<h3><span style="color: #ff6600;">The advantage of SoA</span></h3>
<p>SoA increase performances on in a certain use case: the traversal of a collection that looks at one member of the object. For example, if we want to make every person one year older:</p>
<pre class="lang:c++ decode:true">for (auto&amp; person : persons)
{
   ++person.age;
}</pre>
<p>If we use a traditional std::vector, then what the CPU will load in cache from memory is a chunk of the vector containing the entire objects:</p>
<p><code>[name1, age1, name2, age2, ...]</code></p>
<p>The cache line contains data that we won’t use: here, all the <code>Person</code>s names. Since we only need their ages, this is a waste of the cache.</p>
<p>On the other hand, the SoA allows to load the ages packed together on the cache line:</p>
<p><code>[age1, age2, ...]</code></p>
<p>Which is more efficient.</p>
<p>Furthermore, SIMD operations (Single Instruction, Multiple Data) can be performed when we want to apply the same transformations to continuous objects: depending on the CPU properties, he can increment the ages 4 by 4, 8 by 8 or even 16 by 16.</p>
<p>Two questions may come to your mind when seeing this. The first one is: Does this really make a difference on performance?</p>
<p>The answer is Yes it happens to make a difference, for example in the video game industry.</p>
<p>And the second question would be: what would happen for traversals that look at <b>more than one</b> data member of the object, for example:</p>
<pre class="lang:c++ decode:true ">for (auto&amp; person : persons)
{
   std::cout &lt;&lt; person.name &lt;&lt; “ is “ &lt;&lt; person.age &lt;&lt; years old.\n”;
}</pre>
<p>With a traditional <code>std::vector</code>, this traversal makes a full use of the loaded cache line:</p>
<p><code>[name1, age1, name2, age2, ...]</code></p>
<p>But with an SoA structure, the structure of the cache is not at all optimized for this code hopping back and forth between names and ages.</p>
<p>So which one of AoS or SoA is better for performance? The answer is that it depends on the use case. In the general case an AoS with an <code>std::vector</code> is ok, but there are cases where SoA is necessary. This is why SoA is <a href="https://en.wikipedia.org/wiki/AOS_and_SOA">a thing</a>.</p>
<p><span style="font-weight: 400;">To work efficiently with different data, an hybrid approach is possible, by using a single array storing the components in little arrays :</span></p>
<pre class="lang:c++ decode:true ">struct persons_block {
    std::array&lt;8, std::string&gt; names;
    std::array&lt;8, int&gt; ages;
};

using persons = std::vector&lt;persons_block&gt;;</pre>
<p><span style="font-weight: 400;">The memory layout looks then like that :</span></p>
<p><span style="font-weight: 400;"><code>[names 1 to 8, ages 1 to 8, names 9 to 16, ages 9 to 16, ...]</code></span></p>
<p><span style="font-weight: 400;">With this approach, we can have the best of both world : good memory accesses and SIMD instructions while manipulating differents components at the same time.</span></p>
<h3><span style="color: #ff6600;">Implementation of SoA in C++</span></h3>
<p>But the problem with either form of SoA is that it doesn’t have the interface of a container. SoA or AoS are supposed to accomplish different trade-offs in terms of performance and, ideally, choosing between SoA and AoS should have very limited impact on the look of the code using the collection.</p>
<p>In the next post, we will design a C++ structure that implements SoA in while offering an interface close to the one of <code>std::vector</code>.</p>
<h3>You will also like</h3>
<ul>
<li><a href="https://www.fluentcpp.com/2017/08/04/metaclasses-cpp-summary/">A Summary of the Metaclasses Proposal for C++</a></li>
<li><a href="https://www.fluentcpp.com/2018/04/24/following-conventions-stl/">Make Your Containers Follow the Conventions of the STL</a></li>
</ul>
Don't want to miss out ? <strong>Follow:</strong> &nbsp&nbsp<a class="synved-social-button synved-social-button-follow synved-social-size-48 synved-social-resolution-single synved-social-provider-twitter nolightbox" data-provider="twitter" target="_blank" rel="nofollow" title="Follow me on twitter" href="https://twitter.com/joboccara" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="twitter" title="Follow me on twitter" class="synved-share-image synved-social-image synved-social-image-follow" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/twitter.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-follow synved-social-size-48 synved-social-resolution-single synved-social-provider-linkedin nolightbox" data-provider="linkedin" target="_blank" rel="nofollow" title="Find us on Linkedin" href="https://www.linkedin.com/in/jonathan-boccara-23826921/" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="linkedin" title="Find us on Linkedin" class="synved-share-image synved-social-image synved-social-image-follow" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/linkedin.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-follow synved-social-size-48 synved-social-resolution-single synved-social-provider-rss nolightbox" data-provider="rss" target="_blank" rel="nofollow" title="Subscribe to our RSS Feed" href="https://www.fluentcpp.com/feed/" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px"><img loading="lazy" decoding="async" alt="rss" title="Subscribe to our RSS Feed" class="synved-share-image synved-social-image synved-social-image-follow" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/rss.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><br/>Share this post!<a class="synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-facebook nolightbox" data-provider="facebook" target="_blank" rel="nofollow" title="Check out this post from Fluent C++" href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F18%2Fthe-soa-vector-part-1-optimizing-the-traversal-of-a-collection%2F&#038;t=The%20SoA%20Vector%20%E2%80%93%20Part%201%3A%20Optimizing%20the%20Traversal%20of%20a%20Collection&#038;s=100&#038;p&#091;url&#093;=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F18%2Fthe-soa-vector-part-1-optimizing-the-traversal-of-a-collection%2F&#038;p&#091;images&#093;&#091;0&#093;=&#038;p&#091;title&#093;=The%20SoA%20Vector%20%E2%80%93%20Part%201%3A%20Optimizing%20the%20Traversal%20of%20a%20Collection" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="Facebook" title="Check out this post from Fluent C++" class="synved-share-image synved-social-image synved-social-image-share" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/facebook.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-twitter nolightbox" data-provider="twitter" target="_blank" rel="nofollow" title="Tweet about this" href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F18%2Fthe-soa-vector-part-1-optimizing-the-traversal-of-a-collection%2F&#038;text=Check%20out%20this%20post%20from%20Fluent%20C%2B%2B" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px"><img loading="lazy" decoding="async" alt="twitter" title="Tweet about this" class="synved-share-image synved-social-image synved-social-image-share" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/twitter.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><a class="synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-linkedin nolightbox" data-provider="linkedin" target="_blank" rel="nofollow" title="Share on Linkedin" href="https://www.linkedin.com/shareArticle?mini=true&#038;url=https%3A%2F%2Fwww.fluentcpp.com%2F2018%2F12%2F18%2Fthe-soa-vector-part-1-optimizing-the-traversal-of-a-collection%2F&#038;title=The%20SoA%20Vector%20%E2%80%93%20Part%201%3A%20Optimizing%20the%20Traversal%20of%20a%20Collection" style="font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px"><img loading="lazy" decoding="async" alt="linkedin" title="Share on Linkedin" class="synved-share-image synved-social-image synved-social-image-share" width="48" height="48" style="display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none" src="https://i0.wp.com/www.fluentcpp.com/wp-content/plugins/social-media-feather/synved-social/image/social/regular/96x96/linkedin.png?resize=48%2C48&#038;ssl=1" data-recalc-dims="1" /></a><p>The post <a href="https://www.fluentcpp.com/2018/12/18/the-soa-vector-part-1-optimizing-the-traversal-of-a-collection/">The SoA Vector &#8211; Part 1: Optimizing the Traversal of a Collection</a> appeared first on <a href="https://www.fluentcpp.com">Fluent C++</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.fluentcpp.com/2018/12/18/the-soa-vector-part-1-optimizing-the-traversal-of-a-collection/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4478</post-id>	</item>
	</channel>
</rss>
