提供对 SoA 的 AoS 访问
Provide AoS access to SoA
我将数据以数组结构 (SoA) 或指针结构 (SoP) 的形式布置在内存中,并且有一种方法可以访问该数据,就好像它是以结构数组 (AoS) 形式布置的一样表单 -- 代码如下。
但是,我对 struct AoS_4_SoP
的使用不太满意——虽然这个 struct
似乎使用了模板,但它并不是真正通用的,例如,foo
和bar
在其中进行了硬编码。
两个questions/requests:
1) 对于读写性能,提供的 AoS 访问是否与直接 SoA 访问一样好?
2) 更通用的方案是什么? (我看过quamrana's ,但没有帮助。)
struct SoP{ // Structure of Pointers
int *foo{ nullptr };
double *bar{ nullptr };
SoP( int *xi, double *xd ):foo(xi), bar(xd){};
};
struct SoR{ // Structure of References
int &foo;
double &bar;
SoR( int &xi, double &xd ):foo(xi), bar(xd){};
};
template< typename T, typename S >
struct AoS_4_SoP {
AoS_4_SoP( T *x ) : p( x ){};
T *p;
S operator[](std::size_t idx) const { return { p->foo[idx], p->bar[idx] }; }
const S operator[](std::size_t idx) const { return { p->foo[idx], p->bar[idx] }; }
};
这里有一个 main()
显示了上面的用法:
int main()
{
std::vector< int > ibuf{ 11, 22, 33, 44 };
std::vector< double > dbuf{ 0.11, 0.22, 0.33, 0.44 };;
SoP x_sop( ibuf.data(), dbuf.data() );
ibuf.at(2) = 333;
std::cout << "Access via SoP syntax:\n "
<< x_sop.foo[2]
<< " "
<< x_sop.bar[2] << std::endl;
AoS_4_SoP<SoP, SoR> xacc( &x_sop );
std::cout << "Access via AoS syntax:\n "
<< xacc[2].foo
<< " "
<< xacc[2].bar << std::endl;
// show write access via SoA syntax
ibuf.at(2) = 3333;
dbuf.at( 2 ) = 0.333333; // will get overwritten below
xacc[2].bar = 0.3333;
std::cout << "Values written via SoP, read via SoP:\n "
<< x_sop.foo[2]
<< " "
<< x_sop.bar[2] << std::endl;
// show write access via AoS syntax
xacc[2].foo = 333333;
dbuf.at( 2 ) = 0.3333333333; // will get overwritten below
xacc[2].bar = 0.333333;
std::cout << "Values written via AoS, read via AoS:\n "
<< xacc[2].foo
<< " "
<< xacc[2].bar << std::endl;
}
以上代码可以通过以下方式编译:
// x86_64-w64-mingw32-g++.exe -D_WIN64 -Wall -Wextra -Werror -std=c++11 -O3 -static-libgcc -static-libstdc++ aossoa.cc -o aossoa.exe
并产生以下输出:
Access via SoP syntax:
333 0.33
Access via AoS syntax:
333 0.33
Values written via SoP, read via SoP:
3333 0.3333
Values written via AoS, read via AoS:
333333 0.333333
这是我对 quamrana's solution for the "inverse" use case ( 的改编:
template<class T, class S, class M0, class M1>
struct Accessor2{
T* p;
M0 m0;
M1 m1;
S operator[](std::size_t idx) { return { m0[idx], m1[idx] }; }
const S operator[](std::size_t idx) const { return { m0[idx], m1[idx] }; }
Accessor2(T* x, M0 p0, M1 p1): p(x), m0(p0), m1(p1){}
};
template< typename T, typename S >
struct AoS_4_SoP : public Accessor2<T, S, decltype(T::foo), decltype(T::bar)>
{
#ifndef COMPILER_CAN_INFER_PARENT_CLASS_TEMPLATE_SPECIFICATION
AoS_4_SoP(T *x) : Accessor2<T, S, decltype(T::foo), decltype(T::bar)>
(x, x->foo, x->bar ){}
#else
AoS_4_SoP(T *x):Accessor2(x, x->foo, x->bar ){}
#endif
};
关于 #ifndef COMPILER_CAN_INFER_PARENT_CLASS_TEMPLATE_SPECIFICATION
,我使用的编译器 g++ 6.2.0 (x86_64_6.2.0_posix_seh_rt_v5_rev1/mingw64/bin/x86_64-w64-mingw32-g++.exe
) 无法推断父 class' 模板规范。但是,作为 Lightness Races in Orbit says on this page:基础的 injected-class-name 应该为编译器提供足够的信息。
我觉得这个模板可以用。
template<class T, class U, class D, class S>
struct Accessor {
T* p;
U* (T::*pFirst);
D* (T::*pSecond);
S operator[](size_t index) {
return {(p->*pFirst)[index], (p->*pSecond)[index]};
}
Accessor(T* p_, U * (T::*pF), D * (T::*pS)): p(p_), pFirst(pF), pSecond(pS) {}
};
void main() {
std::vector< int > ibuf{ 11, 22, 33, 44 };
std::vector< double > dbuf{ 0.11, 0.22, 0.33, 0.44 };;
SoP x_sop(ibuf.data(), dbuf.data());
Accessor<SoP, int, double, SoR> aos(&x_sop, &SoP::foo, &SoP::bar);
aos[0].foo;
}
现在模板访问器对 T
的成员名称一无所知。
至少能在VS2015下编译
我将数据以数组结构 (SoA) 或指针结构 (SoP) 的形式布置在内存中,并且有一种方法可以访问该数据,就好像它是以结构数组 (AoS) 形式布置的一样表单 -- 代码如下。
但是,我对 struct AoS_4_SoP
的使用不太满意——虽然这个 struct
似乎使用了模板,但它并不是真正通用的,例如,foo
和bar
在其中进行了硬编码。
两个questions/requests:
1) 对于读写性能,提供的 AoS 访问是否与直接 SoA 访问一样好?
2) 更通用的方案是什么? (我看过quamrana's
struct SoP{ // Structure of Pointers
int *foo{ nullptr };
double *bar{ nullptr };
SoP( int *xi, double *xd ):foo(xi), bar(xd){};
};
struct SoR{ // Structure of References
int &foo;
double &bar;
SoR( int &xi, double &xd ):foo(xi), bar(xd){};
};
template< typename T, typename S >
struct AoS_4_SoP {
AoS_4_SoP( T *x ) : p( x ){};
T *p;
S operator[](std::size_t idx) const { return { p->foo[idx], p->bar[idx] }; }
const S operator[](std::size_t idx) const { return { p->foo[idx], p->bar[idx] }; }
};
这里有一个 main()
显示了上面的用法:
int main()
{
std::vector< int > ibuf{ 11, 22, 33, 44 };
std::vector< double > dbuf{ 0.11, 0.22, 0.33, 0.44 };;
SoP x_sop( ibuf.data(), dbuf.data() );
ibuf.at(2) = 333;
std::cout << "Access via SoP syntax:\n "
<< x_sop.foo[2]
<< " "
<< x_sop.bar[2] << std::endl;
AoS_4_SoP<SoP, SoR> xacc( &x_sop );
std::cout << "Access via AoS syntax:\n "
<< xacc[2].foo
<< " "
<< xacc[2].bar << std::endl;
// show write access via SoA syntax
ibuf.at(2) = 3333;
dbuf.at( 2 ) = 0.333333; // will get overwritten below
xacc[2].bar = 0.3333;
std::cout << "Values written via SoP, read via SoP:\n "
<< x_sop.foo[2]
<< " "
<< x_sop.bar[2] << std::endl;
// show write access via AoS syntax
xacc[2].foo = 333333;
dbuf.at( 2 ) = 0.3333333333; // will get overwritten below
xacc[2].bar = 0.333333;
std::cout << "Values written via AoS, read via AoS:\n "
<< xacc[2].foo
<< " "
<< xacc[2].bar << std::endl;
}
以上代码可以通过以下方式编译:
// x86_64-w64-mingw32-g++.exe -D_WIN64 -Wall -Wextra -Werror -std=c++11 -O3 -static-libgcc -static-libstdc++ aossoa.cc -o aossoa.exe
并产生以下输出:
Access via SoP syntax:
333 0.33
Access via AoS syntax:
333 0.33
Values written via SoP, read via SoP:
3333 0.3333
Values written via AoS, read via AoS:
333333 0.333333
这是我对 quamrana's solution for the "inverse" use case (
template<class T, class S, class M0, class M1>
struct Accessor2{
T* p;
M0 m0;
M1 m1;
S operator[](std::size_t idx) { return { m0[idx], m1[idx] }; }
const S operator[](std::size_t idx) const { return { m0[idx], m1[idx] }; }
Accessor2(T* x, M0 p0, M1 p1): p(x), m0(p0), m1(p1){}
};
template< typename T, typename S >
struct AoS_4_SoP : public Accessor2<T, S, decltype(T::foo), decltype(T::bar)>
{
#ifndef COMPILER_CAN_INFER_PARENT_CLASS_TEMPLATE_SPECIFICATION
AoS_4_SoP(T *x) : Accessor2<T, S, decltype(T::foo), decltype(T::bar)>
(x, x->foo, x->bar ){}
#else
AoS_4_SoP(T *x):Accessor2(x, x->foo, x->bar ){}
#endif
};
关于 #ifndef COMPILER_CAN_INFER_PARENT_CLASS_TEMPLATE_SPECIFICATION
,我使用的编译器 g++ 6.2.0 (x86_64_6.2.0_posix_seh_rt_v5_rev1/mingw64/bin/x86_64-w64-mingw32-g++.exe
) 无法推断父 class' 模板规范。但是,作为 Lightness Races in Orbit says on this page:基础的 injected-class-name 应该为编译器提供足够的信息。
我觉得这个模板可以用。
template<class T, class U, class D, class S>
struct Accessor {
T* p;
U* (T::*pFirst);
D* (T::*pSecond);
S operator[](size_t index) {
return {(p->*pFirst)[index], (p->*pSecond)[index]};
}
Accessor(T* p_, U * (T::*pF), D * (T::*pS)): p(p_), pFirst(pF), pSecond(pS) {}
};
void main() {
std::vector< int > ibuf{ 11, 22, 33, 44 };
std::vector< double > dbuf{ 0.11, 0.22, 0.33, 0.44 };;
SoP x_sop(ibuf.data(), dbuf.data());
Accessor<SoP, int, double, SoR> aos(&x_sop, &SoP::foo, &SoP::bar);
aos[0].foo;
}
现在模板访问器对 T
的成员名称一无所知。
至少能在VS2015下编译