diff options
Diffstat (limited to 'indra/llcommon/lltrace.h')
-rw-r--r-- | indra/llcommon/lltrace.h | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h new file mode 100644 index 0000000000..61d14569cd --- /dev/null +++ b/indra/llcommon/lltrace.h @@ -0,0 +1,446 @@ +/** + * @file lltrace.h + * @brief Runtime statistics accumulation. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef LL_LLTRACE_H +#define LL_LLTRACE_H + +#include "stdtypes.h" +#include "llpreprocessor.h" + +#include "llmemory.h" +#include "llrefcount.h" +#include "llunit.h" +#include "llapr.h" + +#include <list> + +#define LL_RECORD_BLOCK_TIME(block_timer) LLTrace::BlockTimer::Recorder LL_GLUE_TOKENS(block_time_recorder, __COUNTER__)(block_timer); + +namespace LLTrace +{ + class Recording; + + typedef LLUnit<LLUnits::Bytes, F64> Bytes; + typedef LLUnit<LLUnits::Kilobytes, F64> Kilobytes; + typedef LLUnit<LLUnits::Megabytes, F64> Megabytes; + typedef LLUnit<LLUnits::Gigabytes, F64> Gigabytes; + typedef LLUnit<LLUnits::Bits, F64> Bits; + typedef LLUnit<LLUnits::Kilobits, F64> Kilobits; + typedef LLUnit<LLUnits::Megabits, F64> Megabits; + typedef LLUnit<LLUnits::Gigabits, F64> Gigabits; + + typedef LLUnit<LLUnits::Seconds, F64> Seconds; + typedef LLUnit<LLUnits::Milliseconds, F64> Milliseconds; + typedef LLUnit<LLUnits::Minutes, F64> Minutes; + typedef LLUnit<LLUnits::Hours, F64> Hours; + typedef LLUnit<LLUnits::Milliseconds, F64> Milliseconds; + typedef LLUnit<LLUnits::Microseconds, F64> Microseconds; + typedef LLUnit<LLUnits::Nanoseconds, F64> Nanoseconds; + + typedef LLUnit<LLUnits::Meters, F64> Meters; + typedef LLUnit<LLUnits::Kilometers, F64> Kilometers; + typedef LLUnit<LLUnits::Centimeters, F64> Centimeters; + typedef LLUnit<LLUnits::Millimeters, F64> Millimeters; + + void init(); + void cleanup(); + bool isInitialized(); + + LLThreadLocalPointer<class ThreadRecorder>& get_thread_recorder(); + + class MasterThreadRecorder& getMasterThreadRecorder(); + + // one per thread per type + template<typename ACCUMULATOR> + class AccumulatorBuffer : public LLRefCount + { + static const U32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 64; + private: + enum StaticAllocationMarker { STATIC_ALLOC }; + + AccumulatorBuffer(StaticAllocationMarker m) + : mStorageSize(64), + mNextStorageSlot(0), + mStorage(new ACCUMULATOR[DEFAULT_ACCUMULATOR_BUFFER_SIZE]) + {} + + public: + + AccumulatorBuffer(const AccumulatorBuffer& other = getDefaultBuffer()) + : mStorageSize(other.mStorageSize), + mStorage(new ACCUMULATOR[other.mStorageSize]), + mNextStorageSlot(other.mNextStorageSlot) + { + for (S32 i = 0; i < mNextStorageSlot; i++) + { + mStorage[i] = other.mStorage[i]; + } + } + + ~AccumulatorBuffer() + { + if (sPrimaryStorage == mStorage) + { + //TODO pick another primary? + sPrimaryStorage = NULL; + } + } + + LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index) + { + return mStorage[index]; + } + + LL_FORCE_INLINE const ACCUMULATOR& operator[](size_t index) const + { + return mStorage[index]; + } + + void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other) + { + llassert(mNextStorageSlot == other.mNextStorageSlot); + + for (size_t i = 0; i < mNextStorageSlot; i++) + { + mStorage[i].addSamples(other.mStorage[i]); + } + } + + void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other) + { + for (size_t i = 0; i < mNextStorageSlot; i++) + { + mStorage[i] = other.mStorage[i]; + } + } + + void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL) + { + for (size_t i = 0; i < mNextStorageSlot; i++) + { + mStorage[i].reset(other ? &other->mStorage[i] : NULL); + } + } + + void makePrimary() + { + sPrimaryStorage = mStorage; + } + + bool isPrimary() const + { + return sPrimaryStorage == mStorage; + } + + LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage() + { + return sPrimaryStorage.get(); + } + + // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned + size_t reserveSlot() + { + if (LLTrace::isInitialized()) + { + llerrs << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << llendl; + } + size_t next_slot = mNextStorageSlot++; + if (next_slot >= mStorageSize) + { + resize(mStorageSize + (mStorageSize >> 2)); + } + llassert(mStorage && next_slot < mStorageSize); + return next_slot; + } + + void resize(size_t new_size) + { + ACCUMULATOR* old_storage = mStorage; + mStorage = new ACCUMULATOR[new_size]; + for (S32 i = 0; i < mStorageSize; i++) + { + mStorage[i] = old_storage[i]; + } + mStorageSize = new_size; + delete[] old_storage; + } + + static AccumulatorBuffer<ACCUMULATOR>& getDefaultBuffer() + { + static AccumulatorBuffer sBuffer(STATIC_ALLOC); + return sBuffer; + } + + private: + ACCUMULATOR* mStorage; + size_t mStorageSize; + size_t mNextStorageSlot; + static LLThreadLocalPointer<ACCUMULATOR> sPrimaryStorage; + }; + template<typename ACCUMULATOR> LLThreadLocalPointer<ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>::sPrimaryStorage; + + template<typename ACCUMULATOR> + class TraceType + : public LLInstanceTracker<TraceType<ACCUMULATOR>, std::string> + { + public: + TraceType(const char* name, const char* description = NULL) + : LLInstanceTracker(name), + mName(name), + mDescription(description ? description : "") + { + mAccumulatorIndex = AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer().reserveSlot(); + } + + LL_FORCE_INLINE ACCUMULATOR& getPrimaryAccumulator() + { + return AccumulatorBuffer<ACCUMULATOR>::getPrimaryStorage()[mAccumulatorIndex]; + } + + size_t getIndex() const { return mAccumulatorIndex; } + + std::string& getName() { return mName; } + const std::string& getName() const { return mName; } + + protected: + std::string mName; + std::string mDescription; + size_t mAccumulatorIndex; + }; + + template<typename T> + class MeasurementAccumulator + { + public: + typedef T value_t; + typedef MeasurementAccumulator<T> self_t; + + MeasurementAccumulator() + : mSum(0), + mMin((std::numeric_limits<T>::max)()), + mMax((std::numeric_limits<T>::min)()), + mMean(0), + mVarianceSum(0), + mNumSamples(0), + mLastValue(0) + {} + + LL_FORCE_INLINE void sample(T value) + { + T storage_value(value); + mNumSamples++; + mSum += storage_value; + if (storage_value < mMin) + { + mMin = storage_value; + } + if (storage_value > mMax) + { + mMax = storage_value; + } + F64 old_mean = mMean; + mMean += ((F64)storage_value - old_mean) / (F64)mNumSamples; + mVarianceSum += ((F64)storage_value - old_mean) * ((F64)storage_value - mMean); + mLastValue = storage_value; + } + + void addSamples(const self_t& other) + { + if (other.mNumSamples) + { + mSum += other.mSum; + if (other.mMin < mMin) + { + mMin = other.mMin; + } + if (other.mMax > mMax) + { + mMax = other.mMax; + } + F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples); + mNumSamples += other.mNumSamples; + mMean = mMean * weight + other.mMean * (1.f - weight); + + F64 n_1 = (F64)mNumSamples, + n_2 = (F64)other.mNumSamples; + F64 m_1 = mMean, + m_2 = other.mMean; + F64 sd_1 = getStandardDeviation(), + sd_2 = other.getStandardDeviation(); + // combine variance (and hence standard deviation) of 2 different sized sample groups using + // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm + if (n_1 == 0) + { + mVarianceSum = other.mVarianceSum; + } + else if (n_2 == 0) + { + // don't touch variance + // mVarianceSum = mVarianceSum; + } + else + { + mVarianceSum = (F64)mNumSamples + * ((((n_1 - 1.f) * sd_1 * sd_1) + + ((n_2 - 1.f) * sd_2 * sd_2) + + (((n_1 * n_2) / (n_1 + n_2)) + * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2)))) + / (n_1 + n_2 - 1.f)); + } + mLastValue = other.mLastValue; + } + } + + void reset(const self_t* other) + { + mNumSamples = 0; + mSum = 0; + mMin = 0; + mMax = 0; + mMean = 0; + mVarianceSum = 0; + mLastValue = other ? other->mLastValue : 0; + } + + T getSum() const { return (T)mSum; } + T getMin() const { return (T)mMin; } + T getMax() const { return (T)mMax; } + T getLastValue() const { return (T)mLastValue; } + F64 getMean() const { return mMean; } + F64 getStandardDeviation() const { return sqrtf(mVarianceSum / mNumSamples); } + U32 getSampleCount() const { return mNumSamples; } + + private: + T mSum, + mMin, + mMax, + mLastValue; + + F64 mMean, + mVarianceSum; + + U32 mNumSamples; + }; + + template<typename T> + class CountAccumulator + { + public: + typedef CountAccumulator<T> self_t; + typedef T value_t; + + CountAccumulator() + : mSum(0), + mNumSamples(0) + {} + + LL_FORCE_INLINE void add(T value) + { + mNumSamples++; + mSum += value; + } + + void addSamples(const CountAccumulator<T>& other) + { + mSum += other.mSum; + mNumSamples += other.mNumSamples; + } + + void reset(const self_t* other) + { + mNumSamples = 0; + mSum = 0; + } + + T getSum() const { return (T)mSum; } + + U32 getSampleCount() const { return mNumSamples; } + + private: + T mSum; + + U32 mNumSamples; + }; + + class TimerAccumulator + { + public: + TimerAccumulator(); + void addSamples(const TimerAccumulator& other); + void reset(const TimerAccumulator* other); + + // + // members + // + U64 mSelfTimeCounter, + mTotalTimeCounter; + U32 mCalls; + class BlockTimer* mLastCaller; // used to bootstrap tree construction + U16 mActiveCount; // number of timers with this ID active on stack + bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame + }; + + + template <typename T = F64> + class Measurement + : public TraceType<MeasurementAccumulator<typename LLUnits::HighestPrecisionType<T>::type_t> > + { + public: + typedef typename LLUnits::HighestPrecisionType<T>::type_t storage_t; + + Measurement(const char* name, const char* description = NULL) + : TraceType(name, description) + {} + + template<typename UNIT_T> + void sample(UNIT_T value) + { + T converted_value(value); + getPrimaryAccumulator().sample((storage_t)converted_value); + } + }; + + template <typename T = F64> + class Count + : public TraceType<CountAccumulator<typename LLUnits::HighestPrecisionType<T>::type_t> > + { + public: + typedef typename LLUnits::HighestPrecisionType<T>::type_t storage_t; + + Count(const char* name, const char* description = NULL) + : TraceType(name) + {} + + template<typename UNIT_T> + void add(UNIT_T value) + { + T converted_value(value); + getPrimaryAccumulator().add((storage_t)converted_value); + } + }; +} + +#endif // LL_LLTRACE_H |