简体   繁体   English

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

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

I have been experimenting with all kind of timers on Linux and OSX, and would like to try and wrap some of them with the same interface used by std::chrono. 我一直在Linux和OSX上尝试各种计时器,并且想尝试使用std :: chrono使用的相同接口来包装它们中的一些。

That's easy to do for timers that have a well-defined "period" at compile time, eg the POSIX clock_gettime() familiy, the clock_get_time() family on OSX, or gettimeofday(). 对于在编译时具有明确定义的“周期”的定时器,例如POSIX clock_gettime()familiy,OSX上的clock_get_time()系列或gettimeofday(),这很容易做到。

However, there are some useful timers for which the "period" - while constant - is only known at runtime. 但是,有一些有用的计时器,“周期” - 而常量 - 只在运行时才知道。 For example: - POSIX states the period of clock(), CLOCKS_PER_SEC, may be a variable on non-XSI systems - on Linux, the period of times() is given at runtime by sysconf(_SC_CLK_TCK) - on OSX, the period of mach_absolute_time() is given at runtime by mach_timebase_info() - on recent Intel processors, the DST register ticks at a constant rate, but of course that can only be determined at runtime 例如: - POSIX表示clock()的周期,CLOCKS_PER_SEC,可能是非XSI系统上的变量 - 在Linux上,时间段()由运行时由sysconf(_SC_CLK_TCK)给出 - 在OSX上, mach_absolute_time()在运行时由mach_timebase_info()给出 - 在最近的英特尔处理器上,DST寄存器以恒定速率滴答,但当然只能在运行时确定

To wrap these timers in the std::chrono interface, one possibility would be to use a period of std::chrono::nanosecond , and convert the value of each timer to nanoseconds. 要将这些计时器包装在std :: chrono接口中,一种可能性是使用一段std :: chrono :: nanosecond,并将每个计时器的值转换为纳秒。 An other approach could be to use a floating point representation. 另一种方法可以是使用浮点表示。 However, both approaches would introduce a (very small) overhead to the now() function, and a (probably small) loss in precision. 但是,这两种方法都会给now()函数带来(非常小的)开销,并且精度会损失(可能很小)。

The solution I'm trying to pursue is to define a set of classes to represent such "run-time constant" periods, built along the same lines as the std::ratio class. 我试图解决的解决方案是定义一组类来表示这样的“运行时常量”周期,这些周期与std :: ratio类相同。 However I expect that will require rewriting all the related template classes and functions (as they assume constexpr values). 但是我希望这将需要重写所有相关的模板类和函数(因为它们假设constexpr值)。

How do I wrap these kind of timers a la std:chrono ? 我如何将这些计时器包装成一个标准:chrono?

Or use non-constexpr values for the time period of a clock ? 或者在时钟的时间段内使用非constexpr值?

Does anyone have any experience with wrapping these kind of timers a la std:chrono ? 有没有人有任何经验来包装这些计时器:chron?

Actually I do. 其实我这样做。 And on OSX, one of your platforms of interest. 在OSX上,您感兴趣的平台之一。 :-) :-)

You mention: 你提到:

on OSX, the period of mach_absolute_time() is given at runtime by mach_timebase_info() 在OSX上,mach_absolute_time()的周期在运行时由mach_timebase_info()给出

Absolutely correct. 完全正确。 Also on OSX, the libc++ implementation of high_resolution_clock and steady_clock is actually based on mach_absolute_time . 同样在OSX,在libc中++的执行high_resolution_clocksteady_clock实际上是基于mach_absolute_time I'm the author of this code, which is open source with a generous license (do anything you want with it as long as you retain the copyright). 我是这段代码的作者,这是一个开源的慷慨许可证(只要您保留版权,就可以随意做任何事情)。

Here is the source for libc++'s steady_clock::now() . 这是libc ++的steady_clock::now()的源代码。 It is built pretty much the way you surmised. 它的构建方式与您推测的方式非常相似。 The run time period is converted to nanoseconds prior to returning. 运行时间段在返回之前转换为纳秒。 On OS X the conversion factor is very often 1, and the code takes advantage of that fact with an optimization. 在OS X上,转换因子通常为1,并且代码通过优化利用了这一事实。 However the code is general enough to handle non-1 conversion factors. 但是,代码通常足以处理非1转换因子。

On the first call to now() there's a small cost of querying the run time conversion factor to nanoseconds. 在第一次调用now() ,查询运行时转换因子为纳秒的成本很低。 In the general case a floating point conversion factor is computed. 在一般情况下,计算浮点转换因子。 In the common case (conversion factor == 1) the subsequent cost is calling through a function pointer. 在通常情况下(转换因子== 1),后续成本是通过函数指针调用。 I've found that the overhead is really quite reasonable. 我发现开销真的很合理。

On OS X the conversion factor, although not determined until run time, is still a constant (ie does not vary as the program executes), so it only needs to be computed once. 在OS X上,转换因子虽然直到运行时才确定,但仍然是一个常量(即不随程序执行而变化),因此它只需要计算一次。

If you're in a situation where your period is actually varying dynamically, you'll need more infrastructure to handle this. 如果您的周期实际上是动态变化的,那么您需要更多的基础设施来处理这个问题。 Essentially you would need to integrate (calculus) the period vs time curve and then compute an average period between two points in time. 基本上,您需要整合(微积分)周期与时间曲线,然后计算两个时间点之间的平均周期。 That would require a constant monitoring of the period as it changes with time, and <chrono> isn't the right tool for that. 这需要随着时间的推移不断监测周期, <chrono>不是正确的工具。 Such tools are typically handled at the OS level. 这些工具通常在OS级别处理。

[Does anyone have any experience] Or with using non-constexpr values for the time period of a clock ? [有没有经验]或者在时钟的时间段内使用非constexpr值?

After reading through the standard (20.11.5, Class template duration), "period" is expected to be "a specialization of ratio": 在阅读标准(20.11.5,类模板持续时间)后,“期间”预计将是“比率的专业化”:

Remarks: If Period is not a specialization of ratio, the program is ill-formed. 备注:如果期间不是比率的专业化,则该计划格式错误。

and all chrono templates rely heavily on constexpr functionality. 并且所有计时模板都严重依赖于constexpr功能。

Does anyone have any experience with wrapping these kind of timers a la std:chrono ? 有没有人有任何经验来包装这些计时器:chron?

I've found here a suggestion to use a duration with period = 1, boost::rational as rep , though without any concrete examples. 我在这里发现一个建议,使用一个持续时间为period = 1,boost :: rational作为rep,虽然没有任何具体的例子。

I have done a similar thing for my purposes, only for Linux though. 我为自己的目的做了类似的事情,但仅限于Linux。 You find the code here ; 你在这里找到代码; feel free to use the code in whatever way you want. 随时以您想要的方式使用代码。

The challenges my implementation addresses overlap partially with the ones mentioned in your question. 我的实现所遇到的挑战与您问题中提到的问题部分重叠。 Specifically: 特别:

  • The tick factor (required to convert from clock ticks to a time unit based on seconds) is retrieved at run time, but only the first time now() is used . 在运行时检索滴答因子(从时钟滴答转换为基于秒的时间单位所需),但仅使用第一次now() If you are concerned about the small overhead this causes, you may call the now() function once at start-up before you measure any actual intervals. 如果您担心这会导致较小的开销,您可以在启动之前调用now()函数,然后再测量任何实际间隔。 The tick factor is stored in a static variable, which means there is still some overhead as – on the lowest level – each call of the now() function implies checking whether the static variable has been initialized. tick因子存储在一个静态变量中,这意味着仍有一些开销 - 在最低级别 - now()函数的每次调用都意味着检查静态变量是否已经初始化。 However, this overhead will be the same in each call of now() , so it shouldn't impact measuring time intervals. 但是,这个开销now()每次调用中都是相同的 ,所以它不应该影响测量时间间隔。

  • I do not convert to nanoseconds by default, because when measuring relatively long periods of time (eg a few seconds) this causes overflows very quickly. 我默认情况下不会转换为纳秒,因为在测量相对较长的时间段(例如几秒钟)时,这会导致溢出很快。 This is in fact the main reason why I don't use the boost implementation. 这实际上是我不使用boost实现的主要原因。 Instead of converting to nanoseconds, I implement the base unit as a template parameter (called Precision in the code). 我将基本单元实现为模板参数(在代码中称为Precision ),而不是转换为纳秒。 I use std::ratio from C++11 as template arguments. 我使用来自C ++ 11的std::ratio作为模板参数。 So I can choose, for example, a clock<micro> , which implies that calling the now() function will internally convert to microseconds rather than nanoseconds, which means I can measure periods of many seconds or minutes without overflows and still with good precision. 所以我可以选择,例如,一个clock<micro> ,这意味着调用now()函数将在内部转换为微秒而不是纳秒,这意味着我可以测量许多秒或分钟的周期而没有溢出,仍然具有良好的精度。 (This is independent of the unit used to produce output. You can have a clock<micro> and display the result in seconds, etc.) (这与用于产生输出的单位无关。您可以使用clock<micro>并在几秒钟内显示结果等)

  • My clock type, which is called combined_clock combines user time, system time and wall-clock time. 我的时钟类型叫做combined_clock ,它combined_clock了用户时间,系统时间和挂钟时间。 There is a boost clock type for this, too, but it's not compatible with the ratio types and units from std , whereas mine is. 还有一种增强时钟类型,但它与std的比率类型和单位不兼容,而我的是。

The tick factor is retrieved using the ::sysconf() call you suggest, and that is guaranteed to return one and the same value throughout the life time of the process. 使用您建议的::sysconf()调用检索滴答因子,并保证在整个生命周期内返回一个相同的值。

So the way you use it is as follows: 所以你使用它的方式如下:

#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;
}

The usage above involves a pretty-print function that generates output like this: 上面的用法涉及一个漂亮的打印函数,生成如下输出:

Elapsed time: [user 40, system 0, real 1070 millisec]

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

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