diff options
author | Monty Brandenberg <monty@lindenlab.com> | 2010-12-10 17:41:05 -0800 |
---|---|---|
committer | Monty Brandenberg <monty@lindenlab.com> | 2010-12-10 17:41:05 -0800 |
commit | 11d420dd32e643a191c16b04f2fbb42c2b4db628 (patch) | |
tree | aa9aa0be3b72c178e8fd41ea59e55d50d91590bb | |
parent | 4bab98f5cd2a815d10fe494a0a7e51cc237adb4a (diff) |
Decided to refactor a bit. Was using LLSD as an internal data
representation transferring ownership, doing data aggregation
in a very pedantic way. That's just adding unneeded cost and
complication. Used the same objects to transport data as are
collecting it and everything got simpler, faster, easier to
read with fewer gotchas. Bit myself *again* doing the min/max/mean
merges but the unittests where there to pick me up again. Added
a per-region FPS metric while I was at it. This is much asked
for and there was a convenient place to sample the value.
-rw-r--r-- | indra/newview/llappviewer.cpp | 31 | ||||
-rw-r--r-- | indra/newview/llsimplestat.h | 18 | ||||
-rw-r--r-- | indra/newview/lltexturefetch.cpp | 98 | ||||
-rw-r--r-- | indra/newview/lltexturefetch.h | 7 | ||||
-rw-r--r-- | indra/newview/llviewerassetstats.cpp | 286 | ||||
-rw-r--r-- | indra/newview/llviewerassetstats.h | 70 | ||||
-rw-r--r-- | indra/newview/tests/llsimplestat_test.cpp | 158 | ||||
-rw-r--r-- | indra/newview/tests/llviewerassetstats_test.cpp | 595 |
8 files changed, 740 insertions, 523 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c667fba86f..3640d01642 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3804,6 +3804,11 @@ void LLAppViewer::idle() llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl; gObjectList.mNumUnknownUpdates = 0; } + + // ViewerMetrics FPS piggy-backing on the debug timer. + // The 5-second interval is nice for this purpose. If the object debug + // bit moves or is disabled, please give this a suitable home. + LLViewerAssetStatsFF::record_fps_main(frame_rate_clamped); } } @@ -4805,23 +4810,17 @@ void LLAppViewer::metricsSend(bool enable_reporting) { std::string caps_url = regionp->getCapability("ViewerMetrics"); - // *NOTE: Pay attention here. LLSD's are not safe for thread sharing - // and their ownership is difficult to transfer across threads. We do - // it here by having only one reference (the new'd pointer) to the LLSD - // or any subtree of it. This pointer is then transfered to the other - // thread using correct thread logic to do all data ordering. - LLSD * envelope = new LLSD(LLSD::emptyMap()); - { - (*envelope) = gViewerAssetStatsMain->asLLSD(); - (*envelope)["session_id"] = gAgentSessionID; - (*envelope)["agent_id"] = gAgentID; - } - + // Make a copy of the main stats to send into another thread. + // Receiving thread takes ownership. + LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStatsMain)); + // Send a report request into 'thread1' to get the rest of the data - // and have it sent to the stats collector. LLSD ownership transfers - // with this call. - LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, envelope); - envelope = 0; // transfer noted + // and provide some additional parameters while here. + LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, + gAgentSessionID, + gAgentID, + main_stats); + main_stats = 0; // Ownership transferred } else { diff --git a/indra/newview/llsimplestat.h b/indra/newview/llsimplestat.h index f8f4be0390..a90e503adb 100644 --- a/indra/newview/llsimplestat.h +++ b/indra/newview/llsimplestat.h @@ -62,6 +62,9 @@ public: inline void reset() { mCount = 0; } + inline void merge(const LLSimpleStatCounter & src) + { mCount += src.mCount; } + inline U32 operator++() { return ++mCount; } inline U32 getCount() const { return mCount; } @@ -125,6 +128,21 @@ public: ++mCount; } + void merge(const LLSimpleStatMMM<VALUE_T> & src) + { + if (! mCount) + { + *this = src; + } + else if (src.mCount) + { + mMin = llmin(mMin, src.mMin); + mMax = llmax(mMax, src.mMax); + mCount += src.mCount; + mTotal += src.mTotal; + } + } + inline U32 getCount() const { return mCount; } inline Value getMin() const { return mMin; } inline Value getMax() const { return mMax; } diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 73d78c9334..e1f9d7bdcc 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -442,18 +442,18 @@ namespace * | | TE | . +-------+ * | +--+--+ . | Thd1 | * | | . | | - * | (llsd) +-----+ . | Stats | + * | +-----+ . | Stats | * `--------->| RSC | . | | * +--+--+ . | Coll. | * | . +-------+ * +--+--+ . | * | SME |---. . | * +-----+ \ . | - * . \ (llsd) +-----+ | + * . \ (clone) +-----+ | * . `-------->| SM | | * . +--+--+ | * . | | - * . +-----+ (llsd) | + * . +-----+ | * . | RSC |<--------' * . +-----+ * . | @@ -472,11 +472,12 @@ namespace * SR - Set Region. New region UUID is sent to the thread-local * collector. * SME - Send Metrics Enqueued. Enqueue a 'Send Metrics' command - * including an ownership transfer of an LLSD. + * including an ownership transfer of a cloned LLViewerAssetStats. * TFReqSendMetrics carries the data. * SM - Send Metrics. Global metrics reporting operation. Takes - * the remote LLSD from the command, merges it with and LLSD - * from the local collector and sends it to the grid. + * the cloned stats from the command, merges it with the + * thread's local stats, converts to LLSD and sends it on + * to the grid. * AM - Agent Moved. Agent has completed some sort of move to a * new region. * TE - Timer Expired. Metrics timer has expired (on the order @@ -485,7 +486,8 @@ namespace * MSC - Modify Stats Collector. State change in the thread-local * collector. Typically a region change which affects the * global pointers used to find the 'current stats'. - * RSC - Read Stats Collector. Extract collector data in LLSD form. + * RSC - Read Stats Collector. Extract collector data cloning it + * (i.e. deep copy) when necessary. * */ class TFRequest // : public LLQueuedThread::QueuedRequest @@ -539,11 +541,12 @@ public: * * This is the big operation. The main thread gathers metrics * for a period of minutes into LLViewerAssetStats and other - * objects then builds an LLSD to represent the data. It uses - * this command to transfer the LLSD, content *and* ownership, - * to the TextureFetch thread which adds its own metrics and - * kicks of an HTTP POST of the resulting data to the currently - * active metrics collector. + * objects then makes a snapshot of the data by cloning the + * collector. This command transfers the clone, along with a few + * additional arguments (UUIDs), handing ownership to the + * TextureFetch thread. It then merges its own data into the + * cloned copy, converts to LLSD and kicks off an HTTP POST of + * the resulting data to the currently active metrics collector. * * Corresponds to LLTextureFetch::commandSendMetrics() */ @@ -558,16 +561,24 @@ public: * to receive the data. Does not have to * be associated with a particular region. * - * @param report_main Pointer to LLSD containing main - * thread metrics. Ownership transfers - * to the new thread using very carefully - * constructed code. + * @param session_id UUID of the agent's session. + * + * @param agent_id UUID of the agent. (Being pure here...) + * + * @param main_stats Pointer to a clone of the main thread's + * LLViewerAssetStats data. Thread1 takes + * ownership of the copy and disposes of it + * when done. */ TFReqSendMetrics(const std::string & caps_url, - LLSD * report_main) + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats) : TFRequest(), mCapsURL(caps_url), - mReportMain(report_main) + mSessionID(session_id), + mAgentID(agent_id), + mMainStats(main_stats) {} TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined @@ -577,7 +588,9 @@ public: public: const std::string mCapsURL; - LLSD * mReportMain; + const LLUUID mSessionID; + const LLUUID mAgentID; + LLViewerAssetStats * mMainStats; }; /* @@ -2727,9 +2740,11 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) } void LLTextureFetch::commandSendMetrics(const std::string & caps_url, - LLSD * report_main) + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats) { - TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, report_main); + TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats); cmdEnqueue(req); } @@ -2808,14 +2823,14 @@ TFReqSetRegion::doWork(LLTextureFetch *) TFReqSendMetrics::~TFReqSendMetrics() { - delete mReportMain; - mReportMain = 0; + delete mMainStats; + mMainStats = 0; } /** * Implements the 'Send Metrics' command. Takes over - * ownership of the passed LLSD pointer. + * ownership of the passed LLViewerAssetStats pointer. * * Thread: Thread1 (TextureFetch) */ @@ -2893,33 +2908,36 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) static volatile bool reporting_started(false); static volatile S32 report_sequence(0); - // We've already taken over ownership of the LLSD at this point - // and can do normal LLSD sharing operations at this point. But - // still being careful, regardless. - LLSD & main_stats = *mReportMain; - - LLSD thread1_stats = gViewerAssetStatsThread1->asLLSD(); // 'duration' & 'regions' from this LLSD - thread1_stats["message"] = "ViewerAssetMetrics"; // Identifies the type of metrics - thread1_stats["sequence"] = report_sequence; // Sequence number - thread1_stats["initial"] = ! reporting_started; // Initial data from viewer - thread1_stats["break"] = LLTextureFetch::svMetricsDataBreak; // Break in data prior to this report + // We've taken over ownership of the stats copy at this + // point. Get a working reference to it for merging here + // but leave it in 'this'. Destructor will rid us of it. + LLViewerAssetStats & main_stats = *mMainStats; + + // Merge existing stats into those from main, convert to LLSD + main_stats.merge(*gViewerAssetStatsThread1); + LLSD merged_llsd = main_stats.asLLSD(); + + // Add some additional meta fields to the content + merged_llsd["session_id"] = mSessionID; + merged_llsd["agent_id"] = mAgentID; + merged_llsd["message"] = "ViewerAssetMetrics"; // Identifies the type of metrics + merged_llsd["sequence"] = report_sequence; // Sequence number + merged_llsd["initial"] = ! reporting_started; // Initial data from viewer + merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak; // Break in data prior to this report // Update sequence number if (S32_MAX == ++report_sequence) report_sequence = 0; - // Merge the two LLSDs into a single report - LLViewerAssetStatsFF::merge_stats(main_stats, thread1_stats); - // Limit the size of the stats report if necessary. - thread1_stats["truncated"] = truncate_viewer_metrics(10, thread1_stats); + merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); if (! mCapsURL.empty()) { LLCurlRequest::headers_t headers; fetcher->getCurlRequest().post(mCapsURL, headers, - thread1_stats, + merged_llsd, new lcl_responder(report_sequence, report_sequence, LLTextureFetch::svMetricsDataBreak, @@ -2933,7 +2951,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // In QA mode, Metrics submode, log the result for ease of testing if (fetcher->isQAMode()) { - LL_INFOS("Textures") << thread1_stats << LL_ENDL; + LL_INFOS("Textures") << merged_llsd << LL_ENDL; } gViewerAssetStatsThread1->reset(); diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index af30d1bb3b..a8fd3ce244 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -40,6 +40,8 @@ class HTTPGetResponder; class LLTextureCache; class LLImageDecodeThread; class LLHost; +class LLViewerAssetStats; + namespace { class TFRequest; } // Interface class @@ -88,7 +90,10 @@ public: // Commands available to other threads to control metrics gathering operations. void commandSetRegion(U64 region_handle); - void commandSendMetrics(const std::string & caps_url, LLSD * report_main); + void commandSendMetrics(const std::string & caps_url, + const LLUUID & session_id, + const LLUUID & agent_id, + LLViewerAssetStats * main_stats); void commandDataBreak(); LLCurlRequest & getCurlRequest() { return *mCurlGetRequest; } diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index d798786277..399d62d2fc 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -113,11 +113,34 @@ LLViewerAssetStats::PerRegionStats::reset() mRequests[i].mDequeued.reset(); mRequests[i].mResponse.reset(); } - + mFPS.reset(); + mTotalTime = 0; mStartTimestamp = LLViewerAssetStatsFF::get_timestamp(); } + +void +LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionStats & src) +{ + // mRegionHandle, mTotalTime, mStartTimestamp are left alone. + + // mFPS + if (src.mFPS.getCount() && mFPS.getCount()) + { + mFPS.merge(src.mFPS); + } + + // Requests + for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i].mEnqueued.merge(src.mRequests[i].mEnqueued); + mRequests[i].mDequeued.merge(src.mRequests[i].mDequeued); + mRequests[i].mResponse.merge(src.mRequests[i].mResponse); + } +} + + void LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now) { @@ -136,6 +159,19 @@ LLViewerAssetStats::LLViewerAssetStats() } +LLViewerAssetStats::LLViewerAssetStats(const LLViewerAssetStats & src) + : mRegionHandle(src.mRegionHandle), + mResetTimestamp(src.mResetTimestamp) +{ + const PerRegionContainer::const_iterator it_end(src.mRegionStats.end()); + for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it) + { + mRegionStats[it->first] = new PerRegionStats(*it->second); + } + mCurRegionStats = mRegionStats[mRegionHandle]; +} + + void LLViewerAssetStats::reset() { @@ -215,6 +251,12 @@ LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_htt mCurRegionStats->mRequests[int(eac)].mResponse.record(duration); } +void +LLViewerAssetStats::recordFPS(F32 fps) +{ + mCurRegionStats->mFPS.record(fps); +} + LLSD LLViewerAssetStats::asLLSD() { @@ -231,7 +273,7 @@ LLViewerAssetStats::asLLSD() LLSD::String("get_other") }; - // Sub-tags. If you add or delete from this list, mergeRegionsLLSD() must be updated. + // Stats Group Sub-tags. static const LLSD::String enq_tag("enqueued"); static const LLSD::String deq_tag("dequeued"); static const LLSD::String rcnt_tag("resp_count"); @@ -239,6 +281,12 @@ LLViewerAssetStats::asLLSD() static const LLSD::String rmax_tag("resp_max"); static const LLSD::String rmean_tag("resp_mean"); + // MMM Group Sub-tags. + static const LLSD::String cnt_tag("count"); + static const LLSD::String min_tag("min"); + static const LLSD::String max_tag("max"); + static const LLSD::String mean_tag("mean"); + const duration_t now = LLViewerAssetStatsFF::get_timestamp(); mCurRegionStats->accumulateTime(now); @@ -257,7 +305,7 @@ LLViewerAssetStats::asLLSD() LLSD reg_stat = LLSD::emptyMap(); - for (int i = 0; i < EVACCount; ++i) + for (int i = 0; i < LL_ARRAY_SIZE(tags); ++i) { LLSD & slot = reg_stat[tags[i]]; slot = LLSD::emptyMap(); @@ -269,6 +317,15 @@ LLViewerAssetStats::asLLSD() slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6)); } + { + LLSD & slot = reg_stat["fps"]; + slot = LLSD::emptyMap(); + slot[cnt_tag] = LLSD(S32(stats.mFPS.getCount())); + slot[min_tag] = LLSD(F64(stats.mFPS.getMin())); + slot[max_tag] = LLSD(F64(stats.mFPS.getMax())); + slot[mean_tag] = LLSD(F64(stats.mFPS.getMean())); + } + reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6); std::stringstream reg_handle; reg_handle.width(16); @@ -284,181 +341,24 @@ LLViewerAssetStats::asLLSD() return ret; } -/* static */ void -LLViewerAssetStats::mergeRegionsLLSD(const LLSD & src, LLSD & dst) +void +LLViewerAssetStats::merge(const LLViewerAssetStats & src) { - // Merge operator definitions - static const int MOP_ADD_INT(0); - static const int MOP_MIN_REAL(1); - static const int MOP_MAX_REAL(2); - static const int MOP_MEAN_REAL(3); // Requires a 'mMergeOpArg' to weight the input terms - - static const LLSD::String regions_key("regions"); - static const LLSD::String resp_count_key("resp_count"); - - static const struct - { - LLSD::String mName; - int mMergeOp; - } - key_list[] = - { - // Order is important below. We modify the data in-place and - // so operations like MOP_MEAN_REAL which need the "resp_count" - // value for weighting must be performed before "resp_count" - // is modified or the weight will be wrong. Key list is - // defined in asLLSD() and must track it. - - { "resp_mean", MOP_MEAN_REAL }, - { "enqueued", MOP_ADD_INT }, - { "dequeued", MOP_ADD_INT }, - { "resp_min", MOP_MIN_REAL }, - { "resp_max", MOP_MAX_REAL }, - { resp_count_key, MOP_ADD_INT } // Keep last - }; + // mRegionHandle, mCurRegionStats and mResetTimestamp are left untouched. + // Just merge the stats bodies - // Trivial checks - if (! src.has(regions_key)) + const PerRegionContainer::const_iterator it_end(src.mRegionStats.end()); + for (PerRegionContainer::const_iterator it(src.mRegionStats.begin()); it_end != it; ++it) { - return; - } - - if (! dst.has(regions_key)) - { - dst[regions_key] = src[regions_key]; - return; - } - - // Non-trivial cases requiring a deep merge. - const LLSD & root_src(src[regions_key]); - LLSD & root_dst(dst[regions_key]); - - const LLSD::map_const_iterator it_uuid_end(root_src.endMap()); - for (LLSD::map_const_iterator it_uuid(root_src.beginMap()); it_uuid_end != it_uuid; ++it_uuid) - { - if (! root_dst.has(it_uuid->first)) + PerRegionContainer::iterator dst(mRegionStats.find(it->first)); + if (mRegionStats.end() == dst) { - // src[<region>] without matching dst[<region>] - root_dst[it_uuid->first] = it_uuid->second; + // Destination is missing data, just make a private copy + mRegionStats[it->first] = new PerRegionStats(*it->second); } else { - // src[<region>] with matching dst[<region>] - // We have matching source and destination regions. - // Now iterate over each asset bin in the region status. Could iterate over - // an explicit list but this will do as well. - LLSD & reg_dst(root_dst[it_uuid->first]); - const LLSD & reg_src(root_src[it_uuid->first]); - - const LLSD::map_const_iterator it_sets_end(reg_src.endMap()); - for (LLSD::map_const_iterator it_sets(reg_src.beginMap()); it_sets_end != it_sets; ++it_sets) - { - static const LLSD::String no_touch_1("duration"); - - if (no_touch_1 == it_sets->first) - { - continue; - } - else if (! reg_dst.has(it_sets->first)) - { - // src[<region>][<asset>] without matching dst[<region>][<asset>] - reg_dst[it_sets->first] = it_sets->second; - } - else - { - // src[<region>][<asset>] with matching dst[<region>][<asset>] - // Matching stats bin in both source and destination regions. - // Iterate over those bin keys we know how to merge, leave the remainder untouched. - LLSD & bin_dst(reg_dst[it_sets->first]); - const LLSD & bin_src(reg_src[it_sets->first]); - - // The "resp_count" value is needed repeatedly in operations. - const LLSD::Integer bin_src_count(bin_src[resp_count_key].asInteger()); - const LLSD::Integer bin_dst_count(bin_dst[resp_count_key].asInteger()); - - for (int key_index(0); key_index < LL_ARRAY_SIZE(key_list); ++key_index) - { - const LLSD::String & key_name(key_list[key_index].mName); - - if (! bin_src.has(key_name)) - { - // Missing src[<region>][<asset>][<field>] - continue; - } - - const LLSD & src_value(bin_src[key_name]); - - if (! bin_dst.has(key_name)) - { - // src[<region>][<asset>][<field>] without matching dst[<region>][<asset>][<field>] - bin_dst[key_name] = src_value; - } - else - { - // src[<region>][<asset>][<field>] with matching dst[<region>][<asset>][<field>] - LLSD & dst_value(bin_dst[key_name]); - - switch (key_list[key_index].mMergeOp) - { - case MOP_ADD_INT: - // Simple counts, just add - dst_value = dst_value.asInteger() + src_value.asInteger(); - break; - - case MOP_MIN_REAL: - // Minimum - if (bin_src_count) - { - // If src has non-zero count, it's min is meaningful - if (bin_dst_count) - { - dst_value = llmin(dst_value.asReal(), src_value.asReal()); - } - else - { - dst_value = src_value; - } - } - break; - - case MOP_MAX_REAL: - // Maximum - if (bin_src_count) - { - // If src has non-zero count, it's max is meaningful - if (bin_dst_count) - { - dst_value = llmax(dst_value.asReal(), src_value.asReal()); - } - else - { - dst_value = src_value; - } - } - break; - - case MOP_MEAN_REAL: - { - // Mean - F64 src_weight(bin_src_count); - F64 dst_weight(bin_dst_count); - F64 tot_weight(src_weight + dst_weight); - if (tot_weight >= F64(0.5)) - { - dst_value = (((dst_value.asReal() * dst_weight) - + (src_value.asReal() * src_weight)) - / tot_weight); - } - } - break; - - default: - break; - } - } - } - } - } + dst->second->merge(*it->second); } } } @@ -526,6 +426,15 @@ record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration); } +void +record_fps_main(F32 fps) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordFPS(fps); +} + // 'thread1' - should be for TextureFetch thread @@ -590,41 +499,6 @@ cleanup() } -void -merge_stats(const LLSD & src, LLSD & dst) -{ - static const LLSD::String regions_key("regions"); - - // Trivial cases first - if (! src.isMap()) - { - return; - } - - if (! dst.isMap()) - { - dst = src; - return; - } - - // Okay, both src and dst are maps at this point. - // Collector class know how to merge the regions part. - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - // Now merge non-regions bits manually. - const LLSD::map_const_iterator it_end(src.endMap()); - for (LLSD::map_const_iterator it(src.beginMap()); it_end != it; ++it) - { - if (regions_key == it->first) - continue; - - if (dst.has(it->first)) - continue; - - dst[it->first] = it->second; - } -} - } // namespace LLViewerAssetStatsFF diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h index ed2d0f3922..af6bf5b695 100644 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -125,29 +125,48 @@ public: { reset(); } + + PerRegionStats(const PerRegionStats & src) + : LLRefCount(), + mRegionHandle(src.mRegionHandle), + mTotalTime(src.mTotalTime), + mStartTimestamp(src.mStartTimestamp), + mFPS(src.mFPS) + { + for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i] = src.mRequests[i]; + } + } + // Default assignment and destructor are correct. void reset(); + void merge(const PerRegionStats & src); + // Apply current running time to total and reset start point. // Return current timestamp as a convenience. void accumulateTime(duration_t now); public: - region_handle_t mRegionHandle; - duration_t mTotalTime; - duration_t mStartTimestamp; + region_handle_t mRegionHandle; + duration_t mTotalTime; + duration_t mStartTimestamp; + LLSimpleStatMMM<> mFPS; struct { LLSimpleStatCounter mEnqueued; LLSimpleStatCounter mDequeued; LLSimpleStatMMM<duration_t> mResponse; - } mRequests [EVACCount]; + } + mRequests [EVACCount]; }; public: LLViewerAssetStats(); + LLViewerAssetStats(const LLViewerAssetStats &); // Default destructor is correct. LLViewerAssetStats & operator=(const LLViewerAssetStats &); // Not defined @@ -165,6 +184,18 @@ public: void recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp); void recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration); + // Frames-Per-Second Samples + void recordFPS(F32 fps); + + // Merge a source instance into a destination instance. This is + // conceptually an 'operator+=()' method: + // - counts are added + // - minimums are min'd + // - maximums are max'd + // - other scalars are ignored ('this' wins) + // + void merge(const LLViewerAssetStats & src); + // Retrieve current metrics for all visited regions (NULL region UUID/handle excluded) // Returned LLSD is structured as follows: // @@ -177,11 +208,19 @@ public: // resp_mean : float // } // + // &mmm_group = { + // count : int, + // min : float, + // max : float, + // mean : float + // } + // // { // duration: int // regions: { // $: { // Keys are strings of the region's handle in hex // duration: : int, + // fps: : &mmm_group, // get_texture_temp_http : &stats_group, // get_texture_temp_udp : &stats_group, // get_texture_non_temp_http : &stats_group, @@ -195,15 +234,6 @@ public: // } LLSD asLLSD(); - // Merges the "regions" maps in two LLSDs structured as per asLLSD(). - // This takes two LLSDs as returned by asLLSD() and intelligently - // merges the metrics contained in the maps indexed by "regions". - // The remainder of the top-level map of the LLSDs is left unchanged - // in expectation that callers will add other information at this - // level. The "regions" information must be correctly formed or the - // final result is undefined (little defensive action). - static void mergeRegionsLLSD(const LLSD & src, LLSD & dst); - protected: typedef std::map<region_handle_t, LLPointer<PerRegionStats> > PerRegionContainer; @@ -278,6 +308,8 @@ void record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_te void record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration); +void record_fps_main(F32 fps); + /** * Region context, event and duration loggers for Thread 1. @@ -291,18 +323,6 @@ void record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is void record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration); -/** - * @brief Merge two LLSD reports from different collector instances - * - * Use this to merge the LLSD's from two threads. For top-level, - * non-region data the destination (dst) is considered authoritative - * if the key is present in both source and destination. For - * regions, a numerical merge is performed when data are present in - * both source and destination and the 'right thing' is done for - * counts, minimums, maximums and averages. - */ -void merge_stats(const LLSD & src, LLSD & dst); - } // namespace LLViewerAssetStatsFF #endif // LL_LLVIEWERASSETSTATUS_H diff --git a/indra/newview/tests/llsimplestat_test.cpp b/indra/newview/tests/llsimplestat_test.cpp index 5efc9cf857..60a8cac995 100644 --- a/indra/newview/tests/llsimplestat_test.cpp +++ b/indra/newview/tests/llsimplestat_test.cpp @@ -425,4 +425,162 @@ namespace tut ensure("Overflowed MMM<U64> has huge max", (bignum == m1.getMax())); ensure("Overflowed MMM<U64> has fetchable mean", (zero == m1.getMean() || true)); } + + // Testing LLSimpleStatCounter's merge() method + template<> template<> + void stat_counter_index_object_t::test<12>() + { + LLSimpleStatCounter c1; + LLSimpleStatCounter c2; + + ++c1; + ++c1; + ++c1; + ++c1; + + ++c2; + ++c2; + c2.merge(c1); + + ensure_equals("4 merged into 2 results in 6", 6, c2.getCount()); + + ensure_equals("Source of merge is undamaged", 4, c1.getCount()); + } + + // Testing LLSimpleStatMMM's merge() method + template<> template<> + void stat_counter_index_object_t::test<13>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m1.record(3.5); + m1.record(4.5); + m1.record(5.5); + m1.record(6.5); + + m2.record(5.0); + m2.record(7.0); + m2.record(9.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 7, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(3.5), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(41.000/7.000), m2.getMean(), 22); + + + ensure_equals("Source count of merge is undamaged (p1)", 4, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(3.5), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(6.5), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(5.0), m1.getMean(), 22); + + m2.reset(); + + m2.record(-22.0); + m2.record(-1.0); + m2.record(30.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p2)", 7, m2.getCount()); + ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p2)", F32(30.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p2)", F32(27.000/7.000), m2.getMean(), 22); + + } + + // Testing LLSimpleStatMMM's merge() method when src contributes nothing + template<> template<> + void stat_counter_index_object_t::test<14>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m2.record(5.0); + m2.record(7.0); + m2.record(9.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 3, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(5.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(7.000), m2.getMean(), 22); + + ensure_equals("Source count of merge is undamaged (p1)", 0, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(0), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(0), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(0), m1.getMean(), 22); + + m2.reset(); + + m2.record(-22.0); + m2.record(-1.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p2)", 2, m2.getCount()); + ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p2)", F32(-1.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p2)", F32(-11.5), m2.getMean(), 22); + } + + // Testing LLSimpleStatMMM's merge() method when dst contributes nothing + template<> template<> + void stat_counter_index_object_t::test<15>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m1.record(5.0); + m1.record(7.0); + m1.record(9.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 3, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(5.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(9.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(7.000), m2.getMean(), 22); + + ensure_equals("Source count of merge is undamaged (p1)", 3, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(5.0), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(9.0), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(7.0), m1.getMean(), 22); + + m1.reset(); + m2.reset(); + + m1.record(-22.0); + m1.record(-1.0); + + m2.merge(m1); + + ensure_equals("Count after merge (p2)", 2, m2.getCount()); + ensure_approximately_equals("Min after merge (p2)", F32(-22.0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p2)", F32(-1.0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p2)", F32(-11.5), m2.getMean(), 22); + } + + // Testing LLSimpleStatMMM's merge() method when neither dst nor src contributes + template<> template<> + void stat_counter_index_object_t::test<16>() + { + LLSimpleStatMMM<> m1; + LLSimpleStatMMM<> m2; + + m2.merge(m1); + + ensure_equals("Count after merge (p1)", 0, m2.getCount()); + ensure_approximately_equals("Min after merge (p1)", F32(0), m2.getMin(), 22); + ensure_approximately_equals("Max after merge (p1)", F32(0), m2.getMax(), 22); + ensure_approximately_equals("Mean after merge (p1)", F32(0), m2.getMean(), 22); + + ensure_equals("Source count of merge is undamaged (p1)", 0, m1.getCount()); + ensure_approximately_equals("Source min of merge is undamaged (p1)", F32(0), m1.getMin(), 22); + ensure_approximately_equals("Source max of merge is undamaged (p1)", F32(0), m1.getMax(), 22); + ensure_approximately_equals("Source mean of merge is undamaged (p1)", F32(0), m1.getMean(), 22); + } } diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index 153056b3cd..9c54266017 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -44,6 +44,7 @@ static const char * all_keys[] = { "duration", + "fps", "get_other", "get_texture_temp_http", "get_texture_temp_udp", @@ -76,6 +77,19 @@ static const char * sub_keys[] = "resp_mean" }; +static const char * mmm_resp_keys[] = +{ + "fps" +}; + +static const char * mmm_sub_keys[] = +{ + "count", + "max", + "min", + "mean" +}; + static const LLUUID region1("4e2d81a3-6263-6ffe-ad5c-8ce04bee07e8"); static const LLUUID region2("68762cc8-b68b-4e45-854b-e830734f2d4a"); static const U64 region1_handle(0x00000401000003f7ULL); @@ -172,6 +186,15 @@ namespace tut ensure(line, sd[resp_keys[i]].has(sub_keys[j])); } } + + for (int i = 0; i < LL_ARRAY_SIZE(mmm_resp_keys); ++i) + { + for (int j = 0; j < LL_ARRAY_SIZE(mmm_sub_keys); ++j) + { + std::string line = llformat("Key '%s' has '%s' key", mmm_resp_keys[i], mmm_sub_keys[j]); + ensure(line, sd[mmm_resp_keys[i]].has(mmm_sub_keys[j])); + } + } } // Create a non-global instance and check some content @@ -461,293 +484,395 @@ namespace tut ensure("sd[get_gesture_udp][dequeued] is reset", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); } - // Check that the LLSD merger knows what it's doing (basic test) + + // LLViewerAssetStats::merge() basic functions work template<> template<> void tst_viewerassetstats_index_object_t::test<9>() { - LLSD::String reg1_name = region1_handle_str; - LLSD::String reg2_name = region2_handle_str; - - LLSD reg1_stats = LLSD::emptyMap(); - LLSD reg2_stats = LLSD::emptyMap(); - - LLSD & tmp_other1 = reg1_stats["get_other"]; - tmp_other1["enqueued"] = 4; - tmp_other1["dequeued"] = 4; - tmp_other1["resp_count"] = 8; - tmp_other1["resp_max"] = F64(23.2892); - tmp_other1["resp_min"] = F64(0.2829); - tmp_other1["resp_mean"] = F64(2.298928); - - LLSD & tmp_other2 = reg2_stats["get_other"]; - tmp_other2["enqueued"] = 8; - tmp_other2["dequeued"] = 7; - tmp_other2["resp_count"] = 3; - tmp_other2["resp_max"] = F64(6.5); - tmp_other2["resp_min"] = F64(0.01); - tmp_other2["resp_mean"] = F64(4.1); + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 5000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 6000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 8000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 7000000); + s1.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 9000000); - { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 2000000); + s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 3000000); + s2.recordGetServiced(LLViewerAssetType::AT_TEXTURE, true, true, 4000000); - src["regions"][reg1_name] = reg1_stats; - src["duration"] = 24; - dst["regions"][reg2_name] = reg2_stats; - dst["duration"] = 36; + s2.merge(s1); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); + LLSD s2_llsd = s2.asLLSD(); - ensure("region 1 in merged stats", llsd_equals(reg1_stats, dst["regions"][reg1_name])); - ensure("region 2 still in merged stats", llsd_equals(reg2_stats, dst["regions"][reg2_name])); - } + ensure_equals("count after merge", 8, s2_llsd["regions"][region1_handle_str]["get_texture_temp_http"]["resp_count"].asInteger()); + ensure_approximately_equals("min after merge", 2.0, s2_llsd["regions"][region1_handle_str]["get_texture_temp_http"]["resp_min"].asReal(), 22); + ensure_approximately_equals("max after merge", 9.0, s2_llsd["regions"][region1_handle_str]["get_texture_temp_http"]["resp_max"].asReal(), 22); + ensure_approximately_equals("max after merge", 5.5, s2_llsd["regions"][region1_handle_str]["get_texture_temp_http"]["resp_mean"].asReal(), 22); - { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); - - src["regions"][reg1_name] = reg1_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg2_stats; - dst["duration"] = 36; - - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - ensure("src not ruined", llsd_equals(reg1_stats, src["regions"][reg1_name])); - ensure_equals("added enqueued counts", dst["regions"][reg1_name]["get_other"]["enqueued"].asInteger(), 12); - ensure_equals("added dequeued counts", dst["regions"][reg1_name]["get_other"]["dequeued"].asInteger(), 11); - ensure_equals("added response counts", dst["regions"][reg1_name]["get_other"]["resp_count"].asInteger(), 11); - ensure_approximately_equals("min'd minimum response times", dst["regions"][reg1_name]["get_other"]["resp_min"].asReal(), 0.01, 20); - ensure_approximately_equals("max'd maximum response times", dst["regions"][reg1_name]["get_other"]["resp_max"].asReal(), 23.2892, 20); - ensure_approximately_equals("weighted mean of means", dst["regions"][reg1_name]["get_other"]["resp_mean"].asReal(), 2.7901295, 20); - } } - // Maximum merges are interesting when one side contributes nothing + // LLViewerAssetStats::merge() basic functions work without corrupting source data template<> template<> void tst_viewerassetstats_index_object_t::test<10>() { - LLSD::String reg1_name = region1_handle_str; - LLSD::String reg2_name = region2_handle_str; - - LLSD reg1_stats = LLSD::emptyMap(); - LLSD reg2_stats = LLSD::emptyMap(); - - LLSD & tmp_other1 = reg1_stats["get_other"]; - tmp_other1["enqueued"] = 4; - tmp_other1["dequeued"] = 4; - tmp_other1["resp_count"] = 7; - tmp_other1["resp_max"] = F64(-23.2892); - tmp_other1["resp_min"] = F64(-123.2892); - tmp_other1["resp_mean"] = F64(-58.28298); - - LLSD & tmp_other2 = reg2_stats["get_other"]; - tmp_other2["enqueued"] = 8; - tmp_other2["dequeued"] = 7; - tmp_other2["resp_count"] = 0; - tmp_other2["resp_max"] = F64(0); - tmp_other2["resp_min"] = F64(0); - tmp_other2["resp_mean"] = F64(0); - - { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + LLViewerAssetStats s1; + LLViewerAssetStats s2; - src["regions"][reg1_name] = reg1_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg2_stats; - dst["duration"] = 36; + s1.setRegion(region1_handle); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum", - dst["regions"][reg1_name]["get_other"]["resp_max"].asReal(), F64(-23.2892), 20); - } - - { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); - src["regions"][reg1_name] = reg2_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg1_stats; - dst["duration"] = 36; + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - ensure_approximately_equals("src maximum with count 0 does not contribute to merged maximum", - dst["regions"][reg1_name]["get_other"]["resp_max"].asReal(), F64(-23.2892), 20); - } - } + s2.setRegion(region2_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000); + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000); - // Minimum merges are interesting when one side contributes nothing - template<> template<> - void tst_viewerassetstats_index_object_t::test<11>() - { - LLSD::String reg1_name = region1_handle_str; - LLSD::String reg2_name = region2_handle_str; - - LLSD reg1_stats = LLSD::emptyMap(); - LLSD reg2_stats = LLSD::emptyMap(); - - LLSD & tmp_other1 = reg1_stats["get_other"]; - tmp_other1["enqueued"] = 4; - tmp_other1["dequeued"] = 4; - tmp_other1["resp_count"] = 7; - tmp_other1["resp_max"] = F64(123.2892); - tmp_other1["resp_min"] = F64(23.2892); - tmp_other1["resp_mean"] = F64(58.28298); - - LLSD & tmp_other2 = reg2_stats["get_other"]; - tmp_other2["enqueued"] = 8; - tmp_other2["dequeued"] = 7; - tmp_other2["resp_count"] = 0; - tmp_other2["resp_max"] = F64(0); - tmp_other2["resp_min"] = F64(0); - tmp_other2["resp_mean"] = F64(0); - { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); - - src["regions"][reg1_name] = reg1_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg2_stats; - dst["duration"] = 36; + s2.merge(s1); + + LLSD src = s1.asLLSD(); + LLSD dst = s2.asLLSD(); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][region1_handle_str].erase("duration"); + dst.erase("duration"); + dst["regions"][region1_handle_str].erase("duration"); + dst["regions"][region2_handle_str].erase("duration"); + + ensure_equals("merge src has single region", 1, src["regions"].size()); + ensure_equals("merge dst has dual regions", 2, dst["regions"].size()); + ensure("result from src is in dst", llsd_equals(src["regions"][region1_handle_str], + dst["regions"][region1_handle_str])); + } - LLViewerAssetStats::mergeRegionsLLSD(src, dst); + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + s1.reset(); + s2.reset(); - ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum", - dst["regions"][reg1_name]["get_other"]["resp_min"].asReal(), F64(23.2892), 20); - } + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); - { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); - src["regions"][reg1_name] = reg2_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg1_stats; - dst["duration"] = 36; + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 23289200); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 282900); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - ensure_approximately_equals("src minimum with count 0 does not contribute to merged minimum", - dst["regions"][reg1_name]["get_other"]["resp_min"].asReal(), F64(23.2892), 20); + s2.setRegion(region1_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 6500000); + s2.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 10000); + + { + s2.merge(s1); + + LLSD src = s1.asLLSD(); + LLSD dst = s2.asLLSD(); + + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][region1_handle_str].erase("duration"); + dst.erase("duration"); + dst["regions"][region1_handle_str].erase("duration"); + + ensure_equals("src counts okay (enq)", 4, src["regions"][region1_handle_str]["get_other"]["enqueued"].asInteger()); + ensure_equals("src counts okay (deq)", 4, src["regions"][region1_handle_str]["get_other"]["dequeued"].asInteger()); + ensure_equals("src resp counts okay", 2, src["regions"][region1_handle_str]["get_other"]["resp_count"].asInteger()); + ensure_approximately_equals("src respmin okay", 0.2829, src["regions"][region1_handle_str]["get_other"]["resp_min"].asReal(), 20); + ensure_approximately_equals("src respmax okay", 23.2892, src["regions"][region1_handle_str]["get_other"]["resp_max"].asReal(), 20); + + ensure_equals("dst counts okay (enq)", 12, dst["regions"][region1_handle_str]["get_other"]["enqueued"].asInteger()); + ensure_equals("src counts okay (deq)", 11, dst["regions"][region1_handle_str]["get_other"]["dequeued"].asInteger()); + ensure_equals("dst resp counts okay", 4, dst["regions"][region1_handle_str]["get_other"]["resp_count"].asInteger()); + ensure_approximately_equals("dst respmin okay", 0.010, dst["regions"][region1_handle_str]["get_other"]["resp_min"].asReal(), 20); + ensure_approximately_equals("dst respmax okay", 23.2892, dst["regions"][region1_handle_str]["get_other"]["resp_max"].asReal(), 20); } } - // resp_count missing is taken as '0' for maximum calculation + + // Maximum merges are interesting when one side contributes nothing template<> template<> - void tst_viewerassetstats_index_object_t::test<12>() + void tst_viewerassetstats_index_object_t::test<11>() { - LLSD::String reg1_name = region1_handle_str; - LLSD::String reg2_name = region2_handle_str; - - LLSD reg1_stats = LLSD::emptyMap(); - LLSD reg2_stats = LLSD::emptyMap(); - - LLSD & tmp_other1 = reg1_stats["get_other"]; - tmp_other1["enqueued"] = 4; - tmp_other1["dequeued"] = 4; - tmp_other1["resp_count"] = 7; - tmp_other1["resp_max"] = F64(-23.2892); - tmp_other1["resp_min"] = F64(-123.2892); - tmp_other1["resp_mean"] = F64(-58.28298); - - LLSD & tmp_other2 = reg2_stats["get_other"]; - tmp_other2["enqueued"] = 8; - tmp_other2["dequeued"] = 7; - // tmp_other2["resp_count"] = 0; - tmp_other2["resp_max"] = F64(0); - tmp_other2["resp_min"] = F64(0); - tmp_other2["resp_mean"] = F64(0); - + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + // Want to test negative numbers here but have to work in U64 + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + + s2.setRegion(region1_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s2.merge(s1); + + LLSD src = s1.asLLSD(); + LLSD dst = s2.asLLSD(); - src["regions"][reg1_name] = reg1_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg2_stats; - dst["duration"] = 36; + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][region1_handle_str].erase("duration"); + dst.erase("duration"); + dst["regions"][region1_handle_str].erase("duration"); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - ensure_approximately_equals("dst maximum with undefined count does not contribute to merged maximum", - dst["regions"][reg1_name]["get_other"]["resp_max"].asReal(), F64(-23.2892), 20); + ensure_equals("dst counts come from src only", 3, dst["regions"][region1_handle_str]["get_other"]["resp_count"].asInteger()); + + ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum", + dst["regions"][region1_handle_str]["get_other"]["resp_max"].asReal(), F64(0.0), 20); } + // Other way around + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + s1.reset(); + s2.reset(); + + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + // Want to test negative numbers here but have to work in U64 + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 0); + + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s1.merge(s2); + + LLSD src = s2.asLLSD(); + LLSD dst = s1.asLLSD(); - src["regions"][reg1_name] = reg2_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg1_stats; - dst["duration"] = 36; + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][region1_handle_str].erase("duration"); + dst.erase("duration"); + dst["regions"][region1_handle_str].erase("duration"); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - ensure_approximately_equals("src maximum with undefined count does not contribute to merged maximum", - dst["regions"][reg1_name]["get_other"]["resp_max"].asReal(), F64(-23.2892), 20); + ensure_equals("dst counts come from src only (flipped)", 3, dst["regions"][region1_handle_str]["get_other"]["resp_count"].asInteger()); + + ensure_approximately_equals("dst maximum with count 0 does not contribute to merged maximum (flipped)", + dst["regions"][region1_handle_str]["get_other"]["resp_max"].asReal(), F64(0.0), 20); } } - // resp_count unspecified is taken as 0 for minimum merges + // Minimum merges are interesting when one side contributes nothing template<> template<> - void tst_viewerassetstats_index_object_t::test<13>() + void tst_viewerassetstats_index_object_t::test<12>() { - LLSD::String reg1_name = region1.asString(); - LLSD::String reg2_name = region2.asString(); - - LLSD reg1_stats = LLSD::emptyMap(); - LLSD reg2_stats = LLSD::emptyMap(); - - LLSD & tmp_other1 = reg1_stats["get_other"]; - tmp_other1["enqueued"] = 4; - tmp_other1["dequeued"] = 4; - tmp_other1["resp_count"] = 7; - tmp_other1["resp_max"] = F64(123.2892); - tmp_other1["resp_min"] = F64(23.2892); - tmp_other1["resp_mean"] = F64(58.28298); - - LLSD & tmp_other2 = reg2_stats["get_other"]; - tmp_other2["enqueued"] = 8; - tmp_other2["dequeued"] = 7; - // tmp_other2["resp_count"] = 0; - tmp_other2["resp_max"] = F64(0); - tmp_other2["resp_min"] = F64(0); - tmp_other2["resp_mean"] = F64(0); - + LLViewerAssetStats s1; + LLViewerAssetStats s2; + + s1.setRegion(region1_handle); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000); + + s2.setRegion(region1_handle); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s2.merge(s1); + + LLSD src = s1.asLLSD(); + LLSD dst = s2.asLLSD(); - src["regions"][reg1_name] = reg1_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg2_stats; - dst["duration"] = 36; + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][region1_handle_str].erase("duration"); + dst.erase("duration"); + dst["regions"][region1_handle_str].erase("duration"); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - ensure_approximately_equals("dst minimum with undefined count does not contribute to merged minimum", - dst["regions"][reg1_name]["get_other"]["resp_min"].asReal(), F64(23.2892), 20); + ensure_equals("dst counts come from src only", 3, dst["regions"][region1_handle_str]["get_other"]["resp_count"].asInteger()); + + ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum", + dst["regions"][region1_handle_str]["get_other"]["resp_min"].asReal(), F64(2.7), 20); } + // Other way around + s1.setRegion(region1_handle); + s2.setRegion(region1_handle); + s1.reset(); + s2.reset(); + + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s1.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 3800000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2700000); + s1.recordGetServiced(LLViewerAssetType::AT_LSL_BYTECODE, true, true, 2900000); + + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetEnqueued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + s2.recordGetDequeued(LLViewerAssetType::AT_LSL_BYTECODE, true, true); + { - LLSD src = LLSD::emptyMap(); - LLSD dst = LLSD::emptyMap(); + s1.merge(s2); + + LLSD src = s2.asLLSD(); + LLSD dst = s1.asLLSD(); - src["regions"][reg1_name] = reg2_stats; - src["duration"] = 24; - dst["regions"][reg1_name] = reg1_stats; - dst["duration"] = 36; + // Remove time stamps, they're a problem + src.erase("duration"); + src["regions"][region1_handle_str].erase("duration"); + dst.erase("duration"); + dst["regions"][region1_handle_str].erase("duration"); - LLViewerAssetStats::mergeRegionsLLSD(src, dst); - - ensure_approximately_equals("src minimum with undefined count does not contribute to merged minimum", - dst["regions"][reg1_name]["get_other"]["resp_min"].asReal(), F64(23.2892), 20); + ensure_equals("dst counts come from src only (flipped)", 3, dst["regions"][region1_handle_str]["get_other"]["resp_count"].asInteger()); + + ensure_approximately_equals("dst minimum with count 0 does not contribute to merged minimum (flipped)", + dst["regions"][region1_handle_str]["get_other"]["resp_min"].asReal(), F64(2.7), 20); } } + } |