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