在结构阵列 (AoS) 和阵列结构 (SoA) 之间来回切换
Switching back and forth between Array of Structures (AoS) and Structure of Arrays (SoA)
在许多关于面向数据的设计的著作中起着突出作用的一个特征是,在许多情况下,而不是 AoS(结构数组):
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]
作为单一语法并使用 typedef 在排列之间切换:
所以,显然从你的 post 给出 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
。
在许多关于面向数据的设计的著作中起着突出作用的一个特征是,在许多情况下,而不是 AoS(结构数组):
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]
作为单一语法并使用 typedef 在排列之间切换:
所以,显然从你的 post 给出 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
。