【C】牙膏厂分支预测优化与Linux的宏if(likely(x))与if(unlikely(x))
牙膏厂(挤牙膏的Intel)在跳转(包括jcc等条件跳转或者各种短跳转等)后,会刷新指令队列,同样ARM也会刷新它的三级流水线。这会导致CPU不得不重新把指令读取到指令队列里,再排队执行。然而我们的条件概率并不总是平均的,有时候有的条件是被算作“意外”的,比如各种异常判断、小概率事件判断等,在这种情况下如果大概率事件成立的时候,你的CPU都不得不跳转一下的话,那就亏了。所以,Linux的likely和unlikely就是这样的一种优化的宏,让你给编译器一个提示,告诉它什么事件是“很有可能发生”的,什么是“不太可能发生”的。likely就是“有可能”而unlikely就是“不太可能”。
定义:
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
也就是说,它是靠__builtin_expect这个gcc钦定功能来实现的。
典型例子:if(arg == NULL)return WRONG_USAGE;这样的代码,很显然arg是参数,并且是个指针,然后WRONG_USAGE大概是一个用来表达错误的返回值,返回一个“函数使用不当”的返回值。这种设计是为了实现“鲁棒性”(“肉棒性”“罗棒性”……)也就是robust特性(刚那些玩意儿都是robust的音译),保证就算用户瞎几把调用也不会导致删库跑路的严重后果。Windows的API都具有一定程度的肉棒性。
但参数填错、错误调用的情况通常只存在于调试阶段,正常情况下发布的程序都不应该有错误参数的。在此时,这种参数判断就显得有点多余了。我们希望的是让它在这个时候尽量减少开支,也就是不要“因为参数正确了”而刷新指令队列,就好像参数正确反而算是一种惩罚一样。所以这代码要这样改:
if(unlikely(arg == NULL))return WRONG_USAGE;
因为arg不太可能是NULL,所以我们用unlikely来暗示编译器:这种情况不太可能。那么编译器就会帮你减少跳转开支,让代码顺畅运行。而这个时候,你只需要很低的成本就能维持住你的肉棒性,可以说是很赚。
这个宏只对GCC有用,对于非GCC的情况,则需要看对应编译器有没有对应功能,没有的话就只能#define likely(x) (x)了。
参考资料:
How do the likely() and unlikely() macros in the Linux kernel work and what is their benefit?
https://stackoverflow.com/questions/109710/how-do-the-likely-and-unlikely-macros-in-the-linux-kernel-work-and-what-is-t
Branch predictor
https://en.wikipedia.org/wiki/Branch_predictor
都不知道在说什么 看不懂,太高深了,顶起 MSVC有个相似用途的宏叫做__assume:
https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/1b3fsfxw(v%3dvs.100) tangptr@126.com 发表于 2018-6-20 12:41
MSVC有个相似用途的宏叫做__assume:
https://docs.microsoft.com/en-us/previous-versions/visualstudio/vi ...
这个__assume感觉和GCC的还有点不太一样,因为它可以用于告诉编译器,某switch语句的某条件一般跑不到。
此外,这个__assume要是没用好,貌似还会导致编译器的未定义的行为,原文:If the compiler can reach an invalid __assume statement, the program might cause unpredictable and potentially dangerous behavior.大概还是不能乱用。 微软的程序员一般都是用,语句逻辑来处理这些问题。比如说是条件真跳转,if (true) return;或者是条件假才跳转。if (false) goto done;
…
done:
return;
页:
[1]