ArmNN
 22.05
ProfilerTests.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include <armnn/IRuntime.hpp>
7 
8 #include <doctest/doctest.h>
9 
10 #include <algorithm>
11 #include <thread>
12 
13 #include <Profiling.hpp>
14 #include <armnn/Optional.hpp>
15 
16 namespace armnn
17 {
18 
20 {
21  if (!profiler)
22  {
23  return static_cast<size_t>(-1);
24  }
25 
26  return profiler->pProfilerImpl->m_EventSequence.size();
27 }
28 } // namespace armnn
29 
30 namespace
31 {
32 
33 void RegisterUnregisterProfilerSingleThreadImpl(bool &res)
34 {
35  // Important! Don't use CHECK macros in this function as they
36  // seem to have problems when used in threads
37 
38  // Get a reference to the profiler manager.
40 
41  // Check that there's no profiler registered for this thread.
42  res = !profilerManager.GetProfiler();
43 
44  // Create and register a profiler for this thread.
45  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
46  profilerManager.RegisterProfiler(profiler.get());
47 
48  // Check that on a single thread we get the same profiler we registered.
49  res &= profiler.get() == profilerManager.GetProfiler();
50 
51  // Destroy the profiler.
52  profiler.reset();
53 
54  // Check that the profiler has been un-registered for this thread.
55  res &= !profilerManager.GetProfiler();
56 
58 }
59 
60 } // namespace
61 
62 TEST_SUITE("Profiler")
63 {
64 TEST_CASE("EnableDisableProfiling")
65 {
66  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
67 
68  // Check that profiling is disabled by default.
69  CHECK(!profiler->IsProfilingEnabled());
70 
71  // Enable profiling.
72  profiler->EnableProfiling(true);
73 
74  // Check that profiling is enabled.
75  CHECK(profiler->IsProfilingEnabled());
76 
77  // Disable profiling.
78  profiler->EnableProfiling(false);
79 
80  // Check that profiling is disabled.
81  CHECK(!profiler->IsProfilingEnabled());
82 }
83 
84 TEST_CASE("RegisterUnregisterProfilerSingleThread")
85 {
86  bool res = false;
87  RegisterUnregisterProfilerSingleThreadImpl(res);
88  CHECK(res);
89 }
90 
91 TEST_CASE("RegisterUnregisterProfilerMultipleThreads")
92 {
93  bool res[3] = {false, false, false};
94  std::vector<std::thread> threads;
95  for (unsigned int i = 0; i < 3; ++i)
96  {
97  threads.push_back(std::thread([&res, i]() { RegisterUnregisterProfilerSingleThreadImpl(res[i]); }));
98  }
99  std::for_each(threads.begin(), threads.end(), [](std::thread& theThread)
100  {
101  theThread.join();
102  });
103 
104  for (int i = 0 ; i < 3 ; ++i)
105  {
106  CHECK(res[i]);
107  }
108 }
109 
110 TEST_CASE("ProfilingMacros")
111 {
112  // Get a reference to the profiler manager.
114 
115  { // --- No profiler ---
116 
117  // Check that there's no profiler registered for this thread.
118  CHECK(!profilerManager.GetProfiler());
119 
120  // Test scoped event.
122 
123  // Check that we still cannot get a profiler for this thread.
124  CHECK(!profilerManager.GetProfiler());
125  }
126 
127  // Create and register a profiler for this thread.
128  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
129  profilerManager.RegisterProfiler(profiler.get());
130 
131  { // --- Profiler, but profiling disabled ---
132 
133  // Get current event sequence size.
134  size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
135 
136  // Test scoped macro.
138 
139  // Check that no profiling event has been added to the sequence.
140  size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
141  CHECK(eventSequenceSizeBefore == eventSequenceSizeAfter);
142  }
143 
144  // Enable profiling.
145  profiler->EnableProfiling(true);
146 
147  { // --- Profiler, and profiling enabled ---
148 
149  // Get current event sequence size.
150  size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
151 
152  // Test scoped macro.
154 
155  // Check that a profiling event has been added to the sequence.
156  size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
157  CHECK(eventSequenceSizeAfter == eventSequenceSizeBefore + 1);
158  }
159 
160  // Disable profiling here to not print out anything on stdout.
161  profiler->EnableProfiling(false);
162 }
163 
164 #if defined(ARMNNREF_ENABLED)
165 
166 // This test unit needs the reference backend, it's not available if the reference backend is not built
167 
168 TEST_CASE("RuntimeLoadNetwork")
169 {
170  // Get a reference to the profiler manager.
172 
173  // Check that there's no profiler registered for this thread.
174  CHECK(!profilerManager.GetProfiler());
175 
176  // Build a mock-network and load it into the runtime.
179  armnn::NetworkId networkIdentifier = 1;
181  mockNetwork->AddInputLayer(0, "test layer");
182  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
183  runtime->LoadNetwork(networkIdentifier, armnn::Optimize(*mockNetwork, backends, runtime->GetDeviceSpec()));
184 
185  // Check that now there's a profiler registered for this thread (created and registered by the loading the network).
186  CHECK(profilerManager.GetProfiler());
187 
188  // Unload the network.
189  runtime->UnloadNetwork(networkIdentifier);
190 
191  // Check that the profiler has been un-registered for this thread.
192  CHECK(!profilerManager.GetProfiler());
193 }
194 
195 #endif
196 
197 TEST_CASE("WriteEventResults")
198 {
199  // Get a reference to the profiler manager.
201 
202  // Create and register a profiler for this thread.
203  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
204  profileManager.RegisterProfiler(profiler.get());
205 
206  // Enable profiling.
207  profiler->EnableProfiling(true);
208 
209  { // --- Profiler, and profiling enabled ---
210 
211  // Get current event sequence size.
212  size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
213 
214  // Test scoped macro.
215  {
216  // Need to directly create a ScopedProfilingEvent as the one created by the macro falls out of scope
217  // immediately causing the Event.Stop() function method to be called immediately after the Event.Start()
218  // function resulting in periodic test failures on the Dent and Smith HiKeys
221  "test",
223  std::this_thread::sleep_for(std::chrono::milliseconds(10));
224  }
225 
226  // Check that a profiling event has been added to the sequence.
227  size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
228  CHECK(eventSequenceSizeAfter == eventSequenceSizeBefore + 1);
229 
230  std::ostringstream output;
231  profiler->AnalyzeEventsAndWriteResults(output);
232  CHECK(!output.str().empty());
233 
234  // output should contain event name 'test'
235  CHECK(output.str().find("test") != std::string::npos);
236 
237  // output should contain headers
238  CHECK(output.str().find("Event Sequence - Name") != std::string::npos);
239  CHECK(output.str().find("Event Stats - Name") != std::string::npos);
240  CHECK(output.str().find("Total") != std::string::npos);
241  CHECK(output.str().find("Device") != std::string::npos);
242  // output should contain compute device 'CpuAcc'
243  CHECK(output.str().find("CpuAcc") != std::string::npos);
244  // output should not contain un-readable numbers
245  CHECK(output.str().find("e+") == std::string::npos);
246  // output should not contain un-readable numbers
247  CHECK(output.str().find("+") == std::string::npos);
248  // output should not contain zero value
249  CHECK(output.str().find(" 0 ") == std::string::npos);
250  }
251 
252  // Disable profiling here to not print out anything on stdout.
253  profiler->EnableProfiling(false);
254 }
255 
256 TEST_CASE("ProfilerJsonPrinter")
257 {
258  class TestInstrument : public armnn::Instrument
259  {
260  public:
261  virtual ~TestInstrument() {}
262  void Start() override {}
263  void Stop() override {}
264 
265  std::vector<armnn::Measurement> GetMeasurements() const override
266  {
267  std::vector<armnn::Measurement> measurements;
268  measurements.emplace_back(armnn::Measurement("Measurement1",
269  1.0,
270  armnn::Measurement::Unit::TIME_MS));
271  measurements.emplace_back(armnn::Measurement("Measurement2",
272  2.0,
273  armnn::Measurement::Unit::TIME_US));
274  return measurements;
275  }
276 
277  const char* GetName() const override
278  {
279  return "TestInstrument";
280  }
281  };
282 
283  // Get a reference to the profiler manager.
285 
286  // Create and register a profiler for this thread.
287  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
288  profilerManager.RegisterProfiler(profiler.get());
289 
290  profiler->EnableProfiling(true);
291 
292  {
295  "Optimizer",
296  TestInstrument())
299  "Level 0",
300  TestInstrument())
301  {
302  {
305  "Level 1A",
306  TestInstrument())
307  }
308  }
309  }
310  {
313  "LoadedNetwork",
314  TestInstrument())
317  "Level 0",
318  TestInstrument())
319  {
320  {
323  "Level 1A",
324  TestInstrument())
325  }
326  }
327  }
328  {
329  // Test scoped macro.
332  "EnqueueWorkload",
333  TestInstrument())
336  "Level 0",
337  TestInstrument())
338  {
339  {
342  "Level 1A",
343  TestInstrument())
344  }
345  {
348  "Level 1B",
349  TestInstrument())
350 
351  {
354  "Level 2A",
355  TestInstrument())
356  }
357  }
358  }
359  }
360 
361  std::stringbuf buffer;
362  std::ostream json(&buffer);
363  profiler->Print(json);
364 
365  std::string output = buffer.str();
366  armnn::IgnoreUnused(output);
367 
368  // Disable profiling here to not print out anything on stdout.
369  profiler->EnableProfiling(false);
370 
371  // blessed output validated by a human eyeballing the output to make sure it's ok and then copying it here.
372  // validation also included running the blessed output through an online json validation site
373  std::string blessedOutput("{\n\t\"ArmNN\": {\n\t\t\"optimize_measurements_#1\": {\n\t\t\t\"type\": \"Event\""
374  ",\n\t\t\t\"Measurement1_#1\": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\""
375  ": [\n\t\t\t\t\t1.000000\n\t\t\t\t],\n\t\t\t\t\"unit\": \"ms\"\n\t\t\t},\n\t\t\t\""
376  "Measurement2_#1\": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t"
377  "\t\t2.000000\n\t\t\t\t],\n\t\t\t\t\"unit\": \"us\"\n\t\t\t},\n\t\t\t\"Level 0_#2\": {\n"
378  "\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\"Measurement1_#2\": {\n\t\t\t\t\t\"type\": \""
379  "Measurement\",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t1.000000\n\t\t\t\t\t],\n\t\t\t\t\t\""
380  "unit\": \"ms\"\n\t\t\t\t},\n\t\t\t\t\"Measurement2_#2\": {\n\t\t\t\t\t\"type\": \""
381  "Measurement\",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t2.000000\n\t\t\t\t\t],\n\t\t\t\t\t\""
382  "unit\": \"us\"\n\t\t\t\t},\n\t\t\t\t\"Level 1A_#3\": {\n\t\t\t\t\t\"type\": \"Event\",\n"
383  "\t\t\t\t\t\"Measurement1_#3\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n"
384  "\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t1.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"unit\""
385  ": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Measurement2_#3\": {\n\t\t\t\t\t\t\"type\": \""
386  "Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t2.000000\n\t\t\t\t\t\t],\n\t\t\t"
387  "\t\t\t\"unit\": \"us\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\""
388  "loaded_network_measurements_#4\": {\n\t\t\t\"type\": \"Event\",\n\t\t\t\""
389  "Measurement1_#4\": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t"
390  "\t1.000000\n\t\t\t\t],\n\t\t\t\t\"unit\": \"ms\"\n\t\t\t},\n\t\t\t\"Measurement2_#4\""
391  ": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t\t2.000000\n"
392  "\t\t\t\t],\n\t\t\t\t\"unit\": \"us\"\n\t\t\t},\n\t\t\t\"Level 0_#5\": {\n\t\t\t\t\""
393  "type\": \"Event\",\n\t\t\t\t\"Measurement1_#5\": {\n\t\t\t\t\t\"type\": \"Measurement\""
394  ",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t1.000000\n\t\t\t\t\t],\n\t\t\t\t\t\"unit\": \""
395  "ms\"\n\t\t\t\t},\n\t\t\t\t\"Measurement2_#5\": {\n\t\t\t\t\t\"type\": \"Measurement\""
396  ",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t2.000000\n\t\t\t\t\t],\n\t\t\t\t\t\"unit\": \"us\""
397  "\n\t\t\t\t},\n\t\t\t\t\"Level 1A_#6\": {\n\t\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\t\""
398  "Measurement1_#6\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\": [\n"
399  "\t\t\t\t\t\t\t1.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"unit\": \"ms\"\n\t\t\t\t\t},\n"
400  "\t\t\t\t\t\"Measurement2_#6\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\""
401  "raw\": [\n\t\t\t\t\t\t\t2.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"unit\": \"us\""
402  "\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"inference_measurements_#7\": {\n"
403  "\t\t\t\"type\": \"Event\",\n\t\t\t\"Measurement1_#7\": {\n\t\t\t\t\"type\": \""
404  "Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t\t1.000000\n\t\t\t\t],\n\t\t\t\t\"unit\": \""
405  "ms\"\n\t\t\t},\n\t\t\t\"Measurement2_#7\": {\n\t\t\t\t\"type\": \"Measurement\",\n"
406  "\t\t\t\t\"raw\": [\n\t\t\t\t\t2.000000\n\t\t\t\t],\n\t\t\t\t\"unit\": \"us\"\n\t\t\t},\n"
407  "\t\t\t\"Level 0_#8\": {\n\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\"Measurement1_#8\": {\n"
408  "\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t1.000000\n"
409  "\t\t\t\t\t],\n\t\t\t\t\t\"unit\": \"ms\"\n\t\t\t\t},\n\t\t\t\t\"Measurement2_#8\": {\n"
410  "\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t2.000000\n"
411  "\t\t\t\t\t],\n\t\t\t\t\t\"unit\": \"us\"\n\t\t\t\t},\n\t\t\t\t\"Level 1A_#9\": {\n"
412  "\t\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\t\"Measurement1_#9\": {\n\t\t\t\t\t\t\"type\""
413  ": \"Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t1.000000\n\t\t\t\t\t\t],\n"
414  "\t\t\t\t\t\t\"unit\": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Measurement2_#9\": {\n"
415  "\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t2.000000\n"
416  "\t\t\t\t\t\t],\n\t\t\t\t\t\t\"unit\": \"us\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\""
417  "Level 1B_#10\": {\n\t\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\t\"Measurement1_#10\""
418  ": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t"
419  "1.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"unit\": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\""
420  "Measurement2_#10\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\""
421  ": [\n\t\t\t\t\t\t\t2.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"unit\": \"us\"\n"
422  "\t\t\t\t\t},\n\t\t\t\t\t\"Level 2A_#11\": {\n\t\t\t\t\t\t\"type\": \"Event\",\n\t\t\t"
423  "\t\t\t\"Measurement1_#11\": {\n\t\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t"
424  "\t\"raw\": [\n\t\t\t\t\t\t\t\t1.000000\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"unit\": \""
425  "ms\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Measurement2_#11\": {\n\t\t\t\t\t\t\t\"type\": \""
426  "Measurement\",\n\t\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t\t2.000000\n\t\t\t\t\t\t\t],\n"
427  "\t\t\t\t\t\t\t\"unit\": \"us\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n"
428  "\t}\n}\n");
429 
430  CHECK(output == blessedOutput);
432 }
433 
434 }
TEST_SUITE("TestConstTensorLayerVisitor")
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:49
CPU Execution: Reference C++ kernels.
static ProfilerManager & GetInstance()
Definition: Profiling.cpp:572
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:33
Copyright (c) 2021 ARM Limited and Contributors.
void IgnoreUnused(Ts &&...)
#define ARMNN_SCOPED_PROFILING_EVENT(backendId, name)
Definition: Profiling.hpp:220
IProfiler * GetProfiler()
Definition: Profiling.cpp:584
size_t GetProfilerEventSequenceSize(armnn::IProfiler *profiler)
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1847
int NetworkId
Definition: IRuntime.hpp:27
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
void RegisterProfiler(IProfiler *profiler)
Definition: Profiling.cpp:579
CPU Execution: NEON: ArmCompute.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:241
#define ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(backendId, guid,...)
Definition: Profiling.hpp:217
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:476