Rational purify trial性能测试——(1)

  • quantify和简单代码块测试    

       这几天,一直忙于测试程序性能,基于rational 的quantify 工具进行性能测试,能够对exe,dll文件测试,还是很不错的,可以在ibm官方网站下载,不算太大。因为trial版,没有把数据保存起来。

  1. <1>支持EXE,DLL分析
  2. <2>支持Line,function,time分析
  3. <3>支持多线程分析,可以独立分析线程
  4. <4>结果图形化,支持树状调用结果;支持属性列表方式

以前用过parasoft的测试工具,不记得了,当时觉得那个工具挺好的。

quantify可以选择line,function,code,一般选择line就好了。

这类测试工具,是基于细粒度的测试情况,不能较直观或者有效的分析逻辑处理部分,可以认为是微观分析;我们也需要宏观分析程序代码块,那部分代码块执行次数,耗时等等。

写了一个windows 下的简单的测试程序代码块,用于从逻辑换上来分析程序性能的源码;如果需要支持Linux,修改计时函数就行了。

st_stat.h

#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;}
	int 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;
	}

	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) \
struct st_##name##_test: Runnable { \
	st_##name##_test(): Runnable(#name) { \
	test_suite::get_instance().addTest(this); \
	} \
} name##_test_instance

#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"st_stat.h"

#ifdef _WIN32
#include<windows.h>
void call_stat_begin(st_stat*pstat)
{
	pstat->call_count++;
	LARGE_INTEGER large_integer;
	BOOL bRet = ::QueryPerformanceCounter(&large_integer);
	_ASSERT(bRet);
	pstat->start = large_integer;
}
void call_stat_end(st_stat*pstat)
{
	LARGE_INTEGER large_integer;
	BOOL bRet = ::QueryPerformanceCounter(&large_integer);
	_ASSERT(bRet);
	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 initializers
	const_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 initializers
	const_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(),"w+");
	}
	else
	{
		fp=fopen("test_performance.csv","w+");
	}

	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

使用方式,也很简单

开始时:

STAT_GLOBAL_INIT("文件路径");执行一次

结束时:

STAT_GLOBAL_FINISH()

这两个宏,特别是最后一个宏,可以优化后,省略这个调用,为了保持与INIT,所以加上了。

打算再优化下,INIT和FINISH默认都可以省略,这样就更简洁明了

然后就是如下调用了;

STAT_TEST_BEGIN("测试名字");

XXXXXX;测试代码

STAT_TEST_END("测试名字");

  • 结果分析

经过初步Quantify测试结果,得到我现有的程序如下问题:

(1)std:string构造和销毁,耗时太多

(2)malloc和delete调用次数太多,耗时很大

因此,初步从微观可以看出,主要是频繁字符串操作,还有小对象的分配回收;

基本上可以确定方案:需要优化处理字符串,需要使用内存池管理小对象。

而基于程序逻辑测试结果:

函数名 调用次数 调用时间PerformanceCounter
start Tick Count=16480171 ms  
 AddLineGraphToMemory 2043768 842106440
AddRegionGraphToMemory 317901 1462981839
GetShapeFeatureValue 8739066 0
     
LINE_裁剪 2043768 359172490
OutPutReulstFile 70 347857107
REGION_裁剪 274746 -985524651
ReadEveryShapeFile 3702 1332493360
ReadEveryTabFile 249 1898886781

上面逻辑宏观测试结果,REGION_裁剪和AddRegionGraphToMemory的耗时太多,这部分逻辑需要额外关照,嘿嘿!

  • 下一步:

(1)丰富下这个测试代码,让他支持单元测试,即可测试某一个函数

(2)修改bug,当时间太长,出现负数了

猜你喜欢

转载自lvdccyb.iteye.com/blog/1536289