性能测试——(2)基于windows下的测试宏
#ifndef STAT_H#define STAT_H#include<map>#include<string>using namespace std;/***wx,取消原有用宏方式,但测试函数的开销可能会增加测试的精度**/struct st_stat{public:st_stat():call_count(0),time(0){}LARGE_INTEGER start;LARGE_INTEGER end;int call_count;__int64 time;};/**测试函数性能或者代码块的*/class Runnable{public:explicit Runnable(const std::string& name): _name(name) {} ~Runnable() {}const std::string& name() const { return _name; } inline void begin() ;inline void end() ;void diff_time(LONGLONG diff){stat.time -= (stat.end.QuadPart-diff);}int getCallCount(){return stat.call_count;}__int64 getCallTime(){return stat.time;}st_stat stat;private:std::string _name;};class test_suite{public:static test_suite& get_instance(){static test_suite me;return me;}~test_suite(){if(fp) fclose(fp);fp=NULL;}void setWorkingDir(const std::string& val) { _working_dir = val; }const std::string& getWorkingDir() const { return _working_dir; }void setTestFilter(const std::string& val) { _test_filter = val; }const std::string& getTestFilter() const { return _test_filter; }void addTest(Runnable* test) { test_vec.insert(pair<string,Runnable*>(test->name(),test)); }void func_begin(const std::string &name);void func_end(const std::string &name);void init(const std::string &dir);void finish();private:test_suite():fp(NULL),_working_dir(),_test_filter(){}void printHeading();void printFooter();void printError(const std::string&msg);std::string _working_dir;std::string _test_filter;typedef std::map<std::string,Runnable*> MAP;typedef MAP::iterator IT;typedef MAP::const_iterator const_it; MAP test_vec;//所有需要测试的函数列表 std::vector<Runnable*> none_init_vec;//没有初始化的,需要new对象,因此最后需要释放FILE *fp;};void call_stat_begin(st_stat*pstat);void call_stat_end(st_stat*pstat);#if defined ST_TEST_PERFORMANCE#define STAT_TEST_INIT(name) \struct st_##name##_test: Runnable { \st_##name##_test(): Runnable(#name) { \test_suite::get_instance().addTest(this); \} \} name##_test_instance#define STAT_TEST_BEGIN(name) \test_suite::get_instance().func_begin(#name)#define STAT_TEST_END(name) \test_suite::get_instance().func_end(#name)#define STAT_GLOBAL_INIT(fileDir) test_suite::get_instance().init(fileDir);#define STAT_GLOBAL_FINISH() test_suite::get_instance().finish();#else#define STAT_TEST_INIT(name) #define STAT_TEST_BEGIN(name) (void*)(0)#define STAT_TEST_END(name) (void*)(0)#define STAT_GLOBAL_INIT(fileDir) #define STAT_GLOBAL_FINISH() (void*)(0)#endif#endif
st_stat.cpp?文件
?
#include"stdafx.h"#include"st_stat.h"#ifdef _WIN32#include<windows.h>void call_stat_begin(st_stat*pstat){pstat->call_count++;LARGE_INTEGER large_integer;DWORD mask = 1;//DWORD_PTR oldmask = ::SetThreadAffinityMask(::GetCurrentThread(),mask);//特别注意,在准确测试时候是需要设置cpu亲和性,否则多核情况下不准确,这里都注释掉BOOL bRet = ::QueryPerformanceCounter(&large_integer);//::SetThreadAffinityMask(::GetCurrentThread(),oldmask);_ASSERT(bRet);pstat->start = large_integer;}void call_stat_end(st_stat*pstat){DWORD mask = 1;//DWORD_PTR oldmask = ::SetThreadAffinityMask(::GetCurrentThread(),mask);LARGE_INTEGER large_integer;BOOL bRet = ::QueryPerformanceCounter(&large_integer);_ASSERT(bRet);//::SetThreadAffinityMask(::GetCurrentThread(),oldmask);pstat->end = large_integer;pstat->time += pstat->end.QuadPart-pstat->start.QuadPart;}void Runnable::begin(){::call_stat_begin(&stat);}void Runnable::end(){call_stat_end(&stat);}void test_suite::func_begin(const std::string & name){// Run test initializersconst_it it = test_vec.find(name);if(it!=test_vec.end()){LARGE_INTEGER large_integer;BOOL bRet = ::QueryPerformanceCounter(&large_integer);_ASSERT(bRet);(*it).second->begin();return;}Runnable *pTest = new Runnable(name);test_vec.insert(pair<string,Runnable*>(name,pTest));none_init_vec.push_back(pTest);pTest->begin();}void test_suite::func_end(const std::string & name){// Run test initializersconst_it it = test_vec.find(name);if(it!=test_vec.end()){LARGE_INTEGER large_integer;BOOL bRet = ::QueryPerformanceCounter(&large_integer);_ASSERT(bRet);(*it).second->end();(*it).second->diff_time(large_integer.QuadPart);}//printError(name);/*vector<Runnable*>::iterator it = test_vec.begin();for (; it != test_vec.end(); ++it){if ((*it)->name()==name){LARGE_INTEGER large_integer;BOOL bRet = ::QueryPerformanceCounter(&large_integer);_ASSERT(bRet);(*it)->end();(*it)->diff_time(large_integer.QuadPart);}}*/}void test_suite::printHeading(){if(fp){fprintf(fp,"函数名,调用次数,调用时间PerformanceCounter\n");fprintf(fp,"start Tick Count=%d ms\r ",::GetTickCount());}}void test_suite::printFooter(){if(fp){fprintf(fp,"end Tick Count=%d ms\r",::GetTickCount());}}void test_suite:: init(const std::string &dir){setWorkingDir(dir);if(fp){fclose(fp);fp = NULL;}if(getWorkingDir().size()>0){std::string filePath(dir);filePath.append("test_performance.csv");fp=fopen(filePath.c_str(),"a+");}else{fp=fopen("test_performance.csv","a+");}printHeading();}void test_suite::finish(){if(fp){for(IT it = test_vec.begin();it!=test_vec.end();it++){fprintf(fp,"%s,%d,%I64d\r",it->second->name().c_str(),it->second->getCallCount(),it->second->getCallTime());}}if(fp) {fclose(fp);fp =NULL;}std::vector<Runnable*>::iterator it = none_init_vec.begin();for(;it !=none_init_vec.end();it++){if(*it!=NULL){delete *it;*it=NULL;}}}void test_suite::printError(const std::string&msg){if(fp){fprintf(fp,"%s",msg.c_str());}}#endif
?
这个是在上一篇改进下实现的,基于C++实现,不需要定义变量
使用方式:
(1)需要打开测试宏,可以在C++工程宏定义中定义ST_TEST_PERFORMANCE
(2)测试函数f,如下即可
?
bool CCompile::OutPutReulstFile(){STAT_TEST_BEGIN(OutPutReulstFile);//宏定义里面是测试名称,名称同C语言规范,最好是用函数名//原有函数体;STAT_TEST_END(OutPutReulstFile);//与begin对应即可return 0;}
?
?(3)程序启动时调用STAT_GLOBAL_INIT(".")
在进程中退出前,或者需要打印出结果的地方,调用STAT_GLOBAL_FINISH();否则不会生成测试结果文件
STAT_GLOBAL_INIT(string );用于初始化文件路径,打开文件句柄STAT_GLOBAL_FINISH();用于关闭文件句柄,清空测试内存这样会以追加+a方式生成一个文件,test_performance.csv;可以指定文件生成路径。进程正常退出,默认会生成该文件。
?
目前不支持多线程!!!!!这个要十分小心,确保所测试的模块只有一个线程调用。