现代C++中有[[likely]]
和[[unlikely]]
属性。G++和clang++中都有相应的CCD_ 3和CCD_。但也有__builtin_unpredictable(x)
和__builtin_expect_with_probability(x, 1, 0.5)
或(同样)__builtin_expect_with_probability(x, 0, 0.5)
内置,这告诉编译器防止CPU用来自(错误)预测分支的指令填充管道,因为管道从错误预测路径恢复的flush+成本在统计上大于完全没有推测执行的执行。
在if
和else
分支上使用[[likely]]
或相等的[[unlikely]]
属性(如以下片段)是否等效于使用假设的[[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]]
属性就不等价了。
似乎用likely
标记if
和else
分支都会收到警告(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]]
和__builtin_expect(x, 1)
0(至少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