DragonNest/Common/Utility/PerfCheck.cpp
2024-12-19 09:48:26 +08:00

458 lines
No EOL
10 KiB
C++

#include "StdAfx.h"
#include "PerfCheck.h"
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
#include "psapi.h"
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
int g_nPerfCounter = 0;
bool g_bEnableProfile = false;
void (__stdcall *s_pProfileOutputDebugFunc)(const char*, ...) = _OutputDebug;
void SetProfileOutputDebugFuncPtr( void (__stdcall *Func)(const char*, ...) )
{
s_pProfileOutputDebugFunc = Func;
}
typedef struct tagPERFDATA
{
double fMinCounter;
double fMaxCounter;
double fTotalCounter;
LARGE_INTEGER prevCounter;
DWORD nCountHits;
// offset 용
double fFpsTotalCounter;
DWORD dwFpsCountHits;
double fAveFpsTotalCounter;
DWORD dwAveFpsCountHits;
int nPrevPrefCounter;
string strName;
} PERFDATA, *LPPERFDATA;
static bool PerfCmp(const LPPERFDATA lhs, const LPPERFDATA rhs)
{
return lhs->fMaxCounter > rhs->fMaxCounter;
}
class CPerfCheck
{
public:
CPerfCheck()
{
m_vecPerfDataPtr.reserve(64);
}
~CPerfCheck()
{
OutputPerfInfo();
vector<LPPERFDATA>::iterator it = m_vecPerfDataPtr.begin();
vector<LPPERFDATA>::iterator itend = m_vecPerfDataPtr.end();
for (; it != itend; it++)
delete (*it);
}
void OutputPerfInfo( void (__stdcall *Func)(const char*, ...) = OutputDebugFunc )
{
char buf[256];
vector<LPPERFDATA> vecPerfDataPtr;
vecPerfDataPtr.assign(m_vecPerfDataPtr.begin(), m_vecPerfDataPtr.end());
vector<LPPERFDATA>::iterator it = vecPerfDataPtr.begin();
vector<LPPERFDATA>::iterator itend = vecPerfDataPtr.end();
sort(it, itend, PerfCmp);
Func("\n\n----- Profiling Result Start -----\n\n");
Func(" [ Func Name ] Call Count MAX(ms) MIN(ms) AVG(ms)\n\n");
LARGE_INTEGER lFreq;
QueryPerformanceFrequency(&lFreq);
double fFreq = double(lFreq.QuadPart) / 1000.;
for (; it != itend; it++)
{
LPPERFDATA lpPerf = *it;
// 기본적인 프로파일링 결과
sprintf(buf, "%-25s %6d, %6.4lf, %6.4lf, %6.4lf \n", lpPerf->strName.c_str(), lpPerf->nCountHits,
lpPerf->fMaxCounter / fFreq,
(lpPerf->fMinCounter == 100000000. ) ? ( 0. ) : ( lpPerf->fMinCounter / fFreq ),
lpPerf->fTotalCounter / fFreq / double(lpPerf->nCountHits)
);
Func(buf);
// 틱당 계산 결과
if( lpPerf->dwAveFpsCountHits > 0 ) {
sprintf(buf, " └Frame Per Result -> Call : ( %6d ), Ave : ( %6.4lf )\n", lpPerf->dwAveFpsCountHits,
lpPerf->fAveFpsTotalCounter / fFreq );
Func(buf);
}
}
Func("\n----- Profiling Result End -----\n\n");
}
int RegisterPerfID(const char* lpFnName)
{
LPPERFDATA pPerfData = new PERFDATA;
pPerfData->fMinCounter = 100000000.;
pPerfData->fMaxCounter = 0.0;
pPerfData->fTotalCounter = 0.0;
pPerfData->prevCounter.QuadPart = 0;
pPerfData->nCountHits = 0;
pPerfData->strName = lpFnName;
pPerfData->fFpsTotalCounter = 0.0f;
pPerfData->dwFpsCountHits = 0;
pPerfData->fAveFpsTotalCounter = 0.f;
pPerfData->dwAveFpsCountHits = 0;
pPerfData->nPrevPrefCounter = g_nPerfCounter;
m_vecPerfDataPtr.push_back(pPerfData);
return (int)m_vecPerfDataPtr.size() - 1;
}
void StartPerfCheck(int nID)
{
QueryPerformanceCounter(&m_vecPerfDataPtr[nID]->prevCounter);
}
void EndPerfCheck(int nID)
{
LARGE_INTEGER currCount;
QueryPerformanceCounter(&currCount);
LPPERFDATA pPerf = m_vecPerfDataPtr[nID];
double fDiffCount;
fDiffCount = double(currCount.QuadPart - pPerf->prevCounter.QuadPart);
if (pPerf->fMinCounter > fDiffCount)
pPerf->fMinCounter = fDiffCount;
else if (pPerf->fMaxCounter < fDiffCount)
pPerf->fMaxCounter = fDiffCount;
pPerf->fTotalCounter += fDiffCount;
pPerf->nCountHits++;
if( g_nPerfCounter != pPerf->nPrevPrefCounter ) {
bool bFirst = ( pPerf->dwAveFpsCountHits == 0 ) ? true : false;
pPerf->fAveFpsTotalCounter += pPerf->fFpsTotalCounter;
if( !bFirst ) pPerf->fAveFpsTotalCounter /= 2.f;
pPerf->dwAveFpsCountHits += pPerf->dwFpsCountHits;
if( !bFirst ) pPerf->dwAveFpsCountHits /= 2;
pPerf->fFpsTotalCounter = fDiffCount;
pPerf->dwFpsCountHits = 1;
pPerf->nPrevPrefCounter = g_nPerfCounter;
}
else {
pPerf->fFpsTotalCounter += fDiffCount;
pPerf->dwFpsCountHits++;
}
}
protected:
vector<LPPERFDATA> m_vecPerfDataPtr;
};
// Memory Checker
class CProfileMemChecker {
public:
CProfileMemChecker() {}
virtual ~CProfileMemChecker() {
OutputResult( NULL );
}
protected:
struct InfoStruct {
std::string szString;
unsigned int dwUseMem;
int nCallCount;
};
struct TempStruct {
std::string szString;
unsigned int dwCurrentMem;
};
std::vector<InfoStruct> m_vecResult;
std::vector<TempStruct> m_vecTemp;
InfoStruct *FindResult( char *szStr )
{
for( DWORD i=0; i<m_vecResult.size(); i++ ) {
if( strcmp( m_vecResult[i].szString.c_str(), szStr ) == NULL ) return &m_vecResult[i];
}
return NULL;
}
public:
void StartBlock( char *szString )
{
MEMORYSTATUS memInfo;
GlobalMemoryStatus(&memInfo);
TempStruct Block;
Block.szString = szString;
Block.dwCurrentMem = (DWORD)memInfo.dwAvailPhys;
m_vecTemp.push_back( Block );
}
void EndBlock( bool bPrintResult = true )
{
if( m_vecTemp.empty() )
return;
MEMORYSTATUS memInfo;
GlobalMemoryStatus(&memInfo);
TempStruct Block = m_vecTemp[ m_vecTemp.size() - 1 ];
DWORD dwUseMem = (DWORD)( Block.dwCurrentMem - memInfo.dwAvailPhys );
InfoStruct *pInfo = NULL;
pInfo = FindResult( (char*)Block.szString.c_str() );
if( pInfo ) {
pInfo->dwUseMem += dwUseMem;
pInfo->nCallCount++;
}
else {
InfoStruct Info;
Info.szString = Block.szString;
Info.dwUseMem = dwUseMem;
Info.nCallCount = 1;
m_vecResult.push_back( Info );
}
m_vecTemp.erase( m_vecTemp.end() - 1 );
if( bPrintResult ) {
OutputResult( (char*)Block.szString.c_str() );
}
}
void CancleBlock()
{
m_vecTemp.erase( m_vecTemp.end() - 1 );
}
void OutputResult( char *szString = NULL, void (__stdcall *Func)(const char*, ...) = OutputDebugFunc )
{
if( szString ) {
InfoStruct *pInfo = FindResult( szString );
if( pInfo )
Func( "Mem Checker : [ %s ], Use Mem : %d bytes\n", pInfo->szString.c_str(), pInfo->dwUseMem );
}
else {
Func( "\n----------------------- Memory Use Result ----------------------------\n" );
Func( "\nFunction Name\tCall Count Total Allocate memory ( byte )\n" );
for( DWORD i=0; i<m_vecResult.size(); i++ ) {
InfoStruct *pInfo = &m_vecResult[i];
Func( "[ %s ] : %d Use %d bytes\n", pInfo->szString.c_str(), pInfo->nCallCount, pInfo->dwUseMem );
}
}
}
};
// Help Func
#ifdef ENABLE_PROFILER
CPerfCheck s_PerfCheck;
CProfileMemChecker s_ProfileMemCheck;
int RegisterPerfID(const char* lpFnName)
{
return s_PerfCheck.RegisterPerfID(lpFnName);
}
void StartPerfCheck(int nID)
{
s_PerfCheck.StartPerfCheck(nID);
}
void EndPerfCheck(int nID)
{
s_PerfCheck.EndPerfCheck(nID);
}
void ProfilePrint(void (__stdcall *Func)(const char*, ...))
{
s_PerfCheck.OutputPerfInfo(Func);
}
void StartProfileMemCheck(char *lpFnName)
{
s_ProfileMemCheck.StartBlock( lpFnName );
}
void EndProfileMemCheck()
{
s_ProfileMemCheck.EndBlock( false );
}
void ProfileMemPrint(void (__stdcall *Func)(const char*, ...))
{
s_ProfileMemCheck.OutputResult( NULL, Func );
}
#else
int RegisterPerfID(const char* lpFnName) { return 0; }
void StartPerfCheck(int nID) {}
void EndPerfCheck(int nID) {}
void ProfilePrint(void (__stdcall *Func)(const char*, ...)) {}
void StartProfileMemCheck(char *lpFnName) {}
void EndProfileMemCheck() {}
void ProfileMemPrint(void (__stdcall *Func)(const char*, ...)) {}
#endif // ENABLE_PROFILER
#ifdef _TEST_CODE_KAL
class CProfileMemChecker2
{
public:
struct InfoStruct
{
std::string szString;
unsigned int dwUseMem;
int nCallCount;
};
struct TempStruct
{
std::string szString;
unsigned int dwCurrentMem;
};
CProfileMemChecker2() {}
virtual ~CProfileMemChecker2()
{
OutputResult(NULL);
}
void StartBlock(const char *szString)
{
PROCESS_MEMORY_COUNTERS_EX pmc;
memset(&pmc, 0, sizeof(PROCESS_MEMORY_COUNTERS_EX));
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(PROCESS_MEMORY_COUNTERS_EX)))
{
TempStruct Block;
Block.szString = szString;
Block.dwCurrentMem = pmc.PrivateUsage;
m_vecTemp.push_back(Block);
}
}
void EndBlock(bool bPrintResult = true)
{
if (m_vecTemp.empty())
return;
PROCESS_MEMORY_COUNTERS_EX pmc;
memset(&pmc, 0, sizeof(PROCESS_MEMORY_COUNTERS_EX));
pmc.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(PROCESS_MEMORY_COUNTERS_EX)))
{
TempStruct Block = m_vecTemp[m_vecTemp.size() - 1];
DWORD dwUseMem = (DWORD)(pmc.PrivateUsage - Block.dwCurrentMem);
InfoStruct *pInfo = NULL;
pInfo = FindResult((char*)Block.szString.c_str());
if (pInfo)
{
pInfo->dwUseMem += dwUseMem;
pInfo->nCallCount++;
}
else
{
InfoStruct Info;
Info.szString = Block.szString;
Info.dwUseMem = dwUseMem;
Info.nCallCount = 1;
m_vecResult.push_back(Info);
}
m_vecTemp.erase(m_vecTemp.end() - 1);
if (bPrintResult)
{
OutputResult((char*)Block.szString.c_str());
}
}
}
void CancleBlock()
{
m_vecTemp.erase(m_vecTemp.end() - 1);
}
void OutputResult(const char *szString = NULL, void (__stdcall *Func)(const char*, ...) = OutputDebugFunc)
{
if (szString)
{
InfoStruct* pInfo = FindResult(szString);
if (pInfo)
Func("Mem Checker : [ %s ], Use Mem : %d KB\n", pInfo->szString.c_str(), pInfo->dwUseMem / 1024 );
}
else
{
Func( "\n----------------------- Memory Use Result ----------------------------\n" );
Func( "\nFunction Name \t\t Call Count \t\t Total Allocate memory ( byte )\n" );
for( DWORD i=0; i<m_vecResult.size(); i++ ) {
InfoStruct *pInfo = &m_vecResult[i];
Func( "[ %s ] : \t\t %d \t\t Use %d KB\n", pInfo->szString.c_str(), pInfo->nCallCount, pInfo->dwUseMem / 1024 );
}
}
Func( "\n-----------------------------------------------------------------------\n" );
}
InfoStruct* FindResult(const char *szStr)
{
for (DWORD i=0; i<m_vecResult.size(); i++)
{
if (strcmp(m_vecResult[i].szString.c_str(), szStr) == NULL)
return &m_vecResult[i];
}
return NULL;
}
private:
std::vector<InfoStruct> m_vecResult;
std::vector<TempStruct> m_vecTemp;
};
CProfileMemChecker2 g_ProfileMemCheck;
#endif // _TEST_CODE_KAL