为了账号安全,请及时绑定邮箱和手机立即绑定

使用C+提供以纳米秒为单位的时间的计时器功能

使用C+提供以纳米秒为单位的时间的计时器功能

C++ C
饮歌长啸 2019-06-25 13:32:08
使用C+提供以纳米秒为单位的时间的计时器功能我希望计算API返回值所需的时间。这种行动所需的时间是在纳米秒的空间内。由于API是一个C+类/函数,所以我使用timer.h来计算相同的内容:  #include <ctime>   #include <cstdio>   using namespace std;   int main(int argc, char** argv) {       clock_t start;       double diff;       start = clock();       diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;       cout<<"printf: "<< diff <<'\n';       return 0;   }以上代码以秒为单位给出了时间。如何在纳米秒内以更高的精度获得相同的结果?
查看完整描述

3 回答

?
慕尼黑5688855

TA贡献1848条经验 获得超2个赞

这个新的答案使用C+11的。<chrono>设施。虽然还有其他的答案可以说明如何使用<chrono>,它们都没有显示如何使用<chrono>带着RDTSC其他几个答案中提到的设施。所以我想我应该展示如何使用RDTSC带着<chrono>..此外,我将演示如何在时钟上临时编写测试代码,以便您可以在RDTSC和你的系统内置的时钟设施(这很可能是基于clock()clock_gettime()和/或QueryPerformanceCounter.

注意,RDTSC指令是x86特定的。QueryPerformanceCounter只适用于Windows。和clock_gettime()仅限POSIX。下面我介绍两个新的时钟:std::chrono::high_resolution_clockstd::chrono::system_clock,如果您可以假设C+11,则现在是跨平台的。

首先,下面是如何从Intel中创建一个与C+11兼容的时钟。rdtsc装配指令。我就叫它x::clock:

#include <chrono>namespace x{struct clock{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now() noexcept
    {
        unsigned lo, hi;
        asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
        return time_point(duration(static_cast<rep>(hi) << 32 | lo));
    }
};

}  // x

这个时钟所做的就是计算CPU周期,并将其存储在一个无符号64位整数中。您可能需要调整编译器的汇编语言语法。或者您的编译器可能提供一个您可以使用的内部特性(例如:now() {return __rdtsc();}).

要构建一个时钟,您必须给它表示(存储类型)。您还必须提供时钟周期,这必须是一个编译时间常数,即使您的机器可能改变不同的电源模式下的时钟速度。根据这些基本原理,您可以很容易地定义您的时钟的“本地”时间持续时间和时间点。

如果你想要做的只是输出时钟滴答的数量,那么你给出的时钟周期是多少并不重要。只有当您想将时钟滴答的数量转换为一些实时单位(例如纳秒)时,这个常数才会起作用。在这种情况下,你能提供的时钟速度越精确,转换到纳秒(毫秒,随便什么)的精度就越高。

下面是示例代码,演示如何使用x::clock..实际上,我已经对时钟上的代码进行了模板化,因为我想向您展示如何用完全相同的语法使用许多不同的时钟。这个特殊的测试显示了在循环下运行所需时间时的循环开销是什么:

#include <iostream>template <class clock>voidtest_empty_loop(){
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";}

这段代码所做的第一件事就是创建一个“实时”单元来显示结果。我选择了皮秒,但是你可以选择任何你喜欢的单位,无论是积分还是基于浮点。举个例子,有一个预先制作的std::chrono::nanoseconds我可以用的单位。

作为另一个例子,我希望以浮点数的形式打印出每次迭代的平均时钟周期数,因此我创建了另一个基于Double的持续时间,它具有与时钟的刻度相同的单位(称为Cycle在代码中)。

循环是通过调用clock::now()两边都有。如果要命名此函数返回的类型,则如下所示:

typename clock::time_point t0 = clock::now();

(如x::clock例如,也适用于系统提供的时钟)。

要获得以浮点时钟为单位的持续时间,只需减去两个时间点,而要获得每一个迭代值,则将持续时间除以迭代次数。

属性可以在任何时间内获取计数。count()成员函数这将返回内部表示。最后我用std::chrono::duration_cast若要转换持续时间,请执行以下操作Cycle持续时间picoseconds打印出来。

使用此代码很简单:

int main(){
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();}

以上我是用我们自制的测试来练习的。x::clock,并将这些结果与使用两个系统提供的时钟的结果进行比较:std::chrono::high_resolution_clockstd::chrono::system_clock..对我来说这是打印出来的:

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration

这表明每个时钟都有一个不同的刻度周期,因为每个时钟的每一次迭代的滴答数都有很大的不同。然而,当转换为一个已知的时间单位(例如,皮秒),我得到了大约相同的结果,每个时钟(您的里程可能有所不同)。

请注意,我的代码完全没有“神奇的转换常量”。实际上,整个示例中只有两个神奇的数字:

  1. 我的机器的时钟速度来定义

    x::clock.

  2. 要测试的迭代次数。如果更改这个数目会使结果有很大差异,那么您可能应该提高迭代次数,或者在测试时清空计算机上的竞争进程。


查看完整回答
反对 回复 2019-06-25
?
慕沐林林

TA贡献2016条经验 获得超9个赞

有了这样的精确度,最好是在cpu滴答中进行推理,而不是在系统调用中进行推理。像钟()..别忘了,如果执行一条指令需要超过一纳秒.拥有纳秒精度几乎是不可能的。

不过,差不多吧是一个开始:

以下是检索自CPU上次启动以来传递的80x86 CPU时钟滴答号的实际代码。它将在奔腾及以上领域开展工作(386/486没有得到支持)。这段代码实际上是特定于MSVisualC+的,但是只要它支持内联程序集,它可能很容易移植到其他任何东西。

inline __int64 GetCpuClocks(){

    // Counter
    struct { int32 low, high; } counter;

    // Use RDTSC instruction to get clocks count
    __asm push EAX
    __asm push EDX
    __asm __emit 0fh __asm __emit 031h // RDTSC
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX    // Return result
    return *(__int64 *)(&counter);}

这个函数还具有非常快的优点-它通常不需要超过50个CPU周期来执行。

使用计时图:
如果您需要将时钟计数转换为真正经过的时间,请将结果除以芯片的时钟速度。记住,“额定”GHz很可能与你的芯片的实际速度略有不同。要检查芯片的真实速度,可以使用几个非常好的实用程序或Win 32调用QueryPerformanceFrequy()。


查看完整回答
反对 回复 2019-06-25
  • 3 回答
  • 0 关注
  • 740 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信