- UID
- 1
- 精华
- 积分
- 76365
- 威望
- 点
- 宅币
- 个
- 贡献
- 次
- 宅之契约
- 份
- 最后登录
- 1970-1-1
- 在线时间
- 小时
|
牙膏厂(挤牙膏的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://stackoverf low.com/questions/109710 /how-do-the-likely-and-unlikely-macros-in-the-linux-kernel-work-and-what-is -t
Branch predictor
htt ps://en .wikipedia.org/wiki/Branch_predictor
|
|