One feature that plays a prominent role in many of the writings on data oriented design is that there are many cases where rather than AoS (array of structs):
struct C_AoS {
int foo;
double bar;
};
std::vector<C_AoS> cs;
...
std::cout << cs[42].foo << std::endl;
it is more efficient to arrange one's data in SoA (struct of arrays):
struct C_SoA {
std::vector<int> foo;
std::vector<double> bar;
};
C_SoA cs;
...
std::cout << cs.foo[42] << std::endl;
Now what I am looking for is a solution which would allow me to switch between AoS and SoA without changing the calling interface, i.e. that I could, with minimal effort and with no extra runtime cost (at least to the point of excessive indirection), call e.g. cs[42].foo;
regardless of which arrangement of data I'm using.
I should note that the example syntax above is the ideal case, which might very well be impossible, but I'd be very interested in close approximations, too. Any takers?
One major difference between both of them is that- in an Array, the elements are of the same data type while a structure has elements of different data types. You can also define an Array's size during the declaration and write it in numbers within a square bracket preceded by the name of the array.
In computing, array of structures (AoS), structure of arrays (SoA) and array of structures of arrays (AoSoA) refer to contrasting ways to arrange a sequence of records in memory, with regard to interleaving, and are of interest in SIMD and SIMT programming.
I'm going to choose this syntax: cs.foo[42]
to be the single syntax and use typedefs to switch between arrangements:
So, obviously given C_SoA
from your post, the above syntax works and we can have:
typedef C_SoA Arrangement;
Arrangement cs;
In order to use std::vector<C_AoS>
instead we are going to have to introduce something else:
typedef std::vector<C_AoS> AOS;
template<class A, class C, class T>
struct Accessor {
T operator[](size_t index){
return arr[index].*pMember;
}
T (C::*pMember);
A& arr;
Accessor(A& a, T (C::*p)): arr(a), pMember(p){}
};
struct Alt_C_AoS{
Accessor<AOS, C_AoS, int> foo;
Accessor<AOS, C_AoS, double> bar;
AOS aos;
Alt_C_AoS():foo(aos, &C_AoS::foo), bar(aos, &C_AoS::bar){}
};
Now we can have:
//Choose just one arrangement
typedef Alt_C_AoS Arrangement;
//typedef C_SoA Arrangement;
Arrangement cs;
...
std::cout << cs.foo[42] << std::endl;
Essentially this converts container dot member index
into container index dot member
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With