扫描struct嵌套数组的有效方法

时间:2014-01-09 12:12:15

标签: c arrays for-loop performance

我有一个包含多个数组成员的结构:

typedef myData someStruct {
    uint16_t array1 [ARRAY_LENGTH]
    uint16_t array2 [ARRAY_LENGTH]
} myData;
myData testData = {0};  // Global struct

在我的程序中的某个时刻,我需要将数组设置为一组预定义的值,例如,将array1设置为all 0,将array2设置为所有0xFF等。我的第一直觉就是写出一个for循环,如:

void someFunction (myData * test) {
    for (uint16_t i = 0; i < ARRAY_LENGTH; ++i) {
        test->array1[i] = 0xFF;
        test->array2[i] = 0xCC;
    }
}

然而,我接着推断,程序执行此操作所需的操作将类似于:

load address of array1 first position
set value 0xFF;
load far address of array2 first postion
set value 0xCC;
load far address of array1 second position
set value 0xFF;
// and so on...

然而,如果我为每个数组使用一个单独的循环,那么地址会彼此更接近(因为数组和结构连续存储),所以地址加载每次只到下一个字节,使代码实际上更有效如下:

void someFunction (myData * test) {
    uint16_t i = 0;
    for (i; i < ARRAY_LENGTH; ++i)
        test->array1[i] = 0xFF;
    for (i = 0; i < ARRAY_LENGTH; ++i)
        test->array2[i] = 0xCC;
}

我的推理是否正确,第二个更好吗?此外,编译器(例如gcc,例如)通常能够自己进行优化吗?

2 个答案:

答案 0 :(得分:2)

这取决于您的系统架构。例如,在SPARC系统上,缓存行大小为64字节,并且两个阵列都有足够的缓存槽,因此第一个版本将是高效的。第一个数组元素的加载将填充缓存,后续加载将非常快。如果编译器足够智能,它也可以使用预取。

在支持偏移量寻址的ISA上,它实际上并不是每次都获取数组元素的地址,只是增加了一个偏移量。因此它只获取一次数组的基址,然后使用带有基数和偏移量的加载指令。每次循环都会增加寄存器中的偏移量。有些指令集甚至有自动递增。

最好的办法是编写一个示例程序/函数,然后尝试一下。在这个低水平的优化需要全面了解CPu /系统,或许多试错。

答案 1 :(得分:1)

我的谦虚建议:试试看。一个循环解决方案可以节省围绕i的增量和测试的算术运算两个循环可能会带来更好的缓存优化,特别是如果数组与内存页对齐。在这种情况下,每次访问都可能导致缓存未命中和缓存重新加载。就个人而言,如果速度真的很重要,我宁愿选择两个展开的循环。