Remove statistics profiler
Signed-off-by: Jeremy Ong <jcong@amazon.com>
This commit is contained in:
@@ -13,9 +13,6 @@
|
||||
#ifdef USE_PIX
|
||||
#include <AzCore/PlatformIncl.h>
|
||||
#include <WinPixEventRuntime/pix3.h>
|
||||
// The pix3 header unfortunately brings in other Windows macros we need to undef
|
||||
#undef DeleteFile
|
||||
#undef LoadImage
|
||||
#endif
|
||||
|
||||
#ifdef AZ_PROFILE_TELEMETRY
|
||||
@@ -32,11 +29,11 @@
|
||||
* Macro to declare a profile section for the current scope { }.
|
||||
* format is: AZ_PROFILE_SCOPE(categoryName, const char* formatStr, ...)
|
||||
*/
|
||||
# define AZ_PROFILE_SCOPE(category, formatStr, ...) ::AZ::ProfileScope AZ_JOIN(azProfileScope, __LINE__){ #category, formatStr, __VA_ARGS__ }
|
||||
# define AZ_PROFILE_SCOPE(category, ...) ::AZ::ProfileScope AZ_JOIN(azProfileScope, __LINE__){ #category, __VA_ARGS__ }
|
||||
# define AZ_PROFILE_FUNCTION(category) AZ_PROFILE_SCOPE(category, AZ_FUNCTION_SIGNATURE)
|
||||
|
||||
// Prefer using the scoped macros which automatically end the event (AZ_PROFILE_SCOPE/AZ_PROFILE_FUNCTION)
|
||||
# define AZ_PROFILE_BEGIN(category, name, ...) ::AZ::ProfileScope::BeginRegion(#category, name, __VA_ARGS__)
|
||||
# define AZ_PROFILE_BEGIN(category, ...) ::AZ::ProfileScope::BeginRegion(#category, __VA_ARGS__)
|
||||
# define AZ_PROFILE_END() ::AZ::ProfileScope::EndRegion()
|
||||
#endif // AZ_PROFILER_MACRO_DISABLE
|
||||
|
||||
@@ -67,14 +64,11 @@ namespace AZ
|
||||
static uint32_t GetSystemID(const char* system);
|
||||
|
||||
template<typename... T>
|
||||
static void BeginRegion(const char* system, char const* eventName, [[maybe_unused]] T const&... args)
|
||||
static void BeginRegion([[maybe_unused]] const char* system, [[maybe_unused]] char const* eventName, [[maybe_unused]] T const&... args)
|
||||
{
|
||||
// TODO: Verification that the supplied system name corresponds to a known budget
|
||||
#if defined(USE_PIX)
|
||||
PIXBeginEvent(PIX_COLOR_INDEX(GetSystemID(system) & 0xff), eventName, args...);
|
||||
#else
|
||||
(void)system;
|
||||
(void)eventName;
|
||||
#endif
|
||||
// TODO: injecting instrumentation for other profilers
|
||||
}
|
||||
@@ -395,3 +389,9 @@ namespace AZ
|
||||
}
|
||||
} // namespace AZ
|
||||
|
||||
#ifdef USE_PIX
|
||||
// The pix3 header unfortunately brings in other Windows macros we need to undef
|
||||
#undef DeleteFile
|
||||
#undef LoadImage
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "RunningStatisticsManager.h"
|
||||
|
||||
namespace AzFramework
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
bool RunningStatisticsManager::ContainsStatistic(const AZStd::string& name)
|
||||
{
|
||||
auto iterator = m_statisticsNamesToIndexMap.find(name);
|
||||
return iterator != m_statisticsNamesToIndexMap.end();
|
||||
}
|
||||
|
||||
bool RunningStatisticsManager::AddStatistic(const AZStd::string& name, const AZStd::string& units)
|
||||
{
|
||||
if (ContainsStatistic(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AddStatisticValidated(name, units);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RunningStatisticsManager::RemoveStatistic(const AZStd::string& name)
|
||||
{
|
||||
auto iterator = m_statisticsNamesToIndexMap.find(name);
|
||||
if (iterator == m_statisticsNamesToIndexMap.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
AZ::u32 itemIndex = iterator->second;
|
||||
m_statistics.erase(m_statistics.begin() + itemIndex);
|
||||
m_statisticsNamesToIndexMap.erase(iterator);
|
||||
//Update the indices in m_statisticsNamesToIndexMap.
|
||||
while (itemIndex < m_statistics.size())
|
||||
{
|
||||
const AZStd::string& statName = m_statistics[itemIndex].GetName();
|
||||
m_statisticsNamesToIndexMap[statName] = itemIndex;
|
||||
++itemIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void RunningStatisticsManager::ResetStatistic(const AZStd::string& name)
|
||||
{
|
||||
NamedRunningStatistic* stat = GetStatistic(name);
|
||||
if (!stat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stat->Reset();
|
||||
}
|
||||
|
||||
void RunningStatisticsManager::ResetAllStatistics()
|
||||
{
|
||||
for (NamedRunningStatistic& stat : m_statistics)
|
||||
{
|
||||
stat.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void RunningStatisticsManager::PushSampleForStatistic(const AZStd::string& name, double value)
|
||||
{
|
||||
NamedRunningStatistic* stat = GetStatistic(name);
|
||||
if (!stat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stat->PushSample(value);
|
||||
}
|
||||
|
||||
NamedRunningStatistic* RunningStatisticsManager::GetStatistic(const AZStd::string& name, AZ::u32* indexOut)
|
||||
{
|
||||
auto iterator = m_statisticsNamesToIndexMap.find(name);
|
||||
if (iterator == m_statisticsNamesToIndexMap.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const AZ::u32 index = iterator->second;
|
||||
if (indexOut)
|
||||
{
|
||||
*indexOut = index;
|
||||
}
|
||||
return &m_statistics[index];
|
||||
}
|
||||
|
||||
const AZStd::vector<NamedRunningStatistic>& RunningStatisticsManager::GetAllStatistics() const
|
||||
{
|
||||
return m_statistics;
|
||||
}
|
||||
|
||||
void RunningStatisticsManager::AddStatisticValidated(const AZStd::string& name, const AZStd::string& units)
|
||||
{
|
||||
m_statistics.emplace_back(NamedRunningStatistic(name, units));
|
||||
const AZ::u32 itemIndex = static_cast<AZ::u32>(m_statistics.size() - 1);
|
||||
m_statisticsNamesToIndexMap[name] = itemIndex;
|
||||
}
|
||||
|
||||
}//namespace Statistics
|
||||
}//namespace AzFramework
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/BusImpl.h> //Just to get AZ::NullMutex
|
||||
#include <AzCore/std/chrono/types.h>
|
||||
#include <AzCore/Statistics/StatisticsManager.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
//! A helper class that facilitates collecting time spent in blocks (scopes) of code
|
||||
//! and aggregating the measured times as running statistics.
|
||||
//!
|
||||
//! See "StatisticalProfilerProxy.h" for more explanations on the meaning of Statistical Profiling.
|
||||
//!
|
||||
//! The StatisticalProfiler was made as a template to accommodate for several performance needs...
|
||||
//! If all the code that is being profiled is single threaded and you want to identify
|
||||
//! each statistic by its string name, then the default StatisticalProfiler<> works for you.
|
||||
//! If using a map<strings, stat> is too much of what you can afford, then index your
|
||||
//! statistics with an integer or crc32 and your code profiler should be declared as
|
||||
//! StatisticalProfiler<AZ::Crc32>.
|
||||
//! For multi-threaded cases and indexing statistic with Crc32 you can have a profiler like this:
|
||||
//! StatisticalProfiler<AZ::Crc32, AZStd::mutex>.
|
||||
//! The UnitTests mentioned in the first paragraph do benchmarks of different combinations
|
||||
//! of indexing and synchronization primitives.
|
||||
//!
|
||||
//! Even though you can create, subclass and use your own StatisticalProfiler<*,*>, there
|
||||
//! are some things to consider when working with the StatisticalProfilerProxy:
|
||||
//! The StatisticalProfilerProxy OWNS an array of StatisticalProfiler<AZStd::string, AZStd::shared_spin_mutex>.
|
||||
//! You can "manage" one of those StatisticalProfiler by getting a reference to it and
|
||||
//! add Running statistics etc. See The TerrainProfilers mentioned above to see concrete use
|
||||
//! cases on how to work with the StatisticalProfilerProxy.
|
||||
template <class StatIdType = AZStd::string, class MutexType = AZ::NullMutex>
|
||||
class StatisticalProfiler
|
||||
{
|
||||
public:
|
||||
|
||||
//! A Convenience class used to measure time performance of scopes of code
|
||||
//! with constructor/destructor. Suitable to be used as part of a macro
|
||||
//! to facilitate its usage.
|
||||
class TimedScope
|
||||
{
|
||||
public:
|
||||
TimedScope() = delete;
|
||||
|
||||
TimedScope(StatisticalProfiler& profiler, const StatIdType& statId)
|
||||
: m_profiler(profiler), m_statId(statId)
|
||||
{
|
||||
m_startTime = AZStd::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
~TimedScope()
|
||||
{
|
||||
AZStd::chrono::system_clock::time_point stopTime = AZStd::chrono::high_resolution_clock::now();
|
||||
AZStd::chrono::microseconds duration = stopTime - m_startTime;
|
||||
m_profiler.PushSample(m_statId, static_cast<double>(duration.count()));
|
||||
}
|
||||
|
||||
private:
|
||||
StatisticalProfiler& m_profiler;
|
||||
const StatIdType& m_statId;
|
||||
AZStd::chrono::system_clock::time_point m_startTime;
|
||||
}; //class TimedScope
|
||||
|
||||
friend class TimedScope;
|
||||
|
||||
StatisticalProfiler()
|
||||
{
|
||||
}
|
||||
|
||||
StatisticalProfiler(const StatisticalProfiler& other)
|
||||
{
|
||||
m_statisticsManager = other.m_statisticsManager;
|
||||
m_statsVector.clear();
|
||||
m_perFrameAggregates.clear();
|
||||
}
|
||||
|
||||
StatisticalProfiler(StatisticalProfiler&& other)
|
||||
{
|
||||
m_statisticsManager = AZStd::move(other.m_statisticsManager);
|
||||
m_perFrameAggregates = AZStd::move(other.m_perFrameAggregates);
|
||||
}
|
||||
|
||||
virtual ~StatisticalProfiler()
|
||||
{
|
||||
}
|
||||
|
||||
AZ::Statistics::StatisticsManager<StatIdType>& GetStatsManager()
|
||||
{
|
||||
return m_statisticsManager;
|
||||
}
|
||||
|
||||
//! Should be called once per frame, it runs over all existing timed stats in m_statsForPerFrameCalculation
|
||||
//! and accumulates all the values as a single stat per frame.
|
||||
double SummarizePerFrameStats()
|
||||
{
|
||||
AZStd::scoped_lock<MutexType> lock(m_mutex);
|
||||
|
||||
if (m_perFrameAggregates.size() < 1)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double allStatsSumMicroSecs = 0.0;
|
||||
|
||||
for (StatisticalAggregate& aggregate : m_perFrameAggregates)
|
||||
{
|
||||
double statsSumMicroSecs = 0.0;
|
||||
for (const AZ::Statistics::NamedRunningStatistic* stat : aggregate.m_statsForPerFrameCalculation)
|
||||
{
|
||||
statsSumMicroSecs += stat->GetSum();
|
||||
}
|
||||
|
||||
const double frameTime = statsSumMicroSecs - aggregate.m_prevAccumulatedSums;
|
||||
if (frameTime > 0.0)
|
||||
{
|
||||
aggregate.m_statPerFrame->PushSample(frameTime);
|
||||
aggregate.m_prevAccumulatedSums = statsSumMicroSecs;
|
||||
}
|
||||
allStatsSumMicroSecs += statsSumMicroSecs;
|
||||
}
|
||||
|
||||
return allStatsSumMicroSecs;
|
||||
}
|
||||
|
||||
void LogAndResetStats(const char* windowName)
|
||||
{
|
||||
AZStd::scoped_lock<MutexType> lock(m_mutex);
|
||||
|
||||
if (m_statsVector.size() != m_statisticsManager.GetCount())
|
||||
{
|
||||
m_statsVector.clear();
|
||||
m_statisticsManager.GetAllStatistics(m_statsVector);
|
||||
}
|
||||
|
||||
for (AZ::Statistics::NamedRunningStatistic* stat : m_statsVector)
|
||||
{
|
||||
if (stat->GetNumSamples() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const AZStd::string& statReport = stat->GetFormatted();
|
||||
AZ_Printf(windowName, "%s\n", statReport.c_str());
|
||||
stat->Reset();
|
||||
}
|
||||
for (StatisticalAggregate& aggregate : m_perFrameAggregates)
|
||||
{
|
||||
aggregate.m_prevAccumulatedSums = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void PushSample(const StatIdType& statId, double value)
|
||||
{
|
||||
AZStd::scoped_lock<MutexType> lock(m_mutex);
|
||||
AZ::Statistics::NamedRunningStatistic* stat = m_statisticsManager.GetStatistic(statId);
|
||||
if (!stat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stat->PushSample(value);
|
||||
}
|
||||
|
||||
const AZ::Statistics::NamedRunningStatistic* GetStatistic(const StatIdType& statId)
|
||||
{
|
||||
return m_statisticsManager.GetStatistic(statId);
|
||||
}
|
||||
|
||||
int AddPerFrameStatisticalAggregate(const AZStd::vector<StatIdType>& statIds,
|
||||
const StatIdType& timePerFrameStatId,
|
||||
const AZStd::string& timePerFrameStatName)
|
||||
{
|
||||
AZStd::scoped_lock<MutexType> lock(m_mutex);
|
||||
|
||||
m_perFrameAggregates.push_back(StatisticalAggregate());
|
||||
StatisticalAggregate& aggregate = m_perFrameAggregates[m_perFrameAggregates.size() - 1];
|
||||
|
||||
int added_count = 0;
|
||||
for (const StatIdType& statId : statIds)
|
||||
{
|
||||
AZ::Statistics::NamedRunningStatistic* stat = m_statisticsManager.GetStatistic(statId);
|
||||
if (!stat)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const& itor = AZStd::find(aggregate.m_statsForPerFrameCalculation.begin(), aggregate.m_statsForPerFrameCalculation.end(), stat);
|
||||
if (itor != aggregate.m_statsForPerFrameCalculation.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
aggregate.m_statsForPerFrameCalculation.push_back(stat);
|
||||
added_count++;
|
||||
}
|
||||
|
||||
if (added_count < 1)
|
||||
{
|
||||
m_perFrameAggregates.pop_back();
|
||||
return 0;
|
||||
}
|
||||
|
||||
aggregate.m_statPerFrame = m_statisticsManager.AddStatistic(timePerFrameStatId, timePerFrameStatName, "us", true);
|
||||
if (!aggregate.m_statPerFrame)
|
||||
{
|
||||
AZ_Warning("StatisticalProfiler", false, "Per frame stat with name %s already exists\n", timePerFrameStatName.c_str());
|
||||
m_perFrameAggregates.pop_back();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return added_count;
|
||||
}
|
||||
|
||||
const AZ::Statistics::NamedRunningStatistic* GetFirstStatPerFrame() const
|
||||
{
|
||||
if (m_perFrameAggregates.size() < 1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return m_perFrameAggregates[0].m_statPerFrame;
|
||||
}
|
||||
|
||||
protected:
|
||||
//! Lock this before reading/writing to m_timeStatisticsManager, or else...
|
||||
MutexType m_mutex;
|
||||
AZ::Statistics::StatisticsManager<StatIdType> m_statisticsManager;
|
||||
AZStd::vector<AZ::Statistics::NamedRunningStatistic*> m_statsVector;
|
||||
|
||||
|
||||
struct StatisticalAggregate
|
||||
{
|
||||
StatisticalAggregate() : m_statPerFrame(nullptr), m_prevAccumulatedSums(0.0)
|
||||
{
|
||||
|
||||
}
|
||||
AZ::Statistics::NamedRunningStatistic* m_statPerFrame;
|
||||
AZStd::vector<AZ::Statistics::NamedRunningStatistic*> m_statsForPerFrameCalculation;
|
||||
|
||||
//! This one is needed because running statistics are collected many times across
|
||||
//! several frames. This value is used to calculate a per frame sample for @m_totalTimePerFrameStat,
|
||||
//! by subtracting @m_prevAccumulatedSums from the accumulated sum in @m_statisticsManager.
|
||||
double m_prevAccumulatedSums;
|
||||
};
|
||||
|
||||
AZStd::vector<StatisticalAggregate> m_perFrameAggregates;
|
||||
|
||||
}; //class StatisticalProfiler
|
||||
|
||||
}; //namespace Statistics
|
||||
}; //namespace AZ
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/chrono/types.h>
|
||||
#include <AzCore/std/parallel/shared_spin_mutex.h>
|
||||
#include <AzCore/std/parallel/scoped_lock.h>
|
||||
#include <AzCore/std/containers/bitset.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/Statistics/StatisticalProfiler.h>
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
|
||||
|
||||
#if !defined(AZ_PROFILE_TELEMETRY) && defined(AZ_STATISTICAL_PROFILING_ENABLED)
|
||||
|
||||
#if defined(AZ_PROFILE_SCOPE)
|
||||
#undef AZ_PROFILE_SCOPE
|
||||
#endif // #if defined(AZ_PROFILE_SCOPE)
|
||||
|
||||
#define AZ_PROFILE_SCOPE(profiler, scopeNameId) \
|
||||
static_assert(profiler < Count, "Invalid profiler category"); \
|
||||
static const AZStd::string AZ_JOIN(blockName, __LINE__)(scopeNameId); \
|
||||
AZ::Statistics::StatisticalProfilerProxy::TimedScope AZ_JOIN(scope, __LINE__)(profiler, AZ_JOIN(blockName, __LINE__));
|
||||
|
||||
#endif //#if !defined(AZ_PROFILE_TELEMETRY)
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
using StatisticalProfilerId = AZ::Name;
|
||||
|
||||
//! This AZ::Interface<> (Yes, it is an application wide singleton) owns an array of StatisticalProfilers.
|
||||
//! When is this useful?
|
||||
//! When you need to statistically profile code that runs across DLL boundaries.
|
||||
//!
|
||||
//! What is the meaning of "statistically profile" code?
|
||||
//! In regular profiling with tools like RAD Telemetry, every execution of a profiled
|
||||
//! scope of code will be captured when using AZ_PROFILE_SCOPE(). You can collect
|
||||
//! very large amounts of data and do your own post processing and analysis in tools like Excel,etc.
|
||||
//! In contrast, "statistical profiling" means that everytime AZ_PROFILE_SCOPE() is called,
|
||||
//! the time spent in the given scope of code will be mathematically accumulated as part of a unique
|
||||
//! Running statistic. Common statistical parameters like min, max, average, variance and standard deviation
|
||||
//! are calculated on the fly. This approach reduces considerably the amount of data that is collected.
|
||||
//! The data is recorded in the Game/Editor Log file.
|
||||
//!
|
||||
//! This StatisticalProfilerProxy should be used via the AZ_PROFILE_SCOPE() macro, and by using
|
||||
//! this macro the developer gains the flexibility of switching at compile time between profiling
|
||||
//! the code via RAD Telemetry or through statistical profiling.
|
||||
//!
|
||||
//! When creating a new statistical profiler add your category (aka profiler id) in Profiler.h (enum class ProfileCategory).
|
||||
//! Get a reference of the statistical profiler with "GetProfiler(const StatisticalProfilerId& id)" using the profiler Id.
|
||||
//! Once you get a reference to the profiler you can customize it, add Running statistics to it, etc.
|
||||
//! Some class in your code will manage the reference to the statistical profiler and will determine
|
||||
//! the policy on how often to log data to the game logs, etc. For example, by subclassing the TickBus Handler, etc.
|
||||
//!
|
||||
//! The StatisticalProfilerProxySystemComponent guarantees that the StatisticalProfilerProxy singleton exists
|
||||
//! as soon as the AZ::Environment is fully initialized.
|
||||
//! See StatisticalProfiler.h for more details and info.
|
||||
class StatisticalProfilerProxy
|
||||
{
|
||||
public:
|
||||
AZ_TYPE_INFO(StatisticalProfilerProxy, "{1103D0EB-1C32-4854-B9D9-40A2D65BDBD2}");
|
||||
|
||||
using StatIdType = AZStd::string;
|
||||
using StatisticalProfilerType = StatisticalProfiler<StatIdType, AZStd::shared_spin_mutex>;
|
||||
|
||||
//! A Convenience class used to measure time performance of scopes of code
|
||||
//! with constructor/destructor. Suitable to be used as part of a macro
|
||||
//! to facilitate its usage.
|
||||
class TimedScope
|
||||
{
|
||||
public:
|
||||
TimedScope() = delete;
|
||||
|
||||
TimedScope(const StatisticalProfilerId profilerId, const StatIdType& statId)
|
||||
: m_profilerId(profilerId), m_statId(statId)
|
||||
{
|
||||
if (!m_profilerProxy)
|
||||
{
|
||||
m_profilerProxy = AZ::Interface<StatisticalProfilerProxy>::Get();
|
||||
if (!m_profilerProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!m_profilerProxy->IsProfilerActive(profilerId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_startTime = AZStd::chrono::high_resolution_clock::now();
|
||||
}
|
||||
~TimedScope()
|
||||
{
|
||||
if (!m_profilerProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AZStd::chrono::system_clock::time_point stopTime = AZStd::chrono::high_resolution_clock::now();
|
||||
AZStd::chrono::microseconds duration = stopTime - m_startTime;
|
||||
m_profilerProxy->PushSample(m_profilerId, m_statId, static_cast<double>(duration.count()));
|
||||
}
|
||||
|
||||
//! Required only for UnitTests
|
||||
static void ClearCachedProxy()
|
||||
{
|
||||
m_profilerProxy = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
static StatisticalProfilerProxy* m_profilerProxy;
|
||||
const StatisticalProfilerId m_profilerId;
|
||||
const StatIdType& m_statId;
|
||||
AZStd::chrono::system_clock::time_point m_startTime;
|
||||
}; //class TimedScope
|
||||
|
||||
friend class TimedScope;
|
||||
|
||||
StatisticalProfilerProxy()
|
||||
{
|
||||
m_profilers.reserve(static_cast<AZStd::size_t>(Count));
|
||||
for (AZStd::size_t i = 0; i < static_cast<AZStd::size_t>(Count); i++)
|
||||
{
|
||||
m_profilers.emplace_back(StatisticalProfilerType());
|
||||
}
|
||||
AZ::Interface<StatisticalProfilerProxy>::Register(this);
|
||||
}
|
||||
|
||||
virtual ~StatisticalProfilerProxy()
|
||||
{
|
||||
AZ::Interface<StatisticalProfilerProxy>::Unregister(this);
|
||||
}
|
||||
|
||||
// Note that you have to delete these for safety reasons, you will trip a static_assert if you do not
|
||||
StatisticalProfilerProxy(StatisticalProfilerProxy&&) = delete;
|
||||
StatisticalProfilerProxy& operator=(StatisticalProfilerProxy&&) = delete;
|
||||
|
||||
bool IsProfilerActive(StatisticalProfilerId id) const
|
||||
{
|
||||
return m_activeProfilersFlag[static_cast<AZStd::size_t>(id)];
|
||||
}
|
||||
|
||||
StatisticalProfilerType& GetProfiler(StatisticalProfilerId id)
|
||||
{
|
||||
return m_profilers[static_cast<AZStd::size_t>(id)];
|
||||
}
|
||||
|
||||
void ActivateProfiler(StatisticalProfilerId id, bool activate)
|
||||
{
|
||||
m_activeProfilersFlag[static_cast<AZStd::size_t>(id)] = activate;
|
||||
}
|
||||
|
||||
void PushSample(StatisticalProfilerId id, const StatIdType& statId, double value)
|
||||
{
|
||||
m_profilers[static_cast<AZStd::size_t>(id)].PushSample(statId, value);
|
||||
}
|
||||
|
||||
private:
|
||||
AZStd::bitset<static_cast<AZStd::size_t>(Count)> m_activeProfilersFlag;
|
||||
AZStd::vector<StatisticalProfilerType> m_profilers;
|
||||
}; //class StatisticalProfilerProxy
|
||||
|
||||
}; //namespace Statistics
|
||||
}; //namespace AZ
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include "StatisticalProfilerProxySystemComponent.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
StatisticalProfilerProxy* StatisticalProfilerProxy::TimedScope::m_profilerProxy = nullptr;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<StatisticalProfilerProxySystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("StatisticalProfilerService", 0x20066f73));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("StatisticalProfilerService", 0x20066f73));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
StatisticalProfilerProxySystemComponent::StatisticalProfilerProxySystemComponent()
|
||||
: m_StatisticalProfilerProxy(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
StatisticalProfilerProxySystemComponent::~StatisticalProfilerProxySystemComponent()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Activate()
|
||||
{
|
||||
m_StatisticalProfilerProxy = new StatisticalProfilerProxy;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void StatisticalProfilerProxySystemComponent::Deactivate()
|
||||
{
|
||||
delete m_StatisticalProfilerProxy;
|
||||
}
|
||||
} //namespace Statistics
|
||||
} // namespace AZ
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include "StatisticalProfilerProxy.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! This system component manages the globally unique StatisticalProfilerProxy instance.
|
||||
//! And this is all this component does... it simply makes sure the StatisticalProfilerProxy exists.
|
||||
class StatisticalProfilerProxySystemComponent : public AZ::Component
|
||||
{
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AZ::Component Setup
|
||||
AZ_COMPONENT(StatisticalProfilerProxySystemComponent, "{1E15565F-A5C1-4BF2-8AEE-D3880AC9E1EB}")
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AZ::ComponentDescriptor::Reflect
|
||||
static void Reflect(AZ::ReflectContext* reflection);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AZ::ComponentDescriptor::GetProvidedServices
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AZ::ComponentDescriptor::GetIncompatibleServices
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Constructor
|
||||
StatisticalProfilerProxySystemComponent();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! Destructor
|
||||
~StatisticalProfilerProxySystemComponent() override;
|
||||
|
||||
protected:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AZ::Component::Activate
|
||||
void Activate() override;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//! \ref AZ::Component::Deactivate
|
||||
void Deactivate() override;
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Disable copy constructor
|
||||
StatisticalProfilerProxySystemComponent(const StatisticalProfilerProxySystemComponent&) = delete;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The one and only StatisticalProfilerProxy (Which is itself an AZ::Interface<>)
|
||||
StatisticalProfilerProxy* m_StatisticalProfilerProxy;
|
||||
};
|
||||
} //namespace Statistics
|
||||
} // namespace AZ
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
|
||||
#include "NamedRunningStatistic.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
/**
|
||||
* @brief A Collection of Running Statistics, addressable by a hashable
|
||||
* class/primitive. e.g. AZ::Crc32, int, AZStd::string, etc.
|
||||
*
|
||||
*/
|
||||
template <class StatIdType = AZStd::string>
|
||||
class StatisticsManager
|
||||
{
|
||||
public:
|
||||
StatisticsManager() = default;
|
||||
|
||||
StatisticsManager(const StatisticsManager& other)
|
||||
{
|
||||
m_statistics.reserve(other.m_statistics.size());
|
||||
for (auto const& it : other.m_statistics)
|
||||
{
|
||||
const StatIdType& statId = it.first;
|
||||
const NamedRunningStatistic* stat = it.second;
|
||||
m_statistics[statId] = new NamedRunningStatistic(*stat);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~StatisticsManager()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool ContainsStatistic(const StatIdType& statId) const
|
||||
{
|
||||
auto iterator = m_statistics.find(statId);
|
||||
return iterator != m_statistics.end();
|
||||
}
|
||||
|
||||
AZ::u32 GetCount() const
|
||||
{
|
||||
return static_cast<AZ::u32>(m_statistics.size());
|
||||
}
|
||||
|
||||
void GetAllStatistics(AZStd::vector<NamedRunningStatistic*>& vector)
|
||||
{
|
||||
for (auto const& it : m_statistics)
|
||||
{
|
||||
NamedRunningStatistic* stat = it.second;
|
||||
vector.push_back(stat);
|
||||
}
|
||||
}
|
||||
|
||||
//! Helper method to apply units to statistics with empty units string.
|
||||
AZ::u32 ApplyUnits(const AZStd::string& units)
|
||||
{
|
||||
AZ::u32 updatedCount = 0;
|
||||
for (auto& it : m_statistics)
|
||||
{
|
||||
NamedRunningStatistic* stat = it.second;
|
||||
if (stat->GetUnits().empty())
|
||||
{
|
||||
stat->UpdateUnits(units);
|
||||
updatedCount++;
|
||||
}
|
||||
}
|
||||
return updatedCount;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
for (auto& it : m_statistics)
|
||||
{
|
||||
NamedRunningStatistic* stat = it.second;
|
||||
delete stat;
|
||||
}
|
||||
m_statistics.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nullptr if a statistic with such name doesn't exist,
|
||||
* otherwise returns a pointer to the statistic.
|
||||
*/
|
||||
NamedRunningStatistic* GetStatistic(const StatIdType& statId)
|
||||
{
|
||||
auto iterator = m_statistics.find(statId);
|
||||
if (iterator == m_statistics.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return iterator->second;
|
||||
}
|
||||
|
||||
//! Returns false if a NamedRunningStatistic with such id already exists.
|
||||
NamedRunningStatistic* AddStatistic(const StatIdType& statId, const bool failIfExist = true)
|
||||
{
|
||||
if (failIfExist)
|
||||
{
|
||||
NamedRunningStatistic* prevStat = GetStatistic(statId);
|
||||
if (prevStat)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
NamedRunningStatistic* stat = new NamedRunningStatistic();
|
||||
m_statistics[statId] = stat;
|
||||
return stat;
|
||||
}
|
||||
|
||||
//! Returns false if a NamedRunningStatistic with such id already exists.
|
||||
NamedRunningStatistic* AddStatistic(const StatIdType& statId, const AZStd::string& name, const AZStd::string& units, const bool failIfExist = true)
|
||||
{
|
||||
if (failIfExist)
|
||||
{
|
||||
NamedRunningStatistic* prevStat = GetStatistic(statId);
|
||||
if (prevStat)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
NamedRunningStatistic* stat = new NamedRunningStatistic(name, units);
|
||||
m_statistics[statId] = stat;
|
||||
return stat;
|
||||
}
|
||||
|
||||
virtual void RemoveStatistic(const StatIdType& statId)
|
||||
{
|
||||
auto iterator = m_statistics.find(statId);
|
||||
if (iterator == m_statistics.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
NamedRunningStatistic* prevStat = iterator->second;
|
||||
delete prevStat;
|
||||
m_statistics.erase(iterator);
|
||||
}
|
||||
|
||||
void ResetStatistic(const StatIdType& statId)
|
||||
{
|
||||
NamedRunningStatistic* stat = GetStatistic(statId);
|
||||
if (!stat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stat->Reset();
|
||||
}
|
||||
|
||||
void ResetAllStatistics()
|
||||
{
|
||||
for (auto& it : m_statistics)
|
||||
{
|
||||
NamedRunningStatistic* stat = it.second;
|
||||
stat->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void PushSampleForStatistic(const StatIdType& statId, double value)
|
||||
{
|
||||
NamedRunningStatistic* stat = GetStatistic(statId);
|
||||
if (!stat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
stat->PushSample(value);
|
||||
}
|
||||
|
||||
//! Expensive function because it does a reverse lookup
|
||||
bool GetStatId(NamedRunningStatistic* searchStat, StatIdType& statIdOut) const
|
||||
{
|
||||
for (auto& it : m_statistics)
|
||||
{
|
||||
NamedRunningStatistic* stat = it.second;
|
||||
if (stat == searchStat)
|
||||
{
|
||||
statIdOut = it.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
///Key: StatIdType, Value: NamedRunningStatistic*
|
||||
AZStd::unordered_map<StatIdType, NamedRunningStatistic*> m_statistics;
|
||||
};//class StatisticsManager
|
||||
}//namespace Statistics
|
||||
}//namespace AZ
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "TimeDataStatisticsManager.h"
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
void TimeDataStatisticsManager::PushTimeDataSample(const char * registerName, const AZ::Debug::ProfilerRegister::TimeData& timeData)
|
||||
{
|
||||
const AZStd::string statName(registerName);
|
||||
NamedRunningStatistic* statistic = GetStatistic(statName);
|
||||
if (!statistic)
|
||||
{
|
||||
const AZStd::string units("us");
|
||||
AddStatistic(statName, statName, units, false);
|
||||
AZ::Debug::ProfilerRegister::TimeData zeroTimeData;
|
||||
memset(&zeroTimeData, 0, sizeof(AZ::Debug::ProfilerRegister::TimeData));
|
||||
m_previousTimeData[statName] = zeroTimeData;
|
||||
statistic = GetStatistic(statName);
|
||||
AZ_Assert(statistic != nullptr, "Fatal error adding a new statistic object");
|
||||
}
|
||||
|
||||
const AZ::u64 accumulatedTime = timeData.m_time;
|
||||
const AZ::s64 totalNumCalls = timeData.m_calls;
|
||||
const AZ::u64 previousAccumulatedTime = m_previousTimeData[statName].m_time;
|
||||
const AZ::s64 previousTotalNumCalls = m_previousTimeData[statName].m_calls;
|
||||
const AZ::u64 deltaTime = accumulatedTime - previousAccumulatedTime;
|
||||
const AZ::s64 deltaCalls = totalNumCalls - previousTotalNumCalls;
|
||||
|
||||
if (deltaCalls == 0)
|
||||
{
|
||||
//This is the same old data. Let's skip it
|
||||
return;
|
||||
}
|
||||
|
||||
double newSample = static_cast<double>(deltaTime) / deltaCalls;
|
||||
|
||||
statistic->PushSample(newSample);
|
||||
m_previousTimeData[statName] = timeData;
|
||||
}
|
||||
} //namespace Statistics
|
||||
} //namespace AZ
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Debug/Profiler.h>
|
||||
#include <AzCore/Statistics/StatisticsManager.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Statistics
|
||||
{
|
||||
/**
|
||||
* @brief Specialization useful for data generated with AZ::Debug::FrameProfileComponent
|
||||
*
|
||||
* Timer based data collection using AZ_PROFILE_SCOPE(...), available in
|
||||
* AzCore/Debug/Profiler.h can be collected when using AZ::Debug::FrameProfilerComponent
|
||||
* and AZ::Debug::FrameProfilerBus. The method PushTimeDataSample(...) is a convenience
|
||||
* to convert those Timer registers into a RunningStatistic.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class TimeDataStatisticsManager : public StatisticsManager<>
|
||||
{
|
||||
public:
|
||||
TimeDataStatisticsManager() = default;
|
||||
virtual ~TimeDataStatisticsManager() = default;
|
||||
|
||||
/**
|
||||
* @brief Adds one sample data to a specific running stat by name.
|
||||
*
|
||||
* This method is specialized to work with ProfilerRegister::TimeData that can be intercepted
|
||||
* during AZ::Debug::FrameProfilerBus::OnFrameProfilerData().
|
||||
* For each @param registerName a new RunningStat object is created if it doesn't exist.
|
||||
*
|
||||
* Adds the TimeData as one sample for its RunningStatistic.
|
||||
*/
|
||||
void PushTimeDataSample(const char * registerName, const AZ::Debug::ProfilerRegister::TimeData& timeData);
|
||||
|
||||
protected:
|
||||
///We store here the previous value from the previous timer frame data.
|
||||
///This is necessary because AZ_PROFILER_TIMER is cumulative
|
||||
///and we need the time spent for each call.
|
||||
AZStd::unordered_map<AZStd::string, AZ::Debug::ProfilerRegister::TimeData> m_previousTimeData;
|
||||
};
|
||||
} //namespace Statistics
|
||||
} //namespace AZ
|
||||
@@ -566,9 +566,6 @@ set(FILES
|
||||
Statistics/NamedRunningStatistic.h
|
||||
Statistics/RunningStatistic.cpp
|
||||
Statistics/RunningStatistic.h
|
||||
Statistics/StatisticsManager.h
|
||||
Statistics/TimeDataStatisticsManager.cpp
|
||||
Statistics/TimeDataStatisticsManager.h
|
||||
StringFunc/StringFunc.cpp
|
||||
StringFunc/StringFunc.h
|
||||
UserSettings/UserSettings.cpp
|
||||
|
||||
Reference in New Issue
Block a user