在结构阵列(AoS)和阵列结构(SoA)之间来回切换

Switching back and forth between Array of Structures (AoS) and Structure of Arrays (SoA)

本文关键字:阵列 结构 AoS SoA 之间      更新时间:2023-10-16

在许多面向数据的设计著作中,有一个突出的功能是,在许多情况下,

struct C_AoS {
  int    foo;
  double bar;
};
std::vector<C_AoS> cs;
...
std::cout << cs[42].foo << std::endl;

在SoA(数组结构)中排列数据更有效:

struct C_SoA {
  std::vector<int>    foo;
  std::vector<double> bar;
};
C_SoA cs;
...
std::cout << cs.foo[42] << std::endl;

现在,我正在寻找一种解决方案,该解决方案允许我在不更改调用接口的情况下在AoS和SoA之间切换,也就是说,无论我使用哪种数据排列,我都可以以最小的工作量和无额外的运行时成本(至少到了过度间接的程度)调用例如cs[42].foo;

我应该注意到,上面的示例语法是理想的情况,这很可能是不可能的,但我也对近似值非常感兴趣。有人接电话吗?

我将选择以下语法:cs.foo[42]作为单一语法,并使用typedefs在排列之间切换:

所以,很明显,在你的文章中给出了C_SoA,上面的语法是有效的,我们可以得到:

typedef C_SoA Arrangement;
Arrangement cs;

为了使用std::vector<C_AoS>,我们将不得不引入其他东西:

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){}
};

现在我们可以拥有:

//Choose just one arrangement
typedef Alt_C_AoS Arrangement;
//typedef C_SoA Arrangement;
Arrangement cs;
...
std::cout << cs.foo[42] << std::endl;

从本质上讲,这将container dot member index转换为container index dot member