SH-3931 WIP Interesting: Add graphs to visualize scene load metrics

reverted SlaveThreadRecorder update gating
moved processTimes() outside of Recording, so it is called only once per frame
refined sample merge logic so that multi-threaded samples do not stomp on linear history
of a stat
meow-7.2.2
Richard Linden 2013-05-30 20:15:48 -07:00
parent ae6763f7fc
commit e50e600408
6 changed files with 57 additions and 60 deletions

View File

@ -180,6 +180,7 @@ TimeBlockTreeNode& TimeBlock::getTreeNode() const
static LLFastTimer::DeclareTimer FTM_PROCESS_TIMES("Process FastTimer Times");
// not thread safe, so only call on main thread
//static
void TimeBlock::processTimes()
{
@ -195,8 +196,8 @@ void TimeBlock::processTimes()
TimeBlock& timer = *it;
if (&timer == &TimeBlock::getRootTimeBlock()) continue;
// bootstrap tree construction by attaching to last timer to be on stack
// when this timer was called
// bootstrap tree construction by attaching to last timer to be on stack
// when this timer was called
if (timer.getParent() == &TimeBlock::getRootTimeBlock())
{
TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
@ -233,30 +234,30 @@ void TimeBlock::processTimes()
TimeBlockAccumulator* accumulator = timerp->getPrimaryAccumulator();
if (accumulator->mMoveUpTree)
{
{
// since ancestors have already been visited, re-parenting won't affect tree traversal
//step up tree, bringing our descendants with us
LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
" to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL;
timerp->setParent(timerp->getParent()->getParent());
accumulator->mParent = timerp->getParent();
accumulator->mMoveUpTree = false;
//step up tree, bringing our descendants with us
LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
" to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL;
timerp->setParent(timerp->getParent()->getParent());
accumulator->mParent = timerp->getParent();
accumulator->mMoveUpTree = false;
// don't bubble up any ancestors until descendants are done bubbling up
// as ancestors may call this timer only on certain paths, so we want to resolve
// child-most block locations before their parents
it.skipAncestors();
// don't bubble up any ancestors until descendants are done bubbling up
// as ancestors may call this timer only on certain paths, so we want to resolve
// child-most block locations before their parents
it.skipAncestors();
}
}
}
}
// walk up stack of active timers and accumulate current time while leaving timing structures active
BlockTimerStackRecord* stack_record = ThreadTimerStack::getInstance();
BlockTimer* cur_timer = stack_record->mActiveTimer;
TimeBlockAccumulator* accumulator = stack_record->mTimeBlock->getPrimaryAccumulator();
// root defined by parent pointing to self
while(cur_timer && cur_timer->mParentTimerData.mActiveTimer != cur_timer)
while(cur_timer
&& cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self
{
U64 cumulative_time_delta = cur_time - cur_timer->mStartTime;
accumulator->mTotalTimeCounter += cumulative_time_delta - (accumulator->mTotalTimeCounter - cur_timer->mBlockStartTotalTimeCounter);
@ -413,8 +414,11 @@ TimeBlockAccumulator::TimeBlockAccumulator()
mParent(NULL)
{}
void TimeBlockAccumulator::addSamples( const TimeBlockAccumulator& other )
void TimeBlockAccumulator::addSamples( const TimeBlockAccumulator& other, bool append )
{
// we can't merge two unrelated time block samples, as that will screw with the nested timings
// due to the call hierarchy of each thread
llassert(append);
mTotalTimeCounter += other.mTotalTimeCounter - other.mStartTotalTimeCounter;
mSelfTimeCounter += other.mSelfTimeCounter;
mCalls += other.mCalls;

View File

@ -121,12 +121,12 @@ public:
return mStorage[index];
}
void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other)
void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, bool append = true)
{
llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot);
for (size_t i = 0; i < sNextStorageSlot; i++)
{
mStorage[i].addSamples(other.mStorage[i]);
mStorage[i].addSamples(other.mStorage[i], append);
}
}
@ -310,7 +310,7 @@ public:
mLastValue = value;
}
void addSamples(const self_t& other)
void addSamples(const self_t& other, bool append)
{
if (other.mNumSamples)
{
@ -350,7 +350,7 @@ public:
F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples);
mNumSamples += other.mNumSamples;
mMean = mMean * weight + other.mMean * (1.f - weight);
mLastValue = other.mLastValue;
if (append) mLastValue = other.mLastValue;
}
}
@ -434,7 +434,7 @@ public:
mHasValue = true;
}
void addSamples(const self_t& other)
void addSamples(const self_t& other, bool append)
{
if (other.mTotalSamplingTime)
{
@ -476,9 +476,12 @@ public:
mNumSamples += other.mNumSamples;
mTotalSamplingTime += other.mTotalSamplingTime;
mMean = (mMean * weight) + (other.mMean * (1.0 - weight));
mLastValue = other.mLastValue;
mLastSampleTimeStamp = other.mLastSampleTimeStamp;
mHasValue |= other.mHasValue;
if (append)
{
mLastValue = other.mLastValue;
mLastSampleTimeStamp = other.mLastSampleTimeStamp;
mHasValue |= other.mHasValue;
}
}
}
@ -551,7 +554,7 @@ public:
mSum += value;
}
void addSamples(const CountAccumulator<T>& other)
void addSamples(const CountAccumulator<T>& other, bool /*append*/)
{
mSum += other.mSum;
mNumSamples += other.mNumSamples;
@ -596,7 +599,7 @@ public:
};
TimeBlockAccumulator();
void addSamples(const self_t& other);
void addSamples(const self_t& other, bool /*append*/);
void reset(const self_t* other);
void flush() {}
@ -716,6 +719,8 @@ void add(CountStatHandle<T>& count, VALUE_T value)
struct MemStatAccumulator
{
typedef MemStatAccumulator self_t;
MemStatAccumulator()
: mSize(0),
mChildSize(0),
@ -723,7 +728,7 @@ struct MemStatAccumulator
mDeallocatedCount(0)
{}
void addSamples(const MemStatAccumulator& other)
void addSamples(const MemStatAccumulator& other, bool /*append*/)
{
mSize += other.mSize;
mChildSize += other.mChildSize;

View File

@ -97,13 +97,15 @@ void RecordingBuffers::append( const RecordingBuffers& other )
void RecordingBuffers::merge( const RecordingBuffers& other)
{
mCountsFloat.addSamples(other.mCountsFloat);
mCounts.addSamples(other.mCounts);
mSamplesFloat.addSamples(other.mSamplesFloat);
mSamples.addSamples(other.mSamples);
mEventsFloat.addSamples(other.mEventsFloat);
mEvents.addSamples(other.mEvents);
mMemStats.addSamples(other.mMemStats);
mCountsFloat.addSamples(other.mCountsFloat, false);
mCounts.addSamples(other.mCounts, false);
mSamplesFloat.addSamples(other.mSamplesFloat, false);
mSamples.addSamples(other.mSamples, false);
mEventsFloat.addSamples(other.mEventsFloat, false);
mEvents.addSamples(other.mEvents, false);
mMemStats.addSamples(other.mMemStats, false);
// for now, hold out timers from merge, need to be displayed per thread
//mStackTimers.addSamples(other.mStackTimers, false);
}
void RecordingBuffers::reset(RecordingBuffers* other)
@ -190,7 +192,6 @@ void Recording::handleStop()
{
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
mBuffers.write()->flush();
LLTrace::TimeBlock::processTimes();
LLTrace::get_thread_recorder()->deactivate(this);
}

View File

@ -202,21 +202,14 @@ SlaveThreadRecorder::~SlaveThreadRecorder()
mMasterRecorder.removeSlaveThread(this);
}
bool SlaveThreadRecorder::pushToMaster()
void SlaveThreadRecorder::pushToMaster()
{
if (mPushCount != mMasterRecorder.getPullCount())
mThreadRecording.stop();
{
mThreadRecording.stop();
{
LLMutexLock(mMasterRecorder.getSlaveListMutex());
mSharedData.appendFrom(mThreadRecording);
}
mThreadRecording.start();
mPushCount = mMasterRecorder.getPullCount();
return true;
LLMutexLock(mMasterRecorder.getSlaveListMutex());
mSharedData.appendFrom(mThreadRecording);
}
return false;
mThreadRecording.start();
}
void SlaveThreadRecorder::SharedData::appendFrom( const Recording& source )
@ -271,8 +264,6 @@ void MasterThreadRecorder::pullFromSlaveThreads()
(*it)->mSharedData.mergeTo(target_recording_buffers);
(*it)->mSharedData.reset();
}
mPullCount++;
}
void MasterThreadRecorder::addSlaveThread( class SlaveThreadRecorder* child )
@ -298,10 +289,8 @@ void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
}
}
bool MasterThreadRecorder::pushToMaster()
{
return false;
}
void MasterThreadRecorder::pushToMaster()
{}
MasterThreadRecorder::MasterThreadRecorder()
{}

View File

@ -49,7 +49,7 @@ namespace LLTrace
void deactivate(Recording* recording);
active_recording_list_t::reverse_iterator bringUpToDate(Recording* recording);
virtual bool pushToMaster() = 0;
virtual void pushToMaster() = 0;
TimeBlockTreeNode* getTimeBlockTreeNode(S32 index);
@ -80,14 +80,13 @@ namespace LLTrace
void addSlaveThread(class SlaveThreadRecorder* child);
void removeSlaveThread(class SlaveThreadRecorder* child);
/*virtual */ bool pushToMaster();
/*virtual */ void pushToMaster();
// call this periodically to gather stats data from slave threads
void pullFromSlaveThreads();
LLMutex* getSlaveListMutex() { return &mSlaveListMutex; }
U32 getPullCount() { return mPullCount; }
private:
@ -95,7 +94,6 @@ namespace LLTrace
slave_thread_recorder_list_t mSlaveThreadRecorders; // list of slave thread recorders associated with this master
LLMutex mSlaveListMutex; // protects access to slave list
LLAtomicU32 mPullCount; // number of times data has been pulled from slaves
};
class LL_COMMON_API SlaveThreadRecorder : public ThreadRecorder
@ -105,7 +103,7 @@ namespace LLTrace
~SlaveThreadRecorder();
// call this periodically to gather stats data for master thread to consume
/*virtual*/ bool pushToMaster();
/*virtual*/ void pushToMaster();
MasterThreadRecorder* mMaster;
@ -122,7 +120,6 @@ namespace LLTrace
};
SharedData mSharedData;
MasterThreadRecorder& mMasterRecorder;
U32 mPushCount;
};
}

View File

@ -1241,6 +1241,7 @@ bool LLAppViewer::mainLoop()
while (!LLApp::isExiting())
{
LLFastTimer _(FTM_FRAME);
LLTrace::TimeBlock::processTimes();
LLTrace::get_frame_recording().nextPeriod();
LLTrace::TimeBlock::logStats();