在重负载情况下,C++中的clock()函数是否一致?

3

现在,我基本上有一个使用时钟测试程序执行某些操作所需时间的程序,通常准确到几毫秒。我的问题是:如果CPU负载很高,我仍然会得到相同的结果吗?

时钟只在CPU处理我的进程时计数吗?

假设:多核CPU但进程不利用多线程。


那个问题的答案可能取决于许多因素,包括BIOS设置(如超线程),所涉及的CPU是否是多核心的,所使用的代码是否利用了任何类型的线程,以及其他许多因素。请问您能否发布一些代码示例和/或有关正在运行的代码的更多详细信息? - Matt
1
在Windows中,“clock”本质上测量经过的时间,而在Linux和其他类Unix操作系统中,它测量使用了多少CPU时间。 - Mats Petersson
3个回答

4
clock函数的功能取决于操作系统。在Windows中,clock函数提供了经过时间,而在大多数其他操作系统(如Linux、MacOS和其他类Unix操作系统)中,它提供了CPU时间。
根据您实际想要实现的目标,经过时间或CPU时间可能是您想要测量的内容。
在有其他进程运行的系统中,经过时间和CPU使用率之间的差异可能很大(当然,如果您的CPU没有忙于运行应用程序,例如等待网络数据包从电线传输或来自硬盘的文件数据),则经过时间可“供”其他应用程序使用。
当同一系统中存在其他进程运行时,还存在许多误差因素/干扰因素:
如果我们假设您的操作系统支持clock作为CPU时间的度量,那么这里的精度并不总是那么高-例如,它可能会以CPU计时器滴答声的形式计算,并且如果进程正在执行I/O,则其进程可能不会运行“完整的滴答声”。 其他进程可能会在操作系统切换到“将此视为中断时间”之前使用“您”的CPU处理部分中断处理,在处理网络数据包或硬盘I/O等一定百分比的时间内[通常不是巨大的数量,但在非常繁忙的系统中,可以占总时间的几个百分点],如果其他进程在“您”的CPU上运行,则重新加载缓存以在其他进程加载其数据后加载“您”的进程数据所需的时间将计入“您的时间”。这种“干扰”可能会影响您的测量结果-影响程度取决于系统中正在发生的“其他事情”。 如果您的进程通过共享内存与另一个进程共享数据,则还将花费一些时间(再次,通常是微小的数量,但在极端情况下,可能会很显着)处理您的进程和其他进程之间的“高速缓存嗅探请求”,当您的进程无法执行时。 如果操作系统正在切换任务,则“一半”切换到/从您的任务所花费的时间将计入您的进程,而另一半则计入正在切换进/出的其他进程。同样,这通常是微小的数量,但如果您有一个非常繁忙的系统,并且有大量进程切换,则可能会累加。 某些处理器类型(例如Intel的HyperThreading)还与实际核心共享资源,因此该核心上仅有一部分时间用于执行您的进程,并且缓存内容与某些其他进程的数据和指令共享-这意味着您的进程可能会被同一CPU核心上运行的其他线程从高速缓存中“驱逐”出来。 同样,多核CPU通常具有共享的L3缓存,其受到在CPU的其他核心上运行的其他进程的影响。 文件缓存和其他“系统缓存”也会受到其他进程的影响-因此,如果您的进程正在读取某些文件,并且其他进程也访问文件,则缓存内容将比系统不那么繁忙时“不属于您”的更少。 要准确测量您的进程使用系统资源的程度,您需要处理器性能计数器(以及可重复的测试用例,因为您可能需要运行相同的设置多次以确保获得“正确”的性能计数器组合)。当然,大多数这些计数器也是系统范围的,并且例如中断和其他随机干扰中的某些处理将影响测量结果,因此如果您在系统中没有许多其他(繁忙)进程运行,则最准确的结果将是。
当然,在许多情况下,仅测量应用程序的总时间就足够了。只要你有一个可重现的测试用例,在特定场景下每次运行时都能给出相同(或至少相似)的计时结果。
每个应用程序都是不同的,每个系统也都是不同的。性能测量是一个非常庞大的主题,很难涵盖所有方面 - 当然,我们并不在这里回答关于“如何在同一系统中运行其他进程时使我的具有百万位小数的PI运行更快”等极其具体问题。

4

除了同意那些表示时间取决于许多因素的回答之外,我想向您介绍自C++11以来可用的std::chrono库:

#include <chrono>
#include <iostream>

int main() {
      auto beg = std::chrono::high_resolution_clock::now();
      std::cout << "*** Displaying Some Stuff ***" << std::endl;
      auto end = std::chrono::high_resolution_clock::now();
      auto dur = std::chrono::duration_cast<std::chrono::microseconds>(end - beg);
      std::cout << "Elapsed: " << dur.count() << " microseconds" << std::endl;
    }

根据标准,该程序将利用系统提供的最高精度时钟,并以微秒分辨率进行计时(还有其他可用的分辨率;请参阅文档)。
示例运行:
$ g++ example.cpp -std=c++14 -Wall -Wextra -O3
$ ./a.out
*** Displaying Some Stuff ***
Elapsed: 29 microseconds

虽然它比依赖于C风格的std::clock()更冗长,但我认为它提供了更多的表现力,而且你可以在一个漂亮的接口后面隐藏冗长(例如,看看我对之前帖子的回答,在那里我使用std::chrono来构建一个函数计时器)。

3
这将测量墙钟时间。在至少基于 Unix 的操作系统中,clock 将给出 CPU 使用时间 - 对于使用一个 CPU 100% 的应用程序,使用哪个函数都没有多大区别;对于具有多个线程的应用程序,在其中多个线程使用 100% 或使用少于 100% 的 CPU 时,可能会有(潜在的大)差异。 - Mats Petersson

2
CPU中有共享组件,例如最后一级缓存、执行单元(在一个核心内的硬件线程之间),因此在负载较重时会出现抖动,因为即使您的应用程序执行了完全相同数量的指令,每个指令可能需要更多的周期(等待内存,因为数据已从缓存中驱逐,可用的执行单元),而更多的周期意味着更长的执行时间(假设Turbo Boost不会补偿)。
如果您寻求精确的工具,请查看硬件计数器
在查看CPU密集型任务的计时指标时,还要考虑物理CPU上可用的核心数量、超线程以及其他BIOS设置(如Intel CPU上的Turbo Boost)和编码时使用的线程技术。
并行化工具(如OpenMP)提供了内置函数来计算计算和墙壁时间,例如omp_get_wtime( );,这些函数在使用此类型的并行化的程序中通常比clock()更准确。

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