From f17fcd5fd016480acd85ee05c5636338a16bed08 Mon Sep 17 00:00:00 2001 From: Jan Eilers Date: Mon, 26 Jul 2021 22:20:00 +0100 Subject: Different input data for every iteration of ExecuteNetwork * Allows to supply different input data for every execution of a model when using the 'iterations' option in ExecuteNetwork * Removes the option 'simultaneous-iterations' because it's functionallity is now covered by 'iterations' * Adds a deprecation warning message to notify users * Little refactor of warning messages Signed-off-by: Jan Eilers Change-Id: Ib3ab0d6533f6952bfee20d098a890b653c34cc12 --- tests/ExecuteNetwork/ExecuteNetwork.cpp | 113 +++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 15 deletions(-) (limited to 'tests/ExecuteNetwork/ExecuteNetwork.cpp') diff --git a/tests/ExecuteNetwork/ExecuteNetwork.cpp b/tests/ExecuteNetwork/ExecuteNetwork.cpp index a9b5a3c3f4..e757d2c992 100644 --- a/tests/ExecuteNetwork/ExecuteNetwork.cpp +++ b/tests/ExecuteNetwork/ExecuteNetwork.cpp @@ -356,15 +356,31 @@ int MainImpl(const ExecuteNetworkParams& params, model.GetInputQuantizationParams()) : armnn::EmptyOptional(); - for(unsigned int j = 0; j < params.m_SimultaneousIterations ; ++j) + if (params.m_InputTensorDataFilePaths.size() > numInputs) + { + ARMNN_LOG(info) << "Given network has " << numInputs << " input/s. One input-tensor-data file is required " + << "for each input. The user provided " + << params.m_InputTensorDataFilePaths.size() + << " input-tensor-data file/s which will be used to fill the input/s.\n"; + } + + for(unsigned int j = 0; j < params.m_Iterations ; ++j) { std::vector inputDataContainers; for(unsigned int i = 0; i < numInputs; ++i) { + // If there are less input files given than required for the execution of + // params.m_Iterations we simply start with the first input file again + size_t inputFileIndex = j * numInputs + i; + if (!params.m_InputTensorDataFilePaths.empty()) + { + inputFileIndex = inputFileIndex % params.m_InputTensorDataFilePaths.size(); + } + armnn::Optional dataFile = params.m_GenerateTensorData ? armnn::EmptyOptional() : armnn::MakeOptional( - params.m_InputTensorDataFilePaths[(j * numInputs) + i]); + params.m_InputTensorDataFilePaths.at(inputFileIndex)); unsigned int numElements = model.GetInputSize(i); if (params.m_InputTensorShapes.size() > i && params.m_InputTensorShapes[i]) @@ -388,7 +404,7 @@ int MainImpl(const ExecuteNetworkParams& params, const size_t numOutputs = inferenceModelParams.m_OutputBindings.size(); - for (unsigned int j = 0; j < params.m_SimultaneousIterations; ++j) + for (unsigned int j = 0; j < params.m_Iterations; ++j) { std::vector outputDataContainers; for (unsigned int i = 0; i < numOutputs; ++i) @@ -418,13 +434,30 @@ int MainImpl(const ExecuteNetworkParams& params, outputs.push_back(outputDataContainers); } + if (params.m_Iterations > 1) + { + std::stringstream msg; + msg << "Network will be executed " << params.m_Iterations; + if (params.m_Concurrent) + { + msg << " times in an asynchronous manner. "; + } + else + { + msg << " times successively. "; + } + msg << "The input-tensor-data files will be reused recursively if the user didn't provide enough to " + "cover each execution."; + ARMNN_LOG(info) << msg.str(); + } + // Synchronous execution if (!params.m_Concurrent) { for (size_t x = 0; x < params.m_Iterations; x++) { // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds) - auto inference_duration = model.Run(inputs[0], outputs[0]); + auto inference_duration = model.Run(inputs[x], outputs[x]); if (params.m_GenerateTensorData) { @@ -436,13 +469,29 @@ int MainImpl(const ExecuteNetworkParams& params, for (size_t i = 0; i < numOutputs; i++) { const armnn::TensorInfo& infoOut = infosOut[i].second; - auto outputTensorFile = params.m_OutputTensorFiles.empty() ? "" : params.m_OutputTensorFiles[i]; + + // We've made sure before that the number of output files either equals numOutputs, in which case + // we override those files when processing the results of each iteration (only the result of the + // last iteration will be stored), or there are enough + // output files for each output of each iteration. + size_t outputFileIndex = x * numOutputs + i; + if (!params.m_OutputTensorFiles.empty()) + { + outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size(); + ARMNN_LOG(info) << "Writing output " << i << " named: '" + << inferenceModelParams.m_OutputBindings[i] + << "' of iteration: " << x+1 << " to file: '" + << params.m_OutputTensorFiles[outputFileIndex] << "'"; + } + auto outputTensorFile = params.m_OutputTensorFiles.empty() + ? "" + : params.m_OutputTensorFiles[outputFileIndex]; TensorPrinter printer(inferenceModelParams.m_OutputBindings[i], infoOut, outputTensorFile, params.m_DequantizeOutput); - mapbox::util::apply_visitor(printer, outputs[0][i]); + mapbox::util::apply_visitor(printer, outputs[x][i]); } ARMNN_LOG(info) << "\nInference time: " << std::setprecision(2) @@ -481,7 +530,7 @@ int MainImpl(const ExecuteNetworkParams& params, // For the asynchronous execution, we are adding a pool of working memory handles (1 per thread) in the // LoadedNetwork with each scheduled inference having a specific priority - for (size_t i = 0; i < params.m_SimultaneousIterations; ++i) + for (size_t i = 0; i < params.m_Iterations; ++i) { std::shared_ptr cb = callbackManager.GetNewCallback(); inferenceOutputMap.insert({cb->GetInferenceId(), outputs[i]}); @@ -490,7 +539,7 @@ int MainImpl(const ExecuteNetworkParams& params, // Check the results unsigned int j = 0; - for (size_t iteration = 0; iteration < params.m_SimultaneousIterations; ++iteration) + for (size_t iteration = 0; iteration < params.m_Iterations; ++iteration) { auto cb = callbackManager.GetNotifiedCallback(); @@ -522,10 +571,24 @@ int MainImpl(const ExecuteNetworkParams& params, const auto& infosOut = model.GetOutputBindingInfos(); for (size_t i = 0; i < numOutputs; i++) { + // We've made sure before that the number of output files either equals numOutputs, in which + // case we override those files when processing the results of each iteration (only the result + // of the last iteration will be stored), or there are enough + // output files for each output of each iteration. + size_t outputFileIndex = iteration * numOutputs + i; + if (!params.m_OutputTensorFiles.empty()) + { + outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size(); + ARMNN_LOG(info) << "Writing output " << i << " named: '" + << inferenceModelParams.m_OutputBindings[i] + << "' of iteration: " << iteration+1 << " to file: '" + << params.m_OutputTensorFiles[outputFileIndex] << "'"; + } + const armnn::TensorInfo& infoOut = infosOut[i].second; auto outputTensorFile = params.m_OutputTensorFiles.empty() ? "" - : params.m_OutputTensorFiles[(j * numOutputs) + i]; + : params.m_OutputTensorFiles[outputFileIndex]; TensorPrinter printer(inferenceModelParams.m_OutputBindings[i], infoOut, @@ -575,12 +638,12 @@ int MainImpl(const ExecuteNetworkParams& params, ARMNN_LOG(info) << "Asynchronous Execution with std::launch:async... \n"; std::vector>>> inferenceResults; - inferenceResults.reserve(params.m_SimultaneousIterations); + inferenceResults.reserve(params.m_Iterations); // Create WorkingMemHandles for each inference std::vector> workingMemHandles; - workingMemHandles.reserve(params.m_SimultaneousIterations); - for (unsigned int i = 0; i < params.m_SimultaneousIterations; ++i) + workingMemHandles.reserve(params.m_Iterations); + for (unsigned int i = 0; i < params.m_Iterations; ++i) { workingMemHandles.push_back(model.CreateWorkingMemHandle()); } @@ -588,7 +651,7 @@ int MainImpl(const ExecuteNetworkParams& params, // Run each inference in its own thread // start a timer const auto start_time = armnn::GetTimeNow(); - for (unsigned int i = 0; i < params.m_SimultaneousIterations; ++i) + for (unsigned int i = 0; i < params.m_Iterations; ++i) { armnn::experimental::IWorkingMemHandle& workingMemHandleRef = *workingMemHandles[i].get(); @@ -616,10 +679,23 @@ int MainImpl(const ExecuteNetworkParams& params, const auto& infosOut = model.GetOutputBindingInfos(); for (size_t i = 0; i < numOutputs; i++) { + // We've made sure before that the number of output files either equals numOutputs, in which + // case we override those files when processing the results of each iteration (only the result + // of the last iteration will be stored), or there are enough + // output files for each output of each iteration. + size_t outputFileIndex = j * numOutputs + i; + if (!params.m_OutputTensorFiles.empty()) + { + outputFileIndex = outputFileIndex % params.m_OutputTensorFiles.size(); + ARMNN_LOG(info) << "Writing output " << i << " named: '" + << inferenceModelParams.m_OutputBindings[i] + << "' of iteration: " << j+1 << " to file: '" + << params.m_OutputTensorFiles[outputFileIndex] << "'"; + } const armnn::TensorInfo& infoOut = infosOut[i].second; auto outputTensorFile = params.m_OutputTensorFiles.empty() ? "" - : params.m_OutputTensorFiles[(j * numOutputs) + i]; + : params.m_OutputTensorFiles[outputFileIndex]; TensorPrinter printer(inferenceModelParams.m_OutputBindings[i], infoOut, @@ -683,7 +759,14 @@ int main(int argc, const char* argv[]) // Get ExecuteNetwork parameters and runtime options from command line - ProgramOptions ProgramOptions(argc, argv); + // This might throw an InvalidArgumentException if the user provided invalid inputs + ProgramOptions ProgramOptions; + try { + ProgramOptions.ParseOptions(argc, argv); + } catch (const std::exception &e){ + ARMNN_LOG(fatal) << e.what(); + return EXIT_FAILURE; + } // Create runtime std::shared_ptr runtime(armnn::IRuntime::Create(ProgramOptions.m_RuntimeOptions)); -- cgit v1.2.1