3

我一直在 Linux 和 OSX 上试验各种计时器,并想尝试用 std::chrono 使用的相同接口包装其中的一些。

对于在编译时具有明确定义的“周期”的计时器来说,这很容易做到,例如 POSIX clock_gettime() 系列、OSX 上的 clock_get_time() 系列或 gettimeofday()。

然而,有一些有用的计时器,“周期”——虽然是常数——只在运行时才知道。例如: - POSIX 声明 clock() 的周期,CLOCKS_PER_SEC,在非 XSI 系统上可能是一个变量 - 在 Linux 上,times() 的周期由 sysconf(_SC_CLK_TCK) 在运行时给出 - 在 OSX 上,周期mach_absolute_time() 在运行时由 mach_timebase_info() 给出 - 在最近的英特尔处理器上,DST 寄存器以恒定速率滴答,但当然只能在运行时确定

要将这些计时器包装在 std::chrono 接口中,一种可能性是使用一段 std::chrono::nanosecond ,并将每个计时器的值转换为纳秒。另一种方法可能是使用浮点表示。然而,这两种方法都会给 now() 函数带来(非常小的)开销,以及(可能很小的)精度损失。

我试图寻求的解决方案是定义一组类来表示这样的“运行时常量”周期,它们与 std::ratio 类的构建方式相同。但是我希望这将需要重写所有相关的模板类和函数(因为它们假定为 constexpr 值)。

如何将这些计时器包装为 std:chrono ?

或者对时钟的时间段使用非 constexpr 值?

4

3 回答 3

4

有没有人有任何包装这类计时器的经验?

其实我愿意。在 OSX 上,您感兴趣的平台之一。:-)

你提到:

在 OSX 上,mach_absolute_time() 的周期由 mach_timebase_info() 在运行时给出

完全正确。同样在 OSX 上,和的libc ++实现实际上是基于. 我是这段代码的作者,它是开源的,有一个慷慨的许可(只要你保留版权,你可以用它做任何你想做的事情)。high_resolution_clocksteady_clockmach_absolute_time

这是libc++steady_clock::now()的源代码。它的构建方式与您推测的差不多。运行时间段在返回之前转换为纳秒。在 OS X 上,转换因子通常为 1,代码通过优化利用了这一事实。但是,该代码足够通用,可以处理非 1 转换因子。

在第一次调用时,now()将运行时间转换因子查询为纳秒的成本很小。在一般情况下,计算浮点转换因子。在常见情况下(转换因子 == 1),后续成本是通过函数指针调用的。我发现开销真的很合理。

在 OS X 上,转换因子虽然直到运行时才确定,但仍然是一个常数(即不会随着程序执行而变化),因此只需要计算一次。

如果您的月经实际上是动态变化的,那么您将需要更多的基础设施来处理这个问题。本质上,您需要对周期与时间曲线进行积分(微积分),然后计算两个时间点之间的平均周期。这将需要持续监控该周期,因为它会随着时间的推移而变化,而<chrono>这不是正确的工具。此类工具通常在操作系统级别处理。

于 2013-02-27T01:18:57.437 回答
0

我为我的目的做了类似的事情,不过只针对 Linux。你可以在这里找到代码;随意以任何你想要的方式使用代码。

我的实施解决的挑战与您的问题中提到的挑战部分重叠。具体来说:

  • 刻度因子(需要从时钟刻度转换为基于秒的时间单位)在运行时检索,但仅使用第now()一次。如果您担心这会导致较小的开销,您可以now()在启动时调用该函数一次,然后再测量任何实际间隔。刻度因子存储在静态变量中,这意味着仍然存在一些开销,因为在最低级别上,now()函数的每次调用都意味着检查静态变量是否已被初始化。然而,这个开销在每次调用中都是相同的now(),所以它不应该影响测量时间间隔。

  • 默认情况下我不转换为纳秒,因为当测量相对较长的时间段(例如几秒)时,这会导致溢出非常快。这实际上是我不使用 boost 实现的主要原因。我没有转换为纳秒,而是将基本单位实现为模板参数(Precision在代码中调用)。我使用std::ratioC++11 作为模板参数。所以我可以选择,例如 a clock<micro>,这意味着调用该now()函数将在内部转换为微秒而不是纳秒,这意味着我可以测量许多秒或数分钟的周期而不会溢出并且仍然具有良好的精度。(这与用于产生输出的单位无关。您可以有一个clock<micro>并以秒为单位显示结果等)

  • 我的时钟类型,称为combined_clock结合了用户时间、系统时间和挂钟时间。这也有一个升压时钟类型,但它与来自的比率类型和单位不兼容std,而我的是。

刻度因子是使用::sysconf()您建议的调用检索的,并且保证在整个过程的生命周期内返回一个相同的值。

所以你使用它的方式如下:

#include "util/proctime.hpp"

#include <ratio>
#include <chrono>
#include <thread>
#include <utility>
#include <iostream>

int main()
{
  using std::chrono::duration_cast;
  using millisec   = std::chrono::milliseconds;
  using clock_type = rlxutil::combined_clock<std::micro>;

  auto tp1 = clock_type::now();

  /* Perform some random calculations. */
  unsigned long step1 = 1;
  unsigned long step2 = 1;
  for (int i = 0 ; i < 50000000 ; ++i) {
    unsigned long step3 = step1 + step2;
    std::swap(step1,step2);
    std::swap(step2,step3);
  }

  /* Sleep for a while (this adds to real time, but not CPU time). */
  std::this_thread::sleep_for(millisec(1000));

  auto tp2 = clock_type::now();

  std::cout << "Elapsed time: "
            << duration_cast<millisec>(tp2 - tp1)
            << std::endl;

  return 0;
}

上面的用法涉及一个漂亮的打印函数,它生成如下输出:

Elapsed time: [user 40, system 0, real 1070 millisec]
于 2013-02-27T02:10:49.467 回答
0

[有没有人有任何经验] 或者在时钟的时间段内使用非 constexpr 值?

通读标准(20.11.5,类模板持续时间)后,“期间”预计是“比率的专业化”:

备注:如果 Period 不是 ratio 的特化,则程序是非良构的。

并且所有 chrono 模板都严重依赖 constexpr 功能。

有没有人有任何包装这类计时器的经验?

我在这里找到了使用 period = 1, boost::rational as rep 的持续时间的建议,尽管没有任何具体示例。

于 2013-02-09T12:19:23.170 回答