You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在全优化下可复用地测量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,提问作者Ξένη Γήινος

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 21:45:54