关于计时函数

本文详细介绍了在C/C++编程中常用的计时函数,包括time(), clock(), GetTickCount(), QueryPerformanceCounter(), OpenCV中的cvGetTickCount(), 以及C++11中std::chrono模块的计时函数。对比了不同函数的精度、适用场景和潜在限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在用C/C++写一些算法时,经常要分析程序或函数的性能,不可避免地要使用到计时函数。尤其是在做图像处理时,更是要细致到每个函数到底占用了多少时间,以此来作为进一步提高程序效率的依据。

在这儿大致总结一下用过的计时函数。

 

一、最常使用的time()和clock()

1、time()函数,头文件<time.h>,函数原型:time_t time(time_t * _Time);  time_t在VC6.0中定义为long型,在Visual Studio 2010中定义为__int64型,即64位整型(可能是为了防止到2038年1月18日19时14分07秒后清零的情况,64位整型可以表示到3001年1月 1日0时0分0秒(不包括该时间点)之前的时间)。参数为接收该函数返回值的变量的地址,返回值为从1970年1月1日0时0分0秒到当前系统时间所经过的数。

注:用C语言的方式输入输出时,64位整型变量的标识符为%I64d或%I64u(无符号)。

2、clock()函数,头文件也是<time.h>,函数原型:clock_t  clock(void);  clock_t定义为long型(VC和VS2010中都是)。返回值为该进程运行的滴答数。在<time.h>中定义了常量CLOCKS_PER_SEC表示每秒嘀嗒数,Windows下一般是1000,LINUX下一般是1000000。即Windows下clokc()的返回值可当作毫秒用,LINUX下可当作微秒用。(在LINUX下多线程时clock函数不稳定,可能是LINUX下线程实现机制的缘故)。

 

二、WindowsAPI中提供的GetTickCount()函数

GetTickCount()函数,定义在<WinBase.h>中,但需要包含<Windows.h>,否则会报错。没深入了解过Windows编程,故其具体原因不详。

函数原型:DWORD GetTickCount(void); DWORD定义为为long型。该函数返回从操作系统启动所经过的毫秒数。

注1:该函数并非实时发送,而是由系统每18ms发送一次,因此其最小精度为18ms。(VC6.0测试,在15-18毫秒之间,某篇论文上写到MSDN上关于该函数的时间精度说明为10-16毫秒之间);

注2:该函数返回值以32位的双字类型DWORD存储,因此可以存储的最大值是2^32 ms约为49.71天,因此若系统运行时间超过49.71天时,这个数就会归0。在使用时需特别注意。尤其是在编写服务端程序时。

 

三、WindowsAPI中提供的QueryPerformanceCounter()和QueryPerformanceFrequency()函数

1、 QueryPerformanceCounter()函数,定义在<WinBase.h>中,需包含头文件<Windows.h>。

函数原型:BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount );

返回值非零表示硬件支持高精度计数器,零表示不支持。

唯一的参数是被写入的LARGE_INTEGER变量的地址。LARGE_INTEGER是一个联合体,一般可直接引用其QuadPart成员即可,QuadPart定义为LONGLONG,即__int64型,64位整型变量。

该函数(如果硬件支持的话)将硬件的高精度计时器的值写入lpPerformanceCount指向的变量。在某事件前后两次调用该函数,计算差值即可得到计时器间隔。

2、QueryPerformanceFrequency()函数,定义在<WinBase.h>中,需包含头文件<Windows.h>。

函数原型:BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);

返回值同上,非零表示硬件支持高精度计数器,零表示不支持。

该函数将硬件支持的高精度计数器的频率写入lpFrequency指向的变量。

将两次调用QueryPerformanceCounter()函数的差值去除以频率,即可得到以秒为单位的时间间隔。

 

四、在使用OpenCV时,OpenCV中提供了两个用于计算时间的函数

1、cvGetTickCount()函数,定义在<core_c.h>中,一般包含<opencv.hpp>即可。

函数原型:int64 cvGetTickCount( void );

返回值为从依赖于平台的事件(从启动开始 CPU 的ticks 数目, 从1970年开始的微秒数目等等)开始的 tics 的数目。

2、cvGetTickFrequency()函数,定义在<core_c.h>中,一般包含<opencv.hpp>即可。

函数原型:double cvGetTickFrequency( void );

返回值为每个微秒的 tics 的数目

结合使用cvGetTickCount()与cvGetTickFrequency()即可得到某事件消耗的时间。

 

五、C++11 中 std::chrono 模块中提供的计时函数

1、头文件 #include <chrono>

2、std::chrono::steady_clock::now(),std::chrono::system_clock::now(),std::chrono::high_resolution_clock::now(),返回 CPU 的 tick 数。第三个 high_resolution 一般会被 typedef 为前两者中的一个。第一个时钟可以保证单调,不会由于多核操作导致第二次调用的返回值比第一次小;第二个时钟是系统级的时钟,无论哪个进程在哪个核上调用,返回的都是同样的时间,但这个时间不保证是 steady 的,在夏令时/冬令时切换时,会出现非 steady 的情况。因此一般用 std::chrono::steady_clock 即可。参见:https://stackoverflow.com/questions/31552193/difference-between-steady-clock-vs-system-clock?lq=1

3、用法:

auto t_beg = std::chrono::steady_clock::now();
// do something...
auto t_end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_beg);
std::cout << (static_cast<double>(duration.count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den) << " s" << std::endl;

 

 

附测试代码:

 

// TimeTest.cpp : 定义控制台应用程序的入口点。
//
#include <stdio.h>
#include <time.h>
#include <Windows.h>
#include <opencv.hpp>

int main(int argc, char* argv[])
{
	// clock()测试
	clock_t c0 = clock();
	Sleep(1000);
	printf("clock: %ldms\n", clock() - c0);

	// GetTickCount()测试
	DWORD d0 = GetTickCount();
	Sleep(1000);
	printf("GetTickCount: %ldms\n", GetTickCount() - d0);

	// cvGetTickCount()测试
	int64 cv0 = cvGetTickCount();
	Sleep(1000);
	printf("cvGetTickCount: %gus\t", (cvGetTickCount() - cv0) / cvGetTickFrequency());
	printf("cvGetTickFrequency: %g tics/us\n", cvGetTickFrequency());

	// QueryPerformance()测试
	LARGE_INTEGER largeFrequency;
	if (0 == QueryPerformanceFrequency(&largeFrequency)) {
		printf("硬件不支持高精度时钟!\n");
	} else {
		LARGE_INTEGER largeTime0, largeTime1;
		QueryPerformanceCounter(&largeTime0);
		Sleep(1000);
		QueryPerformanceCounter(&largeTime1);
		printf("QueryPerformanceCounter: %I64ums\t", (largeTime1.QuadPart - largeTime0.QuadPart) * 1000 / largeFrequency.QuadPart);
		printf("QueryPerformanceFrequency: %I64u tics/s\n", largeFrequency.QuadPart);
	}

	return 0;
}

 

 

 

总结:

一般计时用time()函数即可,两次time(NULL)的返回值相减就是间隔的秒数,同时time_t的数据类型也方便用localtime()函数去转换成各种各样的格式输出;32位情况下,time_t大约可以计时到2106年。

若要计算程序运行时间,可以用clock()函数也很方便,可以得到毫秒数(LINUX下可能是微秒数);32位情况下,若返回毫秒数,clock()函数可以计时49天。

同样,WindowsAPI中提供的函数QueryPerformanceFrequency()也可以返回毫秒数,计时49天。但这个函数不一定能用,需要先调用QueryPerformanceCounter()进行测试。所以感觉比较鸡肋。可能提供的精度要比clock()好一些。没测试过。

在用OpenCV编程时,用OpenCV的两个函数获得微秒级的时间间隔应该能满足要求;由于返回值是64位的整型,故可以计时585942年,这个一般情况下不会溢出。

若要更精确的控制时间,用QueryPerformanceCounter()函数可以算是最高精度的了。用两次得到的值作差,除以QueryPerformanceFrequency()两个Windows提供的函数,得到的是秒数,把被除数乘以1000可得到毫秒,再乘1000可得到微秒。因为返回的是double类型,所以精度基本可以得到保证。

 

另见:高性能计算下的精确时间处理问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值