繁体   English   中英

扩展std :: chrono功能以处理运行时(非编译时)常量周期

[英]extending the std::chrono functionality to deal with run-time (non compile-time) constant periods

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

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

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

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

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

我如何将这些计时器包装成一个标准:chrono?

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

有没有人有任何经验来包装这些计时器:chron?

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

你提到:

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

完全正确。 同样在OSX,在libc中++的执行high_resolution_clocksteady_clock实际上是基于mach_absolute_time 我是这段代码的作者,这是一个开源的慷慨许可证(只要您保留版权,就可以随意做任何事情)。

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

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

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

如果您的周期实际上是动态变化的,那么您需要更多的基础设施来处理这个问题。 基本上,您需要整合(微积分)周期与时间曲线,然后计算两个时间点之间的平均周期。 这需要随着时间的推移不断监测周期, <chrono>不是正确的工具。 这些工具通常在OS级别处理。

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

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

备注:如果期间不是比率的专业化,则该计划格式错误。

并且所有计时模板都严重依赖于constexpr功能。

有没有人有任何经验来包装这些计时器:chron?

我在这里发现一个建议,使用一个持续时间为period = 1,boost :: rational作为rep,虽然没有任何具体的例子。

我为自己的目的做了类似的事情,但仅限于Linux。 你在这里找到代码; 随时以您想要的方式使用代码。

我的实现所遇到的挑战与您问题中提到的问题部分重叠。 特别:

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

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

  • 我的时钟类型叫做combined_clock ,它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]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM