我有一个类似这样的类:
//Array of Structures
class Unit
{
public:
float v;
float u;
//And similarly many other variables of float type, upto 10-12 of them.
void update()
{
v+=u;
v=v*i*t;
//And many other equations
}
};
我创建了一个Unit类型的对象数组,并对它们调用了update方法。
int NUM_UNITS = 10000;
void ProcessUpdate()
{
Unit *units = new Unit[NUM_UNITS];
for(int i = 0; i < NUM_UNITS; i++)
{
units[i].update();
}
}
为了提高速度,可能还可以自动向量化循环,我将AoS转换为数组结构。
//Structure of Arrays:
class Unit
{
public:
Unit(int NUM_UNITS)
{
v = new float[NUM_UNITS];
}
float *v;
float *u;
//Mnay other variables
void update()
{
for(int i = 0; i < NUM_UNITS; i++)
{
v[i]+=u[i];
//Many other equations
}
}
};
当循环无法自动向量化时,结构数组的性能非常差。对于50个单位,SoA(结构化数组)的更新稍微比AoS(数组的结构化)快一些。但是从100个单位开始,SoA比AoS慢。在300个单位处,SoA几乎是AoS的两倍糟糕。在十万个单位处,SoA比AoS慢4倍。虽然缓存可能会影响SoA,但我没有预料到性能差异会这么大。在cachegrind上进行分析显示,两种方法的缺失数量相似。一个单位对象的大小为48个字节。L1缓存为256K,L2为1MB,L3为8MB。我错过了什么?这真的是一个缓存问题吗?
编辑: 我正在使用gcc 4.5.2。编译器选项是-o3-msse4-ftree-vectorize。
我在SoA中进行了另一个实验。我不是在动态分配数组,而是在编译时分配“v”和“u”。当有10万个单位时,这会带来比动态分配数组的SoA快10倍的性能。这是怎么回事?为什么静态分配内存和动态分配内存之间有如此大的性能差异?