Huy*_* Le 4 c++ linux optimization performance time
#include <iostream>
#include <chrono>
using namespace std;
class MyTimer {
private:
std::chrono::time_point<std::chrono::steady_clock> starter;
std::chrono::time_point<std::chrono::steady_clock> ender;
public:
void startCounter() {
starter = std::chrono::steady_clock::now();
}
double getCounter() {
ender = std::chrono::steady_clock::now();
return double(std::chrono::duration_cast<std::chrono::nanoseconds>(ender - starter).count()) /
1000000; // millisecond output
}
// timer need to have nanosecond precision
int64_t getCounterNs() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - starter).count();
}
};
MyTimer timer1, timer2, timerMain;
volatile int64_t dummy = 0, res1 = 0, res2 = 0;
// time run without any time measure
void func0() {
dummy++;
}
// we're trying to measure the cost of startCounter() and getCounterNs(), not "dummy++"
void func1() {
timer1.startCounter();
dummy++;
res1 += timer1.getCounterNs();
}
void func2() {
// start your counter here
dummy++;
// res2 += end your counter here
}
int main()
{
int i, ntest = 1000 * 1000 * 100;
int64_t runtime0, runtime1, runtime2;
timerMain.startCounter();
for (i=1; i<=ntest; i++) func0();
runtime0 = timerMain.getCounter();
cout << "Time0 = " << runtime0 << "ms\n";
timerMain.startCounter();
for (i=1; i<=ntest; i++) func1();
runtime1 = timerMain.getCounter();
cout << "Time1 = " << runtime1 << "ms\n";
timerMain.startCounter();
for (i=1; i<=ntest; i++) func2();
runtime2 = timerMain.getCounter();
cout << "Time2 = " << runtime2 << "ms\n";
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试分析一个程序,其中某些关键部分的执行时间小于 50 纳秒。我发现我使用的计时器类std::chrono太昂贵(带计时的代码比不带计时的代码多花费 40% 的时间)。如何制作更快的计时器类?
我认为一些特定于操作系统的系统调用将是最快的解决方案。平台是Linux Ubuntu。
编辑:所有代码均使用 -O3 编译。确保每个计时器仅初始化一次,因此测量的成本仅由 startMeasure/stopMeasure 函数引起。我不做任何文本打印。
编辑2:接受的答案不包括实际将周期数转换为纳秒的方法。如果有人能做到这一点,那将非常有帮助。
小智 10
你想要的就是所谓的“微基准测试”。它可能会变得非常复杂。我假设您在 x86_64 上使用 Ubuntu Linux。这对于 ARM、ARM64 或任何其他平台无效。
\nstd::chrono 在 Linux 上的 libstdc++ (gcc) 和 libc++ (clang) 上实现,作为 GLIBC(C 库)的简单包装,它完成了所有繁重的工作。如果您查看 std::chrono::steady_clock::now() 您将看到对clock_gettime() 的调用。
\nClock_gettime() 是一个VDSO,即它是在用户空间中运行的内核代码。它应该非常快,但有时它可能需要做一些内务处理,并且每次调用都需要很长时间。所以我不建议进行微基准测试。
\n几乎每个平台都有一个周期计数器,x86 也有汇编指令rdtsc。asm可以通过构造调用或使用编译器特定的内置函数 __builtin_ia32_rdtsc() 或 __rdtsc()将该指令插入到您的代码中。
这些调用将返回一个 64 位整数,表示自机器加电以来的时钟数。rdtsc 不是立即执行的,但速度很快,大约需要 15-40 个周期才能完成。
\n不能保证在所有平台上每个核心的该计数器都相同,因此当进程从一个核心移动到另一个核心时要小心。但在现代系统中这不应该是一个问题。
\nrdtsc 的另一个问题是,如果编译器发现指令没有副作用,通常会重新排序指令,不幸的是 rdtsc 就是其中之一。因此,如果您发现编译器在欺骗您,则必须在这些计数器读取周围使用假屏障 - 查看生成的程序集。
\n还有一个大问题是cpu本身乱序执行。不仅编译器可以改变执行顺序,CPU 也可以。由于 x86 486 英特尔 CPU 是流水线式的,因此可以同时执行多条指令 - 粗略地说。因此,您最终可能会测量虚假执行。
\n我建议您熟悉微基准测试的类似量子问题。这并不简单。
\n请注意,rdtsc() 将返回周期数。您必须使用时间戳计数器频率转换为纳秒。
\n这是一个例子:
\n#include <iostream>\n#include <cstdio>\n\nvoid dosomething() {\n // yada yada\n}\n\nint main() {\n double sum = 0;\n const uint32_t numloops = 100000000;\n for ( uint32_t j=0; j<numloops; ++j ) {\n uint64_t t0 = __builtin_ia32_rdtsc();\n dosomething();\n uint64_t t1 = __builtin_ia32_rdtsc();\n uint64_t elapsed = t1-t0;\n sum += elapsed;\n }\n std::cout << "Average:" << sum/numloops << std::endl;\n}\nRun Code Online (Sandbox Code Playgroud)\n这篇论文有点过时(2010 年),但它足够最新,可以为您提供有关微基准测试的良好介绍:
\n如何对 Intel\xc2\xae IA-32 和 IA-64 指令集架构上的代码执行时间进行基准测试
\n