The hard ways

Node 源码中的常见代码

前言

node 源码中有一些常见的,有趣的代码片段,文本将对这些内容进行解释、记录。了解这些既可以拓展无用的知识点,又可以方便后续阅读源码。

V8_LIKELY & V8_UNLIKELY

V8_LIKELY & V8_UNLIKELY 在源码中很常见,比如下面代码片段:

template <class T>
Local<T> MaybeLocal<T>::ToLocalChecked() {
  if (V8_UNLIKELY(val_ == nullptr)) V8::ToLocalEmpty();
  return Local<T>(val_);
}

它们的定义是:

#if V8_HAS_BUILTIN_EXPECT
# define V8_UNLIKELY(condition) (__builtin_expect(!!(condition), 0))
# define V8_LIKELY(condition) (__builtin_expect(!!(condition), 1))
#else
# define V8_UNLIKELY(condition) (condition)
# define V8_LIKELY(condition) (condition)
#endif

所以核心其实是 __builtin_expect,它是 GCC 编译器 内置的函数

它的作用是,比如有下面代码:

if (__builtin_expect(x, 0)) {
    foo();
    ...
} else {
    bar();
    ...
}

生成的汇编代码可能大致是:

  cmp   $x, 0
  jne   _foo
_bar:
  call  bar
  ...
  jmp   after_if
_foo:
  call  foo
  ...
after_if:

例子取自:What is the advantage of GCC's __builtin_expect in if else statements?

CPU 加载指令的时候,并不是一条一条加载的,为了性能会一次预读一定数量的指令,这样当加载第一条 cmp $x, 0 指令的时候,_bar 的指令就也一并被加载了。我们注意到,其实在源码中,bar 是在 foo 后面的,之所以有这样的输出,就是我们通过 __builtin_expect(x, 0) 告诉编译器,这个条件判断在实际业务执行中,大概率会是 false,因此可以将 else 部分的指令放前置,可以加速 CPU 的执行

更多拓展可以参考:

Made with gadget