/** * @file llstat.cpp * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2008, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "linden_common.h" #include "llstat.h" #include "lllivefile.h" #include "llerrorcontrol.h" #include "llframetimer.h" #include "timing.h" #include "llsd.h" #include "llsdserialize.h" #include "llstl.h" #include "u64.h" // statics BOOL LLPerfBlock::sStatsEnabled = FALSE; // Flag for detailed information LLPerfBlock::stat_map_t LLPerfBlock::sStatMap; // Map full path string to LLStatTime objects, tracks all active objects std::string LLPerfBlock::sCurrentStatPath = ""; // Something like "/total_time/physics/physics step" //------------------------------------------------------------------------ // Live config file to trigger stats logging static const char STATS_CONFIG_FILE_NAME[] = "/dev/shm/simperf/simperf_proc_config.llsd"; static const F32 STATS_CONFIG_REFRESH_RATE = 5.0; // seconds class LLStatsConfigFile : public LLLiveFile { public: LLStatsConfigFile() : LLLiveFile(filename(), STATS_CONFIG_REFRESH_RATE), mChanged(false), mStatsp(NULL) { } static std::string filename(); protected: /* virtual */ void loadFile(); public: void init(LLPerfStats* statsp); static LLStatsConfigFile& instance(); // return the singleton stats config file bool mChanged; protected: LLPerfStats* mStatsp; }; std::string LLStatsConfigFile::filename() { return STATS_CONFIG_FILE_NAME; } void LLStatsConfigFile::init(LLPerfStats* statsp) { mStatsp = statsp; } LLStatsConfigFile& LLStatsConfigFile::instance() { static LLStatsConfigFile the_file; return the_file; } /* virtual */ // Load and parse the stats configuration file void LLStatsConfigFile::loadFile() { if (!mStatsp) { llwarns << "Tries to load performance configure file without initializing LPerfStats" << llendl; return; } mChanged = true; LLSD stats_config; { llifstream file(filename().c_str()); if (file.is_open()) { LLSDSerialize::fromXML(stats_config, file); if (stats_config.isUndefined()) { llinfos << "Performance statistics configuration file ill-formed, not recording statistics" << llendl; mStatsp->setReportPerformanceDuration( 0.f ); return; } } else { // File went away, turn off stats if it was on if ( mStatsp->frameStatsIsRunning() ) { llinfos << "Performance statistics configuration file deleted, not recording statistics" << llendl; mStatsp->setReportPerformanceDuration( 0.f ); } return; } } F32 duration = 0.f; F32 interval = 0.f; const char * w = "duration"; if (stats_config.has(w)) { duration = (F32)stats_config[w].asReal(); } w = "interval"; if (stats_config.has(w)) { interval = (F32)stats_config[w].asReal(); } mStatsp->setReportPerformanceDuration( duration ); mStatsp->setReportPerformanceInterval( interval ); if ( duration > 0 ) { if ( interval == 0.f ) { llinfos << "Recording performance stats every frame for " << duration << " sec" << llendl; } else { llinfos << "Recording performance stats every " << interval << " seconds for " << duration << " seconds" << llendl; } } else { llinfos << "Performance stats recording turned off" << llendl; } } //------------------------------------------------------------------------ LLPerfStats::LLPerfStats(const std::string& process_name, S32 process_pid) : mFrameStatsFileFailure(FALSE), mSkipFirstFrameStats(FALSE), mProcessName(process_name), mProcessPID(process_pid), mReportPerformanceStatInterval(1.f), mReportPerformanceStatEnd(0.0) { } LLPerfStats::~LLPerfStats() { LLPerfBlock::clearDynamicStats(); mFrameStatsFile.close(); } void LLPerfStats::init() { // Initialize the stats config file instance. (void) LLStatsConfigFile::instance().init(this); (void) LLStatsConfigFile::instance().checkAndReload(); } // Open file for statistics void LLPerfStats::openPerfStatsFile() { if ( !mFrameStatsFile && !mFrameStatsFileFailure ) { std::string stats_file = llformat("/dev/shm/simperf/%s_proc.%d.llsd", mProcessName.c_str(), mProcessPID); mFrameStatsFile.close(); mFrameStatsFile.clear(); mFrameStatsFile.open(stats_file, llofstream::out); if ( mFrameStatsFile.fail() ) { llinfos << "Error opening statistics log file " << stats_file << llendl; mFrameStatsFileFailure = TRUE; } else { LLSD process_info = LLSD::emptyMap(); process_info["name"] = mProcessName; process_info["pid"] = (LLSD::Integer) mProcessPID; process_info["stat_rate"] = (LLSD::Integer) mReportPerformanceStatInterval; // Add process-specific info. addProcessHeaderInfo(process_info); mFrameStatsFile << LLSDNotationStreamer(process_info) << std::endl; } } } // Dump out performance metrics over some time interval void LLPerfStats::dumpIntervalPerformanceStats() { // Ensure output file is OK openPerfStatsFile(); if ( mFrameStatsFile ) { LLSD stats = LLSD::emptyMap(); LLStatAccum::TimeScale scale; if ( getReportPerformanceInterval() == 0.f ) { scale = LLStatAccum::SCALE_PER_FRAME; } else if ( getReportPerformanceInterval() < 0.5f ) { scale = LLStatAccum::SCALE_100MS; } else { scale = LLStatAccum::SCALE_SECOND; } // Write LLSD into log stats["utc_time"] = (LLSD::String) LLError::utcTime(); stats["timestamp"] = U64_to_str((totalTime() / 1000) + (gUTCOffset * 1000)); // milliseconds since epoch stats["frame_number"] = (LLSD::Integer) LLFrameTimer::getFrameCount(); // Add process-specific frame info. addProcessFrameInfo(stats, scale); LLPerfBlock::addStatsToLLSDandReset( stats, scale ); mFrameStatsFile << LLSDNotationStreamer(stats) << std::endl; } } // Set length of performance stat recording void LLPerfStats::setReportPerformanceDuration( F32 seconds ) { if ( seconds <= 0.f ) { mReportPerformanceStatEnd = 0.0; LLPerfBlock::setStatsEnabled( FALSE ); mFrameStatsFile.close(); LLPerfBlock::clearDynamicStats(); } else { mReportPerformanceStatEnd = LLFrameTimer::getElapsedSeconds() + ((F64) seconds); // Clear failure flag to try and create the log file once mFrameStatsFileFailure = FALSE; LLPerfBlock::setStatsEnabled( TRUE ); mSkipFirstFrameStats = TRUE; // Skip the first report (at the end of this frame) } } void LLPerfStats::updatePerFrameStats() { (void) LLStatsConfigFile::instance().checkAndReload(); static LLFrameTimer performance_stats_timer; if ( frameStatsIsRunning() ) { if ( mReportPerformanceStatInterval == 0 ) { // Record info every frame if ( mSkipFirstFrameStats ) { // Skip the first time - was started this frame mSkipFirstFrameStats = FALSE; } else { dumpIntervalPerformanceStats(); } } else { performance_stats_timer.setTimerExpirySec( getReportPerformanceInterval() ); if (performance_stats_timer.checkExpirationAndReset( mReportPerformanceStatInterval )) { dumpIntervalPerformanceStats(); } } if ( LLFrameTimer::getElapsedSeconds() > mReportPerformanceStatEnd ) { // Reached end of time, clear it to stop reporting setReportPerformanceDuration(0.f); // Don't set mReportPerformanceStatEnd directly llinfos << "Recording performance stats completed" << llendl; } } } //------------------------------------------------------------------------ U64 LLStatAccum::sScaleTimes[NUM_SCALES] = { USEC_PER_SEC / 10, // 100 millisec USEC_PER_SEC * 1, // seconds USEC_PER_SEC * 60, // minutes #if ENABLE_LONG_TIME_STATS // enable these when more time scales are desired USEC_PER_SEC * 60*60, // hours USEC_PER_SEC * 24*60*60, // days USEC_PER_SEC * 7*24*60*60, // weeks #endif }; LLStatAccum::LLStatAccum(bool useFrameTimer) : mUseFrameTimer(useFrameTimer), mRunning(FALSE), mLastSampleValue(0.0), mLastSampleValid(FALSE) { } LLStatAccum::~LLStatAccum() { } void LLStatAccum::reset(U64 when) { mRunning = TRUE; mLastTime = when; for (int i = 0; i < NUM_SCALES; ++i) { mBuckets[i].accum = 0.0; mBuckets[i].endTime = when + sScaleTimes[i]; mBuckets[i].lastValid = FALSE; } } void LLStatAccum::sum(F64 value) { sum(value, getCurrentUsecs()); } void LLStatAccum::sum(F64 value, U64 when) { if (!mRunning) { reset(when); return; } if (when < mLastTime) { // This happens a LOT on some dual core systems. lldebugs << "LLStatAccum::sum clock has gone backwards from " << mLastTime << " to " << when << ", resetting" << llendl; reset(when); return; } // how long is this value for U64 timeSpan = when - mLastTime; for (int i = 0; i < NUM_SCALES; ++i) { Bucket& bucket = mBuckets[i]; if (when < bucket.endTime) { bucket.accum += value; } else { U64 timeScale = sScaleTimes[i]; U64 timeLeft = when - bucket.endTime; // how much time is left after filling this bucket if (timeLeft < timeScale) { F64 valueLeft = value * timeLeft / timeSpan; bucket.lastValid = TRUE; bucket.lastAccum = bucket.accum + (value - valueLeft); bucket.accum = valueLeft; bucket.endTime += timeScale; } else { U64 timeTail = timeLeft % timeScale; bucket.lastValid = TRUE; bucket.lastAccum = value * timeScale / timeSpan; bucket.accum = value * timeTail / timeSpan; bucket.endTime += (timeLeft - timeTail) + timeScale; } } } mLastTime = when; } F32 LLStatAccum::meanValue(TimeScale scale) const { if (!mRunning) { return 0.0; } if ( scale == SCALE_PER_FRAME ) { // Per-frame not supported here scale = SCALE_100MS; } if (scale < 0 || scale >= NUM_SCALES) { llwarns << "llStatAccum::meanValue called for unsupported scale: " << scale << llendl; return 0.0; } const Bucket& bucket = mBuckets[scale]; F64 value = bucket.accum; U64 timeLeft = bucket.endTime - mLastTime; U64 scaleTime = sScaleTimes[scale]; if (bucket.lastValid) { value += bucket.lastAccum * timeLeft / scaleTime; } else if (timeLeft < scaleTime) { value *= scaleTime / (scaleTime - timeLeft); } else { value = 0.0; } return (F32)(value / scaleTime); } U64 LLStatAccum::getCurrentUsecs() const { if (mUseFrameTimer) { return LLFrameTimer::getTotalTime(); } else { return totalTime(); } } // ------------------------------------------------------------------------ LLStatRate::LLStatRate(bool use_frame_timer) : LLStatAccum(use_frame_timer) { } void LLStatRate::count(U32 value) { sum((F64)value * sScaleTimes[SCALE_SECOND]); } void LLStatRate::mark() { // Effectively the same as count(1), but sets mLastSampleValue U64 when = getCurrentUsecs(); if ( mRunning && (when > mLastTime) ) { // Set mLastSampleValue to the time from the last mark() F64 duration = ((F64)(when - mLastTime)) / sScaleTimes[SCALE_SECOND]; if ( duration > 0.0 ) { mLastSampleValue = 1.0 / duration; } else { mLastSampleValue = 0.0; } } sum( (F64) sScaleTimes[SCALE_SECOND], when); } // ------------------------------------------------------------------------ LLStatMeasure::LLStatMeasure(bool use_frame_timer) : LLStatAccum(use_frame_timer) { } void LLStatMeasure::sample(F64 value) { U64 when = getCurrentUsecs(); if (mLastSampleValid) { F64 avgValue = (value + mLastSampleValue) / 2.0; F64 interval = (F64)(when - mLastTime); sum(avgValue * interval, when); } else { reset(when); } mLastSampleValid = TRUE; mLastSampleValue = value; } // ------------------------------------------------------------------------ LLStatTime::LLStatTime(const std::string & key) : LLStatAccum(false), mFrameNumber(LLFrameTimer::getFrameCount()), mTotalTimeInFrame(0), mKey(key) #if LL_DEBUG , mRunning(FALSE) #endif { } void LLStatTime::start() { // Reset frame accumluation if the frame number has changed U32 frame_number = LLFrameTimer::getFrameCount(); if ( frame_number != mFrameNumber ) { mFrameNumber = frame_number; mTotalTimeInFrame = 0; } sum(0.0); #if LL_DEBUG // Shouldn't be running already llassert( !mRunning ); mRunning = TRUE; #endif } void LLStatTime::stop() { U64 end_time = getCurrentUsecs(); U64 duration = end_time - mLastTime; sum(F64(duration), end_time); //llinfos << "mTotalTimeInFrame incremented from " << mTotalTimeInFrame << " to " << (mTotalTimeInFrame + duration) << llendl; mTotalTimeInFrame += duration; #if LL_DEBUG mRunning = FALSE; #endif } /* virtual */ F32 LLStatTime::meanValue(TimeScale scale) const { if ( LLStatAccum::SCALE_PER_FRAME == scale ) { return mTotalTimeInFrame; } else { return LLStatAccum::meanValue(scale); } } // ------------------------------------------------------------------------ // Use this constructor for pre-defined LLStatTime objects LLPerfBlock::LLPerfBlock(LLStatTime* stat ) : mPredefinedStat(stat), mDynamicStat(NULL) { if (mPredefinedStat) { // If dynamic stats are turned on, this will create a separate entry in the stat map. initDynamicStat(mPredefinedStat->mKey); // Start predefined stats. These stats are not part of the stat map. mPredefinedStat->start(); } } // Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key. // These are also turned on or off via the switch passed in LLPerfBlock::LLPerfBlock( const char* key1, const char* key2 ) : mPredefinedStat(NULL), mDynamicStat(NULL) { if (!sStatsEnabled) return; if (NULL == key2 || strlen(key2) == 0) { initDynamicStat(key1); } else { std::ostringstream key; key << key1 << "_" << key2; initDynamicStat(key.str()); } } void LLPerfBlock::initDynamicStat(const std::string& key) { // Early exit if dynamic stats aren't enabled. if (!sStatsEnabled) return; mLastPath = sCurrentStatPath; // Save and restore current path sCurrentStatPath += "/" + key; // Add key to current path // See if the LLStatTime object already exists stat_map_t::iterator iter = sStatMap.find(sCurrentStatPath); if ( iter == sStatMap.end() ) { // StatEntry object doesn't exist, so create it mDynamicStat = new StatEntry( key ); sStatMap[ sCurrentStatPath ] = mDynamicStat; // Set the entry for this path } else { // Found this path in the map, use the object there mDynamicStat = (*iter).second; // Get StatEntry for the current path } if (mDynamicStat) { mDynamicStat->mStat.start(); mDynamicStat->mCount++; } else { llwarns << "Initialized NULL dynamic stat at '" << sCurrentStatPath << "'" << llendl; sCurrentStatPath = mLastPath; } } // Destructor does the time accounting LLPerfBlock::~LLPerfBlock() { if (mPredefinedStat) mPredefinedStat->stop(); if (mDynamicStat) { mDynamicStat->mStat.stop(); sCurrentStatPath = mLastPath; // Restore the path in case sStatsEnabled changed during this block } } // Clear the map of any dynamic stats. Static routine void LLPerfBlock::clearDynamicStats() { std::for_each(sStatMap.begin(), sStatMap.end(), DeletePairedPointer()); sStatMap.clear(); } // static - Extract the stat info into LLSD void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats, LLStatAccum::TimeScale scale ) { // If we aren't in per-frame scale, we need to go from second to microsecond. U32 scale_adjustment = 1; if (LLStatAccum::SCALE_PER_FRAME != scale) { scale_adjustment = USEC_PER_SEC; } stat_map_t::iterator iter = sStatMap.begin(); for ( ; iter != sStatMap.end(); ++iter ) { // Put the entry into LLSD "/full/path/to/stat/" = microsecond total time const std::string & stats_full_path = (*iter).first; StatEntry * stat = (*iter).second; if (stat) { if (stat->mCount > 0) { stats[stats_full_path] = LLSD::emptyMap(); stats[stats_full_path]["us"] = (LLSD::Integer) (scale_adjustment * stat->mStat.meanValue(scale)); if (stat->mCount > 1) { stats[stats_full_path]["count"] = (LLSD::Integer) stat->mCount; } stat->mCount = 0; } } else { // WTF? Shouldn't have a NULL pointer in the map. llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl; } } } // ------------------------------------------------------------------------ LLTimer LLStat::sTimer; LLFrameTimer LLStat::sFrameTimer; LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer) { llassert(num_bins > 0); U32 i; mUseFrameTimer = use_frame_timer; mNumValues = 0; mLastValue = 0.f; mLastTime = 0.f; mNumBins = num_bins; mCurBin = (mNumBins-1); mNextBin = 0; mBins = new F32[mNumBins]; mBeginTime = new F64[mNumBins]; mTime = new F64[mNumBins]; mDT = new F32[mNumBins]; for (i = 0; i < mNumBins; i++) { mBins[i] = 0.f; mBeginTime[i] = 0.0; mTime[i] = 0.0; mDT[i] = 0.f; } } LLStat::~LLStat() { delete[] mBins; delete[] mBeginTime; delete[] mTime; delete[] mDT; } void LLStat::reset() { U32 i; mNumValues = 0; mLastValue = 0.f; mCurBin = (mNumBins-1); delete[] mBins; delete[] mBeginTime; delete[] mTime; delete[] mDT; mBins = new F32[mNumBins]; mBeginTime = new F64[mNumBins]; mTime = new F64[mNumBins]; mDT = new F32[mNumBins]; for (i = 0; i < mNumBins; i++) { mBins[i] = 0.f; mBeginTime[i] = 0.0; mTime[i] = 0.0; mDT[i] = 0.f; } } void LLStat::setBeginTime(const F64 time) { mBeginTime[mNextBin] = time; } void LLStat::addValueTime(const F64 time, const F32 value) { if (mNumValues < mNumBins) { mNumValues++; } // Increment the bin counters. mCurBin++; if ((U32)mCurBin == mNumBins) { mCurBin = 0; } mNextBin++; if ((U32)mNextBin == mNumBins) { mNextBin = 0; } mBins[mCurBin] = value; mTime[mCurBin] = time; mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]); //this value is used to prime the min/max calls mLastTime = mTime[mCurBin]; mLastValue = value; // Set the begin time for the next stat segment. mBeginTime[mNextBin] = mTime[mCurBin]; mTime[mNextBin] = mTime[mCurBin]; mDT[mNextBin] = 0.f; } void LLStat::start() { if (mUseFrameTimer) { mBeginTime[mNextBin] = sFrameTimer.getElapsedSeconds(); } else { mBeginTime[mNextBin] = sTimer.getElapsedTimeF64(); } } void LLStat::addValue(const F32 value) { if (mNumValues < mNumBins) { mNumValues++; } // Increment the bin counters. mCurBin++; if ((U32)mCurBin == mNumBins) { mCurBin = 0; } mNextBin++; if ((U32)mNextBin == mNumBins) { mNextBin = 0; } mBins[mCurBin] = value; if (mUseFrameTimer) { mTime[mCurBin] = sFrameTimer.getElapsedSeconds(); } else { mTime[mCurBin] = sTimer.getElapsedTimeF64(); } mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]); //this value is used to prime the min/max calls mLastTime = mTime[mCurBin]; mLastValue = value; // Set the begin time for the next stat segment. mBeginTime[mNextBin] = mTime[mCurBin]; mTime[mNextBin] = mTime[mCurBin]; mDT[mNextBin] = 0.f; } F32 LLStat::getMax() const { U32 i; F32 current_max = mLastValue; if (mNumBins == 0) { current_max = 0.f; } else { for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } if (mBins[i] > current_max) { current_max = mBins[i]; } } } return current_max; } F32 LLStat::getMean() const { U32 i; F32 current_mean = 0.f; U32 samples = 0; for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } current_mean += mBins[i]; samples++; } // There will be a wrap error at 2^32. :) if (samples != 0) { current_mean /= samples; } else { current_mean = 0.f; } return current_mean; } F32 LLStat::getMin() const { U32 i; F32 current_min = mLastValue; if (mNumBins == 0) { current_min = 0.f; } else { for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } if (mBins[i] < current_min) { current_min = mBins[i]; } } } return current_min; } F32 LLStat::getSum() const { U32 i; F32 sum = 0.f; for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } sum += mBins[i]; } return sum; } F32 LLStat::getSumDuration() const { U32 i; F32 sum = 0.f; for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } sum += mDT[i]; } return sum; } F32 LLStat::getPrev(S32 age) const { S32 bin; bin = mCurBin - age; while (bin < 0) { bin += mNumBins; } if (bin == mNextBin) { // Bogus for bin we're currently working on. return 0.f; } return mBins[bin]; } F32 LLStat::getPrevPerSec(S32 age) const { S32 bin; bin = mCurBin - age; while (bin < 0) { bin += mNumBins; } if (bin == mNextBin) { // Bogus for bin we're currently working on. return 0.f; } return mBins[bin] / mDT[bin]; } F64 LLStat::getPrevBeginTime(S32 age) const { S32 bin; bin = mCurBin - age; while (bin < 0) { bin += mNumBins; } if (bin == mNextBin) { // Bogus for bin we're currently working on. return 0.f; } return mBeginTime[bin]; } F64 LLStat::getPrevTime(S32 age) const { S32 bin; bin = mCurBin - age; while (bin < 0) { bin += mNumBins; } if (bin == mNextBin) { // Bogus for bin we're currently working on. return 0.f; } return mTime[bin]; } F32 LLStat::getBin(S32 bin) const { return mBins[bin]; } F32 LLStat::getBinPerSec(S32 bin) const { return mBins[bin] / mDT[bin]; } F64 LLStat::getBinBeginTime(S32 bin) const { return mBeginTime[bin]; } F64 LLStat::getBinTime(S32 bin) const { return mTime[bin]; } F32 LLStat::getCurrent() const { return mBins[mCurBin]; } F32 LLStat::getCurrentPerSec() const { return mBins[mCurBin] / mDT[mCurBin]; } F64 LLStat::getCurrentBeginTime() const { return mBeginTime[mCurBin]; } F64 LLStat::getCurrentTime() const { return mTime[mCurBin]; } F32 LLStat::getCurrentDuration() const { return mDT[mCurBin]; } F32 LLStat::getMeanPerSec() const { U32 i; F32 value = 0.f; F32 dt = 0.f; for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } value += mBins[i]; dt += mDT[i]; } if (dt > 0.f) { return value/dt; } else { return 0.f; } } F32 LLStat::getMeanDuration() const { F32 dur = 0.0f; U32 count = 0; for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++) { if (i == (U32)mNextBin) { continue; } dur += mDT[i]; count++; } if (count > 0) { dur /= F32(count); return dur; } else { return 0.f; } } F32 LLStat::getMaxPerSec() const { U32 i; F32 value; if (mNextBin != 0) { value = mBins[0]/mDT[0]; } else if (mNumValues > 0) { value = mBins[1]/mDT[1]; } else { value = 0.f; } for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } value = llmax(value, mBins[i]/mDT[i]); } return value; } F32 LLStat::getMinPerSec() const { U32 i; F32 value; if (mNextBin != 0) { value = mBins[0]/mDT[0]; } else if (mNumValues > 0) { value = mBins[1]/mDT[1]; } else { value = 0.f; } for (i = 0; (i < mNumBins) && (i < mNumValues); i++) { // Skip the bin we're currently filling. if (i == (U32)mNextBin) { continue; } value = llmin(value, mBins[i]/mDT[i]); } return value; } F32 LLStat::getMinDuration() const { F32 dur = 0.0f; for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++) { dur = llmin(dur, mDT[i]); } return dur; } U32 LLStat::getNumValues() const { return mNumValues; } S32 LLStat::getNumBins() const { return mNumBins; } S32 LLStat::getCurBin() const { return mCurBin; } S32 LLStat::getNextBin() const { return mNextBin; } F64 LLStat::getLastTime() const { return mLastTime; }