Lua 源码中 l_likely, l_unlikey 是什么意思
最近在排查c++程序内部调用lua_pcall时产生C stack overflow
异常,
研究问题时发现lua源码中存在一些likely调用,其实在其他代码中也见到过类似的调用,
那么我们今天就来探究一下它到底是什么逻辑
lua代码:
1 |
|
好,直到这里我们发现这是一个编译时的宏定义,甚至在某些特定情况下l_likely和l_unlikely调用了和没调用没区别
即不论if (l_likely(r))
还是if (l_unlikely(r))
在逻辑语义上是完全等价于if (r)
那么,什么是__builtin_expect ?
1 | Built-in Function: long __builtin_expect (long EXP, long C) |
乍一看满头问号,预测信息? 不对啊,只有我写代码才是玄学才对啊(不是
其实是这样的,这个函数是gcc提供的为编译优化处理的函数,并不会对逻辑语义造成影响
例如:
1 | if (l_ulikely(<绝大多数情况都不会为真>)) { |
上述语句中,在gcc(>=2.96)编译的情况下,编译器将会把else部分的逻辑进行编译优化,以期待减少指令跳转带来的性能损耗
那么具体优化了啥呢?
现在处理器都是流水线的,有些里面有多个逻辑运算单元,系统可以提前取多条指令进行并行处理,但遇到跳转时,则需要重新取指令,这相对于不用重新去指令就降低了速度。
目的是增加条件分支预测的准确性,cpu会提前装载后面的指令,遇到条件转移指令时会提前预测并装载某个分支的指令。unlikely 表示你可以确认该条件是极少发生的,相反 likely 表示该条件多数情况下会发生。编译器会产生相应的代码来优化 cpu 执行效率。
总结
不论if (l_likely(r))
还是if (l_unlikely(r))
在逻辑语义上是完全等价于if (r)
我们可能会在大多数地方找到不一样名字关于likely/unlikely的宏定义,
函数本质上是为了编译时优化逻辑跳转减少重新取指令的开销,并不会影响实际的逻辑语义
因此,在你十分确信绝大多数情况下为真时使用likely,或者在你十分确信绝大多数情况下为假时使用unlikely.
例如问题最开始的if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
, 因为正常情况下不可能引发C Stack Overflow
的异常
Lua 源码中 l_likely, l_unlikey 是什么意思
https://nevermoreluo.github.io/2022/01/07/Lua-源码中-l-likely-l-unlikey-是什么意思/