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
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!=0xFFFF) return;
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!=0xFFFF) return;
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
#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!=0xFFFF) return;
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!=0xFFFF) return;
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;
}
#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;
}