C++,定时器,毫秒级

4
#include <iostream>
#include <conio.h>
#include <ctime>



using namespace std;

double diffclock(clock_t clock1,clock_t clock2)
{
    double diffticks=clock1-clock2;
    double diffms=(diffticks)/(CLOCKS_PER_SEC/1000);
    return diffms;
}
int main()
{
    clock_t start = clock();
    for(int i=0;;i++)
    {

    if(i==10000)break;
    }
    clock_t end = clock();

    cout << diffclock(start,end)<<endl;

    getch();
return 0;
}

我的问题是它返回了一个0,实际上我想检查我的程序运行了多长时间... 我在互联网上找到了很多垃圾,大多数都会得到0,因为开始和结束是相同的。

这个问题涉及到C++,请注意:<


5
您的优化器是否将您的 for 循环排除在外,因为它什么也没有做?也许可以添加一个全局变量的赋值操作。此外,即使它可以正常工作,您的起始和结束时间顺序相反,导致时间为负数。 - Michael Dorgan
1
即使循环没有被优化,计数到10000很可能少于1毫秒。现代计算机速度非常快。 - Mike Seymour
为什么你要这么做?如果你想进行基准测试,循环可能不是一个好的测试。 - Richard
代码在 startend 之间执行所需的时间为 0 - Drew Dormann
1
你的系统中 CLOCKS_PER_SEC 的值可能会导致整数除法问题 - 将 double diffms=(diffticks)/(CLOCKS_PER_SEC/1000) 更改为 double diffms=diffticks/(CLOCKS_PER_SEC/1000.0) - Paul R
显示剩余2条评论
5个回答

13
这里有几个问题。第一个问题是,当传递给 diffclock() 函数时,你显然交换了开始和停止时间。第二个问题是优化。启用优化的任何合理智能编译器都会将整个循环丢弃,因为它没有任何副作用。但即使你解决了以上问题,该程序最可能仍将打印 0。如果你想象每秒进行数十亿次操作,使用先进的乱序执行、预测和现代 CPU 使用的大量其他技术,甚至 CPU 也可以优化掉你的循环。但即使它没有,你需要远远超过 10K 次迭代才能让程序运行更长时间。你可能需要让程序运行一两秒钟才能使 clock() 反映出任何东西。
但最重要的问题是 clock() 本身。该函数不适用于任何性能测量时间。它所做的是给出程序使用的处理器时间的近似值。除了可能由任何给定实现使用的近似方法的模糊性质(因为标准不要求任何特定的东西),POSIX标准还要求 CLOCKS_PER_SEC 等于 1000000,独立于实际分辨率。换句话说——时钟的精度如何并不重要,您的CPU运行频率也不重要。简而言之——这是一个完全无用的数字,因此是一个完全无用的函数。它仍然存在的唯一原因可能是出于历史原因。因此,请不要使用它。
为了实现您要寻找的内容,人们已经开始使用CPU时间戳,也被称为“RDTSC”,以相应CPU指令的名称来读取它。然而,这些天这也大多是无用的,因为:
以下是翻译的结果,按照要求仅返回翻译后的内容:
  1. 现代操作系统可以轻松地将程序从一个CPU迁移到另一个CPU。您可以想象,在另一个CPU上运行一秒钟后读取时间戳并没有多少意义。只有在最新的Intel CPU中,计数器跨CPU核心进行同步。总的来说,这仍然是可能的,但必须采取很多额外的注意(例如,可以为进程设置关联等等)。
  2. 测量程序的CPU指令通常不能准确反映它实际使用的时间。这是因为在真实的程序中,可能存在一些系统调用,其中工作由操作系统内核代表进程执行。在这种情况下,该时间不包括在内。
  3. 还可能发生操作系统暂停进程执行的情况。即使只执行了几条指令,对用户来说也像是一秒钟。因此,这种性能测量可能是无用的。

那么应该怎么做呢?

当涉及到性能分析时,必须使用像perf这样的工具。它可以跟踪许多CPU时钟、缓存未命中、已经执行的分支数量、未执行的分支数量、进程从一个CPU移动到另一个CPU的次数等等。它可以作为一个工具使用,也可以嵌入到你的应用程序中(类似于PAPI)。
如果问题是关于实际花费的时间,人们会使用挂钟。最好是高精度的,也不受NTP调整的影响(单调)。它准确地显示了经过的时间,无论发生了什么。为此,可以使用clock_gettime()。它是SUSv2、POSIX.1-2001标准的一部分。鉴于您使用了getch()来保持终端打开状态,我假设您正在使用Windows。在那里,不幸的是,您没有clock_gettime(),最接近的东西将是性能计数器API:
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);

对于一个便携式的解决方案,最好的选择是std::chrono::high_resolution_clock()。它在C++11中被引入,但大多数工业级编译器(GCC、Clang、MSVC)都支持它。

下面是一个如何使用它的例子。请注意,由于我知道我的CPU会比一毫秒快地执行10000次整数递增,所以我已经将其更改为微秒。我还声明了计数器为volatile,希望编译器不会将其优化掉。

#include <ctime>
#include <chrono>
#include <iostream>

int main()
{
    volatile int i = 0; // "volatile" is to ask compiler not to optimize the loop away.
    auto start = std::chrono::steady_clock::now();
    while (i < 10000) {
        ++i;
    }
    auto end = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "It took me " << elapsed.count() << " microseconds." << std::endl;
}

当我编译并运行它时,它会打印出:
$ g++ -std=c++11 -Wall -o test ./test.cpp && ./test
It took me 23 microseconds.

希望能帮到你。祝好运!


5

乍一看,似乎是在从较大的值中减去较小的值。调用:

diffclock( start, end );

但是diffclock被定义为:
    double diffclock( clock_t clock1, clock_t clock2 ) {

        double diffticks = clock1 - clock2;
        double diffms    = diffticks / ( CLOCKS_PER_SEC / 1000 );

        return diffms;
    }

除此之外,这可能与您转换单位的方式有关。在这个页面上,使用1000来转换为毫秒的方法不同:http://en.cppreference.com/w/cpp/chrono/c/clock

好的,伙计,它从你给的参考资料中起作用了,虽然我尝试了几次它没有工作,现在我复制了一切......然后,奇迹发生了!非常感谢。 顺便说一下,其他人 关于-1的事情...我不知道你们在哪里读到的...但如果你听一下自己...-1-1=0对你来说是吗? - Przmak
@user2137666 -1 - (-1) 对我来说等于0。你呢? - Slava
+1。另外,请考虑使用cppreference。cplus plus dot com是一个充满广告的商业网站。Cppreference是一个社区驱动的基于维基的参考资料,更新更及时,当然也没有广告。 - user405725

3
问题似乎在于循环太短了。我在我的系统上尝试了一下,它返回了0个滴答声。我检查了diffticks是什么,它是0。将循环大小增加到100000000,这样就有了明显的时间延迟,并且我得到了-290的输出(错误--我认为diffticks应该是clock2-clock1,所以我们应该得到290而不是-290)。我还尝试将“1000”更改为“1000.0”进行除法,但没有奏效。
使用优化编译会删除循环,因此您必须不使用它或使循环“做某些事情”,例如在循环体中递增计数器而不是循环计数器。至少这是GCC的做法。

2
注意:这仅适用于c++11及以上版本。
您可以使用std::chrono库。std::chrono有两个不同的对象(timepoint和duration)。Timepoint表示时间点,而duration则表示时间间隔或时间跨度。这个c++库允许我们减去两个时间点以获取经过的时间间隔。因此,您可以设置起始点和终止点。使用函数,您还可以将它们转换为适当的单位。
以下是一个使用high_resolution_clock(这是该库提供的三个时钟之一)的示例:
#include <chrono> 
using namespace std::chrono;

//运行函数之前

auto start = high_resolution_clock::now();

//调用函数后

auto stop = high_resolution_clock::now();

使用duration_cast()函数减去停止时间点和开始时间点,并将其转换为所需的单位。预定义的单位有纳秒、微秒、毫秒、秒、分钟和小时。

auto duration = duration_cast<microseconds>(stop - start);
cout << duration.count() << endl;

你如何将“duration”转换为可用的内容,比如说一个“unsigned long long”? - Kingsley

1
首先,您应该减去end-start而不是相反。
文档说如果值不可用,clock()会返回-1,您是否检查过了?
编译程序时使用了什么优化级别?如果启用优化,编译器可以有效地完全消除您的循环。

嗯...这不是一个大错误,最终你会得到相同的结果,尽管你是正确的,它应该被交换 :) - Przmak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接