From c89829f1f47855227f9a842c979f3a43800ea826 Mon Sep 17 00:00:00 2001 From: James Ward Date: Mon, 12 Oct 2020 14:17:36 +0100 Subject: [PATCH] IVGCVSW-5280 Switch tests/InferenceTest and derived tests over to cxxopts * refactor AddCommandLineOptions() functions to allow checking of required options * add CxxoptsUtils.hpp file for convenience functions !referencetests:268500 Signed-off-by: James Ward Change-Id: Ica954b210b2981b7cd10995f0d75fcb2a2f7b443 --- tests/CxxoptsUtils.hpp | 27 +++++++++++++++ tests/DeepSpeechV1InferenceTest.hpp | 45 ++++++++++++------------- tests/InferenceModel.hpp | 58 +++++++++++++++++--------------- tests/InferenceTest.cpp | 66 ++++++++++++++++++------------------- tests/InferenceTest.hpp | 13 ++++---- tests/InferenceTest.inl | 29 ++++++++-------- tests/MobileNetSsdInferenceTest.hpp | 15 +++++---- tests/YoloInferenceTest.hpp | 21 ++++++------ 8 files changed, 153 insertions(+), 121 deletions(-) create mode 100644 tests/CxxoptsUtils.hpp diff --git a/tests/CxxoptsUtils.hpp b/tests/CxxoptsUtils.hpp new file mode 100644 index 0000000..518fc1b --- /dev/null +++ b/tests/CxxoptsUtils.hpp @@ -0,0 +1,27 @@ +// +// Copyright © 2020 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +/** + * Ensure all mandatory command-line parameters have been passed to cxxopts. + * @param result returned from the cxxopts parse(argc, argv) call + * @param required vector of strings listing the mandatory parameters to be input from the command-line + * @return boolean value - true if all required parameters satisfied, false otherwise + * */ +inline bool CheckRequiredOptions(const cxxopts::ParseResult& result, const std::vector& required) +{ + for(const std::string& str : required) + { + if(result.count(str) == 0) + { + std::cerr << "--" << str << " parameter is mandatory" << std::endl; + return false; + } + } + return true; +} diff --git a/tests/DeepSpeechV1InferenceTest.hpp b/tests/DeepSpeechV1InferenceTest.hpp index d859ba7..ac799cb 100644 --- a/tests/DeepSpeechV1InferenceTest.hpp +++ b/tests/DeepSpeechV1InferenceTest.hpp @@ -99,31 +99,28 @@ public: : m_ConstructModel(constructModel) {} - virtual void AddCommandLineOptions(boost::program_options::options_description& options) override + virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector& required) override { - namespace po = boost::program_options; - - options.add_options() - ("input-seq-dir,s", po::value(&m_InputSeqDir)->required(), - "Path to directory containing test data for m_InputSeq"); - options.add_options() - ("prev-state-h-dir,h", po::value(&m_PrevStateHDir)->required(), - "Path to directory containing test data for m_PrevStateH"); - options.add_options() - ("prev-state-c-dir,c", po::value(&m_PrevStateCDir)->required(), - "Path to directory containing test data for m_PrevStateC"); - options.add_options() - ("logits-dir,l", po::value(&m_LogitsDir)->required(), - "Path to directory containing test data for m_Logits"); - options.add_options() - ("new-state-h-dir,H", po::value(&m_NewStateHDir)->required(), - "Path to directory containing test data for m_NewStateH"); - options.add_options() - ("new-state-c-dir,C", po::value(&m_NewStateCDir)->required(), - "Path to directory containing test data for m_NewStateC"); - - - Model::AddCommandLineOptions(options, m_ModelCommandLineOptions); + options + .allow_unrecognised_options() + .add_options() + ("s,input-seq-dir", "Path to directory containing test data for m_InputSeq", + cxxopts::value(m_InputSeqDir)) + ("h,prev-state-h-dir", "Path to directory containing test data for m_PrevStateH", + cxxopts::value(m_PrevStateHDir)) + ("c,prev-state-c-dir", "Path to directory containing test data for m_PrevStateC", + cxxopts::value(m_PrevStateCDir)) + ("l,logits-dir", "Path to directory containing test data for m_Logits", + cxxopts::value(m_LogitsDir)) + ("H,new-state-h-dir", "Path to directory containing test data for m_NewStateH", + cxxopts::value(m_NewStateHDir)) + ("C,new-state-c-dir", "Path to directory containing test data for m_NewStateC", + cxxopts::value(m_NewStateCDir)); + + required.insert(required.end(), {"input-seq-dir", "prev-state-h-dir", "prev-state-c-dir", "logits-dir", + "new-state-h-dir", "new-state-c-dir"}); + + Model::AddCommandLineOptions(options, m_ModelCommandLineOptions, required); } virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override diff --git a/tests/InferenceModel.hpp b/tests/InferenceModel.hpp index fd8dede..dbc0419 100644 --- a/tests/InferenceModel.hpp +++ b/tests/InferenceModel.hpp @@ -29,7 +29,8 @@ #include "armnn/utility/StringUtils.hpp" #include #include -#include +#include +#include "CxxoptsUtils.hpp" #include #include @@ -347,37 +348,42 @@ public: } }; - static void AddCommandLineOptions(boost::program_options::options_description& desc, CommandLineOptions& options) + static void AddCommandLineOptions(cxxopts::Options& options, + CommandLineOptions& cLineOptions, std::vector& required) { - namespace po = boost::program_options; - const std::vector defaultComputes = { "CpuAcc", "CpuRef" }; const std::string backendsMessage = "Which device to run layers on by default. Possible choices: " + armnn::BackendRegistryInstance().GetBackendIdsAsString(); - desc.add_options() - ("model-dir,m", po::value(&options.m_ModelDir)->required(), - "Path to directory containing model files (.caffemodel/.prototxt/.tflite)") - ("compute,c", po::value>(&options.m_ComputeDevices)-> - default_value(defaultComputes, armnn::stringUtils::StringConcat(defaultComputes, ", "))-> - multitoken(), backendsMessage.c_str()) - ("dynamic-backends-path,b", po::value(&options.m_DynamicBackendsPath), - "Path where to load any available dynamic backend from. " - "If left empty (the default), dynamic backends will not be used.") - ("labels,l", po::value(&options.m_Labels), - "Text file containing one image filename - correct label pair per line, " - "used to test the accuracy of the network.") - ("visualize-optimized-model,v", - po::value(&options.m_VisualizePostOptimizationModel)->default_value(false), - "Produce a dot file useful for visualizing the graph post optimization." - "The file will have the same name as the model with the .dot extention.") - ("fp16-turbo-mode", po::value(&options.m_EnableFp16TurboMode)->default_value(false), - "If this option is enabled FP32 layers, weights and biases will be converted " - "to FP16 where the backend supports it.") - ("bf16-turbo-mode", po::value(&options.m_EnableBf16TurboMode)->default_value(false), - "If this option is enabled FP32 layers, weights and biases will be converted " - "to BF16 where the backend supports it."); + options + .allow_unrecognised_options() + .add_options() + ("m,model-dir", "Path to directory containing model files (.caffemodel/.prototxt/.tflite)", + cxxopts::value(cLineOptions.m_ModelDir)) + ("c,compute", backendsMessage.c_str(), + cxxopts::value>(cLineOptions.m_ComputeDevices)->default_value("CpuRef")) + ("b,dynamic-backends-path", + "Path where to load any available dynamic backend from. " + "If left empty (the default), dynamic backends will not be used.", + cxxopts::value(cLineOptions.m_DynamicBackendsPath)) + ("l,labels", + "Text file containing one image filename - correct label pair per line, " + "used to test the accuracy of the network.", cxxopts::value(cLineOptions.m_Labels)) + ("v,visualize-optimized-model", + "Produce a dot file useful for visualizing the graph post optimization." + "The file will have the same name as the model with the .dot extention.", + cxxopts::value(cLineOptions.m_VisualizePostOptimizationModel)->default_value("false")) + ("fp16-turbo-mode", + "If this option is enabled FP32 layers, weights and biases will be converted " + "to FP16 where the backend supports it.", + cxxopts::value(cLineOptions.m_EnableFp16TurboMode)->default_value("false")) + ("bf16-turbo-mode", + "If this option is enabled FP32 layers, weights and biases will be converted " + "to BF16 where the backend supports it.", + cxxopts::value(cLineOptions.m_EnableBf16TurboMode)->default_value("false")); + + required.emplace_back("model-dir"); } InferenceModel(const Params& params, diff --git a/tests/InferenceTest.cpp b/tests/InferenceTest.cpp index b3b38d1..3392f6e 100644 --- a/tests/InferenceTest.cpp +++ b/tests/InferenceTest.cpp @@ -8,7 +8,7 @@ #include #include "../src/armnn/Profiling.hpp" -#include +#include #include #include @@ -28,53 +28,51 @@ namespace test bool ParseCommandLine(int argc, char** argv, IInferenceTestCaseProvider& testCaseProvider, InferenceTestOptions& outParams) { - namespace po = boost::program_options; - - po::options_description desc("Options"); + cxxopts::Options options("InferenceTest", "Inference iteration parameters"); try { // Adds generic options needed for all inference tests. - desc.add_options() - ("help", "Display help messages") - ("iterations,i", po::value(&outParams.m_IterationCount)->default_value(0), - "Sets the number number of inferences to perform. If unset, a default number will be ran.") - ("inference-times-file", po::value(&outParams.m_InferenceTimesFile)->default_value(""), - "If non-empty, each individual inference time will be recorded and output to this file") - ("event-based-profiling,e", po::value(&outParams.m_EnableProfiling)->default_value(0), - "Enables built in profiler. If unset, defaults to off."); + options + .allow_unrecognised_options() + .add_options() + ("h,help", "Display help messages") + ("i,iterations", "Sets the number of inferences to perform. If unset, will only be run once.", + cxxopts::value(outParams.m_IterationCount)->default_value("0")) + ("inference-times-file", + "If non-empty, each individual inference time will be recorded and output to this file", + cxxopts::value(outParams.m_InferenceTimesFile)->default_value("")) + ("e,event-based-profiling", "Enables built in profiler. If unset, defaults to off.", + cxxopts::value(outParams.m_EnableProfiling)->default_value("0")); + + std::vector required; //to be passed as reference to derived inference tests // Adds options specific to the ITestCaseProvider. - testCaseProvider.AddCommandLineOptions(desc); - } - catch (const std::exception& e) - { - // Coverity points out that default_value(...) can throw a bad_lexical_cast, - // and that desc.add_options() can throw boost::io::too_few_args. - // They really won't in any of these cases. - ARMNN_ASSERT_MSG(false, "Caught unexpected exception"); - std::cerr << "Fatal internal error: " << e.what() << std::endl; - return false; - } - - po::variables_map vm; + testCaseProvider.AddCommandLineOptions(options, required); - try - { - po::store(po::parse_command_line(argc, argv, desc), vm); + auto result = options.parse(argc, argv); - if (vm.count("help")) + if (result.count("help")) { - std::cout << desc << std::endl; + std::cout << options.help() << std::endl; return false; } - po::notify(vm); + CheckRequiredOptions(result, required); + + } + catch (const cxxopts::OptionException& e) + { + std::cerr << e.what() << std::endl << options.help() << std::endl; + return false; } - catch (po::error& e) + catch (const std::exception& e) { - std::cerr << e.what() << std::endl << std::endl; - std::cerr << desc << std::endl; + // Coverity points out that default_value(...) can throw a bad_lexical_cast, + // and that desc.add_options() can throw boost::io::too_few_args. + // They really won't in any of these cases. + ARMNN_ASSERT_MSG(false, "Caught unexpected exception"); + std::cerr << "Fatal internal error: " << e.what() << std::endl; return false; } diff --git a/tests/InferenceTest.hpp b/tests/InferenceTest.hpp index 1dafd01..5ec744c 100644 --- a/tests/InferenceTest.hpp +++ b/tests/InferenceTest.hpp @@ -11,7 +11,8 @@ #include #include -#include +#include +#include namespace armnn @@ -25,7 +26,7 @@ inline std::istream& operator>>(std::istream& in, armnn::Compute& compute) if (compute == armnn::Compute::Undefined) { in.setstate(std::ios_base::failbit); - throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value); + throw cxxopts::OptionException(fmt::format("Unrecognised compute device: {}", token)); } return in; } @@ -38,7 +39,7 @@ inline std::istream& operator>>(std::istream& in, armnn::BackendId& backend) if (compute == armnn::Compute::Undefined) { in.setstate(std::ios_base::failbit); - throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value); + throw cxxopts::OptionException(fmt::format("Unrecognised compute device: {}", token)); } backend = compute; return in; @@ -92,9 +93,9 @@ class IInferenceTestCaseProvider public: virtual ~IInferenceTestCaseProvider() {} - virtual void AddCommandLineOptions(boost::program_options::options_description& options) + virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector& required) { - IgnoreUnused(options); + IgnoreUnused(options, required); }; virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) { @@ -180,7 +181,7 @@ public: template ClassifierTestCaseProvider(TConstructDatabaseCallable constructDatabase, TConstructModelCallable constructModel); - virtual void AddCommandLineOptions(boost::program_options::options_description& options) override; + virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector& required) override; virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override; virtual std::unique_ptr GetTestCase(unsigned int testCaseId) override; virtual bool OnInferenceTestFinished() override; diff --git a/tests/InferenceTest.inl b/tests/InferenceTest.inl index e8401f6..3d6dae3 100644 --- a/tests/InferenceTest.inl +++ b/tests/InferenceTest.inl @@ -6,8 +6,9 @@ #include #include +#include "CxxoptsUtils.hpp" -#include +#include #include #include @@ -181,19 +182,21 @@ ClassifierTestCaseProvider::ClassifierTestCaseProvide template void ClassifierTestCaseProvider::AddCommandLineOptions( - boost::program_options::options_description& options) + cxxopts::Options& options, std::vector& required) { - namespace po = boost::program_options; - - options.add_options() - ("validation-file-in", po::value(&m_ValidationFileIn)->default_value(""), - "Reads expected predictions from the given file and confirms they match the actual predictions.") - ("validation-file-out", po::value(&m_ValidationFileOut)->default_value(""), - "Predictions are saved to the given file for later use via --validation-file-in.") - ("data-dir,d", po::value(&m_DataDir)->required(), - "Path to directory containing test data"); - - InferenceModel::AddCommandLineOptions(options, m_ModelCommandLineOptions); + options + .allow_unrecognised_options() + .add_options() + ("validation-file-in", + "Reads expected predictions from the given file and confirms they match the actual predictions.", + cxxopts::value(m_ValidationFileIn)->default_value("")) + ("validation-file-out", "Predictions are saved to the given file for later use via --validation-file-in.", + cxxopts::value(m_ValidationFileOut)->default_value("")) + ("d,data-dir", "Path to directory containing test data", cxxopts::value(m_DataDir)); + + required.emplace_back("data-dir"); //add to required arguments to check + + InferenceModel::AddCommandLineOptions(options, m_ModelCommandLineOptions, required); } template diff --git a/tests/MobileNetSsdInferenceTest.hpp b/tests/MobileNetSsdInferenceTest.hpp index 2a5d47d..7377640 100644 --- a/tests/MobileNetSsdInferenceTest.hpp +++ b/tests/MobileNetSsdInferenceTest.hpp @@ -152,18 +152,19 @@ public: : m_ConstructModel(constructModel) {} - virtual void AddCommandLineOptions(boost::program_options::options_description& options) override + virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector& required) override { - namespace po = boost::program_options; + options + .allow_unrecognised_options() + .add_options() + ("d,data-dir", "Path to directory containing test data", cxxopts::value(m_DataDir)); - options.add_options() - ("data-dir,d", po::value(&m_DataDir)->required(), - "Path to directory containing test data"); + required.emplace_back("data-dir"); - Model::AddCommandLineOptions(options, m_ModelCommandLineOptions); + Model::AddCommandLineOptions(options, m_ModelCommandLineOptions, required); } - virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override + virtual bool ProcessCommandLineOptions(const InferenceTestOptions& commonOptions) override { if (!ValidateDirectory(m_DataDir)) { diff --git a/tests/YoloInferenceTest.hpp b/tests/YoloInferenceTest.hpp index 81ba0f5..4f391a8 100644 --- a/tests/YoloInferenceTest.hpp +++ b/tests/YoloInferenceTest.hpp @@ -10,13 +10,13 @@ #include #include +#include +#include + #include #include #include -#include -#include - constexpr size_t YoloOutputSize = 1470; template @@ -187,18 +187,17 @@ public: { } - virtual void AddCommandLineOptions(boost::program_options::options_description& options) override + virtual void AddCommandLineOptions(cxxopts::Options& options, std::vector& required) override { - namespace po = boost::program_options; - - options.add_options() - ("data-dir,d", po::value(&m_DataDir)->required(), - "Path to directory containing test data"); + options + .allow_unrecognised_options() + .add_options() + ("d,data-dir", "Path to directory containing test data", cxxopts::value(m_DataDir)); - Model::AddCommandLineOptions(options, m_ModelCommandLineOptions); + Model::AddCommandLineOptions(options, m_ModelCommandLineOptions, required); } - virtual bool ProcessCommandLineOptions(const InferenceTestOptions &commonOptions) override + virtual bool ProcessCommandLineOptions(const InferenceTestOptions& commonOptions) override { if (!ValidateDirectory(m_DataDir)) { -- 2.7.4