xref: /aosp_15_r20/external/armnn/src/armnn/test/ProfilerTests.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
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