我至少可以想到三种选择循环方向的方法。
两个循环,开头一个条件(可能是最快的?):
if (!backwards)
for (int i = 0; i <= end; i++) {
// code
}
else
for (int i = end; i >= 0; i--){
// code
}
在许多元素上循环,在里面测试和增量(我用这个):
for (int l = 0; l < max_len; l++) {
// code
if (!backward)
i++;
else
i--;
}
使用变量增量和结束值(可能是最差的?)
if (backward)
inc = -1;
else
inc = 1;
for (int i = 0; i != end; i += inc) {
// code
}
哪种方式更快?编译器是否在每种情况下对其进行优化?
答案 0 :(得分:4)
在没有特定系统的情况下讨论性能并不是很有意义。对于“通用计算机”,这里要考虑的事情是
改进机器代码的一种旧方法是尽可能写入向下计数循环,因为这将导致“分支如果为零”指令,这比“如果等于”的分支稍微快一些。然而,这种技术起源于编译器废话时的黑暗时代。使用现代优化编译器,迭代顺序不应成为性能问题。所以这个技巧大多已经过时了。
除此之外,根据系统的不同,不同的循环可能会产生相对较少/效率较低的代码。您可以反汇编不同版本并进行检查,但这是一个非常小的问题。
关于分支,第三个版本显然比其他版本好得多,因为它只包含一个单独的分支 - 对循环迭代器的检查,它给出了循环本身。第一版更糟糕,第二版更糟糕。
根据循环实际执行的操作,第3版可能不是数据缓存的理想选择。不可能说。
总的来说,这两个版本中的一个可能效率最高:
for(size_t i=start; i!=end; i+=inc)
或者
size_t offset = backwards ? n-1 : 0;
for(size_t i=0; i<n; i++)
{
size_t index = i - offset;
arr[index] = something;
}
但唯一的方法是实际进行基准测试和反汇编。要做到这一点,您需要指定一个特定的系统。
答案 1 :(得分:1)
根据您选择的选项,我会避免使用方法2,因为它会在关键循环中可能避免的每个元素上添加检查/分支。如果你知道如果你想要的元素元素靠近数组的后面或前面,那么方法1或3可能是最好的。
比较1和3不太直接。我相信在英特尔X86处理器上,性能将与示例1中的for循环相同。即++ i和i + = 1将转换为添加指令,并且NE(不等于)比较将等于LE(小于等于)。但是,一般来说,要确保您需要检查正在使用的处理器/编译器的反汇编。
注意:在这个帖子中还说过,倒计时循环(与0比较)可能会在某些处理器上提供轻微的速度优势。此外,如果使用preincrement ++ i而不是示例中的post增量,某些处理器将获得轻微的性能提升。
顺便说一句:如果要检查数组结构中的所有元素,并且c库是否支持并行性,则可以证明Parallel For是最快的,您可以将元素数除以处理器线程数。
使用带有4个处理器线程的Parallel For的示例:如果您的数组包含1百万个元素并且您有4个线程,则可以让线程1迭代0到249.999,线程2 250,000到499,999,线程3 500,000到749,999 ,并以同时的方式线程4 750,000至999,999。总理论增益将是4倍 - 减去一些开销加上等待最慢线程完成的时间。 (在这种情况下,这个时间量应该是最小的)。