diff options
| author | Dave Parks <davep@lindenlab.com> | 2010-07-01 13:14:11 -0500 | 
|---|---|---|
| committer | Dave Parks <davep@lindenlab.com> | 2010-07-01 13:14:11 -0500 | 
| commit | bc85982431af31df786efd0e96cb6cb0d133f48a (patch) | |
| tree | 8cc4e385195107c0e759e05b4ebe1a16ee48b97f /indra/newview | |
| parent | 8419a692b5a3dc54e2fd440ef5ee925e7c6f45d3 (diff) | |
Generate graphs of performance logs when -analyzeperformance specified on command line.
Diffstat (limited to 'indra/newview')
| -rw-r--r-- | indra/newview/llappviewer.cpp | 39 | ||||
| -rw-r--r-- | indra/newview/llfasttimerview.cpp | 369 | ||||
| -rw-r--r-- | indra/newview/llfasttimerview.h | 1 | 
3 files changed, 389 insertions, 20 deletions
| diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index d4621db329..f140547497 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1292,6 +1292,26 @@ bool LLAppViewer::cleanup()  	// workaround for DEV-35406 crash on shutdown  	LLEventPumps::instance().reset(); +	if (LLFastTimerView::sAnalyzePerformance) +	{ +		llinfos << "Analyzing performance" << llendl; +		 +		if(LLFastTimer::sLog) +		{ +			LLFastTimerView::doAnalysis( +				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance_baseline.slp"), +				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance.slp"), +				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance_report.csv")); +		} +		if(LLFastTimer::sMetricLog) +		{ +			LLFastTimerView::doAnalysis( +				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric_baseline.slp"), +				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric.slp"), +				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric_report.csv")); +		} +	} +  	// remove any old breakpad minidump files from the log directory  	if (! isError())  	{ @@ -1630,25 +1650,6 @@ bool LLAppViewer::cleanup()  	delete mFastTimerLogThread;  	mFastTimerLogThread = NULL; -	if (LLFastTimerView::sAnalyzePerformance) -	{ -		llinfos << "Analyzing performance" << llendl; -		 -		if(LLFastTimer::sLog) -		{ -			LLFastTimerView::doAnalysis( -				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance_baseline.slp"), -				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance.slp"), -				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "performance_report.csv")); -		} -		if(LLFastTimer::sMetricLog) -		{ -			LLFastTimerView::doAnalysis( -				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric_baseline.slp"), -				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric.slp"), -				gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "metric_report.csv")); -		} -	}  	LLMetricPerformanceTester::cleanClass() ;  	llinfos << "Cleaning up Media and Textures" << llendflush; diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 8bc3b5a75f..2bfbe0e1c4 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -38,7 +38,9 @@  #include "llrect.h"  #include "llerror.h"  #include "llgl.h" +#include "llimagepng.h"  #include "llrender.h" +#include "llrendertarget.h"  #include "lllocalcliprect.h"  #include "llmath.h"  #include "llfontgl.h" @@ -1016,6 +1018,370 @@ F64 LLFastTimerView::getTime(const std::string& name)  	return 0.0;  } +void saveChart(const std::string& label, const char* suffix, LLImageRaw* scratch) +{ +	//read result back into raw image +	glReadPixels(0, 0, 1024, 512, GL_RGB, GL_UNSIGNED_BYTE, scratch->getData()); + +	//write results to disk +	LLPointer<LLImagePNG> result = new LLImagePNG(); +	result->encode(scratch, 0.f); + +	std::string ext = result->getExtension(); +	std::string filename = llformat("%s_%s.%s", label.c_str(), suffix, ext.c_str()); +	 +	std::string out_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, filename); +	result->save(out_file); +} + +template <class VEC_TYPE> +void removeOutliers(std::vector<VEC_TYPE>& data, F32 k) +{ +	if (data.size() < 100) +	{ //not enough samples +		return; +	} + +	VEC_TYPE Q1 = data[data.size()/4]; +	VEC_TYPE Q3 = data[data.size()-data.size()/4-1]; + +	VEC_TYPE min = Q1-k*(Q3-Q1); +	VEC_TYPE max = Q3+k*(Q3-Q1); + +	U32 i = 0; +	while (i < data.size() && data[i] < min) +	{ +		i++; +	} + +	S32 j = data.size()-1; +	while (j > 0 && data[j] > max) +	{ +		j--; +	} + +	if (j < data.size()-1) +	{ +		data.erase(data.begin()+j, data.end()); +	} + +	if (i > 0) +	{ +		data.erase(data.begin(), data.begin()+i); +	} +} + +//static +void LLFastTimerView::exportCharts(const std::string& base, const std::string& target) +{ +	//allocate render target for drawing charts  +	LLRenderTarget buffer; +	buffer.allocate(1024,512, GL_RGB, FALSE, FALSE); +	 + +	LLSD cur; + +	LLSD base_data; + +	{ //read base log into memory +		S32 i = 0; +		std::ifstream is(base.c_str()); +		while (!is.eof() && LLSDSerialize::fromXML(cur, is)) +		{ +			base_data[i++] = cur; +		} +		is.close(); +	} + +	LLSD cur_data; +	std::set<std::string> chart_names; + +	{ //read current log into memory +		S32 i = 0; +		std::ifstream is(target.c_str()); +		while (!is.eof() && LLSDSerialize::fromXML(cur, is)) +		{ +			cur_data[i++] = cur; + +			for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter) +			{ +				std::string label = iter->first; +				chart_names.insert(label); +			} +		} +		is.close(); +	} + +	//get time domain +	LLSD::Real cur_total_time = 0.0; + +	for (U32 i = 0; i < cur_data.size(); ++i) +	{ +		cur_total_time += cur_data[i]["Total"]["Time"].asReal(); +	} + +	LLSD::Real base_total_time = 0.0; +	for (U32 i = 0; i < base_data.size(); ++i) +	{ +		base_total_time += base_data[i]["Total"]["Time"].asReal(); +	} + +	LLSD::Real total_time = llmax(base_total_time, cur_total_time); + + +	//allocate raw scratch space +	LLPointer<LLImageRaw> scratch = new LLImageRaw(1024, 512, 3); + +	gGL.pushMatrix(); +	glLoadIdentity(); +	glMatrixMode(GL_PROJECTION); +	glLoadIdentity(); +	glOrtho(-0.05, 1.05, -0.05, 1.05, -1.0, 1.0); + +	//render charts +	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); +	 +	buffer.bindTarget(); + +	for (std::set<std::string>::iterator iter = chart_names.begin(); iter != chart_names.end(); ++iter) +	{ +		std::string label = *iter; +	 +		LLSD::Real cur_start_time = -1.0; +		LLSD::Real cur_end_time = 0.0; + +		LLSD::Real max_time = 0.0; +		LLSD::Integer max_calls = 0.0; +		LLSD::Real max_execution = 0.0; + +		std::vector<LLSD::Real> cur_execution; +		std::vector<LLSD::Real> cur_times; +		std::vector<LLSD::Integer> cur_calls; + +		std::vector<LLSD::Real> base_execution; +		std::vector<LLSD::Real> base_times; +		std::vector<LLSD::Integer> base_calls; + +		for (U32 i = 0; i < cur_data.size(); ++i) +		{ +			LLSD::Real time = cur_data[i][label]["Time"].asReal(); +			LLSD::Integer calls = cur_data[i][label]["Calls"].asInteger(); + +			LLSD::Real execution = 0.0; +			if (calls > 0) +			{ +				execution = time/calls; +				cur_execution.push_back(execution); +				cur_times.push_back(time); +			} + +			cur_calls.push_back(calls); +		} + +		for (U32 i = 0; i < base_data.size(); ++i) +		{ +			LLSD::Real time = base_data[i][label]["Time"].asReal(); +			LLSD::Integer calls = base_data[i][label]["Calls"].asInteger(); + +			LLSD::Real execution = 0.0; +			if (calls > 0) +			{ +				execution = time/calls; +				base_execution.push_back(execution); +				base_times.push_back(time); +			} + +			base_calls.push_back(calls); +		} + +		std::sort(base_calls.begin(), base_calls.end()); +		std::sort(base_times.begin(), base_times.end()); +		std::sort(base_execution.begin(), base_execution.end()); + +		std::sort(cur_calls.begin(), cur_calls.end()); +		std::sort(cur_times.begin(), cur_times.end()); +		std::sort(cur_execution.begin(), cur_execution.end()); + +		//remove outliers +		const U32 OUTLIER_CUTOFF = 512; +		if (base_times.size() > OUTLIER_CUTOFF) +		{  +			removeOutliers(base_times, 1.f); +		} + +		if (base_execution.size() > OUTLIER_CUTOFF) +		{  +			removeOutliers(base_execution, 1.f); +		} + +		if (cur_times.size() > OUTLIER_CUTOFF) +		{  +			removeOutliers(cur_times, 1.f); +		} + +		if (cur_execution.size() > OUTLIER_CUTOFF) +		{  +			removeOutliers(cur_execution, 1.f); +		} + + +		max_time = llmax(base_times.empty() ? 0.0 : *base_times.rbegin(), cur_times.empty() ? 0.0 : *cur_times.rbegin()); +		max_calls = llmax(base_calls.empty() ? 0 : *base_calls.rbegin(), cur_calls.empty() ? 0 : *cur_calls.rbegin()); +		max_execution = llmax(base_execution.empty() ? 0.0 : *base_execution.rbegin(), cur_execution.empty() ? 0.0 : *cur_execution.rbegin()); + + +		LLVector3 last_p; + +		//==================================== +		// basic +		//==================================== +		buffer.clear(); + +		last_p.clear(); + +		LLGLDisable cull(GL_CULL_FACE); + +		LLVector3 base_col(0, 0.7f, 0.f); +		LLVector3 cur_col(1.f, 0.f, 0.f); + +		gGL.setSceneBlendType(LLRender::BT_ADD); + +		gGL.color3fv(base_col.mV); +		for (U32 i = 0; i < base_times.size(); ++i) +		{ +			gGL.begin(LLRender::TRIANGLE_STRIP); +			gGL.vertex3fv(last_p.mV); +			gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +			last_p.set((F32)i/(F32) base_times.size(), base_times[i]/max_time, 0.f); +			gGL.vertex3fv(last_p.mV); +			gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +			gGL.end(); +		} +		 +		gGL.flush(); + +		 +		last_p.clear(); +		{ +			LLGLEnable blend(GL_BLEND); +						 +			gGL.color3fv(cur_col.mV); +			for (U32 i = 0; i < cur_times.size(); ++i) +			{ +				gGL.begin(LLRender::TRIANGLE_STRIP); +				gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +				gGL.vertex3fv(last_p.mV); +				last_p.set((F32) i / (F32) cur_times.size(), cur_times[i]/max_time, 0.f); +				gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +				gGL.vertex3fv(last_p.mV); +				gGL.end(); +			} +			 +			gGL.flush(); +		} + +		saveChart(label, "time", scratch); +		 +		//====================================== +		// calls +		//====================================== +		buffer.clear(); + +		last_p.clear(); + +		gGL.color3fv(base_col.mV); +		for (U32 i = 0; i < base_calls.size(); ++i) +		{ +			gGL.begin(LLRender::TRIANGLE_STRIP); +			gGL.vertex3fv(last_p.mV); +			gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +			last_p.set((F32) i / (F32) base_calls.size(), (F32)base_calls[i]/max_calls, 0.f); +			gGL.vertex3fv(last_p.mV); +			gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +			gGL.end(); +		} +		 +		gGL.flush(); + +		{ +			LLGLEnable blend(GL_BLEND); +			gGL.color3fv(cur_col.mV); +			last_p.clear(); + +			for (U32 i = 0; i < cur_calls.size(); ++i) +			{ +				gGL.begin(LLRender::TRIANGLE_STRIP); +				gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +				gGL.vertex3fv(last_p.mV); +				last_p.set((F32) i / (F32) cur_calls.size(), (F32) cur_calls[i]/max_calls, 0.f); +				gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +				gGL.vertex3fv(last_p.mV); +				gGL.end(); +				 +			} +			 +			gGL.flush(); +		} + +		saveChart(label, "calls", scratch); + +		//====================================== +		// execution +		//====================================== +		buffer.clear(); + + +		gGL.color3fv(base_col.mV); +		U32 count = 0; +		U32 total_count = base_execution.size(); + +		last_p.clear(); + +		for (std::vector<LLSD::Real>::iterator iter = base_execution.begin(); iter != base_execution.end(); ++iter) +		{ +			gGL.begin(LLRender::TRIANGLE_STRIP); +			gGL.vertex3fv(last_p.mV); +			gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +			last_p.set((F32)count/(F32)total_count, *iter/max_execution, 0.f); +			gGL.vertex3fv(last_p.mV); +			gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +			gGL.end(); +			count++; +		} + +		last_p.clear(); +				 +		{ +			LLGLEnable blend(GL_BLEND); +			gGL.color3fv(cur_col.mV); +			count = 0; +			total_count = cur_execution.size(); + +			for (std::vector<LLSD::Real>::iterator iter = cur_execution.begin(); iter != cur_execution.end(); ++iter) +			{ +				gGL.begin(LLRender::TRIANGLE_STRIP); +				gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +				gGL.vertex3fv(last_p.mV); +				last_p.set((F32)count/(F32)total_count, *iter/max_execution, 0.f);			 +				gGL.vertex3f(last_p.mV[0], 0.f, 0.f); +				gGL.vertex3fv(last_p.mV); +				gGL.end(); +				count++; +			} + +			gGL.flush(); +		} + +		saveChart(label, "execution", scratch); +	} + +	buffer.flush(); + +	gGL.popMatrix(); +	glMatrixMode(GL_MODELVIEW); +	gGL.popMatrix(); +} +  //static  LLSD LLFastTimerView::analyzePerformanceLogDefault(std::istream& is)  { @@ -1132,7 +1498,8 @@ void LLFastTimerView::doAnalysisDefault(std::string baseline, std::string target  			base[label]["Samples"].asInteger());			  	} -	 +	exportCharts(baseline, target); +  	os.flush();  	os.close();  } diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h index ac06990913..e604a1f84d 100644 --- a/indra/newview/llfasttimerview.h +++ b/indra/newview/llfasttimerview.h @@ -50,6 +50,7 @@ private:  	static void doAnalysisMetrics(std::string baseline, std::string target, std::string output) ;  	static LLSD analyzeMetricPerformanceLog(std::istream& is) ;  	static LLSD analyzePerformanceLogDefault(std::istream& is) ; +	static void exportCharts(const std::string& base, const std::string& target);  public: | 
