VC中如何获取当前时间(精度达到毫秒级) 如何在VC里获得当前时间戳的精确值

VC++\u7f16\u7a0b\u4e2d \u5982\u4f55\u83b7\u53d6\u5f53\u524d\u65f6\u95f4(\u7cbe\u786e\u5230\u6beb\u79d2)

\u3000\u30001\u3001\u76f4\u63a5\u5229\u7528Pentium CPU\u5185\u90e8\u65f6\u95f4\u6233\u8fdb\u884c\u8ba1\u65f6\u7684\u9ad8\u7cbe\u5ea6\u8ba1\u65f6\u624b\u6bb5\u3002
\u3000\u30002\u3001\u5728 Intel Pentium\u4ee5\u4e0a\u7ea7\u522b\u7684CPU\u4e2d\uff0c\u6709\u4e00\u4e2a\u79f0\u4e3a\u201c\u65f6\u95f4\u6233\uff08Time Stamp\uff09\u201d\u7684\u90e8\u4ef6\uff0c\u5b83\u4ee564\u4f4d\u65e0\u7b26\u53f7\u6574\u578b\u6570\u7684\u683c\u5f0f\uff0c\u8bb0\u5f55\u4e86\u81eaCPU\u4e0a\u7535\u4ee5\u6765\u6240\u7ecf\u8fc7\u7684\u65f6\u949f\u5468\u671f\u6570\u3002\u7531\u4e8e\u76ee\u524d\u7684CPU\u4e3b\u9891\u90fd\u975e\u5e38\u9ad8\uff0c\u56e0\u6b64\u8fd9\u4e2a\u90e8\u4ef6\u53ef\u4ee5\u8fbe\u5230\u7eb3\u79d2\u7ea7\u7684\u8ba1\u65f6\u7cbe\u5ea6\u3002
\u3000\u30003\u3001\u56e0\u4e3aRDTSC\u4e0d\u88abC++\u7684\u5185\u5d4c\u6c47\u7f16\u5668\u76f4\u63a5\u652f\u6301\uff0c\u6240\u4ee5\u8981\u7528_emit\u4f2a\u6307\u4ee4\u76f4\u63a5\u5d4c\u5165\u8be5\u6307\u4ee4\u7684\u673a\u5668\u7801\u5f62\u5f0f0X0F\u30010X31\uff0c\u5982\u4e0b\uff1a
\u3000\u3000inline unsigned __int64 GetCycleCount()
{
\u3000\u3000__asm _emit 0x0F
\u3000\u3000__asm _emit 0x31
}
\u3000\u30004\u3001\u5728\u9700\u8981\u8ba1\u6570\u5668\u7684\u573a\u5408\uff0c\u53ef\u4ee5\u50cf\u4f7f\u7528\u666e\u901a\u7684Win32 API\u4e00\u6837\uff0c\u8c03\u7528\u4e24\u6b21GetCycleCount\u51fd\u6570\uff0c\u6bd4\u8f83\u4e24\u4e2a\u8fd4\u56de\u503c\u7684\u5dee\uff0c\u50cf\u8fd9\u6837\uff1a
\u3000\u3000unsigned long t;
t = (unsigned long)GetCycleCount();
//Do Something time-intensive ...
t -= (unsigned long)GetCycleCount();

\u30001\u3001\u76f4\u63a5\u5229\u7528Pentium CPU\u5185\u90e8\u65f6\u95f4\u6233\u8fdb\u884c\u8ba1\u65f6\u7684\u9ad8\u7cbe\u5ea6\u8ba1\u65f6\u624b\u6bb5\u3002
\u3000\u30002\u3001\u5728 Intel Pentium\u4ee5\u4e0a\u7ea7\u522b\u7684CPU\u4e2d\uff0c\u6709\u4e00\u4e2a\u79f0\u4e3a\u201c\u65f6\u95f4\u6233\uff08Time Stamp\uff09\u201d\u7684\u90e8\u4ef6\uff0c\u5b83\u4ee564\u4f4d\u65e0\u7b26\u53f7\u6574\u578b\u6570\u7684\u683c\u5f0f\uff0c\u8bb0\u5f55\u4e86\u81eaCPU\u4e0a\u7535\u4ee5\u6765\u6240\u7ecf\u8fc7\u7684\u65f6\u949f\u5468\u671f\u6570\u3002\u7531\u4e8e\u76ee\u524d\u7684CPU\u4e3b\u9891\u90fd\u975e\u5e38\u9ad8\uff0c\u56e0\u6b64\u8fd9\u4e2a\u90e8\u4ef6\u53ef\u4ee5\u8fbe\u5230\u7eb3\u79d2\u7ea7\u7684\u8ba1\u65f6\u7cbe\u5ea6\u3002
\u3000\u30003\u3001\u56e0\u4e3aRDTSC\u4e0d\u88abC++\u7684\u5185\u5d4c\u6c47\u7f16\u5668\u76f4\u63a5\u652f\u6301\uff0c\u6240\u4ee5\u8981\u7528_emit\u4f2a\u6307\u4ee4\u76f4\u63a5\u5d4c\u5165\u8be5\u6307\u4ee4\u7684\u673a\u5668\u7801\u5f62\u5f0f0X0F\u30010X31\uff0c\u5982\u4e0b\uff1a
\u3000\u3000inline unsigned __int64 GetCycleCount()
{
\u3000\u3000__asm _emit 0x0F
\u3000\u3000__asm _emit 0x31
}
\u3000\u30004\u3001\u5728\u9700\u8981\u8ba1\u6570\u5668\u7684\u573a\u5408\uff0c\u53ef\u4ee5\u50cf\u4f7f\u7528\u666e\u901a\u7684Win32 API\u4e00\u6837\uff0c\u8c03\u7528\u4e24\u6b21GetCycleCount\u51fd\u6570\uff0c\u6bd4\u8f83\u4e24\u4e2a\u8fd4\u56de\u503c\u7684\u5dee\uff0c\u50cf\u8fd9\u6837\uff1a
\u3000\u3000unsigned long t;
t = (unsigned long)GetCycleCount();
//Do Something time-intensive ...
t -= (unsigned long)GetCycleCount();

对关注性能的程序开发人员而言,一个好的计时部件既是益友,也是良师。计时器既可以作为程序组件帮助程序员精确的控制程序进程,又是一件有力的调试武器,在有经验的程序员手里可以尽快的确定程序的性能瓶颈,或者对不同的算法作出有说服力的性能比较。

在Windows平台下,常用的计时器有两种,一种是timeGetTime多媒体计时器,它可以提供毫秒级的计时。但这个精度对很多应用场合而言还是太粗糙了。另一种是QueryPerformanceCount计数器,随系统的不同可以提供微秒级的计数。对于实时图形处理、多媒体数据流处理、或者实时系统构造的程序员,善用QueryPerformanceCount/QueryPerformanceFrequency是一项基本功。

本文要介绍的,是另一种直接利用Pentium CPU内部时间戳进行计时的高精度计时手段。以下讨论主要得益于《Windows图形编程》一书,第 15页-17页,有兴趣的读者可以直接参考该书。关于RDTSC指令的详细讨论,可以参考Intel产品手册。本文仅仅作抛砖之用。
在 Intel Pentium以上级别的CPU中,有一个称为“时间戳(Time Stamp)”的部件,它以64位无符号整型数的格式,记录了自CPU上电以来所经过的时钟周期数。由于目前的CPU主频都非常高,因此这个部件可以达到纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。

在Pentium以上的CPU中,提供了一条机器指令RDTSC(Read Time Stamp Counter)来读取这个时间戳的数字,并将其保存在EDX:EAX寄存器对中。由于EDX:EAX寄存器对恰好是Win32平台下C++语言保存函数返回值的寄存器,所以我们可以把这条指令看成是一个普通的函数调用。像这样:

inline unsigned __int64 GetCycleCount()
{
__asm RDTSC
}

但是不行,因为RDTSC不被C++的内嵌汇编器直接支持,所以我们要用_emit伪指令直接嵌入该指令的机器码形式0X0F、0X31,如下:

inline unsigned __int64 GetCycleCount()
{
__asm _emit 0x0F
__asm _emit 0x31
}

以后在需要计数器的场合,可以像使用普通的Win32 API一样,调用两次GetCycleCount函数,比较两个返回值的差,像这样:

unsigned long t;
t = (unsigned long)GetCycleCount();
//Do Something time-intensive ...
t -= (unsigned long)GetCycleCount();

《Windows图形编程》第15页编写了一个类,把这个计数器封装起来。有兴趣的读者可以去参考那个类的代码。作者为了更精确的定时,做了一点小小的改进,把执行RDTSC指令的时间,通过连续两次调用GetCycleCount函数计算出来并保存了起来,以后每次计时结束后,都从实际得到的计数中减掉这一小段时间,以得到更准确的计时数字。但我个人觉得这一点点改进意义不大。在我的机器上实测,这条指令大概花掉了几十到100多个周期,在 Celeron 800MHz的机器上,这不过是十分之一微秒的时间。对大多数应用来说,这点时间完全可以忽略不计;而对那些确实要精确到纳秒数量级的应用来说,这个补偿也过于粗糙了。

这个方法的优点是:

1.高精度。可以直接达到纳秒级的计时精度(在1GHz的CPU上每个时钟周期就是一纳秒),这是其他计时方法所难以企及的。

2. 成本低。timeGetTime 函数需要链接多媒体库winmm.lib,QueryPerformance* 函数根据MSDN的说明,需要硬件的支持(虽然我还没有见过不支持的机器)和KERNEL库的支持,所以二者都只能在Windows平台下使用(关于DOS平台下的高精度计时问题,可以参考《图形程序开发人员指南》,里面有关于控制定时器8253的详细说明)。但RDTSC指令是一条CPU指令,凡是i386平台下Pentium以上的机器均支持,甚至没有平台的限制(我相信i386版本UNIX和Linux下这个方法同样适用,但没有条件试验),而且函数调用的开销是最小的。

3. 具有和CPU主频直接对应的速率关系。一个计数相当于1/(CPU主频Hz数)秒,这样只要知道了CPU的主频,可以直接计算出时间。这和 QueryPerformanceCount不同,后者需要通过QueryPerformanceFrequency获取当前计数器每秒的计数次数才能换算成时间。

这个方法的缺点是:

1.现有的C/C++编译器多数不直接支持使用RDTSC指令,需要用直接嵌入机器码的方式编程,比较麻烦。

2.数据抖动比较厉害。其实对任何计量手段而言,精度和稳定性永远是一对矛盾。如果用低精度的timeGetTime来计时,基本上每次计时的结果都是相同的;而RDTSC指令每次结果都不一样,经常有几百甚至上千的差距。这是这种方法高精度本身固有的矛盾。

关于这个方法计时的最大长度,我们可以简单的用下列公式计算:

自CPU上电以来的秒数 = RDTSC读出的周期数 / CPU主频速率(Hz)

64位无符号整数所能表达的最大数字是1.8×10^19,在我的Celeron 800上可以计时大约700年(书中说可以在200MHz的Pentium上计时117年,这个数字不知道是怎么得出来的,与我的计算有出入)。无论如何,我们大可不必关心溢出的问题。

下面是几个小例子,简要比较了三种计时方法的用法与精度

//Timer1.cpp 使用了RDTSC指令的Timer类//KTimer类的定义可以参见《Windows图形编程》P15
//编译行:CL Timer1.cpp /link USER32.lib
#include <stdio.h>
#include "KTimer.h"
main()
{
unsigned t;
KTimer timer;
timer.Start();
Sleep(1000);
t = timer.Stop();
printf("Lasting Time: %d\n",t);
}

//Timer2.cpp 使用了timeGetTime函数
//需包含<mmsys.h>,但由于Windows头文件错综复杂的关系
//简单包含<windows.h>比较偷懒:)
//编译行:CL timer2.cpp /link winmm.lib
#include <windows.h>
#include <stdio.h>

main()
{
DWORD t1, t2;
t1 = timeGetTime();
Sleep(1000);
t2 = timeGetTime();
printf("Begin Time: %u\n", t1);
printf("End Time: %u\n", t2);
printf("Lasting Time: %u\n",(t2-t1));
}

//Timer3.cpp 使用了QueryPerformanceCounter函数
//编译行:CL timer3.cpp /link KERNEl32.lib
#include <windows.h>
#include <stdio.h>

main()
{
LARGE_INTEGER t1, t2, tc;
QueryPerformanceFrequency(&tc);
printf("Frequency: %u\n", tc.QuadPart);
QueryPerformanceCounter(&t1);
Sleep(1000);
QueryPerformanceCounter(&t2);
printf("Begin Time: %u\n", t1.QuadPart);
printf("End Time: %u\n", t2.QuadPart);
printf("Lasting Time: %u\n",( t2.QuadPart- t1.QuadPart));
}

////////////////////////////////////////////////
//以上三个示例程序都是测试1秒钟休眠所耗费的时间
file://测/试环境:Celeron 800MHz / 256M SDRAM
// Windows 2000 Professional SP2
// Microsoft Visual C++ 6.0 SP5
////////////////////////////////////////////////

以下是Timer1的运行结果,使用的是高精度的RDTSC指令
Lasting Time: 804586872

以下是Timer2的运行结果,使用的是最粗糙的timeGetTime API
Begin Time: 20254254
End Time: 20255255
Lasting Time: 1001

以下是Timer3的运行结果,使用的是QueryPerformanceCount API
Frequency: 3579545
Begin Time: 3804729124
End Time: 3808298836
Lasting Time: 3569712

古人说,触类旁通。从一本介绍图形编程的书上得到一个如此有用的实时处理知识,我感到非常高兴。有美不敢自专,希望大家和我一样喜欢这个轻便有效的计时器。

::GetTickCount();
精度达到毫秒级
获得系统开机时间

  • c璇█涓浣曡幏鍙栧綋鍓鐨鏃堕棿?
    绛旓細璇蜂娇鐢╰ime(0)锛宼ime(1)鍙兘鍑洪敊銆備互涓嬪唴瀹规潵鑷綉缁 --- time鏄繖鏍峰0鏄庣殑锛歵ime_t time( time_t *timer );鐢ㄦ硶鏄綘鍏堣嚜宸卞畾涔変竴涓猼ime_t鍙橀噺锛岃鍚庢妸鍙橀噺鐨勫湴鍧浼犵粰瀹冦傚嚱鏁颁細杩斿洖鑷1970骞1鏈1鏃0鐐硅蛋杩囩殑绉掓暟锛屽悓鏃舵妸杩欎釜杩斿洖鍊间繚瀛樺湪浣犱紶杩涙潵鐨勯偅涓猼ime_t*鎸囧悜鐨勫彉閲忛噷闈傚鏋滀綘浼犺繘鏉U...
  • 璇锋暀VC++楂樻墜 濡備綍杈撳嚭楂绮惧害鏃堕棿宸
    绛旓細楂樼簿搴︽椂鎺у嚱鏁 瀵逛簬涓鑸殑瀹炴椂鎺у埗锛屼娇鐢℅etTickCount()鍑芥暟灏卞彲浠ユ弧瓒崇簿搴﹁姹傦紝浣嗚杩涗竴姝ユ彁楂樿鏃剁簿搴︼紝灏辫閲囩敤QueryPerformanceFrequency()鍑芥暟鍜孮ueryPerformanceCounter()鍑芥暟銆傝繖涓や釜鍑芥暟鏄VC鎻愪緵鐨勪粎渚沇indows 9X浣跨敤鐨勯珮绮惧害鏃堕棿鍑芥暟锛屽苟瑕佹眰璁$畻鏈轰粠纭欢涓婃敮鎸侀珮绮惧害璁℃椂鍣ㄣ俀ueryPerformanceFrequency()鍑芥暟鍜...
  • 鍦╒C涓浣浣跨敤绮剧‘瀹氭椂鍣
    绛旓細VC涓鍙互鍒╃敤GetTickCount()鍑芥暟锛岃鍑芥暟鐨勮繑鍥炲兼槸 DWORD鍨嬶紝琛ㄧず浠s涓哄崟浣嶇殑璁$畻鏈哄惎鍔ㄥ悗缁忓巻鐨鏃堕棿闂撮殧銆绮惧害姣擶M_TIMER娑堟伅鏄犲皠楂橈紝鍦ㄨ緝 鐭殑瀹氭椂涓叾璁℃椂璇樊涓15ms锛屽湪杈冮暱鐨勫畾鏃朵腑鍏惰鏃惰宸緝浣庯紝濡傛灉瀹氭椂鏃堕棿澶暱锛屽氨濂借薄姝绘満涓鏍凤紝CPU鍗犵敤鐜囬潪甯搁珮锛屽彧鑳界敤浜庤姹備笉楂樼殑寤舵椂绋嬪簭涓
  • C璇█绋嬪簭杩愯鏃堕棿娴嬭瘯
    绛旓細C璇█鑾峰彇绯荤粺鏃堕棿鐨勫嚑绉嶆柟寮 C璇█涓浣曡幏鍙栨椂闂锛绮惧害濡備綍锛1 浣跨敤time_t time( time_t * timer ) 绮剧‘鍒扮 2 浣跨敤clock_t clock() 寰楀埌鐨勬槸CPU鏃堕棿 绮剧‘鍒1/CLOCKS_PER_SEC绉 3 璁$畻鏃堕棿宸娇鐢╠ouble difftime( time_t timer1, time_t timer0 )4 浣跨敤DWORD GetTickCount() 绮剧‘鍒版绉 ...
  • MFC涓浣曡幏鍙栨椂闂 鐒跺悗杈撳嚭 璇寸殑璇︾粏鐐 璋㈣阿
    绛旓細vc 鑾峰彇褰撳墠鏃堕棿 浣跨敤CTime绫 CTime time = CTime::GetCurrentTime();int m_nYear = time.GetYear(); ///骞 int m_nMonth = time.GetMonth(); ///鏈 int m_nDay = time.GetDay(); ///鏃 int m_nHour = time.GetHour(); ///灏忔椂 int m_nMinute = time.GetMinute...
  • 濡備綍鑾峰彇timestamp绫诲瀷鐨绮惧害鍒扮撼绉掔骇
    绛旓細鑾峰彇鏃堕棿绔狅紝寮濮嬶紝鍋滐紝娴婥PU閫熷害 鐨 鍑芥暟浠涓嬨傦紙MS VC++ 6.0 缂栬瘧鍣ㄣ傦級define NOMINMAX include <windows.h> include <stdio.h> include include <math.h> // "Read Time Stamp Counter".__forceinline unsigned _int64 My_clock(void){ _asm _emit 0x0F _asm _emit 0x31 ...
  • vc++涓浣娴嬭瘯绋嬪簭鐨勮繍琛鏃堕棿
    绛旓細鍩烘湰鐨勫師鐞嗘槸鍦ㄦ墽琛屽緟娴嬪嚱鏁颁箣鍓嶈鍙朇PU鐨勮鏁板櫒锛屾墽琛屽緟娴嬪嚱鏁颁箣鍚庯紝鍐嶈鍙栦竴閬嶏紝涓や釜璁℃暟鍣ㄤ箣宸紝鍐嶉櫎浠PU涓婚锛屽氨鏄緟娴嬪嚱鏁版墽琛屾墍鐢鏃堕棿銆傛祴璇绮惧害涓嶤PU涓婚鏈夊叧锛屼竴鑸彲浠ョ簿纭埌寰閲忕骇銆傝鍙朇PU璁℃暟鍣ㄧ殑鍑芥暟锛歈ueryPerformanceCounter()鑾峰彇CPU璁℃暟鍣ㄧ殑鍑芥暟锛歈ueryPerformanceFrequency()鍏蜂綋鎿嶄綔绀鸿寖锛...
  • 姹傛暀~!labvIEW 涓浣璁板綍褰撳墠鏃堕棿
    绛旓細閫氳繃 鑾峰彇褰撳墠鏃堕棿 vi鏉ヨ幏鍙栧綋鍓嶆椂闂 鍒╃敤浜嬩欢缁撴瀯 鎸変笅鏌愪釜鎸夐挳鏃 姝ゅ嚱鏁板皢鎸変笅鎸夐挳鐨勬椂闂翠俊鎭緭鍏ヤ竴鏄剧ず鎺т欢 鎶婃鏄剧ず鎺т欢鐨勬暟鎹煎湪闇瑕佷娇鐢ㄧ殑鏃跺欒皟鍑烘潵涓嶅氨鍙互浜嗐傘傘備笉闇瑕佹庝箞璁惧畾 绋嶅井鍔犱竴鐐规鍥惧氨瀹屼簡銆傘
  • C++ VC /MFC 闅忎究鐢ㄤ竴绉嶅疄鐜鏃堕棿鐩稿姞
    绛旓細CTimeSpan delta(0, 0, 2600, 0); //璁剧疆2600鍒嗛挓鐨勬椂闂撮棿闅 CTime now = CTime::GetCurrentTime(); //鑾峰彇褰撳墠鏃堕棿 now += delta; //鍔犱笂2600鍒嗛挓鐨勬椂闂撮棿闅 CString strTime; //杞崲涓篊String锛 娉ㄦ剰CTime::Format鍦ㄦ湁浜涙椂鍊欎笉鏀寔涓枃锛岄【杩欑鏂规硶姣旇緝淇濋櫓 strTime.Format(_...
  • C璇█涓璽ime.h澶存枃浠朵腑瀵鏃堕棿鐨勬搷浣滃叿浣撴槸鎬庢牱鐨?
    绛旓細time.h澶存枃浠舵彁渚涘鏃堕棿鎿嶄綔鐨勪竴浜涘嚱鏁帮紝clock()鏄▼搴忓紑濮嬪埌璋冪敤鐨勬绉掓暟銆倀ime_tt_begin,t_end;t_begin=clock();//璁板綍寮濮嬫椂闂 dosomething();//璋冪敤鍑芥暟 t_end=clock();//璁板綍缁撴潫鏃堕棿 printf("Timeused=%.21f\n",(double)(t_end-t_begin)/CLOCKS_PER_SEC);//鏄剧ず鍑芥暟璋冪敤鏃堕棿 ...
  • 扩展阅读:为什么vue3不推荐用vuex了 ... vs获取系统时间 ... vc服用时间 ... 10秒的延时需要拍多久 ... vue获取当前时间以及秒 ... 延时摄影设置多少间隔 ... 维c维b最佳服用时间 ... vb取当前系统时间 ... vc吃多长时间有效果 ...

    本站交流只代表网友个人观点,与本站立场无关
    欢迎反馈与建议,请联系电邮
    2024© 车视网