BenchMark
框架基于这样一种思想:测量时候一定有噪声的存在,导致我们的测量值偏大。因此不应该对多组测量结果求平均,而是应该求最小值。
- 第一个有趣的地方是如何确保一个无用的表达式求值,
Folly
中是利用了句号表达式 + 匿名变量
#define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \
static void funName(paramType); \
FOLLY_MAYBE_UNUSED static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
(::folly::addBenchmark( \
__FILE__, \
stringName, \
[](paramType paramName) -> unsigned { \
funName(paramName); \
return rv; \
}), \
true); \
static void funName(paramType paramName)
宏展开之后,就完成了函数的声明和并将测试函数注入框架。
- 第二个有意思的地方是如何保证一个变量的声明周期在整个代码块内,即我们希望
BENCHMARK_SUSPEND {
v.reserve(n);
}
BENCHMARK_SUSPEND
是通过控制对应变量的生命周期控制计时流程的,因此,我们希望在这个宏中构造的变量的生命周期贯穿整个代码块。
#define BENCHMARK_SUSPEND \
if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) = \
::folly::BenchmarkSuspender()) { \
} else
非常巧妙的利用了在if
中声明的变量的scope
在else之后结束的冷知识 ref:
- 计时函数使用了
clock_gettime
,hook
了 系统调用,基于vdso
进行了自定义封装,避免陷入内核。
struct VdsoInitializer {
VdsoInitializer() {
m_handle = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!m_handle) {
return;
}
void* p = dlsym(m_handle, "__vdso_clock_gettime");
if (p) {
folly::chrono::clock_gettime = (int (*)(clockid_t, timespec*))p;
}
p = dlsym(m_handle, "__vdso_clock_gettime_ns");
if (p) {
folly::chrono::clock_gettime_ns = (int64_t(*)(clockid_t))p;
}
}
~VdsoInitializer() {
if (m_handle) {
clock_gettime = &::clock_gettime;
clock_gettime_ns = &clock_gettime_ns_fallback;
dlclose(m_handle);
}
}
private:
void* m_handle;
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。