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