在 C 中获得准确的执行时间(微秒)

新手上路,请多包涵

我想在用 C++ 实现的程序的微秒内获得准确的执行时间。我试图用clock_t 获得执行时间,但它不准确。

(请注意,微基准测试 _很难_。准确的计时器只是为短时间区域获得有意义的结果所必需的一小部分。有关一些更一般的警告,请参阅 Idiomatic way of performance evaluation?

原文由 user3323616 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

如果您使用的是 c++11 或更高版本,则可以使用 std::chrono::high_resolution_clock

一个简单的用例:

 auto start = std::chrono::high_resolution_clock::now();
...
auto elapsed = std::chrono::high_resolution_clock::now() - start;

long long microseconds = std::chrono::duration_cast<std::chrono::microseconds>(
        elapsed).count();

该解决方案具有便携性的优点。


请注意, 微基准测试很难。很容易测量错误的东西(比如你的基准优化掉了),或者在你的定时区域中包含页面错误,或者没有考虑 CPU 频率空闲与 turbo。

请参阅 绩效评估的惯用方式? 对于一些一般提示,例如,通过首先测试另一个来检查是否会改变哪个看起来更快。

原文由 OlivierLi 发布,翻译遵循 CC BY-SA 4.0 许可协议

以下是如何在 C++ 中获取简单的类似 C 的毫秒、微秒和纳秒时间戳:

新的 C++11 std::chrono 库是我见过或试图弄清楚如何使用的最复杂的一堆C++ 库之一,但至少它是跨平台的!

因此,如果您想简化它并使其更像“C”,包括删除它所做的所有类型安全类的东西,这里有 3 个简单且非常易于使用的函数来获取时间戳毫秒、微秒和纳秒……我只花了大约 12 小时来写*:

注意:在下面的代码中,您可以考虑使用 std::chrono::steady_clock 而不是 std::chrono::high_resolution_clock 。他们从这里( https://en.cppreference.com/w/cpp/chrono )的定义如下:

  • stable_clock (C++11) - 永远不会调整的单调时钟
  • high_resolution_clock (C++11) - 具有最短滴答周期的时钟
#include <chrono>

// NB: ALL OF THESE 3 FUNCTIONS BELOW USE SIGNED VALUES INTERNALLY AND WILL
// EVENTUALLY OVERFLOW (AFTER 200+ YEARS OR SO), AFTER WHICH POINT THEY WILL
// HAVE *SIGNED OVERFLOW*, WHICH IS UNDEFINED BEHAVIOR (IE: A BUG) FOR C/C++.
// But...that's ok...this "bug" is designed into the C++11 specification, so
// whatever. Your machine won't run for 200 years anyway...

// Get time stamp in milliseconds.
uint64_t millis()
{
    uint64_t ms = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::high_resolution_clock::now().time_since_epoch())
            .count();
    return ms;
}

// Get time stamp in microseconds.
uint64_t micros()
{
    uint64_t us = std::chrono::duration_cast<std::chrono::microseconds>(
            std::chrono::high_resolution_clock::now().time_since_epoch())
            .count();
    return us;
}

// Get time stamp in nanoseconds.
uint64_t nanos()
{
    uint64_t ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
            std::chrono::high_resolution_clock::now().time_since_epoch())
            .count();
    return ns;
}

\* (对不起,我更像是一个嵌入式开发人员,而不是标准的计算机程序员,所以所有这些高级的、抽象的 static-member-within-class-within-namespace-within-namespace-within-namespace 的东西让我感到困惑。唐放心,我会好起来的。)

问:为什么 std::chrono

A:因为 C++ 程序员喜欢为事情发疯,所以他们让它为你处理单元。以下是一些 C++ 怪异和使用 std::chrono 的案例。参考这个 cppreference 社区 wiki 页面: https ://en.cppreference.com/w/cpp/chrono/duration。

在此处输入图像描述

因此,您可以声明一个 1 秒的变量并将其更改为微秒,而无需像这样进行强制转换:

 // Create a time object of type `std::chrono::seconds` & initialize it to 1 sec
std::chrono::seconds time_sec(1);
// integer scale conversion with no precision loss: no cast
std::cout << std::chrono::microseconds(time_sec).count() << " microseconds\n";

你甚至可以像这样指定时间,在我看来,这非常奇怪而且太过分了。 C++14 has literally overloaded the characters ms , us , ns , etc. as function call operators to initialize std::chrono objects of像这样的各种类型:

在此处输入图像描述

 auto time_sec = 1s; // <== notice the 's' inside the code there
                    // to specify 's'econds!
// OR:
std::chrono::seconds time_sec = 1s;
// integer scale conversion with no precision loss: no cast
std::cout << std::chrono::microseconds(time_sec).count() << " microseconds\n";

Here are some more examples:

std::chrono::milliseconds time_ms = 1ms;
// OR:
auto time_ms = 1ms;

std::chrono::microseconds time_us = 1us;
// OR:
auto time_us = 1us;

std::chrono::nanoseconds time_ns = 1ns;
// OR:
auto time_ns = 1ns;

就个人而言,我 宁愿 简化语言并这样做,就像我已经做过的那样,并且在这之前的几十年里,C 和 C++ 都已经这样做了:

 // Notice the `_sec` at the end of the variable name to remind me this
// variable has units of *seconds*!
uint64_t time_sec = 1;

这里有一些参考资料:

  1. 时钟类型( https://en.cppreference.com/w/cpp/chrono ):
    1. system_clock
    2. steady_clock
    3. high_resolution_clock
    4. utc_clock
    5. tai_clock
    6. gps_clock
    7. file_clock
    8. 等等
  2. 在 C++ 中获得准确的执行时间(微秒) (@OlivierLi 的回答)
  3. http://en.cppreference.com/w/cpp/chrono/time_point/time_since_epoch
  4. http://en.cppreference.com/w/cpp/chrono/duration - 显示小时、分钟、秒、毫秒等类型
  5. http://en.cppreference.com/w/cpp/chrono/system_clock/now

我还需要看的视频:

  1. CppCon 2016:Howard Hinnant “A <chrono>教程”

有关的:

  1. 我的 3 组时间戳功能(相互交叉链接):
    1. 对于 C 时间戳,请在此处查看我的答案: Get a timestamp in C in microseconds?
    2. 对于 C++ 高分辨率时间戳,请在此处查看我的答案: 在 C++ 中获取准确的执行时间(微秒)
    3. 对于 Python 高分辨率时间戳,请在此处查看我的答案: 如何在 Python 中获得毫秒和微秒分辨率的时间戳?
  2. [我的回答] 在 gcc 中使用 std::chrono 的运算符 ““s——不要忘记使用命名空间声明来访问 std::chrono::duration<> 文字

附录

更多关于 “用户定义的文字”(C++11 起)

operator"" mysuffix() 运算符重载/用户定义文字/后缀函数(从 C++11 开始)是奇怪的 auto time_ms = 1ms; 事情在上面的工作原理。写 1ms 实际上是对函数 operator"" ms() 的函数调用,带有 1 作为输入参数传入,就像你写了一个函数调用一样: operator"" ms(1) 。要了解有关此概念的更多信息,请参阅此处的参考页面: cppreference.com:用户定义的文字(自 C++11 起)

这是定义用户定义文字/后缀函数并使用它的基本演示:

 // 1. Define a function
// used as conversion from degrees (input param) to radians (returned output)
constexpr long double operator"" _deg(long double deg)
{
    long double radians = deg * 3.14159265358979323846264L / 180;
    return radians;
}

// 2. Use it
double x_rad = 90.0_deg;

为什么不直接使用类似 double x_rad = degToRad(90.0); 的东西(就像几十年来在 C 和 C++ 中所做的那样)?我不知道。这与我猜想的 C++ 程序员的方式有关。也许他们正试图让现代 C++ 更加 Pythonic。

这种魔力也是潜在非常有用的 C++ fmt 库的工作原理,这里: https ://github.com/fmtlib/fmt。它由 Victor Zverovich 编写,他也是 C++20 的 std::format 的作者。您可以在 此处查看 函数 detail::udl_formatter<char> operator"" _format(const char* s, size_t n) 的定义。它的用法是这样的:

 "Hello {}"_format("World");

输出:

你好世界

"World" {} 的第一个字符串中。这是另一个例子:

 "I have {} eggs and {} chickens."_format(num_eggs, num_chickens);

样本输出:

我有 29 个鸡蛋和 42 只鸡。

这与 Python 中的 str.format 字符串格式化非常相似。在 此处 阅读 fmt lib 文档。

原文由 Gabriel Staples 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏