From 2c6bc5afa59a88136fd6de4ebf0cb99ea7cdef3f Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Wed, 21 Aug 2013 14:06:57 -0700
Subject: [PATCH] SH-4433 WIP Interesting: Statistics > Ping Sim is always 0 ms
 made getPrimaryAccumulator return a reference since it was an always non-null
 pointer changed unit conversion to perform lazy division in order to avoid
 truncation of timer values

---
 indra/llcommon/llerror.h                      |   2 +
 indra/llcommon/llfasttimer.cpp                |  28 +--
 indra/llcommon/llfasttimer.h                  |  20 +-
 indra/llcommon/llmemory.cpp                   |   2 +-
 indra/llcommon/lltimer.cpp                    |  92 +++++----
 indra/llcommon/lltimer.h                      |   4 +-
 indra/llcommon/lltrace.cpp                    |   2 +-
 indra/llcommon/lltrace.h                      | 100 ++++------
 indra/llcommon/lltraceaccumulators.cpp        |  14 +-
 indra/llcommon/lltraceaccumulators.h          |   3 +
 indra/llcommon/lltracethreadrecorder.cpp      |   6 +-
 indra/llcommon/llunit.h                       | 180 ++++++++++--------
 indra/llcommon/tests/llunits_test.cpp         |  23 ++-
 indra/llui/llstatbar.cpp                      |  24 +--
 indra/llui/llstatbar.h                        |   2 +-
 indra/newview/llviewerobject.cpp              |   9 +-
 .../skins/default/xui/en/floater_stats.xml    |  88 +++------
 17 files changed, 296 insertions(+), 303 deletions(-)

diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index af76a7653a3..6eaab450ed2 100755
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -346,6 +346,8 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
 #define LL_INFOS(...)	lllog(LLError::LEVEL_INFO, false, ##__VA_ARGS__)
 #define LL_WARNS(...)	lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__)
 #define LL_ERRS(...)	lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__)
+// alternative to llassert_always that prints explanatory message
+#define LL_ERRS_IF(exp, ...)	if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")"
 
 // Only print the log message once (good for warnings or infos that would otherwise
 // spam the log file over and over, such as tighter loops).
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index d99c4c990eb..ae3234a87a2 100755
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -189,15 +189,15 @@ void TimeBlock::bootstrapTimerTree()
 		// when this timer was called
 		if (timer.getParent() == &TimeBlock::getRootTimeBlock())
 		{
-			TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
+			TimeBlockAccumulator& accumulator = timer.getPrimaryAccumulator();
 
-			if (accumulator->mLastCaller)
+			if (accumulator.mLastCaller)
 			{
-				timer.setParent(accumulator->mLastCaller);
-				accumulator->mParent = accumulator->mLastCaller;
+				timer.setParent(accumulator.mLastCaller);
+				accumulator.mParent = accumulator.mLastCaller;
 			}
 			// no need to push up tree on first use, flag can be set spuriously
-			accumulator->mMoveUpTree = false;
+			accumulator.mMoveUpTree = false;
 		}
 	}
 }
@@ -223,17 +223,17 @@ void TimeBlock::incrementalUpdateTimerTree()
 		// skip root timer
 		if (timerp != &TimeBlock::getRootTimeBlock())
 		{
-			TimeBlockAccumulator* accumulator = timerp->getPrimaryAccumulator();
+			TimeBlockAccumulator& accumulator = timerp->getPrimaryAccumulator();
 
-			if (accumulator->mMoveUpTree)
+			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;
+				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
@@ -253,7 +253,7 @@ void TimeBlock::updateTimes()
 
 	U64 cur_time = getCPUClockCount64();
 	BlockTimer* cur_timer				= stack_record->mActiveTimer;
-	TimeBlockAccumulator* accumulator	= stack_record->mTimeBlock->getPrimaryAccumulator();
+	TimeBlockAccumulator* accumulator	= &stack_record->mTimeBlock->getPrimaryAccumulator();
 
 	while(cur_timer 
 		&& cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self
@@ -269,7 +269,7 @@ void TimeBlock::updateTimes()
 		cur_timer->mBlockStartTotalTimeCounter = accumulator->mTotalTimeCounter;
 
 		stack_record = &cur_timer->mParentTimerData;
-		accumulator  = stack_record->mTimeBlock->getPrimaryAccumulator();
+		accumulator  = &stack_record->mTimeBlock->getPrimaryAccumulator();
 		cur_timer    = stack_record->mActiveTimer;
 
 		stack_record->mChildTime += cumulative_time_delta;
@@ -299,10 +299,10 @@ void TimeBlock::processTimes()
 		++it)
 	{
 		TimeBlock& timer = *it;
-		TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
+		TimeBlockAccumulator& accumulator = timer.getPrimaryAccumulator();
 
-		accumulator->mLastCaller = NULL;
-		accumulator->mMoveUpTree = false;
+		accumulator.mLastCaller = NULL;
+		accumulator.mMoveUpTree = false;
 	}
 }
 
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index ccf71c3f4cb..7bad6134c5a 100755
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -257,11 +257,11 @@ LL_FORCE_INLINE BlockTimer::BlockTimer(TimeBlock& timer)
 #if FAST_TIMER_ON
 	BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
 	if (!cur_timer_data) return;
-	TimeBlockAccumulator* accumulator = timer.getPrimaryAccumulator();
-	accumulator->mActiveCount++;
-	mBlockStartTotalTimeCounter = accumulator->mTotalTimeCounter;
+	TimeBlockAccumulator& accumulator = timer.getPrimaryAccumulator();
+	accumulator.mActiveCount++;
+	mBlockStartTotalTimeCounter = accumulator.mTotalTimeCounter;
 	// keep current parent as long as it is active when we are
-	accumulator->mMoveUpTree |= (accumulator->mParent->getPrimaryAccumulator()->mActiveCount == 0);
+	accumulator.mMoveUpTree |= (accumulator.mParent->getPrimaryAccumulator().mActiveCount == 0);
 
 	// store top of stack
 	mParentTimerData = *cur_timer_data;
@@ -281,16 +281,16 @@ LL_FORCE_INLINE BlockTimer::~BlockTimer()
 	BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
 	if (!cur_timer_data) return;
 
-	TimeBlockAccumulator* accumulator = cur_timer_data->mTimeBlock->getPrimaryAccumulator();
+	TimeBlockAccumulator& accumulator = cur_timer_data->mTimeBlock->getPrimaryAccumulator();
 
-	accumulator->mCalls++;
-	accumulator->mTotalTimeCounter += total_time - (accumulator->mTotalTimeCounter - mBlockStartTotalTimeCounter);
-	accumulator->mSelfTimeCounter += total_time - cur_timer_data->mChildTime;
-	accumulator->mActiveCount--;
+	accumulator.mCalls++;
+	accumulator.mTotalTimeCounter += total_time - (accumulator.mTotalTimeCounter - mBlockStartTotalTimeCounter);
+	accumulator.mSelfTimeCounter += total_time - cur_timer_data->mChildTime;
+	accumulator.mActiveCount--;
 
 	// store last caller to bootstrap tree creation
 	// do this in the destructor in case of recursion to get topmost caller
-	accumulator->mLastCaller = mParentTimerData.mTimeBlock;
+	accumulator.mLastCaller = mParentTimerData.mTimeBlock;
 
 	// we are only tracking self time, so subtract our total time delta from parents
 	mParentTimerData.mChildTime += total_time;
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 4b21fa3bda9..06334c012a4 100755
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -130,7 +130,7 @@ void LLMemory::updateMemoryInfo()
 	}
 #else
 	//not valid for other systems for now.
-	sAllocatedMemInKB = (U32)(LLMemory::getCurrentRSS() / 1024) ;
+	sAllocatedMemInKB = (U32Bytes)LLMemory::getCurrentRSS();
 	sMaxPhysicalMemInKB = (U32Bytes)U32_MAX ;
 	sAvailPhysicalMemInKB = (U32Bytes)U32_MAX ;
 #endif
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
index da9d2b646c3..9baac20be9b 100755
--- a/indra/llcommon/lltimer.cpp
+++ b/indra/llcommon/lltimer.cpp
@@ -55,11 +55,6 @@ const F64 USEC_TO_SEC_F64 = 0.000001;
 S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
 LLTimer* LLTimer::sTimer = NULL;
 
-F64 gClockFrequency = 0.0;
-F64 gClockFrequencyInv = 0.0;
-F64 gClocksToMicroseconds = 0.0;
-U64 gTotalTimeClockCount = 0;
-U64 gLastTotalTimeClockCount = 0;
 
 //
 // Forward declarations
@@ -213,56 +208,76 @@ U64 get_clock_count()
 #endif
 
 
-void update_clock_frequencies()
+struct TimerInfo
 {
-	gClockFrequency = calc_clock_frequency(50U);
-	gClockFrequencyInv = 1.0/gClockFrequency;
-	gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
-}
+	TimerInfo()
+	:	mClockFrequency(0.0),
+		mTotalTimeClockCount(0),
+		mLastTotalTimeClockCount(0)
+	{}
+
+	void update()
+	{
+		mClockFrequency = calc_clock_frequency(50U);
+		mClockFrequencyInv = 1.0/mClockFrequency;
+		mClocksToMicroseconds = mClockFrequencyInv;
+	}
+	F64						mClockFrequency;
+	F64SecondsImplicit		mClockFrequencyInv;
+	F64MicrosecondsImplicit	mClocksToMicroseconds;
+	U64						mTotalTimeClockCount;
+	U64						mLastTotalTimeClockCount;
+};
 
+TimerInfo& get_timer_info()
+{
+	static TimerInfo sTimerInfo;
+	return sTimerInfo;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
 // returns a U64 number that represents the number of 
-// microseconds since the unix epoch - Jan 1, 1970
+// microseconds since the Unix epoch - Jan 1, 1970
 U64MicrosecondsImplicit totalTime()
 {
 	U64 current_clock_count = get_clock_count();
-	if (!gTotalTimeClockCount)
+	if (!get_timer_info().mTotalTimeClockCount || get_timer_info().mClocksToMicroseconds.value() == 0)
 	{
-		update_clock_frequencies();
-		gTotalTimeClockCount = current_clock_count;
+		get_timer_info().update();
+		get_timer_info().mTotalTimeClockCount = current_clock_count;
 
 #if LL_WINDOWS
-		// Synch us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
+		// Sync us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
 		// Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
 		// make in the future.
 
-		gTotalTimeClockCount = (U64)(time(NULL) * gClockFrequency);
+		get_timer_info().mTotalTimeClockCount = (U64)(time(NULL) * get_timer_info().mClockFrequency);
 #endif
 
 		// Update the last clock count
-		gLastTotalTimeClockCount = current_clock_count;
+		get_timer_info().mLastTotalTimeClockCount = current_clock_count;
 	}
 	else
 	{
-		if (current_clock_count >= gLastTotalTimeClockCount)
+		if (current_clock_count >= get_timer_info().mLastTotalTimeClockCount)
 		{
 			// No wrapping, we're all okay.
-			gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
+			get_timer_info().mTotalTimeClockCount += current_clock_count - get_timer_info().mLastTotalTimeClockCount;
 		}
 		else
 		{
 			// We've wrapped.  Compensate correctly
-			gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
+			get_timer_info().mTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - get_timer_info().mLastTotalTimeClockCount) + current_clock_count;
 		}
 
 		// Update the last clock count
-		gLastTotalTimeClockCount = current_clock_count;
+		get_timer_info().mLastTotalTimeClockCount = current_clock_count;
 	}
 
 	// Return the total clock tick count in microseconds.
-	return U64Microseconds(gTotalTimeClockCount*gClocksToMicroseconds);
+	U64Microseconds time(get_timer_info().mTotalTimeClockCount*get_timer_info().mClocksToMicroseconds);
+	return time;
 }
 
 
@@ -270,9 +285,9 @@ U64MicrosecondsImplicit totalTime()
 
 LLTimer::LLTimer()
 {
-	if (!gClockFrequency)
+	if (!get_timer_info().mClockFrequency)
 	{
-		update_clock_frequencies();
+		get_timer_info().update();
 	}
 
 	mStarted = TRUE;
@@ -298,13 +313,14 @@ void LLTimer::cleanupClass()
 U64MicrosecondsImplicit LLTimer::getTotalTime()
 {
 	// simply call into the implementation function.
-	return totalTime();
+	U64MicrosecondsImplicit total_time = totalTime();
+	return total_time;
 }	
 
 // static
 F64SecondsImplicit LLTimer::getTotalSeconds()
 {
-	return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
+	return F64Microseconds(U64_to_F64(getTotalTime()));
 }
 
 void LLTimer::reset()
@@ -354,7 +370,7 @@ U64 getElapsedTimeAndUpdate(U64& lastClockCount)
 F64SecondsImplicit LLTimer::getElapsedTimeF64() const
 {
 	U64 last = mLastClockCount;
-	return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
+	return (F64)getElapsedTimeAndUpdate(last) * get_timer_info().mClockFrequencyInv;
 }
 
 F32SecondsImplicit LLTimer::getElapsedTimeF32() const
@@ -364,7 +380,7 @@ F32SecondsImplicit LLTimer::getElapsedTimeF32() const
 
 F64SecondsImplicit LLTimer::getElapsedTimeAndResetF64()
 {
-	return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
+	return (F64)getElapsedTimeAndUpdate(mLastClockCount) * get_timer_info().mClockFrequencyInv;
 }
 
 F32SecondsImplicit LLTimer::getElapsedTimeAndResetF32()
@@ -377,7 +393,7 @@ F32SecondsImplicit LLTimer::getElapsedTimeAndResetF32()
 void  LLTimer::setTimerExpirySec(F32SecondsImplicit expiration)
 {
 	mExpirationTicks = get_clock_count()
-		+ (U64)((F32)(expiration * gClockFrequency));
+		+ (U64)((F32)(expiration * get_timer_info().mClockFrequency));
 }
 
 F32SecondsImplicit LLTimer::getRemainingTimeF32() const
@@ -387,7 +403,7 @@ F32SecondsImplicit LLTimer::getRemainingTimeF32() const
 	{
 		return 0.0f;
 	}
-	return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
+	return F32((mExpirationTicks - cur_ticks) * get_timer_info().mClockFrequencyInv);
 }
 
 
@@ -400,7 +416,7 @@ BOOL  LLTimer::checkExpirationAndReset(F32 expiration)
 	}
 
 	mExpirationTicks = cur_ticks
-		+ (U64)((F32)(expiration * gClockFrequency));
+		+ (U64)((F32)(expiration * get_timer_info().mClockFrequency));
 	return TRUE;
 }
 
@@ -499,20 +515,20 @@ BOOL is_daylight_savings()
 
 struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time)
 {
-	S32 pacific_offset_hours;
+	S32Hours pacific_offset_hours;
 	if (pacific_daylight_time)
 	{
-		pacific_offset_hours = 7;
+		pacific_offset_hours = S32Hours(7);
 	}
 	else
 	{
-		pacific_offset_hours = 8;
+		pacific_offset_hours = S32Hours(8);
 	}
 
 	// We subtract off the PST/PDT offset _before_ getting
 	// "UTC" time, because this will handle wrapping around
 	// for 5 AM UTC -> 10 PM PDT of the previous day.
-	utc_time -= pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
+	utc_time -= S32SecondsImplicit(pacific_offset_hours);
  
 	// Internal buffer to PST/PDT (see above)
 	struct tm* internal_time = gmtime(&utc_time);
@@ -529,7 +545,7 @@ struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time)
 }
 
 
-void microsecondsToTimecodeString(U64 current_time, std::string& tcstring)
+void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring)
 {
 	U64 hours;
 	U64 minutes;
@@ -551,9 +567,9 @@ void microsecondsToTimecodeString(U64 current_time, std::string& tcstring)
 }
 
 
-void secondsToTimecodeString(F32 current_time, std::string& tcstring)
+void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring)
 {
-	microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
+	microsecondsToTimecodeString(current_time, tcstring);
 }
 
 
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index 12a453e8971..8b3930e2fae 100755
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -168,8 +168,8 @@ LL_COMMON_API BOOL is_daylight_savings();
 // struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight);
 LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time);
 
-LL_COMMON_API void microsecondsToTimecodeString(U64 current_time, std::string& tcstring);
-LL_COMMON_API void secondsToTimecodeString(F32 current_time, std::string& tcstring);
+LL_COMMON_API void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring);
+LL_COMMON_API void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring);
 
 U64MicrosecondsImplicit LL_COMMON_API totalTime();					// Returns current system time in microseconds
 
diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp
index 436ad9a0a28..05422f91916 100644
--- a/indra/llcommon/lltrace.cpp
+++ b/indra/llcommon/lltrace.cpp
@@ -76,7 +76,7 @@ void TimeBlockTreeNode::setParent( TimeBlock* parent )
 	}
 
 	mParent = parent;
-	mBlock->getPrimaryAccumulator()->mParent = parent;
+	mBlock->getPrimaryAccumulator().mParent = parent;
 	parent_tree_node->mChildren.push_back(mBlock);
 	parent_tree_node->mNeedsSorting = true;
 }
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index 1f86aadabab..bf8e950a8cd 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -80,10 +80,10 @@ class TraceType
 		mAccumulatorIndex(AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer()->reserveSlot())
 	{}
 
-	LL_FORCE_INLINE ACCUMULATOR* getPrimaryAccumulator() const
+	LL_FORCE_INLINE ACCUMULATOR& getPrimaryAccumulator() const
 	{
 		ACCUMULATOR* accumulator_storage = AccumulatorBuffer<ACCUMULATOR>::getPrimaryStorage();
-		return &accumulator_storage[mAccumulatorIndex];
+		return accumulator_storage[mAccumulatorIndex];
 	}
 
 	size_t getIndex() const { return mAccumulatorIndex; }
@@ -137,7 +137,7 @@ template<typename T, typename VALUE_T>
 void record(EventStatHandle<T>& measurement, VALUE_T value)
 {
 	T converted_value(value);
-	measurement.getPrimaryAccumulator()->record(storage_value(converted_value));
+	measurement.getPrimaryAccumulator().record(storage_value(converted_value));
 }
 
 template <typename T = F64>
@@ -160,7 +160,7 @@ template<typename T, typename VALUE_T>
 void sample(SampleStatHandle<T>& measurement, VALUE_T value)
 {
 	T converted_value(value);
-	measurement.getPrimaryAccumulator()->sample(storage_value(converted_value));
+	measurement.getPrimaryAccumulator().sample(storage_value(converted_value));
 }
 
 template <typename T = F64>
@@ -183,7 +183,7 @@ template<typename T, typename VALUE_T>
 void add(CountStatHandle<T>& count, VALUE_T value)
 {
 	T converted_value(value);
-	count.getPrimaryAccumulator()->add(storage_value(converted_value));
+	count.getPrimaryAccumulator().add(storage_value(converted_value));
 }
 
 template<>
@@ -340,49 +340,37 @@ class MemTrackable
 
 	void* operator new(size_t size) 
 	{
-		MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-		if (accumulator)
-		{
-			accumulator->mSize.sample(accumulator->mSize.getLastValue() + (F64)size);
-			accumulator->mAllocatedCount++;
-		}
+		MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+		accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() + (F64)size : (F64)size);
+		accumulator.mAllocatedCount++;
 
 		return ::operator new(size);
 	}
 
 	void operator delete(void* ptr, size_t size)
 	{
-		MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-		if (accumulator)
-		{
-			accumulator->mSize.sample(accumulator->mSize.getLastValue() - (F64)size);
-			accumulator->mAllocatedCount--;
-			accumulator->mDeallocatedCount++;
-		}
+		MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+		accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() - (F64)size : -(F64)size);
+		accumulator.mAllocatedCount--;
+		accumulator.mDeallocatedCount++;
 		::operator delete(ptr);
 	}
 
 	void *operator new [](size_t size)
 	{
-		MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-		if (accumulator)
-		{
-			accumulator->mSize.sample(accumulator->mSize.getLastValue() + (F64)size);
-			accumulator->mAllocatedCount++;
-		}
+		MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+		accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() + (F64)size : (F64)size);
+		accumulator.mAllocatedCount++;
 
 		return ::operator new[](size);
 	}
 
 	void operator delete[](void* ptr, size_t size)
 	{
-		MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-		if (accumulator)
-		{
-			accumulator->mSize.sample(accumulator->mSize.getLastValue() - (F64)size);
-			accumulator->mAllocatedCount--;
-			accumulator->mDeallocatedCount++;
-		}
+		MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+		accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() - (F64)size : -(F64)size);
+		accumulator.mAllocatedCount--;
+		accumulator.mDeallocatedCount++;
 		::operator delete[](ptr);
 	}
 
@@ -405,12 +393,9 @@ class MemTrackable
 	template<typename AMOUNT_T>
 	AMOUNT_T& memClaimAmount(AMOUNT_T& size)
 	{
-		MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+		MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
 		mMemFootprint += (size_t)size;
-		if (accumulator)
-		{
-			accumulator->mSize.sample(accumulator->mSize.getLastValue() + (F64)size);
-		}
+		accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() + (F64)size : (F64)size);
 		return size;
 	}
 
@@ -432,11 +417,8 @@ class MemTrackable
 	template<typename AMOUNT_T>
 	AMOUNT_T& memDisclaimAmount(AMOUNT_T& size)
 	{
-		MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-		if (accumulator)
-		{
-			accumulator->mSize.sample(accumulator->mSize.getLastValue() - (F64)size);
-		}
+		MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+		accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() - (F64)size : -(F64)size);
 		return size;
 	}
 
@@ -448,24 +430,18 @@ class MemTrackable
 	{
 		static void claim(mem_trackable_t& tracker, const TRACKED& tracked)
 		{
-			MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-			if (accumulator)
-			{
-				size_t footprint = MemFootprint<TRACKED>::measure(tracked);
-				accumulator->mSize.sample(accumulator->mSize.getLastValue() + (F64)footprint);
-				tracker.mMemFootprint += footprint;
-			}
+			MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+			size_t footprint = MemFootprint<TRACKED>::measure(tracked);
+			accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() + (F64)footprint : (F64)footprint);
+			tracker.mMemFootprint += footprint;
 		}
 
 		static void disclaim(mem_trackable_t& tracker, const TRACKED& tracked)
 		{
-			MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-			if (accumulator)
-			{
-				size_t footprint = MemFootprint<TRACKED>::measure(tracked);
-				accumulator->mSize.sample(accumulator->mSize.getLastValue() - (F64)footprint);
-				tracker.mMemFootprint -= footprint;
-			}
+			MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+			size_t footprint = MemFootprint<TRACKED>::measure(tracked);
+			accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() - (F64)footprint : -(F64)footprint);
+			tracker.mMemFootprint -= footprint;
 		}
 	};
 
@@ -474,20 +450,14 @@ class MemTrackable
 	{
 		static void claim(mem_trackable_t& tracker, TRACKED& tracked)
 		{
-			MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-			if (accumulator)
-			{
-				accumulator->mChildSize.sample(accumulator->mChildSize.getLastValue() + (F64)MemFootprint<TRACKED>::measure(tracked));
-			}
+			MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+			accumulator.mChildSize.sample(accumulator.mChildSize.hasValue() ? accumulator.mChildSize.getLastValue() + (F64)MemFootprint<TRACKED>::measure(tracked) : (F64)MemFootprint<TRACKED>::measure(tracked));
 		}
 
 		static void disclaim(mem_trackable_t& tracker, TRACKED& tracked)
 		{
-			MemStatAccumulator* accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
-			if (accumulator)
-			{
-				accumulator->mChildSize.sample(accumulator->mChildSize.getLastValue() - (F64)MemFootprint<TRACKED>::measure(tracked));
-			}
+			MemStatAccumulator& accumulator = DERIVED::sMemStat.getPrimaryAccumulator();
+			accumulator.mChildSize.sample(accumulator.mChildSize.hasValue() ? accumulator.mChildSize.getLastValue() - (F64)MemFootprint<TRACKED>::measure(tracked) : -(F64)MemFootprint<TRACKED>::measure(tracked));
 		}
 	};
 };
diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp
index 1fb68c8158e..c79c102afd1 100644
--- a/indra/llcommon/lltraceaccumulators.cpp
+++ b/indra/llcommon/lltraceaccumulators.cpp
@@ -114,10 +114,13 @@ void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other)
 
 void AccumulatorBufferGroup::sync()
 {
-	F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds();
+	if (isPrimary())
+	{
+		F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds();
 
-	mSamples.sync(time_stamp);
-	mMemStats.sync(time_stamp);
+		mSamples.sync(time_stamp);
+		mMemStats.sync(time_stamp);
+	}
 }
 
 void SampleAccumulator::addSamples( const SampleAccumulator& other, EBufferAppendType append_type )
@@ -144,6 +147,7 @@ void SampleAccumulator::addSamples( const SampleAccumulator& other, EBufferAppen
 
 		if (other.mTotalSamplingTime > epsilon)
 		{
+			llassert(mTotalSamplingTime > 0);
 			// 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
 			F64 n_1 = mTotalSamplingTime,
@@ -166,17 +170,16 @@ void SampleAccumulator::addSamples( const SampleAccumulator& other, EBufferAppen
 					/ (n_1 + n_2 - epsilon));
 			}
 
-			llassert(other.mTotalSamplingTime > 0);
 			F64 weight = mTotalSamplingTime / (mTotalSamplingTime + other.mTotalSamplingTime);
 			mNumSamples += other.mNumSamples;
 			mTotalSamplingTime += other.mTotalSamplingTime;
 			mMean = (mMean * weight) + (other.mMean * (1.0 - weight));
+			llassert(mMean < 0 || mMean >= 0);
 		}
 		if (append_type == SEQUENTIAL)
 		{
 			mLastValue = other.mLastValue;
 			mLastSampleTimeStamp = other.mLastSampleTimeStamp;
-			mHasValue = true;
 		}
 	}
 }
@@ -190,6 +193,7 @@ void SampleAccumulator::reset( const SampleAccumulator* other )
 	mMin = mLastValue;
 	mMax = mLastValue;
 	mMean = mLastValue;
+	LL_ERRS_IF(mHasValue && !(mMean < 0) && !(mMean >= 0)) << "Invalid mean after capturing value" << LL_ENDL;
 	mSumOfSquares = 0;
 	mLastSampleTimeStamp = LLTimer::getTotalSeconds();
 	mTotalSamplingTime = 0;
diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h
index f9d223066dd..d4ff4b8d710 100644
--- a/indra/llcommon/lltraceaccumulators.h
+++ b/indra/llcommon/lltraceaccumulators.h
@@ -1,3 +1,4 @@
+
 /** 
  * @file lltraceaccumulators.h
  * @brief Storage for accumulating statistics
@@ -317,6 +318,7 @@ namespace LLTrace
 				mMin = value;
 				mMax = value;
 				mMean = value;
+				llassert(mMean < 0 || mMean >= 0);
 				mLastSampleTimeStamp = time_stamp;
 			}
 			else
@@ -341,6 +343,7 @@ namespace LLTrace
 				mTotalSamplingTime += delta_time;
 				F64 old_mean = mMean;
 				mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean);
+				llassert(mMean < 0 || mMean >= 0);
 				mSumOfSquares += delta_time * (mLastValue - old_mean) * (mLastValue - mMean);
 			}
 			mLastSampleTimeStamp = time_stamp;
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index 8c32e1568b3..28470fb4c40 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -69,13 +69,13 @@ void ThreadRecorder::init()
 		tree_node.mBlock = &time_block;
 		tree_node.mParent = &root_time_block;
 
-		it->getPrimaryAccumulator()->mParent = &root_time_block;
+		it->getPrimaryAccumulator().mParent = &root_time_block;
 	}
 
 	mRootTimer = new BlockTimer(root_time_block);
 	timer_stack->mActiveTimer = mRootTimer;
 
-	TimeBlock::getRootTimeBlock().getPrimaryAccumulator()->mActiveCount = 1;
+	TimeBlock::getRootTimeBlock().getPrimaryAccumulator().mActiveCount = 1;
 }
 
 
@@ -247,8 +247,6 @@ void ThreadRecorder::pushToParent()
 		mThreadRecordingBuffers.reset();
 	}
 }
-	
-
 
 
 static LLFastTimer::DeclareTimer FTM_PULL_TRACE_DATA_FROM_CHILDREN("Pull child thread trace data");
diff --git a/indra/llcommon/llunit.h b/indra/llcommon/llunit.h
index 8fb53dd94d1..f42456fe728 100644
--- a/indra/llcommon/llunit.h
+++ b/indra/llcommon/llunit.h
@@ -156,7 +156,8 @@ struct LLUnit
 	static self_t convert(LLUnit<SOURCE_STORAGE, SOURCE_UNITS> v) 
 	{ 
 		self_t result;
-		ll_convert_units(v, result);
+		STORAGE_TYPE divisor = ll_convert_units(v, result);
+		result.mValue /= divisor;
 		return result;
 	}
 
@@ -193,7 +194,7 @@ struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNIT_TYPE>
 
 	template<typename OTHER_STORAGE, typename OTHER_UNIT>
 	LLUnitImplicit(LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
-	:	base_t(convert(other))
+	:	base_t(other)
 	{}
 
 	// unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD value (F32, S32, etc)
@@ -209,6 +210,8 @@ struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNIT_TYPE>
         base_t::mValue += value;
 	}
 
+	// this overload exists to explicitly catch use of another implicit unit
+	// without ambiguity between conversion to storage_t vs conversion to base_t
 	template<typename OTHER_STORAGE, typename OTHER_UNIT>
 	void operator += (LLUnitImplicit<OTHER_STORAGE, OTHER_UNIT> other)
 	{
@@ -221,6 +224,8 @@ struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNIT_TYPE>
         base_t::mValue -= value;
 	}
 
+	// this overload exists to explicitly catch use of another implicit unit
+	// without ambiguity between conversion to storage_t vs conversion to base_t
 	template<typename OTHER_STORAGE, typename OTHER_UNIT>
 	void operator -= (LLUnitImplicit<OTHER_STORAGE, OTHER_UNIT> other)
 	{
@@ -246,35 +251,35 @@ std::istream& operator >>(std::istream& s, LLUnitImplicit<STORAGE_TYPE, UNIT_TYP
 }
 
 template<typename S1, typename T1, typename S2, typename T2>
-LL_FORCE_INLINE void ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out, ...)
+LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out, ...)
 {
+	S2 divisor(1);
+
 	LL_STATIC_ASSERT((LLIsSameType<T1, T2>::value 
-		|| !LLIsSameType<T1, typename T1::base_unit_t>::value 
-		|| !LLIsSameType<T2, typename T2::base_unit_t>::value), "invalid conversion: incompatible units");
+						|| !LLIsSameType<T1, typename T1::base_unit_t>::value 
+						|| !LLIsSameType<T2, typename T2::base_unit_t>::value), 
+						"conversion requires compatible units");
 
-	if (LLIsSameType<T1, typename T1::base_unit_t>::value)
+	if (LLIsSameType<T1, T2>::value)
 	{
-		if (LLIsSameType<T2, typename T2::base_unit_t>::value)
-
-		{
-			// T1 and T2 fully reduced and equal...just copy
-			out = LLUnit<S2, T2>((S2)in.value());
-		}
-		else
-		{
-			// reduce T2
-			LLUnit<S2, typename T2::base_unit_t> new_out;
-			ll_convert_units(in, new_out);
-			ll_convert_units(new_out, out);
-		}
+		// T1 and T2 same type, just assign
+		out.value((S2)in.value());
 	}
-	else
+	else if (LLIsSameType<T2, typename T2::base_unit_t>::value)
 	{
 		// reduce T1
-		LLUnit<S1, typename T1::base_unit_t> new_in;
-		ll_convert_units(in, new_in);
-		ll_convert_units(new_in, out);
+		LLUnit<S2, typename T1::base_unit_t> new_in;
+		divisor *= (S2)ll_convert_units(in, new_in);
+		divisor *= (S2)ll_convert_units(new_in, out);
 	}
+	else
+	{
+		// reduce T2
+		LLUnit<S2, typename T2::base_unit_t> new_out;
+		divisor *= (S2)ll_convert_units(in, new_out);
+		divisor *= (S2)ll_convert_units(new_out, out);
+	}
+	return divisor;
 }
 
 template<typename T>
@@ -579,77 +584,86 @@ struct LLGetUnitLabel<LLUnit<STORAGE_T, T> >
 	static const char* getUnitLabel() { return T::getUnitLabel(); }
 };
 
-template<typename INPUT_TYPE, typename OUTPUT_TYPE>
+template<typename T>
 struct LLUnitLinearOps
 {
-	typedef LLUnitLinearOps<OUTPUT_TYPE, OUTPUT_TYPE> output_t;
+	typedef LLUnitLinearOps<T> self_t;
 
-	LLUnitLinearOps(INPUT_TYPE val) 
-	:	mInput (val)
+	LLUnitLinearOps(T val) 
+	:	mValue(val),
+		mDivisor(1)
 	{}
 
-	operator OUTPUT_TYPE() const { return (OUTPUT_TYPE)mInput; }
-	INPUT_TYPE mInput;
+	T mValue;
+	T mDivisor;
 
-	template<typename T>
-	output_t operator * (T other)
+	template<typename OTHER_T>
+	self_t operator * (OTHER_T other)
 	{
-		return typename LLResultTypeMultiply<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) * other;
+		return mValue * other;
 	}
 
-	template<typename T>
-	output_t operator / (T other)
+	template<typename OTHER_T>
+	self_t operator / (OTHER_T other)
 	{
-		return typename LLResultTypeDivide<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) / other;
+		mDivisor *= other;
+		return *this;
 	}
 
-	template<typename T>
-	output_t operator + (T other)
+	template<typename OTHER_T>
+	self_t operator + (OTHER_T other)
 	{
-		return typename LLResultTypeAdd<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) + other;
+		mValue += other;
+		return *this;
 	}
 
-	template<typename T>
-	output_t operator - (T other)
+	template<typename OTHER_T>
+	self_t operator - (OTHER_T other)
 	{
-		return typename LLResultTypeSubtract<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) - other;
+		mValue -= other;
+		return *this;
 	}
 };
 
-template<typename INPUT_TYPE, typename OUTPUT_TYPE>
+template<typename T>
 struct LLUnitInverseLinearOps
 {
-	typedef LLUnitInverseLinearOps<OUTPUT_TYPE, OUTPUT_TYPE> output_t;
+	typedef LLUnitInverseLinearOps<T> self_t;
 
-	LLUnitInverseLinearOps(INPUT_TYPE val) 
-	:	mInput(val)
+	LLUnitInverseLinearOps(T val) 
+	:	mValue(val),
+		mDivisor(1)
 	{}
 
-	operator OUTPUT_TYPE() const { return (OUTPUT_TYPE)mInput; }
-	INPUT_TYPE mInput;
+	T mValue;
+	T mDivisor;
 
-	template<typename T>
-	output_t operator * (T other)
+	template<typename OTHER_T>
+	self_t operator * (OTHER_T other)
 	{
-		return typename LLResultTypeDivide<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) / other;
+		mDivisor *= other;
+		return *this;
 	}
 
-	template<typename T>
-	output_t operator / (T other)
+	template<typename OTHER_T>
+	self_t operator / (OTHER_T other)
 	{
-		return typename LLResultTypeMultiply<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) * other;
+		mValue *= other;
+		return *this;
 	}
 
-	template<typename T>
-	output_t operator + (T other)
+	template<typename OTHER_T>
+	self_t operator + (OTHER_T other)
 	{
-		return typename LLResultTypeAdd<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) - other;
+		mValue -= other;
+		return *this;
 	}
 
-	template<typename T>
-	output_t operator - (T other)
+	template<typename OTHER_T>
+	self_t operator - (OTHER_T other)
 	{
-		return typename LLResultTypeSubtract<INPUT_TYPE, OUTPUT_TYPE>::type_t(mInput) + other;
+		mValue += other;
+		return *this;
 	}
 };
 
@@ -666,28 +680,32 @@ struct base_unit_name
 }
 
 
-#define LL_DECLARE_DERIVED_UNIT(base_unit_name, conversion_operation, unit_name, unit_label)	        \
-struct unit_name                                                                                        \
-{                                                                                                       \
-	typedef base_unit_name base_unit_t;                                                                 \
-	static const char* getUnitLabel() { return unit_label; }									        \
-	template<typename T>                                                                                \
-	static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); }		        \
-	template<typename STORAGE_T, typename UNIT_T>                                                       \
-	static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value)				        \
-	{ return LLUnit<STORAGE_T, unit_name>(value); }												        \
-};                                                                                                      \
-	                                                                                                    \
-template<typename S1, typename S2>                                                                      \
-void ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out)                        \
-{                                                                                                       \
-	out = LLUnit<S2, base_unit_name>((S2)(LLUnitLinearOps<S1, S2>(in.value()) conversion_operation));	\
-}                                                                                                       \
-                                                                                                        \
-template<typename S1, typename S2>                                                                      \
-void ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out)                        \
-{                                                                                                       \
-	out = LLUnit<S2, unit_name>((S2)(LLUnitInverseLinearOps<S1, S2>(in.value()) conversion_operation)); \
+#define LL_DECLARE_DERIVED_UNIT(base_unit_name, conversion_operation, unit_name, unit_label)	 \
+struct unit_name                                                                                 \
+{                                                                                                \
+	typedef base_unit_name base_unit_t;                                                          \
+	static const char* getUnitLabel() { return unit_label; }									 \
+	template<typename T>                                                                         \
+	static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); }		 \
+	template<typename STORAGE_T, typename UNIT_T>                                                \
+	static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value)				 \
+	{ return LLUnit<STORAGE_T, unit_name>(value); }												 \
+};                                                                                               \
+	                                                                                             \
+template<typename S1, typename S2>                                                               \
+S2 ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out)                   \
+{                                                                                                \
+	LLUnitLinearOps<S2> op = LLUnitLinearOps<S2>(in.value()) conversion_operation;               \
+	out = LLUnit<S2, base_unit_name>((S2)op.mValue);	                                         \
+	return op.mDivisor;                                                                          \
+}                                                                                                \
+                                                                                                 \
+template<typename S1, typename S2>                                                               \
+S2 ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out)                   \
+{                                                                                                \
+	LLUnitInverseLinearOps<S2> op = LLUnitInverseLinearOps<S2>(in.value()) conversion_operation; \
+	out = LLUnit<S2, unit_name>((S2)op.mValue);                                                  \
+	return op.mDivisor;                                                                          \
 }                                                                                               
 
 #define LL_DECLARE_UNIT_TYPEDEFS(ns, unit_name)                         \
diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp
index a8e9be86cab..b8aef9d15ed 100644
--- a/indra/llcommon/tests/llunits_test.cpp
+++ b/indra/llcommon/tests/llunits_test.cpp
@@ -38,12 +38,9 @@ namespace LLUnits
 	LL_DECLARE_DERIVED_UNIT(Latinum, / 16, Solari, "Sol");
 }
 
-typedef LLUnit<F32, LLUnits::Quatloos> F32Quatloos;
-typedef LLUnit<S32, LLUnits::Quatloos> S32Quatloos;
-typedef LLUnit<F32, LLUnits::Latinum> F32Latinum;
-typedef LLUnit<S32, LLUnits::Latinum> S32Latinum;
-typedef LLUnit<F32, LLUnits::Solari> F32Solari;
-typedef LLUnit<S32, LLUnits::Solari> S32Solari;
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Latinum);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari);
 
 namespace tut
 {
@@ -83,6 +80,15 @@ namespace tut
 
 		LLUnit<U32, Quatloos> unsigned_int_quatloos(float_quatloos);
 		ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42));
+
+		S32Solari int_solari(1);
+
+		float_quatloos = int_solari;
+		ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f));
+
+		int_quatloos = S32Quatloos(1);
+		F32Solari float_solari = int_quatloos;
+		ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f));
 	}
 
 	// conversions to/from base unit
@@ -185,6 +191,11 @@ namespace tut
 		return true;
 	}
 
+	bool accept_implicit_quatloos(S32Quatloos q)
+	{
+		return true;
+	}
+
 	// signature compatibility
 	template<> template<>
 	void units_object_t::test<6>()
diff --git a/indra/llui/llstatbar.cpp b/indra/llui/llstatbar.cpp
index 03a2895289e..8658a2f9686 100755
--- a/indra/llui/llstatbar.cpp
+++ b/indra/llui/llstatbar.cpp
@@ -386,17 +386,24 @@ void LLStatBar::draw()
 	mCurMaxBar = LLSmoothInterpolation::lerp(mCurMaxBar, mMaxBar, 0.05f);
 	mCurMinBar = LLSmoothInterpolation::lerp(mCurMinBar, mMinBar, 0.05f);
 
+	S32 decimal_digits = mDecimalDigits;
+	if (is_approx_equal((F32)(S32)display_value, display_value))
+	{
+		decimal_digits = 0;
+	}
+
 	// rate limited updates
-	if (mLastDisplayValueTimer.getElapsedTimeF32() > MEAN_VALUE_UPDATE_TIME)
+	if (mLastDisplayValueTimer.getElapsedTimeF32() < MEAN_VALUE_UPDATE_TIME)
 	{
-		mLastDisplayValueTimer.reset();
-		drawLabelAndValue(display_value, unit_label, bar_rect);
-		mLastDisplayValue = display_value;
+		display_value = mLastDisplayValue;
 	}
 	else
 	{
-		drawLabelAndValue(mLastDisplayValue, unit_label, bar_rect);
+		mLastDisplayValueTimer.reset();
 	}
+	drawLabelAndValue(display_value, unit_label, bar_rect, mDecimalDigits);
+	mLastDisplayValue = display_value;
+
 
 	if (mDisplayBar
         && (mCountFloatp || mEventFloatp || mSampleFloatp))
@@ -568,16 +575,11 @@ LLRect LLStatBar::getRequiredRect()
 	return rect;
 }
 
-void LLStatBar::drawLabelAndValue( F32 value, std::string &label, LLRect &bar_rect )
+void LLStatBar::drawLabelAndValue( F32 value, std::string &label, LLRect &bar_rect, S32 decimal_digits )
 {
 	LLFontGL::getFontMonospace()->renderUTF8(mLabel, 0, 0, getRect().getHeight(), LLColor4(1.f, 1.f, 1.f, 1.f),
 		LLFontGL::LEFT, LLFontGL::TOP);
 
-	S32 decimal_digits = mDecimalDigits;
-	if (is_approx_equal((F32)(S32)value, value))
-	{
-		decimal_digits = 0;
-	}
 	std::string value_str	= !llisnan(value)
 							? llformat("%10.*f %s", decimal_digits, value, label.c_str())
 							: "n/a";
diff --git a/indra/llui/llstatbar.h b/indra/llui/llstatbar.h
index bf2bd3e2592..f311e4a13cb 100755
--- a/indra/llui/llstatbar.h
+++ b/indra/llui/llstatbar.h
@@ -72,7 +72,7 @@ class LLStatBar : public LLView
 	/*virtual*/ LLRect getRequiredRect();	// Return the height of this object, given the set options.
 
 private:
-	void drawLabelAndValue( F32 mean, std::string &unit_label, LLRect &bar_rect );
+	void drawLabelAndValue( F32 mean, std::string &unit_label, LLRect &bar_rect, S32 decimal_digits );
 	void drawTicks( F32 min, F32 max, F32 value_scale, LLRect &bar_rect );
 
 	F32          mMinBar,
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 8092eda7b2f..9235f23a8c6 100755
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -2439,7 +2439,7 @@ void LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 
 
 // Move an object due to idle-time viewer side updates by interpolating motion
-void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& time, const F32SecondsImplicit& dt)
+void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& time, const F32SecondsImplicit& dt_seconds)
 {
 	// linear motion
 	// PHYSICS_TIMESTEP is used below to correct for the fact that the velocity in object
@@ -2450,8 +2450,9 @@ void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& time, con
 	// to see if object is selected, instead of explicitly
 	// zeroing it out	
 
+	F32 dt = dt_seconds;
 	F64Seconds time_since_last_update = time - mLastMessageUpdateSecs;
-	if (time_since_last_update <= (F64Seconds)0.0 || dt <= (F32Seconds)0.f)
+	if (time_since_last_update <= (F64Seconds)0.0 || dt <= 0.f)
 	{
 		return;
 	}
@@ -2463,7 +2464,7 @@ void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& time, con
 	{	// Old code path ... unbounded, simple interpolation
 		if (!(accel.isExactlyZero() && vel.isExactlyZero()))
 		{
-			LLVector3 pos   = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt.value();  
+			LLVector3 pos   = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;  
 		
 			// region local  
 			setPositionRegion(pos + getPositionRegion());
@@ -2477,7 +2478,7 @@ void LLViewerObject::interpolateLinearMotion(const F64SecondsImplicit& time, con
 	{	// Object is moving, and hasn't been too long since we got an update from the server
 		
 		// Calculate predicted position and velocity
-		LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt.value();	
+		LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;	
 		LLVector3 new_v = accel * dt;
 
 		if (time_since_last_update > sPhaseOutUpdateInterpolationTime &&
diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml
index fc2369276c7..02d1bf6a0ec 100755
--- a/indra/newview/skins/default/xui/en/floater_stats.xml
+++ b/indra/newview/skins/default/xui/en/floater_stats.xml
@@ -42,8 +42,7 @@
                   show_bar="true"/>
         <stat_bar name="packet_loss"
                   label="Packet Loss"
-                  stat="packetslostpercentstat"
-                  decimal_digits="3"/>
+                  stat="packetslostpercentstat"/>
         <stat_bar name="ping"
                   label="Ping Sim"
                   stat="simpingstat"/>
@@ -71,12 +70,10 @@
                     stat="numobjectsstat"/>
           <stat_bar name="newobjs"
                     label="New Objects"
-                    stat="numnewobjectsstat"
-                    bar_max="2000"/>
+                    stat="numnewobjectsstat"/>
           <stat_bar name="object_cache_hits"
                     label="Object Cache Hit Rate"
                     stat="object_cache_hits"
-                    bar_max="100"
                     show_history="true"/>
         </stat_view>
         <stat_view name="texture"
@@ -86,7 +83,6 @@
           <stat_bar name="texture_cache_hits"
                     label="Cache Hit Rate"
                     stat="texture_cache_hits"
-                    bar_max="100"
                     show_history="true"/>
           <stat_bar name="texture_cache_read_latency"
                     label="Cache Read Latency"
@@ -100,20 +96,16 @@
                     stat="numrawimagesstat"/>
           <stat_bar name="gltexmemstat"
                     label="GL Mem"
-                    stat="gltexmemstat"
-                    decimal_digits="1"/>
+                    stat="gltexmemstat"/>
           <stat_bar name="formattedmemstat"
                     label="Formatted Mem"
-                    stat="formattedmemstat"
-                    decimal_digits="3"/>
+                    stat="formattedmemstat"/>
           <stat_bar name="rawmemstat"
                     label="Raw Mem"
-                    stat="rawmemstat"
-                    decimal_digits="3"/>
+                    stat="rawmemstat"/>
           <stat_bar name="glboundmemstat"
                     label="Bound Mem"
-                    stat="glboundmemstat"
-                    decimal_digits="3"/>
+                    stat="glboundmemstat"/>
         </stat_view>
         
         <stat_view name="network"
@@ -168,9 +160,7 @@
                  setting="OpenDebugStatSim">
         <stat_bar name="simtimedilation"
                   label="Time Dilation"
-                  stat="simtimedilation"
-                  decimal_digits="3"
-                  bar_max="1" />
+                  stat="simtimedilation"/>
         <stat_bar name="simfps"
                   label="Sim FPS"
                   stat="simfps"
@@ -202,28 +192,22 @@
                   decimal_digits="1"/>
         <stat_bar name="simmainagents"
                   label="Main Agents"
-                  stat="simmainagents"
-                  bar_max="80"/>
+                  stat="simmainagents"/>
         <stat_bar name="simchildagents"
                   label="Child Agents"
                   stat="simchildagents"/>
         <stat_bar name="simobjects"
                   label="Objects"
-                  stat="simobjects"
-                  bar_max="30000" />
+                  stat="simobjects"/>
         <stat_bar name="simactiveobjects"
                   label="Active Objects"
-                  stat="simactiveobjects"
-                  bar_max="5000"/>
+                  stat="simactiveobjects"/>
         <stat_bar name="simactivescripts"
                   label="Active Scripts"
-                  stat="simactivescripts"
-                  bar_max="15000"/>
+                  stat="simactivescripts"/>
         <stat_bar name="simpctscriptsrun"
                   label="Scripts Run"
-                  stat="simpctscriptsrun"
-                  bar_max="100"
-                  decimal_digits="3"/>
+                  stat="simpctscriptsrun"/>
         <stat_bar name="simscripteps"
                   label="Script Events"
                   stat="simscripteps"
@@ -234,16 +218,13 @@
                    show_label="true">
           <stat_bar name="simsimaistepmsec"
                     label="AI Step Time"
-                    stat="simsimaistepmsec"
-                    decimal_digits="3"/>
+                    stat="simsimaistepmsec"/>
           <stat_bar name="simsimskippedsilhouettesteps"
                     label="Skipped Silhouette Steps"
                     stat="simsimskippedsilhouettesteps"
-                    unit_label="/sec"
-                    bar_max="45"/>
+                    unit_label="/sec"/>
           <stat_bar name="simsimpctsteppedcharacters"
                     stat="simsimpctsteppedcharacters"
-                    bar_max="100"
                     decimal_digits="1"/>
         </stat_view>
         <stat_bar name="siminpps"
@@ -261,7 +242,7 @@
                   label="Pending Uploads"
                   stat="simpendinguploads"/>
         <stat_bar name="simtotalunackedbytes"
-                  label="Total Unacked Bytes"
+                  label="Total Unacked Data"
                   stat="simtotalunackedbytes"
                   decimal_digits="1"/>
         <stat_view name="simperf"
@@ -270,60 +251,47 @@
                    show_label="true">
           <stat_bar name="simframemsec"
                     label="Total Frame Time"
-                    stat="simframemsec"
-                    decimal_digits="3"/>
+                    stat="simframemsec"/>
           <stat_bar name="simnetmsec"
                     label="Net Time"
-                    stat="simnetmsec"
-                    decimal_digits="3"/>
+                    stat="simnetmsec"/>
           <stat_bar name="simsimphysicsmsec"
                     label="Physics Time"
-                    stat="simsimphysicsmsec"
-                    decimal_digits="3"/>
+                    stat="simsimphysicsmsec"/>
           <stat_bar name="simsimothermsec"
                     label="Simulation Time"
-                    stat="simsimothermsec"
-                    decimal_digits="3"/>
+                    stat="simsimothermsec"/>
           <stat_bar name="simagentmsec"
                     label="Agent Time"
-                    stat="simagentmsec"
-                    decimal_digits="3"/>
+                    stat="simagentmsec"/>
           <stat_bar name="simimagesmsec"
                     label="Images Time"
-                    stat="simimagesmsec"
-                    decimal_digits="3"/>
+                    stat="simimagesmsec"/>
           <stat_bar name="simscriptmsec"
                     label="Script Time"
-                    stat="simscriptmsec"
-                    decimal_digits="3"/>
+                    stat="simscriptmsec"/>
           <stat_bar name="simsparemsec"
                     label="Spare Time"
-                    stat="simsparemsec"
-                    decimal_digits="3"/>
+                    stat="simsparemsec"/>
           <stat_view name="timedetails"
                      label="Time Details"
                      follows="left|top|right"
                      show_label="true">
             <stat_bar name="simsimphysicsstepmsec"
                       label="Physics Step"
-                      stat="simsimphysicsstepmsec"
-                      decimal_digits="3"/>
+                      stat="simsimphysicsstepmsec"/>
             <stat_bar name="simsimphysicsshapeupdatemsec"
                       label="Update Phys Shapes"
-                      stat="simsimphysicsshapeupdatemsec"
-                      decimal_digits="3"/>
+                      stat="simsimphysicsshapeupdatemsec"/>
             <stat_bar name="simsimphysicsothermsec"
                       label="Physics Other"
-                      stat="simsimphysicsothermsec"
-                      decimal_digits="3"/>
+                      stat="simsimphysicsothermsec"/>
             <stat_bar name="simsleepmsec"
                       label="Sleep Time"
-                      stat="simsleepmsec"
-                      decimal_digits="3"/>
+                      stat="simsleepmsec"/>
             <stat_bar name="simpumpiomsec"
                       label="Pump IO"
-                      stat="simpumpiomsec"
-                      decimal_digits="3"/>
+                      stat="simpumpiomsec"/>
           </stat_view>
         </stat_view>
       </stat_view>
-- 
GitLab