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 #include <armnnUtils/TensorUtils.hpp>
16
17 namespace armnn
18 {
19
GetProfilerEventSequenceSize(armnn::IProfiler * profiler)20 size_t GetProfilerEventSequenceSize(armnn::IProfiler* profiler)
21 {
22 if (!profiler)
23 {
24 return static_cast<size_t>(-1);
25 }
26
27 return profiler->pProfilerImpl->m_EventSequence.size();
28 }
29 } // namespace armnn
30
31 namespace
32 {
33
RegisterUnregisterProfilerSingleThreadImpl(bool & res)34 void RegisterUnregisterProfilerSingleThreadImpl(bool &res)
35 {
36 // Important! Don't use CHECK macros in this function as they
37 // seem to have problems when used in threads
38
39 // Get a reference to the profiler manager.
40 armnn::ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
41
42 // Check that there's no profiler registered for this thread.
43 res = !profilerManager.GetProfiler();
44
45 // Create and register a profiler for this thread.
46 std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
47 profilerManager.RegisterProfiler(profiler.get());
48
49 // Check that on a single thread we get the same profiler we registered.
50 res &= profiler.get() == profilerManager.GetProfiler();
51
52 // Destroy the profiler.
53 profiler.reset();
54
55 // Check that the profiler has been un-registered for this thread.
56 res &= !profilerManager.GetProfiler();
57
58 armnn::ProfilerManager::GetInstance().RegisterProfiler(nullptr);
59 }
60
61 } // namespace
62
63 TEST_SUITE("Profiler")
64 {
65 TEST_CASE("EnableDisableProfiling")
66 {
67 std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
68
69 // Check that profiling is disabled by default.
70 CHECK(!profiler->IsProfilingEnabled());
71
72 // Enable profiling.
73 profiler->EnableProfiling(true);
74
75 // Check that profiling is enabled.
76 CHECK(profiler->IsProfilingEnabled());
77
78 // Disable profiling.
79 profiler->EnableProfiling(false);
80
81 // Check that profiling is disabled.
82 CHECK(!profiler->IsProfilingEnabled());
83 }
84
85 TEST_CASE("RegisterUnregisterProfilerSingleThread")
86 {
87 bool res = false;
88 RegisterUnregisterProfilerSingleThreadImpl(res);
89 CHECK(res);
90 }
91
92 TEST_CASE("RegisterUnregisterProfilerMultipleThreads")
93 {
94 bool res[3] = {false, false, false};
95 std::vector<std::thread> threads;
96 for (unsigned int i = 0; i < 3; ++i)
97 {
__anon6dac1cea0202() 98 threads.push_back(std::thread([&res, i]() { RegisterUnregisterProfilerSingleThreadImpl(res[i]); }));
99 }
100 std::for_each(threads.begin(), threads.end(), [](std::thread& theThread)
__anon6dac1cea0302(std::thread& theThread) 101 {
102 theThread.join();
103 });
104
105 for (int i = 0 ; i < 3 ; ++i)
106 {
107 CHECK(res[i]);
108 }
109 }
110
111 TEST_CASE("LayerWorkloadConstructorWithEmptyWorkloadInfo")
112 {
113 // Calling AddDetailsToString with a descriptor that contains no tensors or parameters results
114 // in an invalid piece of JSON as the function assumes there will be something after the input
115 // and output tensors are printed and leaves a hanging ','
116 // This test validates that the fix for that continues to work.
117 armnn::ProfilingDetails classOnTest;
118 armnn::ArgMinMaxDescriptor descriptor;
119 armnn::WorkloadInfo workloadInfo;
120 arm::pipe::ProfilingGuid guid;
121 classOnTest.AddDetailsToString("NeonArgMinMaxWorkload_Construct", descriptor, workloadInfo, guid);
122 std::string result = classOnTest.GetProfilingDetails();
123 // Make sure the string ends with a "GUID": "0"\n}"
124 REQUIRE(result.find("GUID\": \"0\"\n}") != std::string::npos);
125 }
126
127 TEST_CASE("LayerWorkloadConstructorWithWorkloadInfoOnlyInputTensor")
128 {
129 // Calling AddDetailsToString with a descriptor that contains a single inout tensor and no output
130 // tensor or parameters results in an invalid piece of JSON as the function assumes there will be
131 // something after the input and output tensors are printed and leaves a hanging ','
132 // This test validates that the fix for that continues to work.
133
134 armnn::ProfilingDetails classOnTest;
135 armnn::ArgMinMaxDescriptor descriptor;
136 armnn::WorkloadInfo workloadInfo;
137 arm::pipe::ProfilingGuid guid;
138 armnn::TensorInfo inputTensorInfo =
139 armnnUtils::GetTensorInfo(1, 1, 1, 1, armnn::DataLayout::NCHW, armnn::DataType::Float32);
140 workloadInfo.m_InputTensorInfos.push_back(inputTensorInfo);
141
142 classOnTest.AddDetailsToString("NeonArgMinMaxWorkload_Construct", descriptor, workloadInfo, guid);
143 std::string result = classOnTest.GetProfilingDetails();
144 // Make sure the string ends with a "Num Dims\": \"4\"\n\t}\n}"
145 REQUIRE(result.find("Num Dims\": \"4\"\n\t}\n}") != std::string::npos);
146 }
147
148 TEST_CASE("LayerWorkloadConstructorWithWorkloadInfoInputAndOutputTensorNoParameters")
149 {
150 // Calling AddDetailsToString with a descriptor that contains no tensors or parameters results
151 // in an invalid piece of JSON as the function assumes there will be something after the input
152 // and output tensors are printed and leaves a hanging ','
153 // This test validates that the fix for that continues to work.
154 armnn::ProfilingDetails classOnTest;
155 armnn::ArgMinMaxDescriptor descriptor;
156 armnn::WorkloadInfo workloadInfo;
157 arm::pipe::ProfilingGuid guid;
158 armnn::TensorInfo inputTensorInfo =
159 armnnUtils::GetTensorInfo(1, 1, 1, 1, armnn::DataLayout::NCHW, armnn::DataType::Float32);
160 workloadInfo.m_InputTensorInfos.push_back(inputTensorInfo);
161
162 // We'll make the output tensrinfo have 5 dimensions to make errors easier to detect.
163 armnn::TensorInfo outputTensorInfo =
164 armnnUtils::GetTensorInfo(1, 1, 1, 1, 1, armnn::DataLayout::NCDHW, armnn::DataType::Float32);
165 workloadInfo.m_OutputTensorInfos.push_back(outputTensorInfo);
166
167 classOnTest.AddDetailsToString("NeonArgMinMaxWorkload_Construct", descriptor, workloadInfo, guid);
168 std::string result = classOnTest.GetProfilingDetails();
169 // Make sure the string ends with a "Num Dims\": \"4\"\n\t}\n}"
170 REQUIRE(result.find("Num Dims\": \"5\"\n\t}\n}") != std::string::npos);
171 }
172
173 TEST_CASE("LayerWorkloadConstructorWithWorkloadInfoMultipleInputAndMultipleOutputTensorNoParameters")
174 {
175 // Calling AddDetailsToString with a descriptor that contains multiple input and output tensors. This is
176 // specifically looking at the usage of "addSeparator" parameter in PrintInfos.
177 armnn::ProfilingDetails classOnTest;
178 armnn::ArgMinMaxDescriptor descriptor;
179 armnn::WorkloadInfo workloadInfo;
180 arm::pipe::ProfilingGuid guid;
181 armnn::TensorInfo inputTensorInfo =
182 armnnUtils::GetTensorInfo(1, 1, 1, 1, armnn::DataLayout::NCHW, armnn::DataType::Float32);
183 // Add two inputs.
184 workloadInfo.m_InputTensorInfos.push_back(inputTensorInfo);
185 workloadInfo.m_InputTensorInfos.push_back(inputTensorInfo);
186
187 // We'll make the output tensrinfo have 5 dimensions to make errors easier to detect.
188 armnn::TensorInfo outputTensorInfo =
189 armnnUtils::GetTensorInfo(1, 1, 1, 1, 1, armnn::DataLayout::NCDHW, armnn::DataType::Float32);
190 // and two outputs.
191 workloadInfo.m_OutputTensorInfos.push_back(outputTensorInfo);
192 workloadInfo.m_OutputTensorInfos.push_back(outputTensorInfo);
193
194 classOnTest.AddDetailsToString("NeonArgMinMaxWorkload_Construct", descriptor, workloadInfo, guid);
195 std::string result = classOnTest.GetProfilingDetails();
196 // Look for the piece in the middle between Input 0 and Input 1:
197 REQUIRE(result.find("Dims\": \"4\"\n\t},\n\t\"Input 1\": {\n\t\t\"Shape\": \"[1,1,1,1]\"") != std::string::npos);
198 // Look for the piece in the middle between Output 0and Output 1:
199 REQUIRE(result.find("Dims\": \"5\"\n\t},\n\t\"Output 1\": {\n\t\t\"Shape\": \"[1,1,1,1,1]\"") != std::string::npos);
200 }
201
202 TEST_CASE("ProfilingMacros")
203 {
204 // Get a reference to the profiler manager.
205 armnn::ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
206
207 { // --- No profiler ---
208
209 // Check that there's no profiler registered for this thread.
210 CHECK(!profilerManager.GetProfiler());
211
212 // Test scoped event.
213 { ARMNN_SCOPED_PROFILING_EVENT(armnn::Compute::CpuAcc, "test"); }
214
215 // Check that we still cannot get a profiler for this thread.
216 CHECK(!profilerManager.GetProfiler());
217 }
218
219 // Create and register a profiler for this thread.
220 std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
221 profilerManager.RegisterProfiler(profiler.get());
222
223 { // --- Profiler, but profiling disabled ---
224
225 // Get current event sequence size.
226 size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
227
228 // Test scoped macro.
229 { ARMNN_SCOPED_PROFILING_EVENT(armnn::Compute::CpuAcc, "test"); }
230
231 // Check that no profiling event has been added to the sequence.
232 size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
233 CHECK(eventSequenceSizeBefore == eventSequenceSizeAfter);
234 }
235
236 // Enable profiling.
237 profiler->EnableProfiling(true);
238
239 { // --- Profiler, and profiling enabled ---
240
241 // Get current event sequence size.
242 size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
243
244 // Test scoped macro.
245 { ARMNN_SCOPED_PROFILING_EVENT(armnn::Compute::CpuAcc, "test"); }
246
247 // Check that a profiling event has been added to the sequence.
248 size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
249 CHECK(eventSequenceSizeAfter == eventSequenceSizeBefore + 1);
250 }
251
252 // Disable profiling here to not print out anything on stdout.
253 profiler->EnableProfiling(false);
254 }
255
256 #if defined(ARMNNREF_ENABLED)
257
258 // This test unit needs the reference backend, it's not available if the reference backend is not built
259
260 TEST_CASE("RuntimeLoadNetwork")
261 {
262 // Get a reference to the profiler manager.
263 armnn::ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
264
265 // Check that there's no profiler registered for this thread.
266 CHECK(!profilerManager.GetProfiler());
267
268 // Build a mock-network and load it into the runtime.
269 armnn::IRuntime::CreationOptions options;
270 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
271 armnn::NetworkId networkIdentifier = 1;
272 armnn::INetworkPtr mockNetwork(armnn::INetwork::Create());
273 mockNetwork->AddInputLayer(0, "test layer");
274 std::vector<armnn::BackendId> backends = { armnn::Compute::CpuRef };
275 runtime->LoadNetwork(networkIdentifier, armnn::Optimize(*mockNetwork, backends, runtime->GetDeviceSpec()));
276
277 // Check that now there's a profiler registered for this thread (created and registered by the loading the network).
278 CHECK(profilerManager.GetProfiler());
279
280 // Unload the network.
281 runtime->UnloadNetwork(networkIdentifier);
282
283 // Check that the profiler has been un-registered for this thread.
284 CHECK(!profilerManager.GetProfiler());
285 }
286
287 #endif
288
289 TEST_CASE("WriteEventResults")
290 {
291 // Get a reference to the profiler manager.
292 armnn::ProfilerManager& profileManager = armnn::ProfilerManager::GetInstance();
293
294 // Create and register a profiler for this thread.
295 std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
296 profileManager.RegisterProfiler(profiler.get());
297
298 // Enable profiling.
299 profiler->EnableProfiling(true);
300
301 { // --- Profiler, and profiling enabled ---
302
303 // Get current event sequence size.
304 size_t eventSequenceSizeBefore = armnn::GetProfilerEventSequenceSize(profiler.get());
305
306 // Test scoped macro.
307 {
308 // Need to directly create a ScopedProfilingEvent as the one created by the macro falls out of scope
309 // immediately causing the Event.Stop() function method to be called immediately after the Event.Start()
310 // function resulting in periodic test failures on the Dent and Smith HiKeys
311 armnn::ScopedProfilingEvent testEvent(armnn::Compute::CpuAcc,
312 armnn::EmptyOptional(),
313 "test",
314 armnn::WallClockTimer());
315 std::this_thread::sleep_for(std::chrono::milliseconds(10));
316 }
317
318 // Check that a profiling event has been added to the sequence.
319 size_t eventSequenceSizeAfter = armnn::GetProfilerEventSequenceSize(profiler.get());
320 CHECK(eventSequenceSizeAfter == eventSequenceSizeBefore + 1);
321
322 std::ostringstream output;
323 profiler->AnalyzeEventsAndWriteResults(output);
324 CHECK(!output.str().empty());
325
326 // output should contain event name 'test'
327 CHECK(output.str().find("test") != std::string::npos);
328
329 // output should contain headers
330 CHECK(output.str().find("Event Sequence - Name") != std::string::npos);
331 CHECK(output.str().find("Event Stats - Name") != std::string::npos);
332 CHECK(output.str().find("Total") != std::string::npos);
333 CHECK(output.str().find("Device") != std::string::npos);
334 // output should contain compute device 'CpuAcc'
335 CHECK(output.str().find("CpuAcc") != std::string::npos);
336 // output should not contain un-readable numbers
337 CHECK(output.str().find("e+") == std::string::npos);
338 // output should not contain un-readable numbers
339 CHECK(output.str().find("+") == std::string::npos);
340 // output should not contain zero value
341 CHECK(output.str().find(" 0 ") == std::string::npos);
342 }
343
344 // Disable profiling here to not print out anything on stdout.
345 profiler->EnableProfiling(false);
346 }
347
348 TEST_CASE("ProfilerJsonPrinter")
349 {
350 class TestInstrument : public armnn::Instrument
351 {
352 public:
~TestInstrument()353 virtual ~TestInstrument() {}
Start()354 void Start() override {}
Stop()355 void Stop() override {}
356
GetMeasurements() const357 std::vector<armnn::Measurement> GetMeasurements() const override
358 {
359 std::vector<armnn::Measurement> measurements;
360 measurements.emplace_back(armnn::Measurement("Measurement1",
361 1.0,
362 armnn::Measurement::Unit::TIME_MS));
363 measurements.emplace_back(armnn::Measurement("Measurement2",
364 2.0,
365 armnn::Measurement::Unit::TIME_US));
366 return measurements;
367 }
368
GetName() const369 const char* GetName() const override
370 {
371 return "TestInstrument";
372 }
373 };
374
375 // Get a reference to the profiler manager.
376 armnn::ProfilerManager& profilerManager = armnn::ProfilerManager::GetInstance();
377
378 // Create and register a profiler for this thread.
379 std::unique_ptr<armnn::IProfiler> profiler = std::make_unique<armnn::IProfiler>();
380 profilerManager.RegisterProfiler(profiler.get());
381
382 profiler->EnableProfiling(true);
383
384 {
385 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
386 armnn::EmptyOptional(),
387 "Optimizer",
388 TestInstrument())
389 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
390 armnn::EmptyOptional(),
391 "Level 0",
392 TestInstrument())
393 {
394 {
395 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
396 armnn::EmptyOptional(),
397 "Level 1A",
398 TestInstrument())
399 }
400 }
401 }
402 {
403 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
404 armnn::EmptyOptional(),
405 "LoadedNetwork",
406 TestInstrument())
407 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
408 armnn::EmptyOptional(),
409 "Level 0",
410 TestInstrument())
411 {
412 {
413 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
414 armnn::EmptyOptional(),
415 "Level 1A",
416 TestInstrument())
417 }
418 }
419 }
420 {
421 // Test scoped macro.
422 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
423 armnn::EmptyOptional(),
424 "EnqueueWorkload",
425 TestInstrument())
426 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
427 armnn::EmptyOptional(),
428 "Level 0",
429 TestInstrument())
430 {
431 {
432 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
433 armnn::EmptyOptional(),
434 "Level 1A",
435 TestInstrument())
436 }
437 {
438 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
439 armnn::EmptyOptional(),
440 "Level 1B",
441 TestInstrument())
442
443 {
444 ARMNN_SCOPED_PROFILING_EVENT_WITH_INSTRUMENTS(armnn::Compute::CpuAcc,
445 armnn::EmptyOptional(),
446 "Level 2A",
447 TestInstrument())
448 }
449 }
450 }
451 }
452
453 std::stringbuf buffer;
454 std::ostream json(&buffer);
455 profiler->Print(json);
456
457 std::string output = buffer.str();
458 armnn::IgnoreUnused(output);
459
460 // Disable profiling here to not print out anything on stdout.
461 profiler->EnableProfiling(false);
462
463 // blessed output validated by a human eyeballing the output to make sure it's ok and then copying it here.
464 // validation also included running the blessed output through an online json validation site
465 std::string blessedOutput("{\n\t\"ArmNN\": {\n\t\t\"optimize_measurements_#1\": {\n\t\t\t\"type\": \"Event\""
466 ",\n\t\t\t\"Measurement1_#1\": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\""
467 ": [\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\""
468 "Measurement2_#1\": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t"
469 "\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"
470 "\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\"Measurement1_#2\": {\n\t\t\t\t\t\"type\": \""
471 "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\""
472 "unit\": \"ms\"\n\t\t\t\t},\n\t\t\t\t\"Measurement2_#2\": {\n\t\t\t\t\t\"type\": \""
473 "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\""
474 "unit\": \"us\"\n\t\t\t\t},\n\t\t\t\t\"Level 1A_#3\": {\n\t\t\t\t\t\"type\": \"Event\",\n"
475 "\t\t\t\t\t\"Measurement1_#3\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n"
476 "\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\""
477 ": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Measurement2_#3\": {\n\t\t\t\t\t\t\"type\": \""
478 "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"
479 "\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\""
480 "loaded_network_measurements_#4\": {\n\t\t\t\"type\": \"Event\",\n\t\t\t\""
481 "Measurement1_#4\": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t"
482 "\t1.000000\n\t\t\t\t],\n\t\t\t\t\"unit\": \"ms\"\n\t\t\t},\n\t\t\t\"Measurement2_#4\""
483 ": {\n\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\"raw\": [\n\t\t\t\t\t2.000000\n"
484 "\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\""
485 "type\": \"Event\",\n\t\t\t\t\"Measurement1_#5\": {\n\t\t\t\t\t\"type\": \"Measurement\""
486 ",\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\": \""
487 "ms\"\n\t\t\t\t},\n\t\t\t\t\"Measurement2_#5\": {\n\t\t\t\t\t\"type\": \"Measurement\""
488 ",\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\""
489 "\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\""
490 "Measurement1_#6\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\": [\n"
491 "\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"
492 "\t\t\t\t\t\"Measurement2_#6\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\""
493 "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\""
494 "\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"
495 "\t\t\t\"type\": \"Event\",\n\t\t\t\"Measurement1_#7\": {\n\t\t\t\t\"type\": \""
496 "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\": \""
497 "ms\"\n\t\t\t},\n\t\t\t\"Measurement2_#7\": {\n\t\t\t\t\"type\": \"Measurement\",\n"
498 "\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"
499 "\t\t\t\"Level 0_#8\": {\n\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\"Measurement1_#8\": {\n"
500 "\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t1.000000\n"
501 "\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"
502 "\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\"raw\": [\n\t\t\t\t\t\t2.000000\n"
503 "\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"
504 "\t\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\t\"Measurement1_#9\": {\n\t\t\t\t\t\t\"type\""
505 ": \"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"
506 "\t\t\t\t\t\t\"unit\": \"ms\"\n\t\t\t\t\t},\n\t\t\t\t\t\"Measurement2_#9\": {\n"
507 "\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"
508 "\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\""
509 "Level 1B_#10\": {\n\t\t\t\t\t\"type\": \"Event\",\n\t\t\t\t\t\"Measurement1_#10\""
510 ": {\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"
511 "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\""
512 "Measurement2_#10\": {\n\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t\"raw\""
513 ": [\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"
514 "\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"
515 "\t\t\t\"Measurement1_#11\": {\n\t\t\t\t\t\t\t\"type\": \"Measurement\",\n\t\t\t\t\t\t"
516 "\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\": \""
517 "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\": \""
518 "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"
519 "\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"
520 "\t}\n}\n");
521
522 CHECK(output == blessedOutput);
523 armnn::ProfilerManager::GetInstance().RegisterProfiler(nullptr);
524 }
525
526 }
527