C++简单的函数计时方法

   这里提供一个简单的函数计时方法,利用QueryPerformanceFrequency和QueryPerformanceCount函数进行精确计时,这是最精确的计时方法。
   原理是在函数被调用时记录时间,结束时利用局部对象的析构函数再记录一次时间,将这个时间差累积到对应的函数的统计数据中。当程序退出时,全局对象释放,将所有函数的数据输出到调试窗口。使用时,需要再函数开头位置插入一条宏:_PF。需要计时的函数内都插入一条这样的宏。为了方便,宏被定义的比较短。


  FunctionName   ActiveTime    Average  Clled
         WinMain     
5.480105    5.480105     1
    
         MsgProc     
0.975687    0.015245    64
    
         InitD3D     
0.222094    0.222094     1
    
    InitGeometry     
0.004631    0.004631     1
    
          Render     
4.876714    0.021771   224
    
   SetupMatrices     
0.001405    0.000006   224
    
         Cleanup     
0.209167    0.209167     1 



#define PERFDATA_INIT_SIZE 32
#define PERFDATA_INC_SIZE  32
class PerformanceDaemon 
{
private
:
    typedef 
struct
 _PERF_DATA
    
{
        DWORD  nID;
        LPSTR pstrName;
        DWORD TotolCalled;
        LONGLONG TotolTime;
        
//LARGE_INTEGER FuncBeginTime;


    }
PERF_DATA,*LPPERF_DATA;


    LPPERF_DATA m_pDatas;
    DWORD m_dwUsed;
    DWORD m_dwSize;

    LARGE_INTEGER m_TickNow;
    LARGE_INTEGER m_Freq;
    
void
 SetSize(DWORD dwSize)
    
{
        assert(dwSize
>
m_dwUsed);
        LPPERF_DATA pData
=new
 PERF_DATA[dwSize];
        memcpy(pData,m_pDatas,m_dwUsed
*sizeof
(PERF_DATA));
        memset(pData
+m_dwUsed,0,(dwSize-m_dwUsed)*sizeof
(PERF_DATA));
        delete []m_pDatas;
        m_pDatas
=
pData;
        m_dwSize
=
dwSize;
    }

public:

    PerformanceDaemon ()
    
{
        m_pDatas
=0
;
        m_dwUsed
=0
;
        m_dwSize
=0
;
        
        SetSize(PERFDATA_INIT_SIZE);

        QueryPerformanceFrequency(
&
m_Freq); 
        QueryPerformanceCounter(
&
m_TickNow);

    }




    
void FuncStart(LPSTR pstrName,DWORD &nID)
    
{
        
if(nID>=m_dwUsed && nID!=0xFFFFreturn
;
        
if(nID==0xFFFF)    //add new function record;

        {
        

            PERF_DATA data;
            SIZE_T nLenght;
            nLenght
=
strlen(pstrName);
            memset(
&data,0,sizeof
(data));
            data.pstrName
=new CHAR[nLenght+1
];
            strcpy(data.pstrName,pstrName);

            
if(m_dwUsed >=
 m_dwSize) 
                SetSize(m_dwSize
+
PERFDATA_INC_SIZE);
            
            nID
=m_dwUsed++
;
            
            

            m_pDatas[nID]
=
data;

        }

        QueryPerformanceCounter(
&m_TickNow);
    }

    
void GetTime(LONGLONG *pTime)
    
{
        
*pTime=
m_TickNow.QuadPart;
    }


    
void FuncEnd(DWORD nID,LONGLONG *pTime)
    
{
        LARGE_INTEGER TickNow;
        QueryPerformanceCounter(
&
TickNow);
    
        
if(nID>=m_dwUsed && nID!=0xFFFFreturn
;
        m_pDatas[nID].TotolTime
+= TickNow.QuadPart-*
pTime;
        m_pDatas[nID].TotolCalled
++
;
        m_TickNow
=
TickNow;    
    
    }


    
void DumpFunctionPerfmance()
    
{
        CHAR strBuffer[
256
];

        OutputDebugString(
"FunctionName\t\tActiveTime\tAverage\t  Clled\n"
); 
        
for(DWORD i=0;i<m_dwUsed;i++
)
        
{
            
double
 t;
            DWORD n
=
m_pDatas[i].TotolCalled;

            t
=m_pDatas[i].TotolTime/double
(m_Freq.QuadPart); 
            sprintf(strBuffer,
"%16s %12f\t%8f%6u\t\n",m_pDatas[i].pstrName,t,t/
n,n);
            OutputDebugString(strBuffer);
        }

    
    }



    
~PerformanceDaemon ()
    
{
        DumpFunctionPerfmance();
        
for(DWORD i=0;i<m_dwUsed;i++
)
        
{
            delete []m_pDatas[i].pstrName;
        
        }

        

        delete []m_pDatas;
    
    
    }


}
;
PerformanceDaemon  Perf;
class
 PerformanceWatcher
{
    DWORD dwID;    
    LONGLONG StartTime;        
//为了解决嵌套调用问题


public:
    
//just for convinience 

    PerformanceWatcher(LPSTR pstrFunName,DWORD &nID)
    
{
        Perf.FuncStart(pstrFunName,nID);
        dwID
=
nID;
        Perf.GetTime(
&
StartTime);
        
    }

    
~PerformanceWatcher()
    
{
        Perf.FuncEnd(dwID,
&
StartTime);
    }


}
;

#ifdef _DEBUG
#define _PF  \

    
static DWORD __PerfCounter=0xFFFF;\
    PerformanceWatcher __PerfWatcher(__FUNCTION__,__PerfCounter);
#else

#define _PF
#endif 



VOID Cleanup()
{
    _PF
    
if( g_pTexture !=
 NULL )
        g_pTexture
->
Release();

    
if( g_pVB !=
 NULL )
        g_pVB
->
Release();

    
if( g_pd3dDevice !=
 NULL )
        g_pd3dDevice
->
Release();

    
if( g_pD3D !=
 NULL )
        g_pD3D
->
Release();
}


据MSDN上的资料,VC6以后的版本上的确支持Profile,但个人感觉没有VC6那么方便了。
VC6 Function Profiling 参考:
http://msdn.microsoft.com/en-us/library/aa269759(VS.60).aspx

另外有一个/callcap的编译宏,这个宏允许函数开始执行和结束时调用两个自定义的函数,参考:
http://msdn.microsoft.com/en-us/library/ms404386.aspx

// File: callcaphooks.c

#include 
<stdio.h>
int main();

void _CAP_Enter_Function(void *p) 
{
    
if (p != main) 
        printf(
"Enter function   (at address %p) at %d\n"
            p, GetTickCount());
        
return;
}

void _CAP_Exit_Function(void *p) 
{
    
if (p != main) 
        printf(
"Leaving function (at address %p) at %d\n"
            p, GetTickCount());
    
return;
}




posted @ 2008-07-17 14:21  JohnsonFeng  阅读(2485)  评论(2编辑  收藏  举报