/** * @file llmetricperformancetester.cpp * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes implementation * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "indra_constants.h" #include "llerror.h" #include "llsdserialize.h" #include "lltreeiterators.h" #include "llmetricperformancetester.h" #include "llfasttimer.h" //---------------------------------------------------------------------------------------------- // LLMetricPerformanceTesterBasic : static methods and testers management //---------------------------------------------------------------------------------------------- LLMetricPerformanceTesterBasic::name_tester_map_t LLMetricPerformanceTesterBasic::sTesterMap ; /*static*/ void LLMetricPerformanceTesterBasic::cleanupClass() { for (name_tester_map_t::value_type& pair : sTesterMap) { delete pair.second; } sTesterMap.clear() ; } /*static*/ bool LLMetricPerformanceTesterBasic::addTester(LLMetricPerformanceTesterBasic* tester) { llassert_always(tester != NULL); std::string name = tester->getTesterName() ; if (getTester(name)) { LL_ERRS() << "Tester name is already used by some other tester : " << name << LL_ENDL ; return false; } sTesterMap.insert(std::make_pair(name, tester)); return true; } /*static*/ void LLMetricPerformanceTesterBasic::deleteTester(std::string name) { name_tester_map_t::iterator tester = sTesterMap.find(name); if (tester != sTesterMap.end()) { delete tester->second; sTesterMap.erase(tester); } } /*static*/ LLMetricPerformanceTesterBasic* LLMetricPerformanceTesterBasic::getTester(std::string name) { // Check for the requested metric name name_tester_map_t::iterator found_it = sTesterMap.find(name) ; if (found_it != sTesterMap.end()) { return found_it->second ; } return NULL ; } /*static*/ // Return true if this metric is requested or if the general default "catch all" metric is requested bool LLMetricPerformanceTesterBasic::isMetricLogRequested(std::string name) { return (LLTrace::BlockTimer::sMetricLog && ((LLTrace::BlockTimer::sLogName == name) || (LLTrace::BlockTimer::sLogName == DEFAULT_METRIC_NAME))); } /*static*/ LLSD LLMetricPerformanceTesterBasic::analyzeMetricPerformanceLog(std::istream& is) { LLSD ret; LLSD cur; while (!is.eof() && LLSDParser::PARSE_FAILURE != LLSDSerialize::fromXML(cur, is)) { for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter) { std::string label = iter->first; LLMetricPerformanceTesterBasic* tester = LLMetricPerformanceTesterBasic::getTester(iter->second["Name"].asString()) ; if(tester) { ret[label]["Name"] = iter->second["Name"] ; auto num_of_metrics = tester->getNumberOfMetrics() ; for(size_t index = 0 ; index < num_of_metrics ; index++) { ret[label][ tester->getMetricName(index) ] = iter->second[ tester->getMetricName(index) ] ; } } } } return ret; } /*static*/ void LLMetricPerformanceTesterBasic::doAnalysisMetrics(std::string baseline, std::string target, std::string output) { if(!LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters()) { return ; } // Open baseline and current target, exit if one is inexistent llifstream base_is(baseline.c_str()); llifstream target_is(target.c_str()); if (!base_is.is_open() || !target_is.is_open()) { LL_WARNS() << "'-analyzeperformance' error : baseline or current target file inexistent" << LL_ENDL; base_is.close(); target_is.close(); return; } //analyze baseline LLSD base = analyzeMetricPerformanceLog(base_is); base_is.close(); //analyze current LLSD current = analyzeMetricPerformanceLog(target_is); target_is.close(); //output comparision llofstream os(output.c_str()); os << "Label, Metric, Base(B), Target(T), Diff(T-B), Percentage(100*T/B)\n"; for (LLMetricPerformanceTesterBasic::name_tester_map_t::value_type& pair : LLMetricPerformanceTesterBasic::sTesterMap) { LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)pair.second); tester->analyzePerformance(&os, &base, ¤t) ; } os.flush(); os.close(); } //---------------------------------------------------------------------------------------------- // LLMetricPerformanceTesterBasic : Tester instance methods //---------------------------------------------------------------------------------------------- LLMetricPerformanceTesterBasic::LLMetricPerformanceTesterBasic(std::string name) : mName(name), mCount(0) { if (mName == std::string()) { LL_ERRS() << "LLMetricPerformanceTesterBasic construction invalid : Empty name passed to constructor" << LL_ENDL ; } mValidInstance = LLMetricPerformanceTesterBasic::addTester(this) ; } LLMetricPerformanceTesterBasic::~LLMetricPerformanceTesterBasic() { } void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd) { incrementCurrentCount() ; } void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd) { LLTrace::BlockTimer::pushLog(*sd); } void LLMetricPerformanceTesterBasic::outputTestResults() { LLSD sd; preOutputTestResults(&sd) ; outputTestRecord(&sd) ; postOutputTestResults(&sd) ; } void LLMetricPerformanceTesterBasic::addMetric(std::string str) { mMetricStrings.push_back(str) ; } /*virtual*/ void LLMetricPerformanceTesterBasic::analyzePerformance(llofstream* os, LLSD* base, LLSD* current) { resetCurrentCount() ; std::string current_label = getCurrentLabelName(); bool in_base = (*base).has(current_label) ; bool in_current = (*current).has(current_label) ; while(in_base || in_current) { LLSD::String label = current_label ; if(in_base && in_current) { *os << llformat("%s\n", label.c_str()) ; for(U32 index = 0 ; index < mMetricStrings.size() ; index++) { switch((*current)[label][ mMetricStrings[index] ].type()) { case LLSD::TypeInteger: compareTestResults(os, mMetricStrings[index], (S32)((*base)[label][ mMetricStrings[index] ].asInteger()), (S32)((*current)[label][ mMetricStrings[index] ].asInteger())) ; break ; case LLSD::TypeReal: compareTestResults(os, mMetricStrings[index], (F32)((*base)[label][ mMetricStrings[index] ].asReal()), (F32)((*current)[label][ mMetricStrings[index] ].asReal())) ; break; default: LL_ERRS() << "unsupported metric " << mMetricStrings[index] << " LLSD type: " << (S32)(*current)[label][ mMetricStrings[index] ].type() << LL_ENDL ; } } } incrementCurrentCount(); current_label = getCurrentLabelName(); in_base = (*base).has(current_label) ; in_current = (*current).has(current_label) ; } } /*virtual*/ void LLMetricPerformanceTesterBasic::compareTestResults(llofstream* os, std::string metric_string, S32 v_base, S32 v_current) { *os << llformat(" ,%s, %d, %d, %d, %.4f\n", metric_string.c_str(), v_base, v_current, v_current - v_base, (v_base != 0) ? 100.f * v_current / v_base : 0) ; } /*virtual*/ void LLMetricPerformanceTesterBasic::compareTestResults(llofstream* os, std::string metric_string, F32 v_base, F32 v_current) { *os << llformat(" ,%s, %.4f, %.4f, %.4f, %.4f\n", metric_string.c_str(), v_base, v_current, v_current - v_base, (fabs(v_base) > 0.0001f) ? 100.f * v_current / v_base : 0.f ) ; } //---------------------------------------------------------------------------------------------- // LLMetricPerformanceTesterWithSession //---------------------------------------------------------------------------------------------- LLMetricPerformanceTesterWithSession::LLMetricPerformanceTesterWithSession(std::string name) : LLMetricPerformanceTesterBasic(name), mBaseSessionp(NULL), mCurrentSessionp(NULL) { } LLMetricPerformanceTesterWithSession::~LLMetricPerformanceTesterWithSession() { if (mBaseSessionp) { delete mBaseSessionp ; mBaseSessionp = NULL ; } if (mCurrentSessionp) { delete mCurrentSessionp ; mCurrentSessionp = NULL ; } } /*virtual*/ void LLMetricPerformanceTesterWithSession::analyzePerformance(llofstream* os, LLSD* base, LLSD* current) { // Load the base session resetCurrentCount() ; mBaseSessionp = loadTestSession(base) ; // Load the current session resetCurrentCount() ; mCurrentSessionp = loadTestSession(current) ; if (!mBaseSessionp || !mCurrentSessionp) { LL_ERRS() << "Error loading test sessions." << LL_ENDL ; } // Compare compareTestSessions(os) ; // Release memory if (mBaseSessionp) { delete mBaseSessionp ; mBaseSessionp = NULL ; } if (mCurrentSessionp) { delete mCurrentSessionp ; mCurrentSessionp = NULL ; } } //---------------------------------------------------------------------------------------------- // LLTestSession //---------------------------------------------------------------------------------------------- LLMetricPerformanceTesterWithSession::LLTestSession::~LLTestSession() { }