for循环迭代是否等同于分支?

Does a for loop iteration equate as a branch?

当我在正常的一天编程时,我确保所有的分支很可能没有被采用。

int retval = do_somting();
if(!retval) { /* Less-than-likely event*/ }

这个乐观主义者分支预测,导致 CPU 的预测位设置为 "do not take"。但是,在 for 循环之后,预测位会被强制返回 "take" 吗?

// prediction = "likely take"
if(false) { }

// prediction = "probably take"
if(false) { }

// prediction = "probably not take"
if(false) { }

// prediction = "likely not take"
if(false) { }

/* ... thousands of other if(false) that are speedy-fast */

for(int i = 0; i < 5; i++) { }
// prediction = "likely take"?

我知道这是一个不切实际的微不足道的优化,但是嘿,你知道的越多。

编辑: 假设 GCC 不会破坏上面的所有代码,我们也只讨论 amd64 架构。因为我没有意识到这个问题有多低级。

事实证明,分支预测取决于 CPU 的模型。

根据 this paper,在将循环与正常分支相关联时,分支预测以无数种方式处理。一些 CPUs 有一个单独的预测器循环。所以这意味着 if 语句根本不会影响 for 语句的预测。其他人也有同样的预测。

无论如何,这个问题没有一个正确的答案。谈分支效率时,for循环是不可衡量的。

...当然,除非您计划 运行 您的程序仅在 CPU 的单个模型上运行。

大多数具有分支预测的架构(包括 AMD64)认为短 downward/forward jumps/branches 不太可能,短 upward/backward branches/jumps 可能。这意味着大多数循环预计会继续循环。由于初始条件,这使得 do-while 循环比 for 循环或 while 循环更有效;然而,大多数优化编译器会尽可能将这些情况优化为类似代码。

您可以通过使用 __builtin_expect() 的条件来查看 -O3 优化级别的 gcc 汇编的差异。不太可能的分支通常是向前跳转,而可能的条件要么根本不分支,要么向后跳转。这可能涉及反转逻辑。注意:在-O3,gcc经常会将代码复制到不太可能的分支中,以便尽可能减少可能情况下的分支。

这是有道理的,因为适合高速缓存行的循环如果分支到它的开头就不会出现高速缓存未命中。类似地,由于程序通常在函数内线性前进,因此最近执行的代码也很可能已经在缓存中。当您用一堆额外的 "optimized" 条件替换循环时,在某个时候(可能大约 4 个条件)缓存未命中将覆盖您可能以可读性和可维护性为代价获得的任何微小好处。