通常我們大量使用array of structure來開發(fā)程序,因為array of structure 具有面向?qū)ο蟮奶卣?,易于描述客觀世界,代碼也容易理解。但是 array of structure 卻常常會阻礙程序的并行化。
structure of array 與之相反,它易于并行化,但拙于描述客觀世界,代碼也變得難以理解。
要想代碼性能好就要使用structure of array , 要想開發(fā)效率高就要使用array of structure, 設計之初就要做出選擇,開發(fā)后期如果想轉(zhuǎn)換到另一種方案將會大費周章。
Intel 的 Array building block 提供了一套編程接口 讓我們可以從array of structure 的視角編寫基于 structure of array的程序。這話說起來有點繞,可以這樣理解,在邏輯層是array of structure , 在物理層是structrue of array.
在C++中我們?nèi)绾螌崿F(xiàn)這種邏輯層(array of structure )/物理層(structrue of array )的分離與映射呢?
這是我們基于array of structure 的程序
struct RGB
{
int r;
int g;
int b;
};
template<class T>
void test(T& rgb, size_t n)
{
int i =0;
for(i=0;i<SIZE;i++){
rgb[i].r = 3*i;
rgb[i].g = 3*i + 1;
rgb[i].b = 3*i + 2;
}
for(i=0;i<SIZE;i++){
rgb[i].b=rgb[i].r + rgb[i].g;
}
}
#define SIZE 65536
int main()
{
RGB* rgb = new RGB[SIZE];
test(rgb, SIZE);
}
要將上面的程序轉(zhuǎn)換為SOA,我們首先為RGB定義一個影子
struct RGBshadow
{
RGBshadow(int& r, int& g, int& b):r(r),g(g),b(b){}
int& r;
int& g;
int& b;
};
然后我們有一個模板類用于定義SOA類,此類為通用類
template<class Shadow, typename T1, typename T2, typename T3>
class SOA
{
public:
typedef T1 aligned_t1 __attribute__((aligned(16)));
typedef T2 aligned_t2 __attribute__((aligned(16)));
typedef T3 aligned_t3 __attribute__((aligned(16)));
public:
SOA(int n){
r = (aligned_t1*)_mm_malloc(n*sizeof(T1), 64);
g = (aligned_t2*)_mm_malloc(n*sizeof(T2), 64);
b = (aligned_t3*)_mm_malloc(n*sizeof(T3), 64);
}
~SOA(){
if(r) _mm_free(r);
if(g) _mm_free(g);
if(b) _mm_free(b);
}
Shadow operator [] ( size_t i){
return Shadow(r[i],g[i],b[i]);
}
private:
aligned_t1* r ;
aligned_t2* g ;
aligned_t3* b ;
};
#define SIZE 65536
int main()
{
RGB* rgb = new RGB[SIZE];
test(rgb, SIZE);
SOA<RGBshadow, int, int,int> soa(SIZE);
test(soa, SIZE);
}
編譯器會自動向量化test(soa,SIZE);
test(rgb, SIZE);中的第二個for循環(huán)生成的代碼如下:
.L14:
movl (%rbx,%rax), %edx
addl 4(%rbx,%rax), %edx
movl %edx, 8(%rbx,%rax)
addq $12, %rax
cmpq $786432, %rax
jne .L14
test(soa, SIZE);中的第二個for循環(huán)生成的代碼如下:
.L16:
movdqa (%rsi,%rax), %xmm0
paddd (%rcx,%rax), %xmm0
movdqa %xmm0, (%rdx,%rax)
addq $16, %rax
cmpq $262144, %rax
jne .L16
要將AOS轉(zhuǎn)換為SOA,分如下三步
1。 定義一個影子結(jié)構
2。 利用SOA<shadow,...>模板定義相應的SOA結(jié)構
3。 修改業(yè)務代碼,SOA<shadow,...> 與AOS有相同的操作方式,因而可以盡量少的修改代碼。