From 851f995287c0973368250b3dd8ed9a884b6a96b0 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 28 Oct 2010 08:48:26 -0700 Subject: ESC-109 Write single-thread asset stats collector for wearable. Code-complete with unit tests and foundation for other collectors. Interestingly, sim and viewer have two different ideas about asset type enumeration (compatible, one's just longer). Both are missing mesh though that's to be expected. --- indra/newview/llviewerassetstats.cpp | 277 +++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 indra/newview/llviewerassetstats.cpp (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp new file mode 100644 index 0000000000..f74b394d78 --- /dev/null +++ b/indra/newview/llviewerassetstats.cpp @@ -0,0 +1,277 @@ +/** + * @file llviewerassetstats.cpp + * @brief + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerassetstats.h" + +#include "stdtypes.h" + +/* + * References + * + * Project: + * + * + * Test Plan: + * + * + * Jiras: + * + * + * Unit Tests: + * + * + */ + + +// ------------------------------------------------------ +// Global data definitions +// ------------------------------------------------------ +LLViewerAssetStats * gViewerAssetStats = NULL; + + +// ------------------------------------------------------ +// Local declarations +// ------------------------------------------------------ +namespace +{ + +static LLViewerAssetStats::EViewerAssetCategories +asset_type_to_category(const LLViewerAssetType::EType at); + +} + +// ------------------------------------------------------ +// LLViewerAssetStats class definition +// ------------------------------------------------------ +LLViewerAssetStats::LLViewerAssetStats() +{ + reset(); +} + + +void +LLViewerAssetStats::reset() +{ + for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i].mEnqueued.reset(); + mRequests[i].mDequeued.reset(); + mRequests[i].mResponse.reset(); + } +} + +void +LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at) +{ + const EViewerAssetCategories eac(asset_type_to_category(at)); + + ++mRequests[int(eac)].mEnqueued; +} + +void +LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at) +{ + const EViewerAssetCategories eac(asset_type_to_category(at)); + + ++mRequests[int(eac)].mDequeued; +} + +void +LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, F64 duration) +{ + const EViewerAssetCategories eac(asset_type_to_category(at)); + + mRequests[int(eac)].mResponse.record(duration); +} + +const LLSD +LLViewerAssetStats::asLLSD() const +{ + // Top-level tags + static const LLSD::String tags[EVACCount] = + { + LLSD::String("get_texture"), + LLSD::String("get_wearable"), + LLSD::String("get_sound"), + LLSD::String("get_gesture"), + LLSD::String("get_other") + }; + + // Sub-tags + static const LLSD::String enq_tag("enqueued"); + static const LLSD::String deq_tag("dequeued"); + static const LLSD::String rcnt_tag("resp_count"); + static const LLSD::String rmin_tag("resp_min"); + static const LLSD::String rmax_tag("resp_max"); + static const LLSD::String rmean_tag("resp_mean"); + + LLSD ret = LLSD::emptyMap(); + + for (int i = 0; i < EVACCount; ++i) + { + LLSD & slot = ret[tags[i]]; + slot = LLSD::emptyMap(); + slot[enq_tag] = LLSD(S32(mRequests[i].mEnqueued.getCount())); + slot[deq_tag] = LLSD(S32(mRequests[i].mDequeued.getCount())); + slot[rcnt_tag] = LLSD(S32(mRequests[i].mResponse.getCount())); + slot[rmin_tag] = LLSD(mRequests[i].mResponse.getMin()); + slot[rmax_tag] = LLSD(mRequests[i].mResponse.getMax()); + slot[rmean_tag] = LLSD(mRequests[i].mResponse.getMean()); + } + + return ret; +} + +// ------------------------------------------------------ +// Global free-function definitions (LLViewerAssetStatsFF namespace) +// ------------------------------------------------------ + +namespace LLViewerAssetStatsFF +{ + +void +record_enqueue(LLViewerAssetType::EType at) +{ + if (! gViewerAssetStats) + return; + + gViewerAssetStats->recordGetEnqueued(at); +} + +void +record_dequeue(LLViewerAssetType::EType at) +{ + if (! gViewerAssetStats) + return; + + gViewerAssetStats->recordGetDequeued(at); +} + +void +record_response(LLViewerAssetType::EType at, F64 duration) +{ + if (! gViewerAssetStats) + return; + + gViewerAssetStats->recordGetServiced(at, duration); +} + +} // namespace LLViewerAssetStatsFF + + +// ------------------------------------------------------ +// Local function definitions +// ------------------------------------------------------ + +namespace +{ + +LLViewerAssetStats::EViewerAssetCategories +asset_type_to_category(const LLViewerAssetType::EType at) +{ + // For statistical purposes, we divide GETs into several + // populations of asset fetches: + // - textures which are de-prioritized in the asset system + // - wearables (clothing, bodyparts) which directly affect + // user experiences when they log in + // - sounds + // - gestures + // - everything else. + // + llassert_always(26 == LLViewerAssetType::AT_COUNT); + + // Multiple asset definitions are floating around so this requires some + // maintenance and attention. + static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] = + { + LLViewerAssetStats::EVACTextureGet, // (0) AT_TEXTURE + LLViewerAssetStats::EVACSoundGet, // AT_SOUND + LLViewerAssetStats::EVACOtherGet, // AT_CALLINGCARD + LLViewerAssetStats::EVACOtherGet, // AT_LANDMARK + LLViewerAssetStats::EVACOtherGet, // AT_SCRIPT + LLViewerAssetStats::EVACWearableGet, // AT_CLOTHING + LLViewerAssetStats::EVACOtherGet, // AT_OBJECT + LLViewerAssetStats::EVACOtherGet, // AT_NOTECARD + LLViewerAssetStats::EVACOtherGet, // AT_CATEGORY + LLViewerAssetStats::EVACOtherGet, // AT_ROOT_CATEGORY + LLViewerAssetStats::EVACOtherGet, // (10) AT_LSL_TEXT + LLViewerAssetStats::EVACOtherGet, // AT_LSL_BYTECODE + LLViewerAssetStats::EVACOtherGet, // AT_TEXTURE_TGA + LLViewerAssetStats::EVACWearableGet, // AT_BODYPART + LLViewerAssetStats::EVACOtherGet, // AT_TRASH + LLViewerAssetStats::EVACOtherGet, // AT_SNAPSHOT_CATEGORY + LLViewerAssetStats::EVACOtherGet, // AT_LOST_AND_FOUND + LLViewerAssetStats::EVACSoundGet, // AT_SOUND_WAV + LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_TGA + LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_JPEG + LLViewerAssetStats::EVACGestureGet, // (20) AT_ANIMATION + LLViewerAssetStats::EVACGestureGet, // AT_GESTURE + LLViewerAssetStats::EVACOtherGet, // AT_SIMSTATE + LLViewerAssetStats::EVACOtherGet, // AT_FAVORITE + LLViewerAssetStats::EVACOtherGet, // AT_LINK + LLViewerAssetStats::EVACOtherGet, // AT_LINK_FOLDER +#if 0 + // When LLViewerAssetType::AT_COUNT == 49 + LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_START + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // (30) + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // (40) + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_END + LLViewerAssetStats::EVACOtherGet, // AT_CURRENT_OUTFIT + LLViewerAssetStats::EVACOtherGet, // AT_OUTFIT + LLViewerAssetStats::EVACOtherGet // AT_MY_OUTFITS +#endif + }; + + if (at < 0 || at >= LLViewerAssetType::AT_COUNT) + { + return LLViewerAssetStats::EVACOtherGet; + } + return asset_to_bin_map[at]; +} + +} // anonymous namespace -- cgit v1.2.3 From 1ed9d997a6c380f71f2da182c8083321e35b5034 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 29 Oct 2010 08:50:03 -0700 Subject: ESC-111 Texture interfaces Mainly expand the categories to include protocol and location/temp nature of texture asset. --- indra/newview/llviewerassetstats.cpp | 157 ++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 68 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index f74b394d78..0852573bbd 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -65,7 +65,7 @@ namespace { static LLViewerAssetStats::EViewerAssetCategories -asset_type_to_category(const LLViewerAssetType::EType at); +asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp); } @@ -90,25 +90,25 @@ LLViewerAssetStats::reset() } void -LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at) +LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp) { - const EViewerAssetCategories eac(asset_type_to_category(at)); + const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); ++mRequests[int(eac)].mEnqueued; } void -LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at) +LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_http, bool is_temp) { - const EViewerAssetCategories eac(asset_type_to_category(at)); + const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); ++mRequests[int(eac)].mDequeued; } void -LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, F64 duration) +LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) { - const EViewerAssetCategories eac(asset_type_to_category(at)); + const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); mRequests[int(eac)].mResponse.record(duration); } @@ -119,10 +119,13 @@ LLViewerAssetStats::asLLSD() const // Top-level tags static const LLSD::String tags[EVACCount] = { - LLSD::String("get_texture"), - LLSD::String("get_wearable"), - LLSD::String("get_sound"), - LLSD::String("get_gesture"), + LLSD::String("get_texture_temp_http"), + LLSD::String("get_texture_temp_udp"), + LLSD::String("get_texture_non_temp_http"), + LLSD::String("get_texture_non_temp_udp"), + LLSD::String("get_wearable_udp"), + LLSD::String("get_sound_udp"), + LLSD::String("get_gesture_udp"), LLSD::String("get_other") }; @@ -159,30 +162,30 @@ namespace LLViewerAssetStatsFF { void -record_enqueue(LLViewerAssetType::EType at) +record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp) { if (! gViewerAssetStats) return; - gViewerAssetStats->recordGetEnqueued(at); + gViewerAssetStats->recordGetEnqueued(at, with_http, is_temp); } void -record_dequeue(LLViewerAssetType::EType at) +record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp) { if (! gViewerAssetStats) return; - gViewerAssetStats->recordGetDequeued(at); + gViewerAssetStats->recordGetDequeued(at, with_http, is_temp); } void -record_response(LLViewerAssetType::EType at, F64 duration) +record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) { if (! gViewerAssetStats) return; - gViewerAssetStats->recordGetServiced(at, duration); + gViewerAssetStats->recordGetServiced(at, with_http, is_temp, duration); } } // namespace LLViewerAssetStatsFF @@ -196,7 +199,7 @@ namespace { LLViewerAssetStats::EViewerAssetCategories -asset_type_to_category(const LLViewerAssetType::EType at) +asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp) { // For statistical purposes, we divide GETs into several // populations of asset fetches: @@ -213,57 +216,57 @@ asset_type_to_category(const LLViewerAssetType::EType at) // maintenance and attention. static const LLViewerAssetStats::EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] = { - LLViewerAssetStats::EVACTextureGet, // (0) AT_TEXTURE - LLViewerAssetStats::EVACSoundGet, // AT_SOUND - LLViewerAssetStats::EVACOtherGet, // AT_CALLINGCARD - LLViewerAssetStats::EVACOtherGet, // AT_LANDMARK - LLViewerAssetStats::EVACOtherGet, // AT_SCRIPT - LLViewerAssetStats::EVACWearableGet, // AT_CLOTHING - LLViewerAssetStats::EVACOtherGet, // AT_OBJECT - LLViewerAssetStats::EVACOtherGet, // AT_NOTECARD - LLViewerAssetStats::EVACOtherGet, // AT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // AT_ROOT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // (10) AT_LSL_TEXT - LLViewerAssetStats::EVACOtherGet, // AT_LSL_BYTECODE - LLViewerAssetStats::EVACOtherGet, // AT_TEXTURE_TGA - LLViewerAssetStats::EVACWearableGet, // AT_BODYPART - LLViewerAssetStats::EVACOtherGet, // AT_TRASH - LLViewerAssetStats::EVACOtherGet, // AT_SNAPSHOT_CATEGORY - LLViewerAssetStats::EVACOtherGet, // AT_LOST_AND_FOUND - LLViewerAssetStats::EVACSoundGet, // AT_SOUND_WAV - LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_TGA - LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_JPEG - LLViewerAssetStats::EVACGestureGet, // (20) AT_ANIMATION - LLViewerAssetStats::EVACGestureGet, // AT_GESTURE - LLViewerAssetStats::EVACOtherGet, // AT_SIMSTATE - LLViewerAssetStats::EVACOtherGet, // AT_FAVORITE - LLViewerAssetStats::EVACOtherGet, // AT_LINK - LLViewerAssetStats::EVACOtherGet, // AT_LINK_FOLDER + LLViewerAssetStats::EVACTextureTempHTTPGet, // (0) AT_TEXTURE + LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND + LLViewerAssetStats::EVACOtherGet, // AT_CALLINGCARD + LLViewerAssetStats::EVACOtherGet, // AT_LANDMARK + LLViewerAssetStats::EVACOtherGet, // AT_SCRIPT + LLViewerAssetStats::EVACWearableUDPGet, // AT_CLOTHING + LLViewerAssetStats::EVACOtherGet, // AT_OBJECT + LLViewerAssetStats::EVACOtherGet, // AT_NOTECARD + LLViewerAssetStats::EVACOtherGet, // AT_CATEGORY + LLViewerAssetStats::EVACOtherGet, // AT_ROOT_CATEGORY + LLViewerAssetStats::EVACOtherGet, // (10) AT_LSL_TEXT + LLViewerAssetStats::EVACOtherGet, // AT_LSL_BYTECODE + LLViewerAssetStats::EVACOtherGet, // AT_TEXTURE_TGA + LLViewerAssetStats::EVACWearableUDPGet, // AT_BODYPART + LLViewerAssetStats::EVACOtherGet, // AT_TRASH + LLViewerAssetStats::EVACOtherGet, // AT_SNAPSHOT_CATEGORY + LLViewerAssetStats::EVACOtherGet, // AT_LOST_AND_FOUND + LLViewerAssetStats::EVACSoundUDPGet, // AT_SOUND_WAV + LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_TGA + LLViewerAssetStats::EVACOtherGet, // AT_IMAGE_JPEG + LLViewerAssetStats::EVACGestureUDPGet, // (20) AT_ANIMATION + LLViewerAssetStats::EVACGestureUDPGet, // AT_GESTURE + LLViewerAssetStats::EVACOtherGet, // AT_SIMSTATE + LLViewerAssetStats::EVACOtherGet, // AT_FAVORITE + LLViewerAssetStats::EVACOtherGet, // AT_LINK + LLViewerAssetStats::EVACOtherGet, // AT_LINK_FOLDER #if 0 // When LLViewerAssetType::AT_COUNT == 49 - LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_START - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // (30) - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // (40) - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // - LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_END - LLViewerAssetStats::EVACOtherGet, // AT_CURRENT_OUTFIT - LLViewerAssetStats::EVACOtherGet, // AT_OUTFIT - LLViewerAssetStats::EVACOtherGet // AT_MY_OUTFITS + LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_START + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // (30) + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // (40) + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // + LLViewerAssetStats::EVACOtherGet, // AT_FOLDER_ENSEMBLE_END + LLViewerAssetStats::EVACOtherGet, // AT_CURRENT_OUTFIT + LLViewerAssetStats::EVACOtherGet, // AT_OUTFIT + LLViewerAssetStats::EVACOtherGet // AT_MY_OUTFITS #endif }; @@ -271,7 +274,25 @@ asset_type_to_category(const LLViewerAssetType::EType at) { return LLViewerAssetStats::EVACOtherGet; } - return asset_to_bin_map[at]; + LLViewerAssetStats::EViewerAssetCategories ret(asset_to_bin_map[at]); + if (LLViewerAssetStats::EVACTextureTempHTTPGet == ret) + { + // Indexed with [is_temp][with_http] + static const LLViewerAssetStats::EViewerAssetCategories texture_bin_map[2][2] = + { + { + LLViewerAssetStats::EVACTextureNonTempUDPGet, + LLViewerAssetStats::EVACTextureNonTempHTTPGet, + }, + { + LLViewerAssetStats::EVACTextureTempUDPGet, + LLViewerAssetStats::EVACTextureTempHTTPGet, + } + }; + + ret = texture_bin_map[is_temp][with_http]; + } + return ret; } } // anonymous namespace -- cgit v1.2.3 From fd2d4dc1b16430edd367a8b0f4162238bbb7e22c Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 10 Nov 2010 08:44:53 -0800 Subject: ESC-110 ESC-111 Cleanup passes on the two threaded collectors with better comments and more complete unit tests. --- indra/newview/llviewerassetstats.cpp | 172 +++++++++++++++++++++++++++++------ 1 file changed, 145 insertions(+), 27 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 0852573bbd..a6c4685bf1 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -47,7 +47,7 @@ * * * Unit Tests: - * + * indra/newview/tests/llviewerassetstats_test.cpp * */ @@ -55,7 +55,8 @@ // ------------------------------------------------------ // Global data definitions // ------------------------------------------------------ -LLViewerAssetStats * gViewerAssetStats = NULL; +LLViewerAssetStats * gViewerAssetStatsMain(0); +LLViewerAssetStats * gViewerAssetStatsThread1(0); // ------------------------------------------------------ @@ -69,6 +70,21 @@ asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool i } +// ------------------------------------------------------ +// LLViewerAssetStats::PerRegionStats struct definition +// ------------------------------------------------------ +void +LLViewerAssetStats::PerRegionStats::reset() +{ + for (int i(0); i < LL_ARRAY_SIZE(mRequests); ++i) + { + mRequests[i].mEnqueued.reset(); + mRequests[i].mDequeued.reset(); + mRequests[i].mResponse.reset(); + } +} + + // ------------------------------------------------------ // LLViewerAssetStats class definition // ------------------------------------------------------ @@ -81,20 +97,55 @@ LLViewerAssetStats::LLViewerAssetStats() void LLViewerAssetStats::reset() { - for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) + // Empty the map of all region stats + mRegionStats.clear(); + + // If we have a current stats, reset it, otherwise, as at construction, + // create a new one. + if (mCurRegionStats) { - mRequests[i].mEnqueued.reset(); - mRequests[i].mDequeued.reset(); - mRequests[i].mResponse.reset(); + mCurRegionStats->reset(); } + else + { + mCurRegionStats = new PerRegionStats(mRegionID); + } + + // And add reference to map + mRegionStats[mRegionID] = mCurRegionStats; } + +void +LLViewerAssetStats::setRegionID(const LLUUID & region_id) +{ + if (region_id == mRegionID) + { + // Already active, ignore. + return; + } + + PerRegionContainer::iterator new_stats = mRegionStats.find(region_id); + if (mRegionStats.end() == new_stats) + { + // Haven't seen this region_id before, create a new block make it current. + mCurRegionStats = new PerRegionStats(region_id); + mRegionStats[region_id] = mCurRegionStats; + } + else + { + mCurRegionStats = new_stats->second; + } + mRegionID = region_id; +} + + void LLViewerAssetStats::recordGetEnqueued(LLViewerAssetType::EType at, bool with_http, bool is_temp) { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - ++mRequests[int(eac)].mEnqueued; + ++(mCurRegionStats->mRequests[int(eac)].mEnqueued); } void @@ -102,7 +153,7 @@ LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_htt { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - ++mRequests[int(eac)].mDequeued; + ++(mCurRegionStats->mRequests[int(eac)].mDequeued); } void @@ -110,7 +161,7 @@ LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_htt { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - mRequests[int(eac)].mResponse.record(duration); + mCurRegionStats->mRequests[int(eac)].mResponse.record(duration); } const LLSD @@ -139,16 +190,33 @@ LLViewerAssetStats::asLLSD() const LLSD ret = LLSD::emptyMap(); - for (int i = 0; i < EVACCount; ++i) + for (PerRegionContainer::const_iterator it = mRegionStats.begin(); + mRegionStats.end() != it; + ++it) { - LLSD & slot = ret[tags[i]]; - slot = LLSD::emptyMap(); - slot[enq_tag] = LLSD(S32(mRequests[i].mEnqueued.getCount())); - slot[deq_tag] = LLSD(S32(mRequests[i].mDequeued.getCount())); - slot[rcnt_tag] = LLSD(S32(mRequests[i].mResponse.getCount())); - slot[rmin_tag] = LLSD(mRequests[i].mResponse.getMin()); - slot[rmax_tag] = LLSD(mRequests[i].mResponse.getMax()); - slot[rmean_tag] = LLSD(mRequests[i].mResponse.getMean()); + if (it->first.isNull()) + { + // Never emit NULL UUID in results. + continue; + } + + const PerRegionStats & stats = *it->second; + + LLSD reg_stat = LLSD::emptyMap(); + + for (int i = 0; i < EVACCount; ++i) + { + LLSD & slot = reg_stat[tags[i]]; + slot = LLSD::emptyMap(); + slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); + slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); + slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); + slot[rmin_tag] = LLSD(stats.mRequests[i].mResponse.getMin()); + slot[rmax_tag] = LLSD(stats.mRequests[i].mResponse.getMax()); + slot[rmean_tag] = LLSD(stats.mRequests[i].mResponse.getMean()); + } + + ret[it->first.asString()] = reg_stat; } return ret; @@ -161,31 +229,81 @@ LLViewerAssetStats::asLLSD() const namespace LLViewerAssetStatsFF { +// Target thread is elaborated in the function name. This could +// have been something 'templatey' like specializations iterated +// over a set of constants but with so few, this is clearer I think. + +void +set_region_main(const LLUUID & region_id) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->setRegionID(region_id); +} + +void +record_enqueue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordGetEnqueued(at, with_http, is_temp); +} + +void +record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordGetDequeued(at, with_http, is_temp); +} + +void +record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +{ + if (! gViewerAssetStatsMain) + return; + + gViewerAssetStatsMain->recordGetServiced(at, with_http, is_temp, duration); +} + + +void +set_region_thread1(const LLUUID & region_id) +{ + if (! gViewerAssetStatsThread1) + return; + + gViewerAssetStatsThread1->setRegionID(region_id); +} + void -record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp) +record_enqueue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp) { - if (! gViewerAssetStats) + if (! gViewerAssetStatsThread1) return; - gViewerAssetStats->recordGetEnqueued(at, with_http, is_temp); + gViewerAssetStatsThread1->recordGetEnqueued(at, with_http, is_temp); } void -record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp) +record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp) { - if (! gViewerAssetStats) + if (! gViewerAssetStatsThread1) return; - gViewerAssetStats->recordGetDequeued(at, with_http, is_temp); + gViewerAssetStatsThread1->recordGetDequeued(at, with_http, is_temp); } void -record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) { - if (! gViewerAssetStats) + if (! gViewerAssetStatsThread1) return; - gViewerAssetStats->recordGetServiced(at, with_http, is_temp, duration); + gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration); } } // namespace LLViewerAssetStatsFF -- cgit v1.2.3 From deeef0c73ead965f7202bb5ac4c8481354f3b08e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 10 Nov 2010 10:13:31 -0800 Subject: Need precompiled header include for windows. --- indra/newview/llviewerassetstats.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index a6c4685bf1..37e7c43f36 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -30,6 +30,8 @@ * $/LicenseInfo$ */ +#include "llviewerprecompiledheaders.h" + #include "llviewerassetstats.h" #include "stdtypes.h" -- cgit v1.2.3 From d666a3d92cb5dd9844c29e5472db542de7b5ac9e Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Thu, 18 Nov 2010 08:43:09 -0800 Subject: ESC-154 ESC-155 ESC-156 Asset fetch requests wrapped to allow for measurements. Asset fetch enqueues, dequeues and completion times recorded to asset stats collector. Texture fetch operations (http and udp) recorded to asset stats collector. Stats collector time vallue switched from F32 to U64 which is the more common type in the viewer. Cross-thread mechanism introduced to communicate region changes and generate global statistics messages. Facility to deliver metrics via Capabilities sketched in but needs additional work. Documentation and diagrams added. --- indra/newview/llviewerassetstats.cpp | 87 ++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 8 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 37e7c43f36..09c0364f09 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -37,6 +37,35 @@ #include "stdtypes.h" /* + * Classes and utility functions for per-thread and per-region + * asset and experiential metrics to be aggregated grid-wide. + * + * The basic metrics grouping is LLViewerAssetStats::PerRegionStats. + * This provides various counters and simple statistics for asset + * fetches binned into a few categories. One of these is maintained + * for each region encountered and the 'current' region is available + * as a simple reference. Each thread (presently two) interested + * in participating in these stats gets an instance of the + * LLViewerAssetStats class so that threads are completely + * independent. + * + * The idea of a current region is used for simplicity and speed + * of categorization. Each metrics event could have taken a + * region uuid argument resulting in a suitable lookup. Arguments + * against this design include: + * + * - Region uuid not trivially available to caller. + * - Cost (cpu, disruption in real work flow) too high. + * - Additional precision not really meaningful. + * + * By itself, the LLViewerAssetStats class is thread- and + * viewer-agnostic and can be used anywhere without assumptions + * of global pointers and other context. For the viewer, + * a set of free functions are provided in the namespace + * LLViewerAssetStatsFF which *do* implement viewer-native + * policies about per-thread globals and will do correct + * defensive tests of same. + * * References * * Project: @@ -103,7 +132,7 @@ LLViewerAssetStats::reset() mRegionStats.clear(); // If we have a current stats, reset it, otherwise, as at construction, - // create a new one. + // create a new one as we must always have a current stats block. if (mCurRegionStats) { mCurRegionStats->reset(); @@ -130,7 +159,7 @@ LLViewerAssetStats::setRegionID(const LLUUID & region_id) PerRegionContainer::iterator new_stats = mRegionStats.find(region_id); if (mRegionStats.end() == new_stats) { - // Haven't seen this region_id before, create a new block make it current. + // Haven't seen this region_id before, create a new block and make it current. mCurRegionStats = new PerRegionStats(region_id); mRegionStats[region_id] = mCurRegionStats; } @@ -159,7 +188,7 @@ LLViewerAssetStats::recordGetDequeued(LLViewerAssetType::EType at, bool with_htt } void -LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_http, bool is_temp, duration_t duration) { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); @@ -213,9 +242,9 @@ LLViewerAssetStats::asLLSD() const slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); - slot[rmin_tag] = LLSD(stats.mRequests[i].mResponse.getMin()); - slot[rmax_tag] = LLSD(stats.mRequests[i].mResponse.getMax()); - slot[rmean_tag] = LLSD(stats.mRequests[i].mResponse.getMean()); + slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin())); + slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax())); + slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean())); } ret[it->first.asString()] = reg_stat; @@ -231,9 +260,24 @@ LLViewerAssetStats::asLLSD() const namespace LLViewerAssetStatsFF { +// // Target thread is elaborated in the function name. This could // have been something 'templatey' like specializations iterated // over a set of constants but with so few, this is clearer I think. +// +// As for the threads themselves... rather than do fine-grained +// locking as we gather statistics, this code creates a collector +// for each thread, allocated and run independently. Logging +// happens at relatively infrequent intervals and at that time +// the data is sent to a single thread to be aggregated into +// a single entity with locks, thread safety and other niceties. +// +// A particularly fussy implementation would distribute the +// per-thread pointers across separate cache lines. But that should +// be beyond current requirements. +// + +// 'main' thread - initial program thread void set_region_main(const LLUUID & region_id) @@ -263,7 +307,7 @@ record_dequeue_main(LLViewerAssetType::EType at, bool with_http, bool is_temp) } void -record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration) { if (! gViewerAssetStatsMain) return; @@ -272,6 +316,8 @@ record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, } +// 'thread1' - should be for TextureFetch thread + void set_region_thread1(const LLUUID & region_id) { @@ -300,7 +346,7 @@ record_dequeue_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp } void -record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, F64 duration) +record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration) { if (! gViewerAssetStatsThread1) return; @@ -308,6 +354,31 @@ record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_tem gViewerAssetStatsThread1->recordGetServiced(at, with_http, is_temp, duration); } + +void +init() +{ + if (! gViewerAssetStatsMain) + { + gViewerAssetStatsMain = new LLViewerAssetStats; + } + if (! gViewerAssetStatsThread1) + { + gViewerAssetStatsThread1 = new LLViewerAssetStats; + } +} + +void +cleanup() +{ + delete gViewerAssetStatsMain; + gViewerAssetStatsMain = 0; + + delete gViewerAssetStatsThread1; + gViewerAssetStatsThread1 = 0; +} + + } // namespace LLViewerAssetStatsFF -- cgit v1.2.3 From a99db82e9b3ce25bf2745721b57f0259a770b26a Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 19 Nov 2010 15:14:40 -0800 Subject: ESC-155 Multi-threaded umbrella collector for stats aggregation Code complete with the intelligence to merge counts, mins, maxes and means with reasonable defences. Added QAMode controls to the viewer so that we can QA this more quickly by reducing the timing interval and sending the metrics body to local logging as well as to the caps service. --- indra/newview/llviewerassetstats.cpp | 248 +++++++++++++++++++++++++++++++++-- 1 file changed, 239 insertions(+), 9 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 09c0364f09..c0287863f6 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -113,6 +113,16 @@ LLViewerAssetStats::PerRegionStats::reset() mRequests[i].mDequeued.reset(); mRequests[i].mResponse.reset(); } + + mTotalTime = 0; + mStartTimestamp = LLViewerAssetStatsFF::get_timestamp(); +} + +void +LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now) +{ + mTotalTime += (now - mStartTimestamp); + mStartTimestamp = now; } @@ -144,6 +154,9 @@ LLViewerAssetStats::reset() // And add reference to map mRegionStats[mRegionID] = mCurRegionStats; + + // Start timestamp consistent with per-region collector + mResetTimestamp = mCurRegionStats->mStartTimestamp; } @@ -155,7 +168,12 @@ LLViewerAssetStats::setRegionID(const LLUUID & region_id) // Already active, ignore. return; } - + + // Get duration for current set + const duration_t now = LLViewerAssetStatsFF::get_timestamp(); + mCurRegionStats->accumulateTime(now); + + // Prepare new set PerRegionContainer::iterator new_stats = mRegionStats.find(region_id); if (mRegionStats.end() == new_stats) { @@ -167,6 +185,7 @@ LLViewerAssetStats::setRegionID(const LLUUID & region_id) { mCurRegionStats = new_stats->second; } + mCurRegionStats->mStartTimestamp = now; mRegionID = region_id; } @@ -195,8 +214,8 @@ LLViewerAssetStats::recordGetServiced(LLViewerAssetType::EType at, bool with_htt mCurRegionStats->mRequests[int(eac)].mResponse.record(duration); } -const LLSD -LLViewerAssetStats::asLLSD() const +LLSD +LLViewerAssetStats::asLLSD() { // Top-level tags static const LLSD::String tags[EVACCount] = @@ -211,17 +230,18 @@ LLViewerAssetStats::asLLSD() const LLSD::String("get_other") }; - // Sub-tags + // Sub-tags. If you add or delete from this list, mergeLLSD() must be updated. static const LLSD::String enq_tag("enqueued"); static const LLSD::String deq_tag("dequeued"); static const LLSD::String rcnt_tag("resp_count"); static const LLSD::String rmin_tag("resp_min"); static const LLSD::String rmax_tag("resp_max"); static const LLSD::String rmean_tag("resp_mean"); - - LLSD ret = LLSD::emptyMap(); - for (PerRegionContainer::const_iterator it = mRegionStats.begin(); + const duration_t now = LLViewerAssetStatsFF::get_timestamp(); + LLSD regions = LLSD::emptyMap(); + + for (PerRegionContainer::iterator it = mRegionStats.begin(); mRegionStats.end() != it; ++it) { @@ -231,7 +251,8 @@ LLViewerAssetStats::asLLSD() const continue; } - const PerRegionStats & stats = *it->second; + PerRegionStats & stats = *it->second; + stats.accumulateTime(now); LLSD reg_stat = LLSD::emptyMap(); @@ -247,12 +268,185 @@ LLViewerAssetStats::asLLSD() const slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean())); } - ret[it->first.asString()] = reg_stat; + reg_stat["duration"] = LLSD::Integer(stats.mTotalTime / 1000000); + + regions[it->first.asString()] = reg_stat; } + LLSD ret = LLSD::emptyMap(); + ret["regions"] = regions; + ret["duration"] = LLSD::Integer((now - mResetTimestamp) / 1000000); + return ret; } +/* static */ void +LLViewerAssetStats::mergeLLSD(const LLSD & src, LLSD & dst) +{ + // 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 root_key_list[] = + { + "duration", + regions_key + }; + + static const struct + { + LLSD::String mName; + int mMergeOp; + LLSD::String mMergeOpArg; + } + 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, "resp_count" }, + { "enqueued", MOP_ADD_INT, "" }, + { "dequeued", MOP_ADD_INT, "" }, + { "resp_count", MOP_ADD_INT, "" }, + { "resp_min", MOP_MIN_REAL, "" }, + { "resp_max", MOP_MAX_REAL, "" } + }; + + // First normalized the root keys but remember if we need to do full merge + const bool needs_deep_merge(src.has(regions_key) && dst.has(regions_key)); + + for (int root_index(0); root_index < LL_ARRAY_SIZE(root_key_list); ++root_index) + { + const LLSD::String & key_name(root_key_list[root_index]); + + if ((! src.has(key_name)) || dst.has(key_name)) + continue; + + // key present in source, not in dst here + dst[key_name] = src[key_name]; + } + + if (! needs_deep_merge) + return; + + // Okay, had both src and dst 'regions' section, do the deep merge + + const LLSD & root_src(src[regions_key]); + LLSD & root_dst(dst[regions_key]); + + const LLSD::map_const_iterator it_end(root_src.endMap()); + for (LLSD::map_const_iterator it(root_src.beginMap()); it_end != it; ++it) + { + if (! root_dst.has(it->first)) + { + // src[] without matching dst[] + root_dst[it->first] = it->second; + } + else + { + // src[] with matching dst[] + // 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->first]); + const LLSD & reg_src(root_src[it->first]); + + const LLSD::map_const_iterator it_src_bin_end(reg_src.endMap()); + for (LLSD::map_const_iterator it_src_bin(reg_src.beginMap()); it_src_bin_end != it_src_bin; ++it_src_bin) + { + static const LLSD::String no_touch_1("duration"); + + if (no_touch_1 == it_src_bin->first) + { + continue; + } + else if (! reg_dst.has(it_src_bin->first)) + { + // src[][] without matching dst[][] + reg_dst[it_src_bin->first] = it_src_bin->second; + } + else + { + // src[][] with matching dst[][] + // 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_src_bin->first]); + const LLSD & bin_src(reg_src[it_src_bin->first]); + + 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[][][] + continue; + } + + const LLSD & src_value(bin_src[key_name]); + + if (! bin_dst.has(key_name)) + { + // src[][][] without matching dst[][][] + bin_dst[key_name] = src_value; + } + else + { + // src[][][] with matching dst[][][] + 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 + dst_value = llmin(dst_value.asReal(), src_value.asReal()); + break; + + case MOP_MAX_REAL: + // Maximum + dst_value = llmax(dst_value.asReal(), src_value.asReal()); + break; + + case MOP_MEAN_REAL: + { + // Mean + const LLSD::String & weight_key(key_list[key_index].mMergeOpArg); + F64 src_weight(bin_src[weight_key].asReal()); + F64 dst_weight(bin_dst[weight_key].asReal()); + 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; + } + } + } + } + } + } + } +} + + // ------------------------------------------------------ // Global free-function definitions (LLViewerAssetStatsFF namespace) // ------------------------------------------------------ @@ -377,7 +571,43 @@ cleanup() delete gViewerAssetStatsThread1; gViewerAssetStatsThread1 = 0; } + + +void +merge_stats(const LLSD & src, LLSD & dst) +{ + static const LLSD::String regions_key("regions"); + static const LLSD::String dur_key("duration"); + + // 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 it's part + LLViewerAssetStats::mergeLLSD(src, dst); + + // Now merge non-collector 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 || dur_key == it->first) + continue; + + if (dst.has(it->first)) + continue; + + dst[it->first] = it->second; + } +} } // namespace LLViewerAssetStatsFF -- cgit v1.2.3 From f98a622325d8982d32ae98e189f5d3ec6ada183f Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 22 Nov 2010 10:26:25 -0800 Subject: ESC-154 ESC-156 Metrics integration into viewer's threads Removed declared but undefined interfaces from LLTextureFetch family. Had inserted the cross-thread command processor into some of LLTextureFetchWorker's processing which was unnatural and probably wrong. Moved it to LLTextureFetch which turned out to be far, far more natural. Better documentation on the asLLSD() format. Refined LLSD stats merger logic and enhanced unit tests to verify same. --- indra/newview/llviewerassetstats.cpp | 68 +++++++++++++++--------------------- 1 file changed, 28 insertions(+), 40 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index c0287863f6..c3e58cdd56 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -230,7 +230,7 @@ LLViewerAssetStats::asLLSD() LLSD::String("get_other") }; - // Sub-tags. If you add or delete from this list, mergeLLSD() must be updated. + // Sub-tags. If you add or delete from this list, mergeRegionsLLSD() must be updated. static const LLSD::String enq_tag("enqueued"); static const LLSD::String deq_tag("dequeued"); static const LLSD::String rcnt_tag("resp_count"); @@ -281,7 +281,7 @@ LLViewerAssetStats::asLLSD() } /* static */ void -LLViewerAssetStats::mergeLLSD(const LLSD & src, LLSD & dst) +LLViewerAssetStats::mergeRegionsLLSD(const LLSD & src, LLSD & dst) { // Merge operator definitions static const int MOP_ADD_INT(0); @@ -290,11 +290,6 @@ LLViewerAssetStats::mergeLLSD(const LLSD & src, LLSD & dst) 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 root_key_list[] = - { - "duration", - regions_key - }; static const struct { @@ -318,35 +313,29 @@ LLViewerAssetStats::mergeLLSD(const LLSD & src, LLSD & dst) { "resp_max", MOP_MAX_REAL, "" } }; - // First normalized the root keys but remember if we need to do full merge - const bool needs_deep_merge(src.has(regions_key) && dst.has(regions_key)); - - for (int root_index(0); root_index < LL_ARRAY_SIZE(root_key_list); ++root_index) + // Trivial checks + if (! src.has(regions_key)) { - const LLSD::String & key_name(root_key_list[root_index]); - - if ((! src.has(key_name)) || dst.has(key_name)) - continue; - - // key present in source, not in dst here - dst[key_name] = src[key_name]; + return; } - if (! needs_deep_merge) + if (! dst.has(regions_key)) + { + dst[regions_key] = src[regions_key]; return; - - // Okay, had both src and dst 'regions' section, do the deep merge - + } + + // 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_end(root_src.endMap()); - for (LLSD::map_const_iterator it(root_src.beginMap()); it_end != it; ++it) + 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->first)) + if (! root_dst.has(it_uuid->first)) { // src[] without matching dst[] - root_dst[it->first] = it->second; + root_dst[it_uuid->first] = it_uuid->second; } else { @@ -354,30 +343,30 @@ LLViewerAssetStats::mergeLLSD(const LLSD & src, LLSD & dst) // 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->first]); - const LLSD & reg_src(root_src[it->first]); + LLSD & reg_dst(root_dst[it_uuid->first]); + const LLSD & reg_src(root_src[it_uuid->first]); - const LLSD::map_const_iterator it_src_bin_end(reg_src.endMap()); - for (LLSD::map_const_iterator it_src_bin(reg_src.beginMap()); it_src_bin_end != it_src_bin; ++it_src_bin) + 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_src_bin->first) + if (no_touch_1 == it_sets->first) { continue; } - else if (! reg_dst.has(it_src_bin->first)) + else if (! reg_dst.has(it_sets->first)) { // src[][] without matching dst[][] - reg_dst[it_src_bin->first] = it_src_bin->second; + reg_dst[it_sets->first] = it_sets->second; } else { // src[][] with matching dst[][] // 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_src_bin->first]); - const LLSD & bin_src(reg_src[it_src_bin->first]); + LLSD & bin_dst(reg_dst[it_sets->first]); + const LLSD & bin_src(reg_src[it_sets->first]); for (int key_index(0); key_index < LL_ARRAY_SIZE(key_list); ++key_index) { @@ -577,7 +566,6 @@ void merge_stats(const LLSD & src, LLSD & dst) { static const LLSD::String regions_key("regions"); - static const LLSD::String dur_key("duration"); // Trivial cases first if (! src.isMap()) @@ -592,14 +580,14 @@ merge_stats(const LLSD & src, LLSD & dst) } // Okay, both src and dst are maps at this point. - // Collector class know how to merge it's part - LLViewerAssetStats::mergeLLSD(src, dst); + // Collector class know how to merge the regions part. + LLViewerAssetStats::mergeRegionsLLSD(src, dst); - // Now merge non-collector bits manually. + // 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 || dur_key == it->first) + if (regions_key == it->first) continue; if (dst.has(it->first)) -- cgit v1.2.3 From 9ec3334184c71879c2f8bd0f27095b71c4302559 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 23 Nov 2010 13:31:22 -0500 Subject: ESC-154 ESC-156 Data collection and control for viewer metrics Detect QAMode (and new QAModeMetricsSubmode) settings which enable logging of metrics report locally and a faster cycle time to reduce test waiting. Do this only in the main thread and propagate the result via collector constructors (will likely move that out and put it in llappviewer/lltexturefetch which is more correct scope). Managed to deadlock myself with a recursive mutex (sheesh). --- indra/newview/llviewerassetstats.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index c3e58cdd56..a63c1bf66d 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -129,7 +129,8 @@ LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now) // ------------------------------------------------------ // LLViewerAssetStats class definition // ------------------------------------------------------ -LLViewerAssetStats::LLViewerAssetStats() +LLViewerAssetStats::LLViewerAssetStats(bool qa_mode) + : mQAMode(qa_mode) { reset(); } @@ -539,15 +540,15 @@ record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_tem void -init() +init(bool qa_mode) { if (! gViewerAssetStatsMain) { - gViewerAssetStatsMain = new LLViewerAssetStats; + gViewerAssetStatsMain = new LLViewerAssetStats(qa_mode); } if (! gViewerAssetStatsThread1) { - gViewerAssetStatsThread1 = new LLViewerAssetStats; + gViewerAssetStatsThread1 = new LLViewerAssetStats(qa_mode); } } -- cgit v1.2.3 From 3962b155b4939f831dfd82701d46c4f15aa9f7ac Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Tue, 23 Nov 2010 12:29:15 -0800 Subject: ESC-154 ESC-156 Integrating metrics collector into viewer. After discussions, renamed 'QAModeMetricsSubmetrics' to 'QAModeMetrics' and confirmed that LLCachedControl<> is the way to go. Moved the resulting flag out of LLViewerAssetStats (where it didn't belong) and it lives in both LLAppViewer and LLTextureFetch where it does belong. --- indra/newview/llviewerassetstats.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index a63c1bf66d..3d7f9f932f 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -129,8 +129,7 @@ LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now) // ------------------------------------------------------ // LLViewerAssetStats class definition // ------------------------------------------------------ -LLViewerAssetStats::LLViewerAssetStats(bool qa_mode) - : mQAMode(qa_mode) +LLViewerAssetStats::LLViewerAssetStats() { reset(); } @@ -540,15 +539,15 @@ record_response_thread1(LLViewerAssetType::EType at, bool with_http, bool is_tem void -init(bool qa_mode) +init() { if (! gViewerAssetStatsMain) { - gViewerAssetStatsMain = new LLViewerAssetStats(qa_mode); + gViewerAssetStatsMain = new LLViewerAssetStats(); } if (! gViewerAssetStatsThread1) { - gViewerAssetStatsThread1 = new LLViewerAssetStats(qa_mode); + gViewerAssetStatsThread1 = new LLViewerAssetStats(); } } -- cgit v1.2.3 From a4bf7322895cac318abc3ac0a000086d227fc2fe Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Wed, 24 Nov 2010 15:02:46 -0800 Subject: ESC-154 ESC-155 Viewer metrics fixes for min/max merged values, floating timestamps. The min/max response time calculations needed to be sensitive to the response counts to know if their was actual data. Failure to do so introduced a gratuitous min/max test against zero values which tended to corrupt the mins. Unit tests added to test for this condition. Finished conversion of times to floating point seconds. Removed two logging events used to debug the cross-thread messaging. Looks like a code completion point. --- indra/newview/llviewerassetstats.cpp | 60 +++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 18 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 3d7f9f932f..502a3aa340 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -263,19 +263,19 @@ LLViewerAssetStats::asLLSD() slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); - slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin())); - slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax())); - slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean())); + slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6)); + slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6)); + slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6)); } - reg_stat["duration"] = LLSD::Integer(stats.mTotalTime / 1000000); + reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6); regions[it->first.asString()] = reg_stat; } LLSD ret = LLSD::emptyMap(); ret["regions"] = regions; - ret["duration"] = LLSD::Integer((now - mResetTimestamp) / 1000000); + ret["duration"] = LLSD::Real((now - mResetTimestamp) * 1.0e-6); return ret; } @@ -290,12 +290,12 @@ LLViewerAssetStats::mergeRegionsLLSD(const LLSD & src, LLSD & dst) 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; - LLSD::String mMergeOpArg; } key_list[] = { @@ -305,12 +305,12 @@ LLViewerAssetStats::mergeRegionsLLSD(const LLSD & src, LLSD & dst) // is modified or the weight will be wrong. Key list is // defined in asLLSD() and must track it. - { "resp_mean", MOP_MEAN_REAL, "resp_count" }, - { "enqueued", MOP_ADD_INT, "" }, - { "dequeued", MOP_ADD_INT, "" }, - { "resp_count", MOP_ADD_INT, "" }, - { "resp_min", MOP_MIN_REAL, "" }, - { "resp_max", MOP_MAX_REAL, "" } + { "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 }; // Trivial checks @@ -368,6 +368,10 @@ LLViewerAssetStats::mergeRegionsLLSD(const LLSD & src, LLSD & dst) 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); @@ -395,25 +399,45 @@ LLViewerAssetStats::mergeRegionsLLSD(const LLSD & src, LLSD & dst) case MOP_ADD_INT: // Simple counts, just add dst_value = dst_value.asInteger() + src_value.asInteger(); - break; case MOP_MIN_REAL: // Minimum - dst_value = llmin(dst_value.asReal(), src_value.asReal()); + 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 - dst_value = llmax(dst_value.asReal(), src_value.asReal()); + 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 - const LLSD::String & weight_key(key_list[key_index].mMergeOpArg); - F64 src_weight(bin_src[weight_key].asReal()); - F64 dst_weight(bin_dst[weight_key].asReal()); + 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)) { -- cgit v1.2.3 From 0f2ed092c5712cd5dcd928e079671df383227068 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Mon, 29 Nov 2010 08:31:08 -0800 Subject: ESC-154 ESC-156 Now using region hash rather than region uuid as identifier. In the viewer, the region's UUID is acquired very late and isn't generally used as the canonical region identifier. The U64 region hash is a better and more consistently used identifier so I'm moving over to using that as the region key. Don't have a proper reserved invalid region hash which is unfortunate, but then, so much is. --- indra/newview/llviewerassetstats.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 502a3aa340..cc41a95893 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -130,6 +130,7 @@ LLViewerAssetStats::PerRegionStats::accumulateTime(duration_t now) // LLViewerAssetStats class definition // ------------------------------------------------------ LLViewerAssetStats::LLViewerAssetStats() + : mRegionHandle(U64(0)) { reset(); } @@ -149,11 +150,11 @@ LLViewerAssetStats::reset() } else { - mCurRegionStats = new PerRegionStats(mRegionID); + mCurRegionStats = new PerRegionStats(mRegionHandle); } // And add reference to map - mRegionStats[mRegionID] = mCurRegionStats; + mRegionStats[mRegionHandle] = mCurRegionStats; // Start timestamp consistent with per-region collector mResetTimestamp = mCurRegionStats->mStartTimestamp; @@ -161,9 +162,9 @@ LLViewerAssetStats::reset() void -LLViewerAssetStats::setRegionID(const LLUUID & region_id) +LLViewerAssetStats::setRegion(region_handle_t region_handle) { - if (region_id == mRegionID) + if (region_handle == mRegionHandle) { // Already active, ignore. return; @@ -174,19 +175,19 @@ LLViewerAssetStats::setRegionID(const LLUUID & region_id) mCurRegionStats->accumulateTime(now); // Prepare new set - PerRegionContainer::iterator new_stats = mRegionStats.find(region_id); + PerRegionContainer::iterator new_stats = mRegionStats.find(region_handle); if (mRegionStats.end() == new_stats) { // Haven't seen this region_id before, create a new block and make it current. - mCurRegionStats = new PerRegionStats(region_id); - mRegionStats[region_id] = mCurRegionStats; + mCurRegionStats = new PerRegionStats(region_handle); + mRegionStats[region_handle] = mCurRegionStats; } else { mCurRegionStats = new_stats->second; } mCurRegionStats->mStartTimestamp = now; - mRegionID = region_id; + mRegionHandle = region_handle; } @@ -245,9 +246,9 @@ LLViewerAssetStats::asLLSD() mRegionStats.end() != it; ++it) { - if (it->first.isNull()) + if (0 == it->first) { - // Never emit NULL UUID in results. + // Never emit NULL UUID/handle in results. continue; } @@ -269,8 +270,11 @@ LLViewerAssetStats::asLLSD() } reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6); - - regions[it->first.asString()] = reg_stat; + std::stringstream reg_handle; + reg_handle.width(16); + reg_handle.fill('0'); + reg_handle << std::hex << it->first; + regions[reg_handle.str()] = reg_stat; } LLSD ret = LLSD::emptyMap(); @@ -487,12 +491,12 @@ namespace LLViewerAssetStatsFF // 'main' thread - initial program thread void -set_region_main(const LLUUID & region_id) +set_region_main(LLViewerAssetStats::region_handle_t region_handle) { if (! gViewerAssetStatsMain) return; - gViewerAssetStatsMain->setRegionID(region_id); + gViewerAssetStatsMain->setRegion(region_handle); } void @@ -526,12 +530,12 @@ record_response_main(LLViewerAssetType::EType at, bool with_http, bool is_temp, // 'thread1' - should be for TextureFetch thread void -set_region_thread1(const LLUUID & region_id) +set_region_thread1(LLViewerAssetStats::region_handle_t region_handle) { if (! gViewerAssetStatsThread1) return; - gViewerAssetStatsThread1->setRegionID(region_id); + gViewerAssetStatsThread1->setRegion(region_handle); } void -- cgit v1.2.3 From a59c43f1adff35107e59fdfc3016d4329324bbaf Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 3 Dec 2010 18:34:20 -0500 Subject: ESC-210 Non-active regions were getting extra duration time. Metrics were crediting inactive regions (those not current but contributing to the sample) with additional time at the end of the sample interval. Corrected. --- indra/newview/llviewerassetstats.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index cc41a95893..d798786277 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -240,8 +240,9 @@ LLViewerAssetStats::asLLSD() static const LLSD::String rmean_tag("resp_mean"); const duration_t now = LLViewerAssetStatsFF::get_timestamp(); - LLSD regions = LLSD::emptyMap(); + mCurRegionStats->accumulateTime(now); + LLSD regions = LLSD::emptyMap(); for (PerRegionContainer::iterator it = mRegionStats.begin(); mRegionStats.end() != it; ++it) @@ -253,7 +254,6 @@ LLViewerAssetStats::asLLSD() } PerRegionStats & stats = *it->second; - stats.accumulateTime(now); LLSD reg_stat = LLSD::emptyMap(); -- cgit v1.2.3 From 11d420dd32e643a191c16b04f2fbb42c2b4db628 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Fri, 10 Dec 2010 17:41:05 -0800 Subject: 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. --- indra/newview/llviewerassetstats.cpp | 286 ++++++++++------------------------- 1 file changed, 80 insertions(+), 206 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') 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[] without matching dst[] - 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[] with matching dst[] - // 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[][] without matching dst[][] - reg_dst[it_sets->first] = it_sets->second; - } - else - { - // src[][] with matching dst[][] - // 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[][][] - continue; - } - - const LLSD & src_value(bin_src[key_name]); - - if (! bin_dst.has(key_name)) - { - // src[][][] without matching dst[][][] - bin_dst[key_name] = src_value; - } - else - { - // src[][][] with matching dst[][][] - 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 -- cgit v1.2.3 From bb53d27b7ad6e7bb7b1871f103b221703d56e4d2 Mon Sep 17 00:00:00 2001 From: Monty Brandenberg Date: Sat, 11 Dec 2010 16:16:07 -0500 Subject: ESC-211 ESC-212 Use arrays in payload to grid and compact payload First, introduced a compact payload format that allows blocks of metrics to be dropped from the viewer->collector payload compressing 1200 bytes of LLSD into about 300, give-or-take. Then converted to using LLSD arrays in the payload to enumerate the regions encountered. This simplifies much data handling from the viewer all the way into the final formatter of the metrics on the grid. --- indra/newview/llviewerassetstats.cpp | 42 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 399d62d2fc..5ad7725b3e 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -33,6 +33,7 @@ #include "llviewerprecompiledheaders.h" #include "llviewerassetstats.h" +#include "llregionhandle.h" #include "stdtypes.h" @@ -258,7 +259,7 @@ LLViewerAssetStats::recordFPS(F32 fps) } LLSD -LLViewerAssetStats::asLLSD() +LLViewerAssetStats::asLLSD(bool compact_output) { // Top-level tags static const LLSD::String tags[EVACCount] = @@ -290,7 +291,7 @@ LLViewerAssetStats::asLLSD() const duration_t now = LLViewerAssetStatsFF::get_timestamp(); mCurRegionStats->accumulateTime(now); - LLSD regions = LLSD::emptyMap(); + LLSD regions = LLSD::emptyArray(); for (PerRegionContainer::iterator it = mRegionStats.begin(); mRegionStats.end() != it; ++it) @@ -307,16 +308,25 @@ LLViewerAssetStats::asLLSD() for (int i = 0; i < LL_ARRAY_SIZE(tags); ++i) { - LLSD & slot = reg_stat[tags[i]]; - slot = LLSD::emptyMap(); - slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); - slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); - slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); - slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6)); - slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6)); - slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6)); + PerRegionStats::prs_group & group(stats.mRequests[i]); + + if ((! compact_output) || + group.mEnqueued.getCount() || + group.mDequeued.getCount() || + group.mResponse.getCount()) + { + LLSD & slot = reg_stat[tags[i]]; + slot = LLSD::emptyMap(); + slot[enq_tag] = LLSD(S32(stats.mRequests[i].mEnqueued.getCount())); + slot[deq_tag] = LLSD(S32(stats.mRequests[i].mDequeued.getCount())); + slot[rcnt_tag] = LLSD(S32(stats.mRequests[i].mResponse.getCount())); + slot[rmin_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMin() * 1.0e-6)); + slot[rmax_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMax() * 1.0e-6)); + slot[rmean_tag] = LLSD(F64(stats.mRequests[i].mResponse.getMean() * 1.0e-6)); + } } + if ((! compact_output) || stats.mFPS.getCount()) { LLSD & slot = reg_stat["fps"]; slot = LLSD::emptyMap(); @@ -326,12 +336,12 @@ LLViewerAssetStats::asLLSD() 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); - reg_handle.fill('0'); - reg_handle << std::hex << it->first; - regions[reg_handle.str()] = reg_stat; + U32 grid_x(0), grid_y(0); + grid_from_region_handle(it->first, &grid_x, &grid_y); + reg_stat["grid_x"] = LLSD::Integer(grid_x); + reg_stat["grid_y"] = LLSD::Integer(grid_y); + reg_stat["duration"] = LLSD::Real(stats.mTotalTime * 1.0e-6); + regions.append(reg_stat); } LLSD ret = LLSD::emptyMap(); -- cgit v1.2.3 From 6371ce7727cfb26e5b4ab4ac59b0600cf23655c0 Mon Sep 17 00:00:00 2001 From: Merov Linden Date: Fri, 4 Feb 2011 15:38:15 -0800 Subject: Clean up license headers in a handfull of files --- indra/newview/llviewerassetstats.cpp | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) (limited to 'indra/newview/llviewerassetstats.cpp') diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 5ad7725b3e..7024b2c785 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -2,31 +2,25 @@ * @file llviewerassetstats.cpp * @brief * - * $LicenseInfo:firstyear=2010&license=viewergpl$ - * - * Copyright (c) 2010, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -- cgit v1.2.3