如何在全优化下可复用地测量C++内联函数执行时间?
如何在全优化下可复用地测量纳秒级函数的执行时间?
问题背景
我有一批仅需数纳秒即可完成的函数,需要测量它们的执行时间来评估性能。基本思路是:获取时间戳,循环多次执行同一代码块,再次获取时间戳,计算时间差并除以迭代次数。但遇到两个核心问题:
- 直接在
main函数中写测试代码时,编译器会识别出重复计算未使用结果,将循环优化为空操作,导致得到极小的无效数值。虽然通过伪造结果使用的方式在VS2022中有效,但在MSYS2 g++(Code::Blocks)中不生效。 - 将测试逻辑封装成复用函数后,测量数值大幅偏高——因为我的很多函数是内联函数,封装成
std::function会阻止内联优化,无法反映真实性能。
最小复现代码
#include <array> #include <chrono> #include <cmath> #include <functional> #include <iomanip> #include <iostream> #include <utility> #include <vector> using std::array; using std::chrono::steady_clock; using std::chrono::duration; using std::cout; using std::vector; using std::function; double d = 0.0; float r = 0.0; constexpr double DU = 1.0 / (uint64_t(1) << 52); constexpr float FU = 1.0 / (1 << 23); constexpr array<double, 10> LOG2_POLY9 = { -3.27179702e00, 7.19195108e00, -7.34289702e00, 5.01474324e00, -1.64079955e00, -3.25941179e-01, 5.83100708e-01, -2.58134034e-01, 5.44419681e-02, -4.66794032e-03, }; template <std::size_t N, std::size_t... I> inline double apply_poly_impl(double m1, const std::array<double, N>& data, std::index_sequence<I...>) { double s = 0; double m = 1; ((s += std::get<I>(data) * m, m *= m1), ...); return s; } template <std::size_t N> inline double apply_poly(double m1, const std::array<double, N>& data) { return apply_poly_impl(m1, data, std::make_index_sequence<N>{}); } inline float fast_log2_p9(float f) { uint32_t bits = std::bit_cast<uint32_t>(f); int e = ((bits >> 23) & 0xff) - 127; double m1 = 1 + (bits & 0x7fffff) * FU; double s = apply_poly(m1, LOG2_POLY9); return e + s; } template <typename T> double timeit(const function<T(T)>& func, const vector<T>& values, int runs = 1048576){ auto start = steady_clock::now(); size_t len = values.size(); for (int64_t i = 0; i < runs; i++) { func(values[i % len]); } auto end = steady_clock::now(); duration<double, std::nano> time = end - start; return time.count() / runs; } int main() { double r4096 = 1.0 / 4096; double n; vector<double> random_doubles(256); vector<float> random_floats(256); for (int j = 0; j < 256; j++) { n = rand() % 4096 + (rand() % 4096) * r4096; random_doubles[j] = n; random_doubles[j] = float(n); } cout << std::setprecision(16); auto start = steady_clock::now(); for (int64_t i = 0; i < 1048576; i++) { r = fast_log2_p9(random_floats[i % 256]); } auto end = steady_clock::now(); duration<double, std::nano> time = end - start; r = fast_log2_p9(0.7413864135742188f); cout << "fast_log_p9: " << time.count() / 1048576 << " nanoseconds\n"; cout << r << '\n'; cout << timeit<float>(fast_log2_p9, random_floats); }
编译配置与输出
Visual Studio 2022 编译标志
/permissive- /ifcOutput "x64\Release\" /GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Ob1 /sdl /Fd"x64\Release\vc143.pdb" /Zc:inline /fp:fast /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /std:c17 /Gd /Oi /MD /std:c++20 /FC /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Ot /Fp"x64\Release\exponentiation.pch" /diagnostics:column
输出
PS C:\Users\Xeni> C:\Users\Xeni\source\repos\exponentiation\x64\Release\exponentiation.exe fast_log_p9: 0.9078025817871094 nanoseconds -0.4317024052143097 15.96231460571289
Code::Blocks(MSYS2 g++)编译命令
g++.exe -Wall -fexceptions -fomit-frame-pointer -fexpensive-optimizations -flto -O3 -m64 --std=c++20 -march=native -fext-numeric-literals -c D:\MyScript\CodeBlocks\testapp\main.cpp -o obj\Release\main.o g++.exe -o bin\Release\testapp.exe obj\Release\main.o -O3 -flto -s -static-libstdc++ -static-libgcc -static -m64
输出
PS C:\Users\Xeni> D:\MyScript\CodeBlocks\testapp\bin\Release\testapp.exe fast_log_p9: 9.5367431640625e-05 nanoseconds -0.4317024052143097 7.897567749023438
核心诉求
如何在开启所有优化的情况下,以可复用的方式测量代码块的执行时间?
附带测试结果
在VS2022中开启/fp:fast标志,对比自定义函数与std::log2f的性能:
测试代码片段
start = steady_clock::now(); for (int64_t i = 0; i < 1048576; i++) { r = std::log2f(random_floats[i % 256]); } end = steady_clock::now(); time = end - start; r = std::log2f(0.7413864135742188f); cout << "std::log2f: " << time.count() / 1048576 << " nanoseconds\n"; cout << r << '\n';
输出
fast_log2_p9: 0.9081840515136719 nanoseconds -0.4317024052143097 std::log2f: 54.67109680175781 nanoseconds -0.4317024052143097
另外,测试内联优化的影响时发现:开启inline关键字时函数耗时41纳秒,移除后耗时29纳秒(仅两次测试,误差较大),但结果不符合预期——禁用内联本应使代码更慢,说明当前基准测试未正确反映内联优化的真实效果。
有人提到使用benchmark::DoNotOptimize(r);,但不清楚其确切用途,且需要确保在开启所有优化的情况下测量函数真实执行时间(与实际使用场景一致)。
内容的提问来源于stack exchange,提问作者Ξένη Γήινος
相关产品推荐
相关产品推荐

