ArmNN
 21.02
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 #include <armnn/TypesUtils.hpp>
9 
10 #include <boost/test/unit_test.hpp>
11 #include <boost/test/tools/output_test_stream.hpp>
12 
13 #include <memory>
14 #include <thread>
15 #include <ostream>
16 
17 #include <Profiling.hpp>
18 
19 namespace armnn
20 {
21 
23 {
24  if (!profiler)
25  {
26  return static_cast<size_t>(-1);
27  }
28 
29  return profiler->pProfilerImpl->m_EventSequence.size();
30 }
31 } // namespace armnn
32 
33 namespace
34 {
35 
36 void RegisterUnregisterProfilerSingleThreadImpl(bool &res)
37 {
38  // Important! Don't use BOOST_TEST macros in this function as they
39  // seem to have problems when used in threads
40 
41  // Get a reference to the profiler manager.
43 
44  // Check that there's no profiler registered for this thread.
45  res = !profilerManager.GetProfiler();
46 
47  // Create and register a profiler for this thread.
48  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
49  profilerManager.RegisterProfiler(profiler.get());
50 
51  // Check that on a single thread we get the same profiler we registered.
52  res &= profiler.get() == profilerManager.GetProfiler();
53 
54  // Destroy the profiler.
55  profiler.reset();
56 
57  // Check that the profiler has been un-registered for this thread.
58  res &= !profilerManager.GetProfiler();
59 
61 }
62 
63 } // namespace
64 
65 BOOST_AUTO_TEST_SUITE(Profiler)
66 
67 BOOST_AUTO_TEST_CASE(EnableDisableProfiling)
68 {
69  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
70 
71  // Check that profiling is disabled by default.
72  BOOST_TEST(!profiler->IsProfilingEnabled());
73 
74  // Enable profiling.
75  profiler->EnableProfiling(true);
76 
77  // Check that profiling is enabled.
78  BOOST_TEST(profiler->IsProfilingEnabled());
79 
80  // Disable profiling.
81  profiler->EnableProfiling(false);
82 
83  // Check that profiling is disabled.
84  BOOST_TEST(!profiler->IsProfilingEnabled());
85 }
86 
87 BOOST_AUTO_TEST_CASE(RegisterUnregisterProfilerSingleThread)
88 {
89  bool res = false;
90  RegisterUnregisterProfilerSingleThreadImpl(res);
91  BOOST_TEST(res);
92 }
93 
94 BOOST_AUTO_TEST_CASE(RegisterUnregisterProfilerMultipleThreads)
95 {
96  bool res[3] = {false, false, false};
97  std::thread thread1([&res]() { RegisterUnregisterProfilerSingleThreadImpl(res[0]); });
98  std::thread thread2([&res]() { RegisterUnregisterProfilerSingleThreadImpl(res[1]); });
99  std::thread thread3([&res]() { RegisterUnregisterProfilerSingleThreadImpl(res[2]); });
100 
101  thread1.join();
102  thread2.join();
103  thread3.join();
104 
105  for (int i = 0 ; i < 3 ; i++)
106  {
107  BOOST_TEST(res[i]);
108  }
109 }
110 
111 BOOST_AUTO_TEST_CASE(ProfilingMacros)
112 {
113  // Get a reference to the profiler manager.
115 
116  { // --- No profiler ---
117 
118  // Check that there's no profiler registered for this thread.
119  BOOST_TEST(!profilerManager.GetProfiler());
120 
121  // Test scoped event.
123 
124  // Check that we still cannot get a profiler for this thread.
125  BOOST_TEST(!profilerManager.GetProfiler());
126  }
127 
128  // Create and register a profiler for this thread.
129  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
130  profilerManager.RegisterProfiler(profiler.get());
131 
132  { // --- Profiler, but profiling disabled ---
133 
134  // Get current event sequence size.
135  size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
136 
137  // Test scoped macro.
139 
140  // Check that no profiling event has been added to the sequence.
141  size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
142  BOOST_TEST(eventSequenceSizeBefore == eventSequenceSizeAfter);
143  }
144 
145  // Enable profiling.
146  profiler->EnableProfiling(true);
147 
148  { // --- Profiler, and profiling enabled ---
149 
150  // Get current event sequence size.
151  size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
152 
153  // Test scoped macro.
155 
156  // Check that a profiling event has been added to the sequence.
157  size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
158  BOOST_TEST(eventSequenceSizeAfter == eventSequenceSizeBefore + 1);
159  }
160 
161  // Disable profiling here to not print out anything on stdout.
162  profiler->EnableProfiling(false);
163 }
164 
165 #if defined(ARMNNREF_ENABLED)
166 
167 // This test unit needs the reference backend, it's not available if the reference backend is not built
168 
169 BOOST_AUTO_TEST_CASE(RuntimeLoadNetwork)
170 {
171  // Get a reference to the profiler manager.
173 
174  // Check that there's no profiler registered for this thread.
175  BOOST_TEST(!profilerManager.GetProfiler());
176 
177  // Build a mock-network and load it into the runtime.
180  armnn::NetworkId networkIdentifier = 1;
182  mockNetwork->AddInputLayer(0, "test layer");
183  std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
184  runtime->LoadNetwork(networkIdentifier, armnn::Optimize(*mockNetwork, backends, runtime->GetDeviceSpec()));
185 
186  // Check that now there's a profiler registered for this thread (created and registered by the loading the network).
187  BOOST_TEST(profilerManager.GetProfiler());
188 
189  // Unload the network.
190  runtime->UnloadNetwork(networkIdentifier);
191 
192  // Check that the profiler has been un-registered for this thread.
193  BOOST_TEST(!profilerManager.GetProfiler());
194 }
195 
196 #endif
197 
198 BOOST_AUTO_TEST_CASE(WriteEventResults)
199 {
200  // Get a reference to the profiler manager.
202 
203  // Create and register a profiler for this thread.
204  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
205  profileManager.RegisterProfiler(profiler.get());
206 
207  // Enable profiling.
208  profiler->EnableProfiling(true);
209 
210  { // --- Profiler, and profiling enabled ---
211 
212  // Get current event sequence size.
213  size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
214 
215  // Test scoped macro.
216  {
217  // Need to directly create a ScopedProfilingEvent as the one created by the macro falls out of scope
218  // immediately causing the Event.Stop() function method to be called immediately after the Event.Start()
219  // function resulting in periodic test failures on the Dent and Smith HiKeys
221  std::this_thread::sleep_for(std::chrono::milliseconds(10));
222  }
223 
224  // Check that a profiling event has been added to the sequence.
225  size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
226  BOOST_TEST(eventSequenceSizeAfter == eventSequenceSizeBefore + 1);
227 
228  boost::test_tools::output_test_stream output;
229  profiler->AnalyzeEventsAndWriteResults(output);
230  BOOST_TEST(!output.is_empty(false));
231 
232  // output should contain event name 'test'
233  BOOST_CHECK(output.str().find("test") != std::string::npos);
234 
235  // output should contain headers
236  BOOST_CHECK(output.str().find("Event Sequence - Name") != std::string::npos);
237  BOOST_CHECK(output.str().find("Event Stats - Name") != std::string::npos);
238  BOOST_CHECK(output.str().find("Total") != std::string::npos);
239  BOOST_CHECK(output.str().find("Device") != std::string::npos);
240  // output should contain compute device 'CpuAcc'
241  BOOST_CHECK(output.str().find("CpuAcc") != std::string::npos);
242  // output should not contain un-readable numbers
243  BOOST_CHECK(output.str().find("e+") == std::string::npos);
244  // output should not contain un-readable numbers
245  BOOST_CHECK(output.str().find("+") == std::string::npos);
246  // output should not contain zero value
247  BOOST_CHECK(output.str().find(" 0 ") == std::string::npos);
248  }
249 
250  // Disable profiling here to not print out anything on stdout.
251  profiler->EnableProfiling(false);
252 }
253 
254 BOOST_AUTO_TEST_CASE(ProfilerJsonPrinter)
255 {
256  class TestInstrument : public armnn::Instrument
257  {
258  public:
259  virtual ~TestInstrument() {}
260  void Start() override {}
261  void Stop() override {}
262 
263  std::vector<armnn::Measurement> GetMeasurements() const override
264  {
265  std::vector<armnn::Measurement> measurements;
266  measurements.emplace_back(armnn::Measurement("Measurement1",
267  1.0,
268  armnn::Measurement::Unit::TIME_MS));
269  measurements.emplace_back(armnn::Measurement("Measurement2",
270  2.0,
271  armnn::Measurement::Unit::TIME_US));
272  return measurements;
273  }
274 
275  const char* GetName() const override
276  {
277  return "TestInstrument";
278  }
279  };
280 
281  // Get a reference to the profiler manager.
283 
284  // Create and register a profiler for this thread.
285  std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
286  profilerManager.RegisterProfiler(profiler.get());
287 
288  profiler->EnableProfiling(true);
289 
290  {
291  // Test scoped macro.
292  ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc, "EnqueueWorkload", TestInstrument())
294  {
295  {
297  }
298 
299  {
301 
302  {
304  }
305  }
306  }
307  }
308 
309  std::stringbuf buffer;
310  std::ostream json(&buffer);
311  profiler->Print(json);
312 
313  std::string output = buffer.str();
314  armnn::IgnoreUnused(output);
315 
316  // Disable profiling here to not print out anything on stdout.
317  profiler->EnableProfiling(false);
318 
319  // blessed output validated by a human eyeballing the output to make sure it's ok and then copying it here.
320  // validation also included running the blessed output through an online json validation site
321  std::string blessedOutput("{\n\t\"ArmNN\": {\n\t\t\"inference_measurements_#1\": {\n\t\t\t\"type\": \""
322  "Event\",\n\t\t\t\"Measurement1_#1\": {\n\t\t\t\t\"type\": \""
323  "Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t\t1.000000\n\t\t\t\t],\n\t\t\t\t\""
324  "unit\": \"ms\"\n\t\t\t},\n\t\t\t\"Measurement2_#1\": {\n\t\t\t\t\"type\": \""
325  "Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t\t2.000000\n\t\t\t\t],\n\t\t\t\t\""
326  "unit\": \"us\"\n\t\t\t},\n\t\t\t\"Level 0_#2\": {\n\t\t\t\t\"type\": \""
327  "Event\",\n\t\t\t\t\"Measurement1_#2\": {\n\t\t\t\t\t\"type\": \""
328  "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\""
329  "unit\": \"ms\"\n\t\t\t\t},\n\t\t\t\t\"Measurement2_#2\": {\n\t\t\t\t\t\"type\": \""
330  "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\""
331  "unit\": \"us\"\n\t\t\t\t},\n\t\t\t\t\"Level 1A_#3\": {\n\t\t\t\t\t\"type\": \""
332  "Event\",\n\t\t\t\t\t\"Measurement1_#3\": {\n\t\t\t\t\t\t\"type\": \""
333  "Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t"
334  "1.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\""
335  "unit\": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Measurement2_#3\": {\n\t\t\t\t\t\t\"type\": \""
336  "Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t"
337  "2.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\""
338  "unit\": \"us\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"Level 1B_#4\": {\n\t\t\t\t\t\""
339  "type\": \"Event\",\n\t\t\t\t\t\"Measurement1_#4\": {\n\t\t\t\t\t\t\"type\": \""
340  "Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t"
341  "1.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\""
342  "unit\": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Measurement2_#4\": {\n\t\t\t\t\t\t\""
343  "type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t"
344  "2.000000\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\""
345  "unit\": \"us\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Level 2A_#5\": {\n\t\t\t\t\t\t\""
346  "type\": \"Event\",\n\t\t\t\t\t\t\"Measurement1_#5\": {\n\t\t\t\t\t\t\t\"type\": \""
347  "Measurement\",\n\t\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t\t"
348  "1.000000\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\""
349  "unit\": \"ms\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"Measurement2_#5\": {\n\t\t\t\t\t\t\t\""
350  "type\": \"Measurement\",\n\t\t\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t\t\t"
351  "2.000000\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\""
352  "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\t}\n}\n");
353 
354  BOOST_CHECK(output == blessedOutput);
356 }
357 
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:37
CPU Execution: Reference C++ kernels.
static ProfilerManager & GetInstance()
Definition: Profiling.cpp:489
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:26
int NetworkId
Definition: IRuntime.hpp:20
Copyright (c) 2021 ARM Limited and Contributors.
void IgnoreUnused(Ts &&...)
#define ARMNN_SCOPED_PROFILING_EVENT(backendId, name)
Definition: Profiling.hpp:173
IProfiler * GetProfiler()
Definition: Profiling.cpp:501
size_t GetProfilerEventSequenceSize(armnn::IProfiler *profiler)
#define ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(backendId,...)
Definition: Profiling.hpp:170
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:1502
BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer)
BOOST_AUTO_TEST_SUITE_END()
void RegisterProfiler(IProfiler *profiler)
Definition: Profiling.cpp:496
CPU Execution: NEON: ArmCompute.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:173
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:510