c++ - 模拟属性“不可预测”

现代 C++ 中有 [[likely]][[unlikely]] 属性。在 G++ 和 clang++ 中有对应的 __builtin_expect(x, 1)__builtin_expect(x, 0) 内置函数。但也有 __builtin_unpredictable(x)__builtin_expect_with_probability(x, 1, 0.5) 或(同样)__builtin_expect_with_probability(x, 0, 0.5) 内置函数,它们告诉编译器阻止 CPU 用来自(错误)预测分支的指令填充 pipeline,因为刷新 + 恢复的成本 <来自错误预测路径的 c79> 在统计上大于执行 w/o speculative execution 。

[[likely]][[unlikely]] 两个分支上的 ifelse 属性的使用,如以下片段中的使用假设的 [[unpredictable]] 属性的等价物吗?

if (x) [[likely]] {
    // "if" branch
} else [[likely]] {
    // "else" branch
}

或者

if (x) [[unlikely]] {
    // "if" branch
} else [[unlikely]] {
    // "else" branch
}

据我所知,如果有 else,编译器默认将 if 分支视为 [[likely]],如果没有 else,则默认将其视为 [[unlikely]](因为它通常是不愉快的路径检查形式,提前退出当前功能)。因此,如果我只是省略任何一个属性,那么它不等同于指定假设的 [[unpredictable]] 属性。

回答1

似乎用 likely 标记 ifelse 分支都会收到警告(unlikely 相同):

main.cpp:9:27: warning: both branches of ‘if’ statement marked as ‘likely’ [-Wattributes]
    9 |             if (i*j == p) [[likely]]
      |                           ^~~~~~~~~~
   10 |                 return 0;
   11 |             else [[likely]]
      |                  ~~~~~~~~~~

所以我想这个问题仍然是假设性的。至于一个最小的完整示例,似乎 g++ 支持 [[likely]][[unlikely]] 但不支持 __builtin_unpredictable。同样, clang++ 支持 __builtin_unpredictable 但不支持 [[likely]][[unlikely]] (至少 clang 10 不支持。因此比较所有三个选项很棘手。这是一个比较 [[likely]][[unlikely]] 的最小完整示例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
bool isPrime(int p) {
    for (int i=2;i<p;i++)
        for (int j=2;j<p;j++)
            if (i*j == p) [[likely]]
                return 0;
            // else [[unlikely]] <--- gets a warning
            //     j++; <--- remove from for loop 
    return 1; }
int main(int argc, char **argv) {
    int numPrimes=0;
    int start=atoi(argv[1]);
    int end=atoi(argv[2]);
    for (int p=atoi(argv[1]);p<atoi(argv[2]);p++)
        if (isPrime(p)) { numPrimes++;}
    printf("There are %d primes between %d and %d",numPrimes,start,end);
}

这是评估:

$ g++ -std=c++2a -O3 main.cpp -o main
$ time ./main 2 10000
There are 1229 primes between 2 and 10000
real    1m16.083s
user    1m14.014s
sys 0m0.247s
$ sed -i "s/likely/unlikely/g" main.cpp
$ g++ -std=c++2a -O3 main.cpp -o main
$ time ./main 2 10000
There are 1229 primes between 2 and 10000
real    0m38.277s
user    0m38.100s
sys 0m0.012s

相似文章

随机推荐

最新文章