如果我正在搜索一组值并为每个值运行代码,并且我想在找到某个质量时打开布尔值,然后在我运行代码时再次关闭对于那个对象,运行条件来检查是否需要关闭布尔值或者在每个循环中将其关闭更快是否更快?
例如(伪代码):
bool found = false;
for(particle in literallyAHaystack) {
bool isNeedle = particle == "needle";
if(isNeedle) {
found = true;
}
// [some code that uses the 'found' variable]
if(isNeedle) {
found = false;
}
}
VS
bool found = false;
for(particle in literallyAHaystack) {
bool isNeedle = particle == "needle";
if(isNeedle) {
found = true;
}
// [some code that uses the 'found' variable]
found = false; // a conditional no longer surrounds this statement
}
我知道这是非常低级且通常毫无意义的优化,但我仍然对它的真实性感兴趣。我希望不要因为这个问题的微不足道而冒犯任何人。
答案 0 :(得分:1)
如果bool found
是一个局部变量,那么在每种情况下无条件地将它设置为false
几乎肯定会更好,几乎每个CPU架构都是如此。如果它在编译器输出中完全存在(而不是仅仅变成分支逻辑的一部分),它可能只会在寄存器中。写入会记录最便宜的操作之一,比分支便宜得多。
即使它曾经访问内存,回写缓存也很常见,因此重复存储到同一位置只是在L1中命中而不会产生流量到较大的共享缓存或主内存。
如果编译器在每个循环结束时发出实际将标志存储在内存中的代码,则检查下一次迭代的标志将导致存储转发延迟~5个周期(例如在Intel Haswell上)。
但如果出现问题,编译器的错误就是不能很好地优化代码。这就是我们使用编译器而不是直接在asm中编写的原因:编译器可能完全优化掉found
变量。这很好,并不是重组你的C的理由。
有关x86上此类内容的更多信息,请参阅http://agner.org/optimize/标记wiki中的x86和其他链接。
要了解您的代码是如何编译的,请将其放在http://gcc.godbolt.org/上(并使用O3 -march=haswell -ffast-math
或其他内容。)
如果found
是全局的(并且可能最后被另一个线程修改过),那么只有先读取它才有意义,因此运行代码的核心不需要使其他核心的副本无效。缓存行。
我正在想象一个标志,它是不同线程可能使用的共享状态结构(由锁定保护的关键部分中的代码使用)的一部分。 (在这种情况下,这将是一个糟糕的设计,因为found
在使用后总是留下false
,因此没有持久状态,所以它应该是本地的。)
尽管如此,为了避免那个商店,可能不值得使用条件分支。如果在任何循环迭代中根本修改了该标志(即,如果针匹配),那么您也可以根据需要随时修改它。
如果您可以从某些商店到同一地点零,而不仅仅是更少,那么避免商店大多只有用。缓存有效。
e.g。在向量化时,如果你想要写一个向量的3个元素而不是所有4个元素,那么对目的地进行重叠存储有时很有用,而且可以写出超出你存储内容的结尾。例如在this code。
答案 1 :(得分:0)
第二个将更快"因为没有条件检查,因此不需要跳跃。
它会明显加快吗?几乎绝对不是。无论如何,编译器可能已经进行了这种优化,虽然我不引用它 - 我不确定它是否存在。