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