// // Copyright © 2019 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #include #include #include "ProfilingTestUtils.hpp" #include "PrintPacketHeaderHandler.hpp" #include #include "TestTimelinePacketHandler.hpp" #include #include #include #include #include using namespace armnn::profiling; using namespace armnn; using namespace std::chrono_literals; class FileOnlyHelperService : public ProfilingService { public: // Wait for a notification from the send thread bool WaitForPacketsSent(uint32_t timeout = 1000) { return ProfilingService::WaitForPacketSent(m_ProfilingService, timeout); } armnn::profiling::ProfilingService m_ProfilingService; }; TEST_SUITE("FileOnlyProfilingDecoratorTests") { TEST_CASE("TestFileOnlyProfiling") { // Get all registered backends std::vector suitableBackends = GetSuitableBackendRegistered(); // Run test for each backend separately for (auto const& backend : suitableBackends) { // Enable m_FileOnly but also provide ILocalPacketHandler which should consume the packets. // This won't dump anything to file. armnn::IRuntime::CreationOptions creationOptions; creationOptions.m_ProfilingOptions.m_EnableProfiling = true; creationOptions.m_ProfilingOptions.m_FileOnly = true; creationOptions.m_ProfilingOptions.m_CapturePeriod = 100; creationOptions.m_ProfilingOptions.m_TimelineEnabled = true; ILocalPacketHandlerSharedPtr localPacketHandlerPtr = std::make_shared(); creationOptions.m_ProfilingOptions.m_LocalPacketHandlers.push_back(localPacketHandlerPtr); armnn::RuntimeImpl runtime(creationOptions); // ensure the GUID generator is reset to zero GetProfilingService(&runtime).ResetGuidGenerator(); // Load a simple network // build up the structure of the network INetworkPtr net(INetwork::Create()); IConnectableLayer* input = net->AddInputLayer(0, "input"); ElementwiseUnaryDescriptor descriptor(UnaryOperation::Rsqrt); IConnectableLayer* Rsqrt = net->AddElementwiseUnaryLayer(descriptor, "Rsqrt"); IConnectableLayer* output = net->AddOutputLayer(0, "output"); input->GetOutputSlot(0).Connect(Rsqrt->GetInputSlot(0)); Rsqrt->GetOutputSlot(0).Connect(output->GetInputSlot(0)); input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32)); Rsqrt->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32)); std::vector backendsVec {backend}; IOptimizedNetworkPtr optNet = Optimize(*net, backendsVec, runtime.GetDeviceSpec()); // Load it into the runtime. It should succeed. armnn::NetworkId netId; CHECK(runtime.LoadNetwork(netId, std::move(optNet)) == Status::Success); // Creates structures for input & output. std::vector inputData(16); std::vector outputData(16); for (unsigned int i = 0; i < 16; ++i) { inputData[i] = 9.0; outputData[i] = 3.0; } InputTensors inputTensors { {0, ConstTensor(runtime.GetInputTensorInfo(netId, 0), inputData.data())} }; OutputTensors outputTensors { {0, Tensor(runtime.GetOutputTensorInfo(netId, 0), outputData.data())} }; // Does the inference. runtime.EnqueueWorkload(netId, inputTensors, outputTensors); static_cast(localPacketHandlerPtr.get())->WaitOnInferenceCompletion(3000); const TimelineModel &model = static_cast(localPacketHandlerPtr.get())->GetTimelineModel(); for (auto &error : model.GetErrors()) { std::cout << error.what() << std::endl; } CHECK(model.GetErrors().empty()); std::vector desc = GetModelDescription(model); std::vector expectedOutput; expectedOutput.push_back("Entity [0] name = input type = layer"); expectedOutput.push_back(" connection [17] from entity [0] to entity [1]"); expectedOutput.push_back(" child: Entity [26] backendId = " + backend.Get() + " type = workload"); expectedOutput.push_back("Entity [1] name = Rsqrt type = layer"); expectedOutput.push_back(" connection [25] from entity [1] to entity [2]"); expectedOutput.push_back(" child: Entity [18] backendId = " + backend.Get() + " type = workload"); expectedOutput.push_back("Entity [2] name = output type = layer"); expectedOutput.push_back(" child: Entity [30] backendId = " + backend.Get() + " type = workload"); expectedOutput.push_back("Entity [6] processId = [processId] type = network"); expectedOutput.push_back(" child: Entity [0] name = input type = layer"); expectedOutput.push_back(" child: Entity [1] name = Rsqrt type = layer"); expectedOutput.push_back(" child: Entity [2] name = output type = layer"); expectedOutput.push_back(" execution: Entity [34] type = inference"); expectedOutput.push_back(" event: [8] class [start_of_life]"); expectedOutput.push_back("Entity [18] backendId = " + backend.Get() + " type = workload"); expectedOutput.push_back(" execution: Entity [47] type = workload_execution"); expectedOutput.push_back("Entity [26] backendId = " + backend.Get() + " type = workload"); expectedOutput.push_back(" execution: Entity [39] type = workload_execution"); expectedOutput.push_back("Entity [30] backendId = " + backend.Get() + " type = workload"); expectedOutput.push_back(" execution: Entity [55] type = workload_execution"); expectedOutput.push_back("Entity [34] type = inference"); expectedOutput.push_back(" child: Entity [39] type = workload_execution"); expectedOutput.push_back(" child: Entity [47] type = workload_execution"); expectedOutput.push_back(" child: Entity [55] type = workload_execution"); expectedOutput.push_back(" event: [37] class [start_of_life]"); expectedOutput.push_back(" event: [63] class [end_of_life]"); expectedOutput.push_back("Entity [39] type = workload_execution"); expectedOutput.push_back(" event: [43] class [start_of_life]"); expectedOutput.push_back(" event: [45] class [end_of_life]"); expectedOutput.push_back("Entity [47] type = workload_execution"); expectedOutput.push_back(" event: [51] class [start_of_life]"); expectedOutput.push_back(" event: [53] class [end_of_life]"); expectedOutput.push_back("Entity [55] type = workload_execution"); expectedOutput.push_back(" event: [59] class [start_of_life]"); expectedOutput.push_back(" event: [61] class [end_of_life]"); CHECK(CompareOutput(desc, expectedOutput)); } } TEST_CASE("DumpOutgoingValidFileEndToEnd") { // Get all registered backends std::vector suitableBackends = GetSuitableBackendRegistered(); // Run test for each backend separately for (auto const& backend : suitableBackends) { // Create a temporary file name. fs::path tempPath = armnnUtils::Filesystem::NamedTempFile("DumpOutgoingValidFileEndToEnd_CaptureFile.txt"); // Make sure the file does not exist at this point CHECK(!fs::exists(tempPath)); armnn::IRuntime::CreationOptions options; options.m_ProfilingOptions.m_EnableProfiling = true; options.m_ProfilingOptions.m_FileOnly = true; options.m_ProfilingOptions.m_IncomingCaptureFile = ""; options.m_ProfilingOptions.m_OutgoingCaptureFile = tempPath.string(); options.m_ProfilingOptions.m_CapturePeriod = 100; options.m_ProfilingOptions.m_TimelineEnabled = true; ILocalPacketHandlerSharedPtr localPacketHandlerPtr = std::make_shared(); options.m_ProfilingOptions.m_LocalPacketHandlers.push_back(localPacketHandlerPtr); armnn::RuntimeImpl runtime(options); // ensure the GUID generator is reset to zero GetProfilingService(&runtime).ResetGuidGenerator(); // Load a simple network // build up the structure of the network INetworkPtr net(INetwork::Create()); IConnectableLayer* input = net->AddInputLayer(0, "input"); ElementwiseUnaryDescriptor descriptor(UnaryOperation::Rsqrt); IConnectableLayer* Rsqrt = net->AddElementwiseUnaryLayer(descriptor, "Rsqrt"); IConnectableLayer* output = net->AddOutputLayer(0, "output"); input->GetOutputSlot(0).Connect(Rsqrt->GetInputSlot(0)); Rsqrt->GetOutputSlot(0).Connect(output->GetInputSlot(0)); input->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32)); Rsqrt->GetOutputSlot(0).SetTensorInfo(TensorInfo({ 1, 1, 4, 4 }, DataType::Float32)); std::vector backendsVec{backend}; IOptimizedNetworkPtr optNet = Optimize(*net, backendsVec, runtime.GetDeviceSpec()); // Load it into the runtime. It should succeed. armnn::NetworkId netId; CHECK(runtime.LoadNetwork(netId, std::move(optNet)) == Status::Success); // Creates structures for input & output. std::vector inputData(16); std::vector outputData(16); for (unsigned int i = 0; i < 16; ++i) { inputData[i] = 9.0; outputData[i] = 3.0; } InputTensors inputTensors { {0, ConstTensor(runtime.GetInputTensorInfo(netId, 0), inputData.data())} }; OutputTensors outputTensors { {0, Tensor(runtime.GetOutputTensorInfo(netId, 0), outputData.data())} }; // Does the inference. runtime.EnqueueWorkload(netId, inputTensors, outputTensors); static_cast(localPacketHandlerPtr.get())->WaitOnInferenceCompletion(3000); // In order to flush the files we need to gracefully close the profiling service. options.m_ProfilingOptions.m_EnableProfiling = false; GetProfilingService(&runtime).ResetExternalProfilingOptions(options.m_ProfilingOptions, true); // The output file size should be greater than 0. CHECK(fs::file_size(tempPath) > 0); // NOTE: would be an interesting exercise to take this file and decode it // Delete the tmp file. CHECK(fs::remove(tempPath)); } } }