为什么std :: chrono :: time_point不足以存储struct timespec? [英] Why std::chrono::time_point is not large enough to store struct timespec?

查看:286
本文介绍了为什么std :: chrono :: time_point不足以存储struct timespec?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试最近的 std :: chrono api,我发现在64位Linux架构和gcc编译器中 time_point duration 类不能以最大分辨率(纳秒)处理操作系统的最大时间范围。事实上,这些类的存储似乎是一个64位整数类型,与 timespec 和 timeval 相比,它们在内部使用两个64位整数,一个秒和一个纳秒:
#include< chrono>
#include< typeinfo>
#include< time.h>

使用namespace std;
使用namespace std :: chrono;

int main()
{
cout<< sizeof(time_point<纳秒>)<< ENDL; // 8
cout<< sizeof(time_point< nanoseconds> :: duration)<< ENDL; // 8
cout<< sizeof(time_point< nanoseconds> :: duration :: rep)<< ENDL; // 8
cout<< typeid(time_point< nanoseconds> :: duration :: rep).name()<< ENDL; // l
cout<< sizeof(struct timespec)<< ENDL; // 16
cout<< sizeof(struct timeval)<< ENDL; // 16
return 0;

$ / code>

在64位Windows(MSVC2017)上,情况非常相似:存储类型也是一个64位整数。处理稳定(又称单调)时钟时,这不是问题,但存储限制使得不同的API实现不适合存储更大的日期和更长的时间跨度,从而为类似于Y2K的错误创造了基础。问题是否得到承认?是否有更好的实现或API改进计划? 解决方案

如果你需要超精密的精度,你通常不需要很大的范围。如果你需要一个非常大的范围,你通常不需要非常高的精度。



例如,如果您在纳秒级别的交易,您是否经常需要想想超过+/- 292年?如果你需要考虑大于这个范围的话,那么微秒就会给你+/- 292 1000年。



macOS system_clock 实际上会返回微秒,而不是纳秒。所以这个时钟可以从1970年开始运行292年,直到它溢出。

Windows system_clock 的精度为100纳秒单位,所以有一个+/- 29.2千年的范围。

如果几十万年还不够,请尝试毫秒。现在你的范围可以达到+/- 292万欧元。

最后,如果你只是 >具有超过几百年的纳秒精度,< chrono> 允许您自定义存储:

 使用dnano =持续时间< double,nano>; 

这会让您将纳秒存储为 double 。如果您的平台支持128位整数类型,您也可以使用它:

  using big_nano = duration< __ int128_t,nano> ;; 

如果您为 timespec ,甚至可以使用 作为存储空间(我不推荐使用它)。



您还可以实现精细度超过纳秒,但你会牺牲范围。例如:

 使用picoseconds = duration< int64_t,pico>; 

这个范围只有+/- .292年(几个月)。所以你必须小心。如果您的源时钟具有亚纳秒级的精度,那么它非常适合计时。



查看此视频了解有关< chrono> 的更多信息。



创建,操作和存储日期的范围大于当前公历的有效期,我创建了开源日期库,它使用日历服务扩展了< chrono> 库。这个库将年份存储在一个有符号的16位整数中,因此具有+/- 32K年的范围。它可以像这样使用:

  #includedate.h

int
main()
{
using namespace std :: chrono;
使用名称空间日期;
system_clock :: time_point now = sys_days {may / 30/2017} + 19h + 40min + 10s;
}

更新
$ b

在下面的评论中,询问如何将 duration< int32_t,nano> 标准化为秒和纳秒(然后将秒一个time_point)。

首先,我会警惕将纳秒纳入32位。范围仅略高于+/- 2秒。但是,下面是我如何分离出这样的单元:

 使用ns = duration< int32_t,nano>; 
auto n = ns :: max();
auto s = duration_cast< seconds>(n);
n - = s;

请注意,这仅适用于 n 为正。要正确处理负面 n ,最好的办法是:

  auto n = ns :: max(); 
auto s = floor< seconds>(n);
n - = s; C中引入了

std :: floor ++ 17。如果您希望更早,可以从此处 here



我偏到上面的减法操作,因为我发现它更具可读性。但是,这也适用(如果 n 不是负数):

  auto s = duration_cast<秒>(n); 
n%= 1s;



1s 是在C ++中引入的14。在C ++ 11中,您将不得不使用秒秒{1}



s ),您可以将它添加到 time_point 中。


I'm trying the recent std::chrono api and I found that on 64 bit Linux architecture and gcc compiler the time_point and duration classes are not able to handle the maximum time range of the operating system at the maximum resolution (nanoseconds). In fact it seems the storage for these classes is a 64bit integral type, compared to timespec and timeval which are internally using two 64 bit integers, one for seconds and one for nanoseconds:

#include <iostream>
#include <chrono>
#include <typeinfo>
#include <time.h>

using namespace std;
using namespace std::chrono;

int main()
{
    cout << sizeof(time_point<nanoseconds>) << endl;                       // 8
    cout << sizeof(time_point<nanoseconds>::duration) << endl;             // 8
    cout << sizeof(time_point<nanoseconds>::duration::rep) << endl;        // 8
    cout << typeid(time_point<nanoseconds>::duration::rep).name() << endl; // l
    cout << sizeof(struct timespec) << endl;                               // 16
    cout << sizeof(struct timeval) << endl;                                // 16
    return 0;
}

On 64 bit Windows (MSVC2017) the situation is very similar: the storage type is also a 64 bit integer. This is not a problem when dealing with steady (aka monotonic) clocks, but storage limitations make the the different API implementations not suitable to store bigger dates and wider time spans, creating the ground for Y2K-like bugs. Is the problem acknowledged? Are there plans for better implementations or API improvements?

解决方案

This was done so that you get maximum flexibility along with compact size. If you need ultra-fine precision, you usually don't need a very large range. And if you need a very large range, you usually don't need very high precision.

For example, if you're trafficking in nanoseconds, do you regularly need to think about more than +/- 292 years? And if you need to think about a range greater than that, well microseconds gives you +/- 292 thousand years.

The macOS system_clock actually returns microseconds, not nanoseconds. So that clock can run for 292 thousand years from 1970 until it overflows.

The Windows system_clock has a precision of 100-ns units, and so has a range of +/- 29.2 thousand years.

If a couple hundred thousand years is still not enough, try out milliseconds. Now you're up to a range of +/- 292 million years.

Finally, if you just have to have nanosecond precision out for more than a couple hundred years, <chrono> allows you to customize the storage too:

using dnano = duration<double, nano>;

This gives you nanoseconds stored as a double. If your platform supports a 128 bit integral type, you can use that too:

using big_nano = duration<__int128_t, nano>;

Heck, if you write overloaded operators for timespec, you can even use that for the storage (I don't recommend it though).

You can also achieve precisions finer than nanoseconds, but you'll sacrifice range in doing so. For example:

using picoseconds = duration<int64_t, pico>;

This has a range of only +/- .292 years (a few months). So you do have to be careful with that. Great for timing things though if you have a source clock that gives you sub-nanosecond precision.

Check out this video for more information on <chrono>.

For creating, manipulating and storing dates with a range greater than the validity of the current Gregorian calendar, I've created this open-source date library which extends the <chrono> library with calendrical services. This library stores the year in a signed 16 bit integer, and so has a range of +/- 32K years. It can be used like this:

#include "date.h"

int
main()
{
    using namespace std::chrono;
    using namespace date;
    system_clock::time_point now = sys_days{may/30/2017} + 19h + 40min + 10s;
}

Update

In the comments below the question is asked how to "normalize" duration<int32_t, nano> into seconds and nanoseconds (and then add the seconds to a time_point).

First, I would be wary of stuffing nanoseconds into 32 bits. The range is just a little over +/- 2 seconds. But here's how I separate out units like this:

    using ns = duration<int32_t, nano>;
    auto n = ns::max();
    auto s = duration_cast<seconds>(n);
    n -= s;

Note that this only works if n is positive. To correctly handle negative n, the best thing to do is:

    auto n = ns::max();
    auto s = floor<seconds>(n);
    n -= s;

std::floor is introduced with C++17. If you want it earlier, you can grab it from here or here.

I'm partial to the subtraction operation above, as I just find it more readable. But this also works (if n is not negative):

    auto s = duration_cast<seconds>(n);
    n %= 1s;

The 1s is introduced in C++14. In C++11, you will have to use seconds{1} instead.

Once you have seconds (s), you can add that to your time_point.

这篇关于为什么std :: chrono :: time_point不足以存储struct timespec?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆