高精度計時、延時器第二版

來源:互聯網
上載者:User

關於Windows下延時、計時的精度,我以前寫過一篇文字,給出了一種通過嵌入彙編代碼讀取CPU指令周期寄存器來達到毫秒級的延時、計時的方法。(見http://blog.csdn.net/qiqi5521/archive/2007/11/03/1864642.aspx文中分析了WindowsAPI的計時精度,闡述了指令周期計時法的原理,看本篇前建議先看這篇。不過當時代碼是作為一個VC++動態連結程式庫實現的,一直想把它改寫為一個C++類。今天終於動手了,起因源於wxWidgets論壇上有一個人在問這個問題,終於引起了我的興趣,於是克服了惰性,動了一把手。設計目標:在VC++和gcc下都能夠編譯。為了達到這個目標,首先要解決兩個問題:1、vc++和gcc對64位整形的聲明方法不一樣,vc++是__int64,gcc是long long 。2、VC++和gcc嵌入彙編的方法不一樣,gcc使用AT&T彙編,vc++使用intel彙編。為瞭解決這兩個問題,決定條件編譯,用 _MSC_VER 這個宏來判斷編譯環境是VC++還是gcc,如果有 _MSC_VER 宏,說明是VC++環境。

代碼如下:
asmdelay.h 
---------------------------------------------------- 
//精確定時類。//功能1:擷取CPU主頻(MHz)//功能2:精確延時(毫秒級)//功能3:精確計時(毫秒級)//編譯: VC++ / gcc//注意: VC++下編譯,需要進入當前工程的Settings,選中asmdelay.cpp,選擇C/C++選項卡,從Category組合框中選中Precompiled Headers,選擇Not Using Precompiled headers。
#ifndef __ASMDELAY_h__24687444848486484894589754#define __ASMDELAY_h__24687444848486484894589754
#ifdef _MSC_VER typedef unsigned __int64 __UINT64;#else typedef unsigned long long __UINT64;#endif
class CAsmDelay{private:    unsigned long m_Freq;                 //CPU主頻    __UINT64  m_TimeStamp;                //計時器開啟時的CPU時間戳記    int GetFrequency(void) ;              //計算CPU主頻的函數    __UINT64 GetCycleCount();             //讀取CPU的指令周期寄存器public:    unsigned long GetCPUFreq();           //查看CPU主頻    CAsmDelay();    ~CAsmDelay(); void msDelay(long ms);                //毫秒級延時 void StartCalculagraph();             //啟動計時器 long StopCalculagraph();              //結束計時器,並返回從啟動到關閉經過了多少毫秒。};
#endif
---------------------------------------------------- 
asmdelay.cpp 
---------------------------------------------------- 
#include "asmdelay.h"#include "windows.h"
CAsmDelay::CAsmDelay(){    m_Freq = GetFrequency();}
CAsmDelay::~CAsmDelay(){}
unsigned long CAsmDelay::GetCPUFreq(){    return m_Freq;}
__UINT64 CAsmDelay::GetCycleCount()//讀取CPU的指令周期寄存器{   volatile __UINT64 CycleCount;
#ifdef _MSC_VER __asm RDTSC __asm mov DWORD PTR CycleCount, EAX __asm mov DWORD PTR (CycleCount+4), EDX return CycleCount;#else    volatile unsigned long lo=0,hi=0,    *phi=((unsigned long *)(&CycleCount)) + 1,    *plo=(unsigned long *)&CycleCount;
 __asm__ ("RDTSC");    __asm__  __volatile__ ("movl %%eax,%0; movl %%edx,%1;"                        :"=r"(lo),"=r"(hi)::"%eax","%edx");
    *phi=hi;    *plo=lo;    return CycleCount;#endif
}
//擷取CPU頻率的內建函式  //MHzint CAsmDelay::GetFrequency(void){
 _LARGE_INTEGER CurrTicks, TicksCount; __UINT64 iStartCounter, iStopCounter;
 unsigned long dwOldProcessP = GetPriorityClass(GetCurrentProcess()); unsigned long dwOldThreadP = GetThreadPriority(GetCurrentThread());
 SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
 QueryPerformanceFrequency(&TicksCount); QueryPerformanceCounter(&CurrTicks);
 TicksCount.QuadPart /= 16; TicksCount.QuadPart += CurrTicks.QuadPart;
 iStartCounter=GetCycleCount();
 while(CurrTicks.QuadPart<TicksCount.QuadPart) QueryPerformanceCounter(&CurrTicks);
 iStopCounter = GetCycleCount();
 SetThreadPriority(GetCurrentThread(), dwOldThreadP); SetPriorityClass(GetCurrentProcess(), dwOldProcessP);
 return (int)((iStopCounter-iStartCounter)/62500);
}
void CAsmDelay::msDelay(long ms){ //把毫秒數轉成刻度數 __UINT64 msecToCycle = ( m_Freq * 1000 ) * ms;  //記錄當前刻度 __UINT64 tmr= GetCycleCount(); //延時,直到刻度數滿足預定的毫秒數; while(GetCycleCount()-tmr < msecToCycle) { }}
void CAsmDelay::StartCalculagraph()             //啟動計時器{ m_TimeStamp = GetCycleCount();}long CAsmDelay::StopCalculagraph()              //結束計時器,並返回從啟動到關閉經過了多少毫秒。{ __UINT64 TimeStampDiff = GetCycleCount()-m_TimeStamp; return TimeStampDiff / (m_Freq *1000);}
---------------------------------------------------- 
測試工程:
http://wxforum.shadonet.com/download.php?id=731
說明:實現毫秒級延時、計時的代碼。
編譯器相容性:VC++ / gcc
包含工程:.dev 用wxDev-C++可編譯
.dsw 用VC++6可編譯
兩個工程共用asmdelay.cpp .h
wxDev-C++和VC++程式運行介面:

-------------------------------------------------------------
附:gcc內嵌彙編的調試小經驗:
1、AT&T彙編和intel彙編有很大不同,AT&T彙編mov指令資料類型用加尾碼來實現,如movb movw movl
而且運算元是從左向右拷貝,和intel彙編相反。
2、 gcc通過格式化的字串來產生彙編代碼,如本文中所示:
 
    __asm__ __volatile__ ("movl %%eax,%0; movl %%edx,%1;" 
 
                        :"=r"(lo),"=r"(hi)::"%eax","%edx"); 
         第一個部分是一個字串: "movl %%eax,%0; movl %%edx,%1;" ,是彙編指令,可以有多條彙編指令,用;分割
         緊接著是:分隔字元,然後是第二部分,是輸出部(目標運算元),在這裡可以串連C語言變數,輸出部的各個元素用逗號分隔,第一個元素對應%0,第二個元素對應%1,依此類推。
         "=r"中的=表示輸出,r表示連結寄存器變數,也可以用g表示可以是寄存器或記憶體變數,也可以用m表示記憶體變數,有__volatile__ 的時候它們效果是一樣的,具體情況參見5。
          括弧中是串連的進階語言變數。
         第三部分是輸入部(源運算元),由於輸入部沒有串連進階語言變數,所以省略了,兩個連續的::其實是由於省略了第三部分的緣故
         最後一部分,第四部分,是限定寄存器,修飾第一部分中的寄存器。
3、如果gcc開啟了速度最佳化,那麼內嵌了彙編代碼的函數裡面的進階語言變數最好用 volatile 修飾,以保證運行結果的正確。否則的話,由於最佳化會導致
   程式在寄存器中分配變數,而彙編往往會使用寄存器,就會不可避免的產生衝突而導致錯誤的結果。
4、__volatile__ 修飾符告訴編譯器,禁止最佳化。阻止編譯器刪除、重組彙編指令。
5、約束字母:
:"=r" (__dummy)
“=r”表示相應的目標運算元可以使用任何一個通用寄存器,並且變數__dummy 存放在這個寄存器中,但如果是:
:“=m”(__dummy)
“=m”就表示相應的目標運算元是存放在記憶體單元__dummy中。這裡面的r和m就叫做約束字母。
約束字母很多,下表給出幾個主要的約束字母及其含義:

      字母 含義
   m, v,o 表示記憶體單元
   R 表示任何通用寄存器
   Q 表示寄存器eax, ebx, ecx,edx之一
   I, h 表示直接運算元
   E, F 表示浮點數
   G 表示“任意”
   a, b.c d 表示要求使用寄存器eax/ax/al, ebx/bx/bl, ecx/cx/cl或edx/dx/dl
   S, D 表示要求使用寄存器esi或edi
   I 表示常數(0~31)
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.