你想要的就是所谓的“微基准测试”。它可能会变得非常复杂。我假设您在 x86_64 上使用 Ubuntu Linux。这对于 ARM、ARM64 或任何其他平台无效。
std::chrono 在 Linux 上的 libstdc++ (gcc) 和 libc++ (clang) 上实现,作为 GLIBC(C 库)的一个简单包装,它完成了所有繁重的工作。如果您查看 std::chrono::steady_clock::now() 您将看到对clock_gettime() 的调用。
Clock_gettime() 是一个VDSO,即它是在用户空间中运行的内核代码。它应该非常快,但有时它可能需要做一些内务处理,并且每次调用都需要很长时间。所以我不推荐进行微基准测试。
几乎每个平台都有一个周期计数器,x86 有汇编指令rdtsc
。该指令可以通过制作插入到您的代码中asm
调用或使用编译器特定的内置函数 __builtin_ia32_rdtsc() 或 __rdtsc()。
这些调用将返回一个 64 位整数,表示自机器加电以来的时钟数。 rdtsc 不是立即执行的,但速度很快,大约需要 15-40 个周期才能完成。
不能保证在所有平台上每个核心的该计数器都相同,因此当进程从一个核心移动到另一个核心时要小心。但在现代系统中这不应该是一个问题。
rdtsc 的另一个问题是,如果编译器发现指令没有副作用,它们通常会重新排序指令,不幸的是 rdtsc 就是其中之一。因此,如果您发现编译器在欺骗您,则必须在这些计数器读取周围使用假屏障 - 查看生成的程序集。
还有一个大问题是cpu本身乱序执行。不仅编译器可以改变执行顺序,CPU 也可以。由于 x86 486 英特尔 CPU 是流水线式的,因此可以同时执行多条指令 - 粗略地说。因此,您最终可能会测量虚假执行。
我建议您熟悉微基准测试的类似量子问题。这并不简单。
请注意,rdtsc() 将返回周期数。您必须使用时间戳计数器频率转换为纳秒。
这是一个例子:
#include <iostream>
#include <cstdio>
void dosomething() {
// yada yada
}
int main() {
double sum = 0;
const uint32_t numloops = 100000000;
for ( uint32_t j=0; j<numloops; ++j ) {
uint64_t t0 = __builtin_ia32_rdtsc();
dosomething();
uint64_t t1 = __builtin_ia32_rdtsc();
uint64_t elapsed = t1-t0;
sum += elapsed;
}
std::cout << "Average:" << sum/numloops << std::endl;
}
这篇论文有点过时(2010 年),但它足够最新,可以为您提供有关微基准测试的良好介绍:
如何对英特尔® IA-32 和 IA-64 指令集架构上的代码执行时间进行基准测试 https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf