2 // Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
6 #include <Filesystem.hpp>
7 #include <LabelsAndEventClasses.hpp>
8 #include <ProfilingService.hpp>
9 #include "ProfilingTestUtils.hpp"
10 #include "PrintPacketHeaderHandler.hpp"
11 #include <Runtime.hpp>
12 #include "TestTimelinePacketHandler.hpp"
14 #include <boost/test/unit_test.hpp>
20 using namespace armnn::profiling;
21 using namespace armnn;
23 using namespace std::chrono_literals;
25 class FileOnlyHelperService : public ProfilingService
28 // Wait for a notification from the send thread
29 bool WaitForPacketsSent(uint32_t timeout = 1000)
31 return ProfilingService::WaitForPacketSent(m_ProfilingService, timeout);
33 armnn::profiling::ProfilingService m_ProfilingService;
36 BOOST_AUTO_TEST_SUITE(FileOnlyProfilingDecoratorTests)
38 BOOST_AUTO_TEST_CASE(TestFileOnlyProfiling)
40 // Get all registered backends
41 std::vector<BackendId> suitableBackends = GetSuitableBackendRegistered();
43 // Run test for each backend separately
44 for (auto const& backend : suitableBackends)
46 // Enable m_FileOnly but also provide ILocalPacketHandler which should consume the packets.
47 // This won't dump anything to file.
48 armnn::Runtime::CreationOptions creationOptions;
49 creationOptions.m_ProfilingOptions.m_EnableProfiling = true;
50 creationOptions.m_ProfilingOptions.m_FileOnly = true;
51 creationOptions.m_ProfilingOptions.m_CapturePeriod = 100;
52 creationOptions.m_ProfilingOptions.m_TimelineEnabled = true;
53 ILocalPacketHandlerSharedPtr localPacketHandlerPtr = std::make_shared<TestTimelinePacketHandler>();
54 creationOptions.m_ProfilingOptions.m_LocalPacketHandlers.push_back(localPacketHandlerPtr);
56 armnn::Runtime runtime(creationOptions);
57 // ensure the GUID generator is reset to zero
58 GetProfilingService(&runtime).ResetGuidGenerator();
60 // Load a simple network
61 // build up the structure of the network
62 INetworkPtr net(INetwork::Create());
64 IConnectableLayer* input = net->AddInputLayer(0, "input");
66 ElementwiseUnaryDescriptor descriptor(UnaryOperation::Rsqrt);
67 IConnectableLayer* Rsqrt = net->AddElementwiseUnaryLayer(descriptor, "Rsqrt");
69 IConnectableLayer* output = net->AddOutputLayer(0, "output");
71 input->GetOutputSlot(0).Connect(Rsqrt->GetInputSlot(0));
72 Rsqrt->GetOutputSlot(0).Connect(output->GetInputSlot(0));
74 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
75 Rsqrt->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
77 std::vector<armnn::BackendId> backendsVec {backend};
78 IOptimizedNetworkPtr optNet = Optimize(*net, backendsVec, runtime.GetDeviceSpec());
80 // Load it into the runtime. It should succeed.
81 armnn::NetworkId netId;
82 BOOST_TEST(runtime.LoadNetwork(netId, std::move(optNet)) == Status::Success);
84 // Creates structures for input & output.
85 std::vector<float> inputData(16);
86 std::vector<float> outputData(16);
87 for (unsigned int i = 0; i < 16; ++i) {
92 InputTensors inputTensors
94 {0, ConstTensor(runtime.GetInputTensorInfo(netId, 0), inputData.data())}
96 OutputTensors outputTensors
98 {0, Tensor(runtime.GetOutputTensorInfo(netId, 0), outputData.data())}
101 // Does the inference.
102 runtime.EnqueueWorkload(netId, inputTensors, outputTensors);
104 static_cast<TestTimelinePacketHandler *>(localPacketHandlerPtr.get())->WaitOnInferenceCompletion(3000);
106 const TimelineModel &model =
107 static_cast<TestTimelinePacketHandler *>(localPacketHandlerPtr.get())->GetTimelineModel();
109 for (auto &error : model.GetErrors()) {
110 std::cout << error.what() << std::endl;
112 BOOST_TEST(model.GetErrors().empty());
113 std::vector<std::string> desc = GetModelDescription(model);
114 std::vector<std::string> expectedOutput;
115 expectedOutput.push_back("Entity [0] name = input type = layer");
116 expectedOutput.push_back(" connection [17] from entity [0] to entity [1]");
117 expectedOutput.push_back(" child: Entity [26] backendId = " + backend.Get() + " type = workload");
118 expectedOutput.push_back("Entity [1] name = Rsqrt type = layer");
119 expectedOutput.push_back(" connection [25] from entity [1] to entity [2]");
120 expectedOutput.push_back(" child: Entity [18] backendId = " + backend.Get() + " type = workload");
121 expectedOutput.push_back("Entity [2] name = output type = layer");
122 expectedOutput.push_back(" child: Entity [30] backendId = " + backend.Get() + " type = workload");
123 expectedOutput.push_back("Entity [6] processId = [processId] type = network");
124 expectedOutput.push_back(" child: Entity [0] name = input type = layer");
125 expectedOutput.push_back(" child: Entity [1] name = Rsqrt type = layer");
126 expectedOutput.push_back(" child: Entity [2] name = output type = layer");
127 expectedOutput.push_back(" execution: Entity [34] type = inference");
128 expectedOutput.push_back(" event: [8] class [start_of_life]");
129 expectedOutput.push_back("Entity [18] backendId = " + backend.Get() + " type = workload");
130 expectedOutput.push_back(" execution: Entity [47] type = workload_execution");
131 expectedOutput.push_back("Entity [26] backendId = " + backend.Get() + " type = workload");
132 expectedOutput.push_back(" execution: Entity [39] type = workload_execution");
133 expectedOutput.push_back("Entity [30] backendId = " + backend.Get() + " type = workload");
134 expectedOutput.push_back(" execution: Entity [55] type = workload_execution");
135 expectedOutput.push_back("Entity [34] type = inference");
136 expectedOutput.push_back(" child: Entity [39] type = workload_execution");
137 expectedOutput.push_back(" child: Entity [47] type = workload_execution");
138 expectedOutput.push_back(" child: Entity [55] type = workload_execution");
139 expectedOutput.push_back(" event: [37] class [start_of_life]");
140 expectedOutput.push_back(" event: [63] class [end_of_life]");
141 expectedOutput.push_back("Entity [39] type = workload_execution");
142 expectedOutput.push_back(" event: [43] class [start_of_life]");
143 expectedOutput.push_back(" event: [45] class [end_of_life]");
144 expectedOutput.push_back("Entity [47] type = workload_execution");
145 expectedOutput.push_back(" event: [51] class [start_of_life]");
146 expectedOutput.push_back(" event: [53] class [end_of_life]");
147 expectedOutput.push_back("Entity [55] type = workload_execution");
148 expectedOutput.push_back(" event: [59] class [start_of_life]");
149 expectedOutput.push_back(" event: [61] class [end_of_life]");
150 BOOST_TEST(CompareOutput(desc, expectedOutput));
154 BOOST_AUTO_TEST_CASE(DumpOutgoingValidFileEndToEnd)
156 // Get all registered backends
157 std::vector<BackendId> suitableBackends = GetSuitableBackendRegistered();
159 // Run test for each backend separately
160 for (auto const& backend : suitableBackends)
162 // Create a temporary file name.
163 fs::path tempPath = armnnUtils::Filesystem::NamedTempFile("DumpOutgoingValidFileEndToEnd_CaptureFile.txt");
164 // Make sure the file does not exist at this point
165 BOOST_CHECK(!fs::exists(tempPath));
167 armnn::Runtime::CreationOptions options;
168 options.m_ProfilingOptions.m_EnableProfiling = true;
169 options.m_ProfilingOptions.m_FileOnly = true;
170 options.m_ProfilingOptions.m_IncomingCaptureFile = "";
171 options.m_ProfilingOptions.m_OutgoingCaptureFile = tempPath.string();
172 options.m_ProfilingOptions.m_CapturePeriod = 100;
173 options.m_ProfilingOptions.m_TimelineEnabled = true;
175 ILocalPacketHandlerSharedPtr localPacketHandlerPtr = std::make_shared<TestTimelinePacketHandler>();
176 options.m_ProfilingOptions.m_LocalPacketHandlers.push_back(localPacketHandlerPtr);
178 armnn::Runtime runtime(options);
179 // ensure the GUID generator is reset to zero
180 GetProfilingService(&runtime).ResetGuidGenerator();
182 // Load a simple network
183 // build up the structure of the network
184 INetworkPtr net(INetwork::Create());
186 IConnectableLayer* input = net->AddInputLayer(0, "input");
188 ElementwiseUnaryDescriptor descriptor(UnaryOperation::Rsqrt);
189 IConnectableLayer* Rsqrt = net->AddElementwiseUnaryLayer(descriptor, "Rsqrt");
191 IConnectableLayer* output = net->AddOutputLayer(0, "output");
193 input->GetOutputSlot(0).Connect(Rsqrt->GetInputSlot(0));
194 Rsqrt->GetOutputSlot(0).Connect(output->GetInputSlot(0));
196 input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
197 Rsqrt->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32));
200 std::vector<BackendId> backendsVec{backend};
201 IOptimizedNetworkPtr optNet = Optimize(*net, backendsVec, runtime.GetDeviceSpec());
203 // Load it into the runtime. It should succeed.
204 armnn::NetworkId netId;
205 BOOST_TEST(runtime.LoadNetwork(netId, std::move(optNet)) == Status::Success);
207 // Creates structures for input & output.
208 std::vector<float> inputData(16);
209 std::vector<float> outputData(16);
210 for (unsigned int i = 0; i < 16; ++i) {
215 InputTensors inputTensors
217 {0, ConstTensor(runtime.GetInputTensorInfo(netId, 0), inputData.data())}
219 OutputTensors outputTensors
221 {0, Tensor(runtime.GetOutputTensorInfo(netId, 0), outputData.data())}
224 // Does the inference.
225 runtime.EnqueueWorkload(netId, inputTensors, outputTensors);
227 static_cast<TestTimelinePacketHandler *>(localPacketHandlerPtr.get())->WaitOnInferenceCompletion(3000);
229 // In order to flush the files we need to gracefully close the profiling service.
230 options.m_ProfilingOptions.m_EnableProfiling = false;
231 GetProfilingService(&runtime).ResetExternalProfilingOptions(options.m_ProfilingOptions, true);
233 // The output file size should be greater than 0.
234 BOOST_CHECK(fs::file_size(tempPath) > 0);
236 // NOTE: would be an interesting exercise to take this file and decode it
238 // Delete the tmp file.
239 BOOST_CHECK(fs::remove(tempPath));
243 BOOST_AUTO_TEST_SUITE_END()