鉴于我有两个平行且大小相同的以下结构数组:
struct Matrix
{
float data[16];
};
struct Vec4
{
float data[4];
}
//Matrix arrM[256]; //for illustration
//Vec4 arrV[256];
让我们说我希望尽可能快地顺序迭代这两个数组。让我们说这个函数是这样的:
for (int i=0; i < 256; ++i)
{
readonlyfunc(arrMPtr[i].data);
readonlyfunc(arrVPtr[i].data
}
假设我的分配是针对每个数组进行的,在静态分配或堆内存的情况下都是如此。假设我的缓存行大小为64字节。
如果我将数据存储为,我是否会获得相同的缓存位置和性能:
A)
//aligned
static Matrix arrM[256];
static Vec4 arrV[256];
Matrix* arrMPtr = arrM[0];
Vec4* arrVPtr = arrV[0];
vs
B)
//aligned
char* ptr = (char*) malloc(256*sizeof(Matrix)+256*sizeof(Vec4));
Matrix* arrMPtr = (Matrix*) ptr;
Vec4* arrVPtr = (Vec4*) ptr+256*sizeof(Matrix);
答案 0 :(得分:3)
如何分配内存(堆或静态分配)对内存的缓存能力没有影响。由于这两个数据结构都相当大(分别为1024和4096字节),因此第一个和最后一个元素的精确对齐可能也无关紧要(但如果您使用SSE指令访问内容,则无关紧要!) 。
内存是否靠近不会产生巨大的差异,只要分配足够小以容易适应缓存,但大到足以占用多个缓存行。
如果您按顺序通过两个数组,您可能会发现使用具有20个浮点值的结构可以更好地工作。但是,只有在您不需要对具有单个阵列更有意义的数据执行其他操作时,这才有效。
编译器翻译代码的能力可能存在差异,以避免额外的内存访问。这显然取决于实际代码(例如,编译器内联函数是否包含for-loop,是否内联readonlyfunc
代码等等)如果是这样,静态分配可以从指针变量转换(它将指针的地址加载到数据的地址中进行恒定的地址计算。它可能不会在如此大的循环中产生巨大的差异。
总而言之,在性能方面,有时小事情会产生很大的差异,所以如果这非常重要,请使用您的编译器,实际代码进行一些实验。根据我们的经验,我们只能提供相对投机的建议。不同的编译器使用相同的代码执行不同的操作,不同的处理器使用相同的机器代码执行不同的操作(两种不同的实际体系结构(无论是指令集架构ARM与X86,还是体系结构的实现,如AMD Opteron与Intel Atom或ARM Cortex) A15 vs Cortex M3)。特定系统上的内存配置也会影响事物,缓存大小等等。
答案 1 :(得分:0)
如果不了解您的工作和测试内容,就不可能说出来。使用数组结构可能更有效。
struct MatrixVec
{
float m[16];
float v[4];
};
重要的一点是malloc从堆中分配内存,而静态数组则在堆栈上分配。堆栈已经可能存在于L1缓存中,而堆中的内存必须被读入。相反,您可以尝试使用名为alloca
的动态内存分配的一个鲜为人知的函数,该函数在堆栈上分配内存。在你的情况下,你可以尝试
char* ptr = (char*) alloca(256*sizeof(Matrix)+256*sizeof(Vec4))
参见Agner Fog的Optimizing software in C++。请参见“9.6动态内存分配”部分。以下是alloca
与malloc