StatisticalProfilerTests.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Debug/TraceMessageBus.h>
  9. #include <Tests/StatisticalProfilerHelpers.h>
  10. namespace UnitTest
  11. {
  12. template<typename ProfilerType, typename StatIdType>
  13. void RecordStatistics(ProfilerType& profiler, const int loopCount, const StatIdType& rootId, const StatIdType& loopId)
  14. {
  15. using TimedScopeType = typename ProfilerType::TimedScope;
  16. TimedScopeType rootScope(profiler, rootId);
  17. for (int i = 0; i < loopCount; ++i)
  18. {
  19. TimedScopeType loopScope(profiler, loopId);
  20. }
  21. }
  22. void RecordStatistics(const int loopCount,
  23. const AZ::Statistics::StatisticalProfilerProxy::StatIdType& rootId,
  24. const AZ::Statistics::StatisticalProfilerProxy::StatIdType& loopId)
  25. {
  26. AZ::Statistics::StatisticalProfilerProxy::TimedScope rootScope(ProfilerProxyGroup, rootId);
  27. for (int i = 0; i < loopCount; ++i)
  28. {
  29. AZ::Statistics::StatisticalProfilerProxy::TimedScope loopScope(ProfilerProxyGroup, loopId);
  30. }
  31. }
  32. class AllocatorsWithTraceFixture
  33. : public LeakDetectionFixture
  34. , public AZ::Debug::TraceMessageBus::Handler
  35. {
  36. public:
  37. void SetUp() override
  38. {
  39. LeakDetectionFixture::SetUp();
  40. AZ::Debug::TraceMessageBus::Handler::BusConnect();
  41. }
  42. void TearDown() override
  43. {
  44. AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
  45. LeakDetectionFixture::TearDown();
  46. }
  47. bool OnPrintf(const char* window, const char* message) override
  48. {
  49. return OnOutput(window, message);
  50. }
  51. bool OnOutput(const char* window, const char* message) override
  52. {
  53. printf("%s: %s", window, message);
  54. return false;
  55. }
  56. };
  57. // -- AZ::Statistics::StatisticalProfiler tests --
  58. template<class S = AZStd::string, class M = AZ::NullMutex>
  59. struct StatisticalProfilerTestTraits
  60. {
  61. using StatIdType = S;
  62. using MutexType = M;
  63. using ProfilerType = AZ::Statistics::StatisticalProfiler<StatIdType, MutexType>;
  64. };
  65. template<class Traits>
  66. class StatisticalProfilerFixture
  67. : public AllocatorsWithTraceFixture
  68. {
  69. };
  70. using StatisticalProfilerTestTypes = ::testing::Types<
  71. StatisticalProfilerTestTraits<>,
  72. StatisticalProfilerTestTraits<StringHash>,
  73. StatisticalProfilerTestTraits<AZ::Crc32>,
  74. StatisticalProfilerTestTraits<AZ::HashValue32>,
  75. StatisticalProfilerTestTraits<AZ::HashValue64>,
  76. StatisticalProfilerTestTraits<AZStd::string, AZStd::mutex>,
  77. StatisticalProfilerTestTraits<StringHash, AZStd::mutex>,
  78. StatisticalProfilerTestTraits<AZ::Crc32, AZStd::mutex>,
  79. StatisticalProfilerTestTraits<AZ::HashValue32, AZStd::mutex>,
  80. StatisticalProfilerTestTraits<AZ::HashValue64, AZStd::mutex>,
  81. StatisticalProfilerTestTraits<AZStd::string, AZStd::spin_mutex>,
  82. StatisticalProfilerTestTraits<StringHash, AZStd::spin_mutex>,
  83. StatisticalProfilerTestTraits<AZ::Crc32, AZStd::spin_mutex>,
  84. StatisticalProfilerTestTraits<AZ::HashValue32, AZStd::spin_mutex>,
  85. StatisticalProfilerTestTraits<AZ::HashValue64, AZStd::spin_mutex>,
  86. StatisticalProfilerTestTraits<AZStd::string, AZStd::shared_mutex>,
  87. StatisticalProfilerTestTraits<StringHash, AZStd::shared_mutex>,
  88. StatisticalProfilerTestTraits<AZ::Crc32, AZStd::shared_mutex>,
  89. StatisticalProfilerTestTraits<AZ::HashValue32, AZStd::shared_mutex>,
  90. StatisticalProfilerTestTraits<AZ::HashValue64, AZStd::shared_mutex>
  91. >;
  92. TYPED_TEST_CASE(StatisticalProfilerFixture, StatisticalProfilerTestTypes);
  93. TYPED_TEST(StatisticalProfilerFixture, ProfileCode_SingleThread_ValidateStatistics)
  94. {
  95. using ProfilerType = typename TypeParam::ProfilerType;
  96. using StatIdType = typename TypeParam::StatIdType;
  97. ProfilerType profiler;
  98. const AZStd::string statNamePerformance("PerformanceResult");
  99. const auto statIdPerformance = ConvertNameToStatId<StatIdType>(statNamePerformance);
  100. const AZStd::string statNameBlock("Block");
  101. const auto statIdBlock = ConvertNameToStatId<StatIdType>(statNameBlock);
  102. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
  103. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdBlock, statNameBlock, "us") != nullptr);
  104. RecordStatistics(profiler, SmallIterationCount, statIdPerformance, statIdBlock);
  105. ASSERT_TRUE(profiler.GetStatistic(statIdPerformance) != nullptr);
  106. EXPECT_EQ(profiler.GetStatistic(statIdPerformance)->GetNumSamples(), 1);
  107. ASSERT_TRUE(profiler.GetStatistic(statIdBlock) != nullptr);
  108. EXPECT_EQ(profiler.GetStatistic(statIdBlock)->GetNumSamples(), SmallIterationCount);
  109. }
  110. template<class Traits>
  111. class ThreadedStatisticalProfilerFixture
  112. : public AllocatorsWithTraceFixture
  113. {
  114. };
  115. using ThreadedStatisticalProfilerTestTypes = ::testing::Types<
  116. StatisticalProfilerTestTraits<AZStd::string, AZStd::mutex>,
  117. StatisticalProfilerTestTraits<StringHash, AZStd::mutex>,
  118. StatisticalProfilerTestTraits<AZ::Crc32, AZStd::mutex>,
  119. StatisticalProfilerTestTraits<AZ::HashValue32, AZStd::mutex>,
  120. StatisticalProfilerTestTraits<AZ::HashValue64, AZStd::mutex>,
  121. StatisticalProfilerTestTraits<AZStd::string, AZStd::spin_mutex>,
  122. StatisticalProfilerTestTraits<StringHash, AZStd::spin_mutex>,
  123. StatisticalProfilerTestTraits<AZ::Crc32, AZStd::spin_mutex>,
  124. StatisticalProfilerTestTraits<AZ::HashValue32, AZStd::spin_mutex>,
  125. StatisticalProfilerTestTraits<AZ::HashValue64, AZStd::spin_mutex>,
  126. StatisticalProfilerTestTraits<AZStd::string, AZStd::shared_mutex>,
  127. StatisticalProfilerTestTraits<StringHash, AZStd::shared_mutex>,
  128. StatisticalProfilerTestTraits<AZ::Crc32, AZStd::shared_mutex>,
  129. StatisticalProfilerTestTraits<AZ::HashValue32, AZStd::shared_mutex>,
  130. StatisticalProfilerTestTraits<AZ::HashValue64, AZStd::shared_mutex>
  131. >;
  132. TYPED_TEST_CASE(ThreadedStatisticalProfilerFixture, ThreadedStatisticalProfilerTestTypes);
  133. TYPED_TEST(ThreadedStatisticalProfilerFixture, ProfileCode_4Threads_ValidateStatistics)
  134. {
  135. using ProfilerType = typename TypeParam::ProfilerType;
  136. using StatIdType = typename TypeParam::StatIdType;
  137. ProfilerType profiler;
  138. const AZStd::string statNameThread1("thread1");
  139. const auto statIdThread1 = ConvertNameToStatId<StatIdType>(statNameThread1);
  140. const AZStd::string statNameThread1Loop("thread1_loop");
  141. const auto statIdThread1Loop = ConvertNameToStatId<StatIdType>(statNameThread1Loop);
  142. const AZStd::string statNameThread2("thread2");
  143. const auto statIdThread2 = ConvertNameToStatId<StatIdType>(statNameThread2);
  144. const AZStd::string statNameThread2Loop("thread2_loop");
  145. const auto statIdThread2Loop = ConvertNameToStatId<StatIdType>(statNameThread2Loop);
  146. const AZStd::string statNameThread3("thread3");
  147. const auto statIdThread3 = ConvertNameToStatId<StatIdType>(statNameThread3);
  148. const AZStd::string statNameThread3Loop("thread3_loop");
  149. const auto statIdThread3Loop = ConvertNameToStatId<StatIdType>(statNameThread3Loop);
  150. const AZStd::string statNameThread4("thread4");
  151. const auto statIdThread4 = ConvertNameToStatId<StatIdType>(statNameThread4);
  152. const AZStd::string statNameThread4Loop("thread4_loop");
  153. const auto statIdThread4Loop = ConvertNameToStatId<StatIdType>(statNameThread4Loop);
  154. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread1, statNameThread1, "us"));
  155. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread1Loop, statNameThread1Loop, "us"));
  156. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread2, statNameThread2, "us"));
  157. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread2Loop, statNameThread2Loop, "us"));
  158. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3, statNameThread3, "us"));
  159. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3Loop, statNameThread3Loop, "us"));
  160. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread4, statNameThread4, "us"));
  161. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread4Loop, statNameThread4Loop, "us"));
  162. AZStd::thread t1([&](){
  163. RecordStatistics(profiler, MediumIterationCount, statIdThread1, statIdThread1Loop);
  164. });
  165. AZStd::thread t2([&](){
  166. RecordStatistics(profiler, MediumIterationCount, statIdThread2, statIdThread2Loop);
  167. });
  168. AZStd::thread t3([&](){
  169. RecordStatistics(profiler, MediumIterationCount, statIdThread3, statIdThread3Loop);
  170. });
  171. AZStd::thread t4([&](){
  172. RecordStatistics(profiler, MediumIterationCount, statIdThread4, statIdThread4Loop);
  173. });
  174. t1.join();
  175. t2.join();
  176. t3.join();
  177. t4.join();
  178. ASSERT_TRUE(profiler.GetStatistic(statIdThread1) != nullptr);
  179. EXPECT_EQ(profiler.GetStatistic(statIdThread1)->GetNumSamples(), 1);
  180. ASSERT_TRUE(profiler.GetStatistic(statIdThread1Loop) != nullptr);
  181. EXPECT_EQ(profiler.GetStatistic(statIdThread1Loop)->GetNumSamples(), MediumIterationCount);
  182. ASSERT_TRUE(profiler.GetStatistic(statIdThread2) != nullptr);
  183. EXPECT_EQ(profiler.GetStatistic(statIdThread2)->GetNumSamples(), 1);
  184. ASSERT_TRUE(profiler.GetStatistic(statIdThread2Loop) != nullptr);
  185. EXPECT_EQ(profiler.GetStatistic(statIdThread2Loop)->GetNumSamples(), MediumIterationCount);
  186. ASSERT_TRUE(profiler.GetStatistic(statIdThread3) != nullptr);
  187. EXPECT_EQ(profiler.GetStatistic(statIdThread3)->GetNumSamples(), 1);
  188. ASSERT_TRUE(profiler.GetStatistic(statIdThread3Loop) != nullptr);
  189. EXPECT_EQ(profiler.GetStatistic(statIdThread3Loop)->GetNumSamples(), MediumIterationCount);
  190. ASSERT_TRUE(profiler.GetStatistic(statIdThread4) != nullptr);
  191. EXPECT_EQ(profiler.GetStatistic(statIdThread4)->GetNumSamples(), 1);
  192. ASSERT_TRUE(profiler.GetStatistic(statIdThread4Loop) != nullptr);
  193. EXPECT_EQ(profiler.GetStatistic(statIdThread4Loop)->GetNumSamples(), MediumIterationCount);
  194. }
  195. // -- AZ::Statistics::StatisticalProfilerProxy tests --
  196. class StatisticalProfilerProxyFixture
  197. : public AllocatorsWithTraceFixture
  198. {
  199. public:
  200. using ProxyType = AZ::Statistics::StatisticalProfilerProxy;
  201. void SetUp() override
  202. {
  203. AllocatorsWithTraceFixture::SetUp();
  204. ProxyType::TimedScope::ClearCachedProxy();
  205. }
  206. };
  207. TEST_F(StatisticalProfilerProxyFixture, ProfileCode_SingleThread_ValidateStatistics)
  208. {
  209. ProxyType profilerProxy;
  210. ProxyType* proxy = AZ::Interface<ProxyType>::Get();
  211. ASSERT_TRUE(proxy != nullptr);
  212. ProxyType::StatisticalProfilerType& profiler = proxy->GetProfiler(ProfilerProxyGroup);
  213. const AZStd::string statNamePerformance("PerformanceResult");
  214. const auto statIdPerformance = ConvertNameToStatId<ProxyType::StatIdType>(statNamePerformance);
  215. const AZStd::string statNameBlock("Block");
  216. const auto statIdBlock = ConvertNameToStatId<ProxyType::StatIdType>(statNameBlock);
  217. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdPerformance, statNamePerformance, "us") != nullptr);
  218. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdBlock, statNameBlock, "us") != nullptr);
  219. proxy->ActivateProfiler(ProfilerProxyGroup, true);
  220. RecordStatistics(SmallIterationCount, statIdPerformance, statIdBlock);
  221. ASSERT_TRUE(profiler.GetStatistic(statIdPerformance) != nullptr);
  222. EXPECT_EQ(profiler.GetStatistic(statIdPerformance)->GetNumSamples(), 1);
  223. ASSERT_TRUE(profiler.GetStatistic(statIdBlock) != nullptr);
  224. EXPECT_EQ(profiler.GetStatistic(statIdBlock)->GetNumSamples(), SmallIterationCount);
  225. }
  226. TEST_F(StatisticalProfilerProxyFixture, ProfileCode_4Threads_ValidateStatistics)
  227. {
  228. ProxyType profilerProxy;
  229. ProxyType* proxy = AZ::Interface<ProxyType>::Get();
  230. ASSERT_TRUE(proxy != nullptr);
  231. ProxyType::StatisticalProfilerType& profiler = proxy->GetProfiler(ProfilerProxyGroup);
  232. const AZStd::string statNameThread1("thread1");
  233. const auto statIdThread1 = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread1);
  234. const AZStd::string statNameThread1Loop("thread1_loop");
  235. const auto statIdThread1Loop = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread1Loop);
  236. const AZStd::string statNameThread2("thread2");
  237. const auto statIdThread2 = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread2);
  238. const AZStd::string statNameThread2Loop("thread2_loop");
  239. const auto statIdThread2Loop = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread2Loop);
  240. const AZStd::string statNameThread3("thread3");
  241. const auto statIdThread3 = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread3);
  242. const AZStd::string statNameThread3Loop("thread3_loop");
  243. const auto statIdThread3Loop = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread3Loop);
  244. const AZStd::string statNameThread4("thread4");
  245. const auto statIdThread4 = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread4);
  246. const AZStd::string statNameThread4Loop("thread4_loop");
  247. const auto statIdThread4Loop = ConvertNameToStatId<ProxyType::StatIdType>(statNameThread4Loop);
  248. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread1, statNameThread1, "us"));
  249. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread1Loop, statNameThread1Loop, "us"));
  250. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread2, statNameThread2, "us"));
  251. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread2Loop, statNameThread2Loop, "us"));
  252. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3, statNameThread3, "us"));
  253. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread3Loop, statNameThread3Loop, "us"));
  254. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread4, statNameThread4, "us"));
  255. ASSERT_TRUE(profiler.GetStatsManager().AddStatistic(statIdThread4Loop, statNameThread4Loop, "us"));
  256. proxy->ActivateProfiler(ProfilerProxyGroup, true);
  257. AZStd::thread t1([&](){
  258. RecordStatistics(MediumIterationCount, statIdThread1, statIdThread1Loop);
  259. });
  260. AZStd::thread t2([&](){
  261. RecordStatistics(MediumIterationCount, statIdThread2, statIdThread2Loop);
  262. });
  263. AZStd::thread t3([&](){
  264. RecordStatistics(MediumIterationCount, statIdThread3, statIdThread3Loop);
  265. });
  266. AZStd::thread t4([&](){
  267. RecordStatistics(MediumIterationCount, statIdThread4, statIdThread4Loop);
  268. });
  269. t1.join();
  270. t2.join();
  271. t3.join();
  272. t4.join();
  273. ASSERT_TRUE(profiler.GetStatistic(statIdThread1) != nullptr);
  274. EXPECT_EQ(profiler.GetStatistic(statIdThread1)->GetNumSamples(), 1);
  275. ASSERT_TRUE(profiler.GetStatistic(statIdThread1Loop) != nullptr);
  276. EXPECT_EQ(profiler.GetStatistic(statIdThread1Loop)->GetNumSamples(), MediumIterationCount);
  277. ASSERT_TRUE(profiler.GetStatistic(statIdThread2) != nullptr);
  278. EXPECT_EQ(profiler.GetStatistic(statIdThread2)->GetNumSamples(), 1);
  279. ASSERT_TRUE(profiler.GetStatistic(statIdThread2Loop) != nullptr);
  280. EXPECT_EQ(profiler.GetStatistic(statIdThread2Loop)->GetNumSamples(), MediumIterationCount);
  281. ASSERT_TRUE(profiler.GetStatistic(statIdThread3) != nullptr);
  282. EXPECT_EQ(profiler.GetStatistic(statIdThread3)->GetNumSamples(), 1);
  283. ASSERT_TRUE(profiler.GetStatistic(statIdThread3Loop) != nullptr);
  284. EXPECT_EQ(profiler.GetStatistic(statIdThread3Loop)->GetNumSamples(), MediumIterationCount);
  285. ASSERT_TRUE(profiler.GetStatistic(statIdThread4) != nullptr);
  286. EXPECT_EQ(profiler.GetStatistic(statIdThread4)->GetNumSamples(), 1);
  287. ASSERT_TRUE(profiler.GetStatistic(statIdThread4Loop) != nullptr);
  288. EXPECT_EQ(profiler.GetStatistic(statIdThread4Loop)->GetNumSamples(), MediumIterationCount);
  289. proxy->ActivateProfiler(ProfilerProxyGroup, false);
  290. }
  291. }//namespace UnitTest