这是一个愚蠢的问题,但这一直困扰着我,我无法用Google的方式解决它。
考虑以下数组:
struct SomeDataStruct
{
uint64_t ValueOne;
uint64_t ValueTwo;
uint64_t ValueThree;
};
SomeDataStruct _veryLargeArray[1024];
现在,以下哪种方法可以更快地遍历每个元素并对每个元素执行某些操作?
方法1:
for (int i = 0; i < 1024; ++i)
{
_veryLargeArray[i].ValueOne += 1;
_veryLargeArray[i].ValueTwo += 1;
_veryLargeArray[i].ValueThree = _veryLargeArray[i].ValueOne + _veryLargeArray[i].ValueTwo;
}
方法2:
SomeDataStruct * pEndOfStruct = &(_veryLargeArray[1024]);
for (SomeDataStruct * ptr = _veryLargeArray; ptr != pEndOfStruct; ptr += 1)
{
ptr->ValueOne += 1;
ptr->ValueTwo += 1;
ptr->ValueThree = ptr->ValueOne + ptr->ValueTwo;
}
我知道这个问题从表面上看确实很愚蠢,但是我想知道的是,编译器是否以每种给定的实现for循环的方式进行智能/特殊处理?在第一种情况下,如果编译器每次实际上都查找BaseArrayPointer + Offset,则可能确实会占用大量内存,但是如果编译器足够聪明,它将用整个数组填充L2缓存并处理{}之间的代码正确。
如果编译器每次都解析指针,第二种方法就可以解决,但是对于编译器而言,要弄清楚是否可以将整个数组复制到L2高速缓存中并将其遍历,可能真的很困难。
很抱歉这个愚蠢的问题,我在学习c ++的过程中获得了很多乐趣,并开始做那件事,而您却对此无所适从。只是好奇是否有人知道是否有“确定的”答案。
答案 0 :(得分:1)
除非您要查看中间汇编语言输出并分析CPU的缓存行为,否则能够回答此问题的唯一方法是分析代码。运行数百或数千次,看看需要多长时间。
如果您想要最快的代码,请编写最简单,最明显的版本,并将其留给优化的编译器。如果您试图花哨的话,使用这样的循环,就有可能使编译器混乱,并且它无法优化内容。
我已经看到一个简单的C循环编译比手工编码的程序集要快,而经过手工优化的C版本的编译结果要比手工编码的程序集慢。
另一方面,可能需要花一点时间来了解缓存以及引擎盖下发生的事情。但是通常,这是在您发现代码不够快之后发生的。否则可能会导致过早优化,即root of all evil。