From f39f8d8597c59057118f67cacf70d246f95fea9b Mon Sep 17 00:00:00 2001 From: Jan Eilers Date: Tue, 26 Oct 2021 16:57:34 +0100 Subject: Move command line parsing in external delegate to DelegateOptions * Moves the creation of a DelegateOption object from armnn_external_delegate to DelegateOptions. * This allows this code to be reused elsewhere * Allow boolean values of DelegateOptions to be passed as strings e.g. 'true' or 'false' * Add unit tests Signed-off-by: Jan Eilers Change-Id: I0ada17f511027dd3f47a85142cae346464682f5a --- delegate/include/DelegateOptions.hpp | 135 +++++++++++++ delegate/src/DelegateOptions.cpp | 187 ++++++++++++++++++ delegate/src/armnn_external_delegate.cpp | 318 +----------------------------- delegate/src/test/DelegateOptionsTest.cpp | 84 ++++++++ include/armnn/utility/StringUtils.hpp | 44 +++++ src/armnn/test/UtilityTests.cpp | 29 ++- 6 files changed, 484 insertions(+), 313 deletions(-) diff --git a/delegate/include/DelegateOptions.hpp b/delegate/include/DelegateOptions.hpp index 24a2e5c597..5bc2e59070 100644 --- a/delegate/include/DelegateOptions.hpp +++ b/delegate/include/DelegateOptions.hpp @@ -37,6 +37,141 @@ public: const armnn::Optional& logSeverityLevel = armnn::EmptyOptional(), const armnn::Optional& func = armnn::EmptyOptional()); + + /** + * This constructor processes delegate options in form of command line arguments. + * It works in conjunction with the TfLite external delegate plugin. + * + * Available options: + * + * Option key: "backends" \n + * Possible values: ["EthosNPU"/"GpuAcc"/"CpuAcc"/"CpuRef"] \n + * Descriptions: A comma separated list without whitespaces of + * backends which should be used for execution. Falls + * back to next backend in list if previous doesn't + * provide support for operation. e.g. "GpuAcc,CpuAcc" + * + * Option key: "dynamic-backends-path" \n + * Possible values: [filenameString] \n + * Descriptions: This is the directory that will be searched for any dynamic backends. + * + * Option key: "logging-severity" \n + * Possible values: ["trace"/"debug"/"info"/"warning"/"error"/"fatal"] \n + * Description: Sets the logging severity level for ArmNN. Logging + * is turned off if this option is not provided. + * + * Option key: "gpu-tuning-level" \n + * Possible values: ["0"/"1"/"2"/"3"] \n + * Description: 0=UseOnly(default), 1=RapidTuning, 2=NormalTuning, + * 3=ExhaustiveTuning. Requires option gpu-tuning-file. + * 1,2 and 3 will create a tuning-file, 0 will apply the + * tunings from an existing file + * + * Option key: "gpu-mlgo-tuning-file" \n + * Possible values: [filenameString] \n + * Description: File name for the MLGO tuning file + * + * Option key: "gpu-tuning-file" \n + * Possible values: [filenameString] \n + * Description: File name for the tuning file. + * + * Option key: "gpu-enable-profiling" \n + * Possible values: ["true"/"false"] \n + * Description: Enables GPU profiling + * + * Option key: "gpu-kernel-profiling-enabled" \n + * Possible values: ["true"/"false"] \n + * Description: Enables GPU kernel profiling + * + * Option key: "save-cached-network" \n + * Possible values: ["true"/"false"] \n + * Description: Enables saving of the cached network to a file, + * specified with the cached-network-filepath option + * + * Option key: "cached-network-filepath" \n + * Possible values: [filenameString] \n + * Description: If non-empty, the given file will be used to load/save the cached network. + * If save-cached-network is given then the cached network will be saved to the given file. + * To save the cached network a file must already exist. + * If save-cached-network is not given then the cached network will be loaded from the given file. + * This will remove initial compilation time of kernels and speed up the first execution. + * + * Option key: "enable-fast-math" \n + * Possible values: ["true"/"false"] \n + * Description: Enables fast_math options in backends that support it + * + * Option key: "number-of-threads" \n + * Possible values: ["1"-"64"] \n + * Description: Assign the number of threads used by the CpuAcc backend. + * Default is set to 0 (Backend will decide number of threads to use). + * + * Option key: "reduce-fp32-to-fp16" \n + * Possible values: ["true"/"false"] \n + * Description: Reduce Fp32 data to Fp16 for faster processing + * + * Option key: "reduce-fp32-to-bf16" \n + * Possible values: ["true"/"false"] \n + * Description: Reduce Fp32 data to Bf16 for faster processing + * + * Option key: "debug-data" \n + * Possible values: ["true"/"false"] \n + * Description: Add debug data for easier troubleshooting + * + * Option key: "memory-import" \n + * Possible values: ["true"/"false"] \n + * Description: Enable memory import + * + * Option key: "enable-internal-profiling" \n + * Possible values: ["true"/"false"] \n + * Description: Enable the internal profiling feature. + * + * Option key: "internal-profiling-detail" \n + * Possible values: [1/2] \n + * Description: Set the detail on the internal profiling. 1 = DetailsWithEvents, 2 = DetailsOnly. + * + * Option key: "enable-external-profiling" \n + * Possible values: ["true"/"false"] \n + * Description: Enable the external profiling feature. + * + * Option key: "timeline-profiling" \n + * Possible values: ["true"/"false"] \n + * Description: Indicates whether external timeline profiling is enabled or not. + * + * Option key: "outgoing-capture-file" \n + * Possible values: [filenameString] \n + * Description: Path to a file in which outgoing timeline profiling messages will be stored. + * + * Option key: "incoming-capture-file" \n + * Possible values: [filenameString] \n + * Description: Path to a file in which incoming timeline profiling messages will be stored. + * + * Option key: "file-only-external-profiling" \n + * Possible values: ["true"/"false"] \n + * Description: Enable profiling output to file only. + * + * Option key: "counter-capture-period" \n + * Possible values: Integer, Default is 10000u + * Description: Value in microseconds of the profiling capture period. \n + * + * Option key: "profiling-file-format" \n + * Possible values: String of ["binary"] \n + * Description: The format of the file used for outputting profiling data. Currently on "binary" is supported. + * + * Option key: "serialize-to-dot" \n + * Possible values: [filenameString] \n + * Description: Serialize the optimized network to the file specified in "dot" format. + * + * @param[in] option_keys Delegate option names + * @param[in] options_values Delegate option values + * @param[in] num_options Number of delegate options + * @param[in,out] report_error Error callback function + * + */ + DelegateOptions(char const* const* options_keys, + char const* const* options_values, + size_t num_options, + void (*report_error)(const char*)); + const std::vector& GetBackends() const { return m_Backends; } void SetBackends(const std::vector& backends) { m_Backends = backends; } diff --git a/delegate/src/DelegateOptions.cpp b/delegate/src/DelegateOptions.cpp index c19697d6c6..d477d9839d 100644 --- a/delegate/src/DelegateOptions.cpp +++ b/delegate/src/DelegateOptions.cpp @@ -4,6 +4,8 @@ // #include +#include +#include namespace armnnDelegate { @@ -48,4 +50,189 @@ DelegateOptions::DelegateOptions(const std::vector& backends, { } +DelegateOptions::DelegateOptions(char const* const* options_keys, + char const* const* options_values, + size_t num_options, + void (*report_error)(const char*)) +{ + armnn::IRuntime::CreationOptions runtimeOptions; + armnn::OptimizerOptions optimizerOptions; + bool internalProfilingState = false; + armnn::ProfilingDetailsMethod internalProfilingDetail = armnn::ProfilingDetailsMethod::DetailsWithEvents; + armnn::IRuntime::CreationOptions::ExternalProfilingOptions extProfilingParams; + for (size_t i = 0; i < num_options; ++i) + { + // Process backends + if (std::string(options_keys[i]) == std::string("backends")) + { + // The backend option is a comma separated string of backendIDs that needs to be split + std::vector backends; + char* dup = strdup(options_values[i]); + char* pch = std::strtok(dup, ","); + while (pch != NULL) + { + backends.push_back(pch); + pch = strtok (NULL, ","); + } + this->SetBackends(backends); + } + // Process dynamic-backends-path + else if (std::string(options_keys[i]) == std::string("dynamic-backends-path")) + { + runtimeOptions.m_DynamicBackendsPath = std::string(options_values[i]); + } + // Process logging level + else if (std::string(options_keys[i]) == std::string("logging-severity")) + { + this->SetLoggingSeverity(options_values[i]); + } + // Process GPU backend options + else if (std::string(options_keys[i]) == std::string("gpu-tuning-level")) + { + armnn::BackendOptions option("GpuAcc", {{"TuningLevel", atoi(options_values[i])}}); + runtimeOptions.m_BackendOptions.push_back(option); + } + else if (std::string(options_keys[i]) == std::string("gpu-mlgo-tuning-file")) + { + armnn::BackendOptions option("GpuAcc", {{"MLGOTuningFilePath", std::string(options_values[i])}}); + optimizerOptions.m_ModelOptions.push_back(option); + } + else if (std::string(options_keys[i]) == std::string("gpu-tuning-file")) + { + armnn::BackendOptions option("GpuAcc", {{"TuningFile", std::string(options_values[i])}}); + runtimeOptions.m_BackendOptions.push_back(option); + } + else if (std::string(options_keys[i]) == std::string("gpu-enable-profiling")) + { + runtimeOptions.m_EnableGpuProfiling = (*options_values[i] != '0'); + } + else if (std::string(options_keys[i]) == std::string("gpu-kernel-profiling-enabled")) + { + armnn::BackendOptions option("GpuAcc", {{"KernelProfilingEnabled", + armnn::stringUtils::StringToBool(options_values[i])}}); + runtimeOptions.m_BackendOptions.push_back(option); + } + else if (std::string(options_keys[i]) == std::string("save-cached-network")) + { + armnn::BackendOptions option("GpuAcc", {{"SaveCachedNetwork", + armnn::stringUtils::StringToBool(options_values[i])}}); + optimizerOptions.m_ModelOptions.push_back(option); + } + else if (std::string(options_keys[i]) == std::string("cached-network-filepath")) + { + armnn::BackendOptions option("GpuAcc", {{"CachedNetworkFilePath", std::string(options_values[i])}}); + optimizerOptions.m_ModelOptions.push_back(option); + } + // Process GPU & CPU backend options + else if (std::string(options_keys[i]) == std::string("enable-fast-math")) + { + armnn::BackendOptions modelOptionGpu("GpuAcc", {{"FastMathEnabled", + armnn::stringUtils::StringToBool(options_values[i])}}); + optimizerOptions.m_ModelOptions.push_back(modelOptionGpu); + + armnn::BackendOptions modelOptionCpu("CpuAcc", {{"FastMathEnabled", + armnn::stringUtils::StringToBool(options_values[i])}}); + optimizerOptions.m_ModelOptions.push_back(modelOptionCpu); + } + // Process CPU backend options + else if (std::string(options_keys[i]) == std::string("number-of-threads")) + { + unsigned int numberOfThreads = armnn::numeric_cast(atoi(options_values[i])); + armnn::BackendOptions modelOption("CpuAcc", {{"NumberOfThreads", numberOfThreads}}); + optimizerOptions.m_ModelOptions.push_back(modelOption); + } + // Process reduce-fp32-to-fp16 option + else if (std::string(options_keys[i]) == std::string("reduce-fp32-to-fp16")) + { + optimizerOptions.m_ReduceFp32ToFp16 = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process reduce-fp32-to-bf16 option + else if (std::string(options_keys[i]) == std::string("reduce-fp32-to-bf16")) + { + optimizerOptions.m_ReduceFp32ToBf16 = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process debug-data + else if (std::string(options_keys[i]) == std::string("debug-data")) + { + optimizerOptions.m_Debug = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process memory-import + else if (std::string(options_keys[i]) == std::string("memory-import")) + { + optimizerOptions.m_ImportEnabled = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process enable-internal-profiling + else if (std::string(options_keys[i]) == std::string("enable-internal-profiling")) + { + internalProfilingState = *options_values[i] != '0'; + optimizerOptions.m_ProfilingEnabled = internalProfilingState; + } + // Process internal-profiling-detail + else if (std::string(options_keys[i]) == std::string("internal-profiling-detail")) + { + uint32_t detailLevel = static_cast(std::stoul(options_values[i])); + switch (detailLevel) + { + case 1: + internalProfilingDetail = armnn::ProfilingDetailsMethod::DetailsWithEvents; + break; + case 2: + internalProfilingDetail = armnn::ProfilingDetailsMethod::DetailsOnly; + break; + default: + internalProfilingDetail = armnn::ProfilingDetailsMethod::Undefined; + break; + } + } + // Process enable-external-profiling + else if (std::string(options_keys[i]) == std::string("enable-external-profiling")) + { + extProfilingParams.m_EnableProfiling = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process timeline-profiling + else if (std::string(options_keys[i]) == std::string("timeline-profiling")) + { + extProfilingParams.m_TimelineEnabled = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process outgoing-capture-file + else if (std::string(options_keys[i]) == std::string("outgoing-capture-file")) + { + extProfilingParams.m_OutgoingCaptureFile = options_values[i]; + } + // Process incoming-capture-file + else if (std::string(options_keys[i]) == std::string("incoming-capture-file")) + { + extProfilingParams.m_IncomingCaptureFile = options_values[i]; + } + // Process file-only-external-profiling + else if (std::string(options_keys[i]) == std::string("file-only-external-profiling")) + { + extProfilingParams.m_FileOnly = armnn::stringUtils::StringToBool(options_values[i]); + } + // Process counter-capture-period + else if (std::string(options_keys[i]) == std::string("counter-capture-period")) + { + extProfilingParams.m_CapturePeriod = static_cast(std::stoul(options_values[i])); + } + // Process profiling-file-format + else if (std::string(options_keys[i]) == std::string("profiling-file-format")) + { + extProfilingParams.m_FileFormat = options_values[i]; + } + // Process serialize-to-dot + else if (std::string(options_keys[i]) == std::string("serialize-to-dot")) + { + this->SetSerializeToDot(options_values[i]); + } + else + { + throw armnn::Exception("Unknown option for the ArmNN Delegate given: " + std::string(options_keys[i])); + } + } + + this->SetRuntimeOptions(runtimeOptions); + this->SetOptimizerOptions(optimizerOptions); + this->SetInternalProfilingParams(internalProfilingState, internalProfilingDetail); + this->SetExternalProfilingParams(extProfilingParams); +} } // namespace armnnDelegate diff --git a/delegate/src/armnn_external_delegate.cpp b/delegate/src/armnn_external_delegate.cpp index 5919a6c5ab..c3875740e1 100644 --- a/delegate/src/armnn_external_delegate.cpp +++ b/delegate/src/armnn_external_delegate.cpp @@ -26,327 +26,23 @@ namespace tflite extern "C" { -std::vector gpu_options {"gpu-tuning-level", - "gpu-tuning-file", - "gpu-kernel-profiling-enabled"}; - /** - * Create an ArmNN delegate plugin - * - * Available options: - * - * Option key: "backends" \n - * Possible values: ["EthosNPU"/"GpuAcc"/"CpuAcc"/"CpuRef"] \n - * Descriptions: A comma separated list without whitespaces of - * backends which should be used for execution. Falls - * back to next backend in list if previous doesn't - * provide support for operation. e.g. "GpuAcc,CpuAcc" - * - * Option key: "dynamic-backends-path" \n - * Possible values: [filenameString] \n - * Descriptions: This is the directory that will be searched for any dynamic backends. - * - * Option key: "logging-severity" \n - * Possible values: ["trace"/"debug"/"info"/"warning"/"error"/"fatal"] \n - * Description: Sets the logging severity level for ArmNN. Logging - * is turned off if this option is not provided. - * - * Option key: "gpu-tuning-level" \n - * Possible values: ["0"/"1"/"2"/"3"] \n - * Description: 0=UseOnly(default), 1=RapidTuning, 2=NormalTuning, - * 3=ExhaustiveTuning. Requires option gpu-tuning-file. - * 1,2 and 3 will create a tuning-file, 0 will apply the - * tunings from an existing file - * - * Option key: "gpu-mlgo-tuning-file" \n - * Possible values: [filenameString] \n - * Description: File name for the MLGO tuning file - * - * Option key: "gpu-tuning-file" \n - * Possible values: [filenameString] \n - * Description: File name for the tuning file. - * - * Option key: "gpu-enable-profiling" \n - * Possible values: ["true"/"false"] \n - * Description: Enables GPU profiling - * - * Option key: "gpu-kernel-profiling-enabled" \n - * Possible values: ["true"/"false"] \n - * Description: Enables GPU kernel profiling - * - * Option key: "save-cached-network" \n - * Possible values: ["true"/"false"] \n - * Description: Enables saving of the cached network to a file, - * specified with the cached-network-filepath option - * - * Option key: "cached-network-filepath" \n - * Possible values: [filenameString] \n - * Description: If non-empty, the given file will be used to load/save the cached network. - * If save-cached-network is given then the cached network will be saved to the given file. - * To save the cached network a file must already exist. - * If save-cached-network is not given then the cached network will be loaded from the given file. - * This will remove initial compilation time of kernels and speed up the first execution. - * - * Option key: "enable-fast-math" \n - * Possible values: ["true"/"false"] \n - * Description: Enables fast_math options in backends that support it - * - * Option key: "number-of-threads" \n - * Possible values: ["1"-"64"] \n - * Description: Assign the number of threads used by the CpuAcc backend. - * Default is set to 0 (Backend will decide number of threads to use). - * - * Option key: "reduce-fp32-to-fp16" \n - * Possible values: ["true"/"false"] \n - * Description: Reduce Fp32 data to Fp16 for faster processing - * - * Option key: "reduce-fp32-to-bf16" \n - * Possible values: ["true"/"false"] \n - * Description: Reduce Fp32 data to Bf16 for faster processing - * - * Option key: "debug-data" \n - * Possible values: ["true"/"false"] \n - * Description: Add debug data for easier troubleshooting - * - * Option key: "memory-import" \n - * Possible values: ["true"/"false"] \n - * Description: Enable memory import - * - * Option key: "enable-internal-profiling" \n - * Possible values: ["true"/"false"] \n - * Description: Enable the internal profiling feature. - * - * Option key: "internal-profiling-detail" \n - * Possible values: [1/2] \n - * Description: Set the detail on the internal profiling. 1 = DetailsWithEvents, 2 = DetailsOnly. - * - * Option key: "enable-external-profiling" \n - * Possible values: ["true"/"false"] \n - * Description: Enable the external profiling feature. - * - * Option key: "timeline-profiling" \n - * Possible values: ["true"/"false"] \n - * Description: Indicates whether external timeline profiling is enabled or not. - * - * Option key: "outgoing-capture-file" \n - * Possible values: [filenameString] \n - * Description: Path to a file in which outgoing timeline profiling messages will be stored. - * - * Option key: "incoming-capture-file" \n - * Possible values: [filenameString] \n - * Description: Path to a file in which incoming timeline profiling messages will be stored. - * - * Option key: "file-only-external-profiling" \n - * Possible values: ["true"/"false"] \n - * Description: Enable profiling output to file only. - * - * Option key: "counter-capture-period" \n - * Possible values: Integer, Default is 10000u - * Description: Value in microseconds of the profiling capture period. \n - * - * Option key: "profiling-file-format" \n - * Possible values: String of ["binary"] \n - * Description: The format of the file used for outputting profiling data. Currently on "binary" is supported. - * - * Option key: "serialize-to-dot" \n - * Possible values: [filenameString] \n - * Description: Serialize the optimized network to the file specified in "dot" format. - * - * @param[in] option_keys Delegate option names - * @param[in] options_values Delegate option values - * @param[in] num_options Number of delegate options - * @param[in,out] report_error Error callback function - * - * @return An ArmNN delegate if it succeeds else NULL - */ + * Implementation of the TfLite external delegate plugin + * + * For details about what options_keys and option_values are supported please see: + * armnnDelegate::DelegateOptions::DelegateOptions(char const* const*, char const* const*,size_t,void (*)(const char*)) + */ TfLiteDelegate* tflite_plugin_create_delegate(char** options_keys, char** options_values, size_t num_options, void (*report_error)(const char*)) { - // Returning null indicates an error during delegate creation so we initialize with that + // Returning null indicates an error during delegate creation, we initialize with that TfLiteDelegate* delegate = nullptr; try { - // (Initializes with CpuRef backend) - armnnDelegate::DelegateOptions options = armnnDelegate::TfLiteArmnnDelegateOptionsDefault(); - - armnn::IRuntime::CreationOptions runtimeOptions; - armnn::OptimizerOptions optimizerOptions; - bool internalProfilingState = false; - armnn::ProfilingDetailsMethod internalProfilingDetail = armnn::ProfilingDetailsMethod::DetailsWithEvents; - armnn::IRuntime::CreationOptions::ExternalProfilingOptions extProfilingParams; - for (size_t i = 0; i < num_options; ++i) - { - // Process backends - if (std::string(options_keys[i]) == std::string("backends")) - { - // The backend option is a comma separated string of backendIDs that needs to be split - std::vector backends; - char* pch; - pch = strtok(options_values[i],","); - while (pch != NULL) - { - backends.push_back(pch); - pch = strtok (NULL, ","); - } - options.SetBackends(backends); - } - // Process dynamic-backends-path - else if (std::string(options_keys[i]) == std::string("dynamic-backends-path")) - { - runtimeOptions.m_DynamicBackendsPath = std::string(options_values[i]); - } - // Process logging level - else if (std::string(options_keys[i]) == std::string("logging-severity")) - { - options.SetLoggingSeverity(options_values[i]); - } - // Process GPU backend options - else if (std::string(options_keys[i]) == std::string("gpu-tuning-level")) - { - armnn::BackendOptions option("GpuAcc", {{"TuningLevel", atoi(options_values[i])}}); - runtimeOptions.m_BackendOptions.push_back(option); - } - else if (std::string(options_keys[i]) == std::string("gpu-mlgo-tuning-file")) - { - armnn::BackendOptions option("GpuAcc", {{"MLGOTuningFilePath", std::string(options_values[i])}}); - optimizerOptions.m_ModelOptions.push_back(option); - } - else if (std::string(options_keys[i]) == std::string("gpu-tuning-file")) - { - armnn::BackendOptions option("GpuAcc", {{"TuningFile", std::string(options_values[i])}}); - runtimeOptions.m_BackendOptions.push_back(option); - } - else if (std::string(options_keys[i]) == std::string("gpu-enable-profiling")) - { - runtimeOptions.m_EnableGpuProfiling = (*options_values[i] != '0'); - } - else if (std::string(options_keys[i]) == std::string("gpu-kernel-profiling-enabled")) - { - armnn::BackendOptions option("GpuAcc", {{"KernelProfilingEnabled", (*options_values[i] != '0')}}); - runtimeOptions.m_BackendOptions.push_back(option); - } - else if (std::string(options_keys[i]) == std::string("save-cached-network")) - { - armnn::BackendOptions option("GpuAcc", {{"SaveCachedNetwork", (*options_values[i] != '0')}}); - optimizerOptions.m_ModelOptions.push_back(option); - } - else if (std::string(options_keys[i]) == std::string("cached-network-filepath")) - { - armnn::BackendOptions option("GpuAcc", {{"CachedNetworkFilePath", std::string(options_values[i])}}); - optimizerOptions.m_ModelOptions.push_back(option); - } - // Process GPU & CPU backend options - else if (std::string(options_keys[i]) == std::string("enable-fast-math")) - { - armnn::BackendOptions modelOptionGpu("GpuAcc", {{"FastMathEnabled", (*options_values[i] != '0')}}); - optimizerOptions.m_ModelOptions.push_back(modelOptionGpu); - - armnn::BackendOptions modelOptionCpu("CpuAcc", {{"FastMathEnabled", (*options_values[i] != '0')}}); - optimizerOptions.m_ModelOptions.push_back(modelOptionCpu); - } - // Process CPU backend options - else if (std::string(options_keys[i]) == std::string("number-of-threads")) - { - unsigned int numberOfThreads = armnn::numeric_cast(atoi(options_values[i])); - armnn::BackendOptions modelOption("CpuAcc", {{"NumberOfThreads", numberOfThreads}}); - optimizerOptions.m_ModelOptions.push_back(modelOption); - } - // Process reduce-fp32-to-fp16 option - else if (std::string(options_keys[i]) == std::string("reduce-fp32-to-fp16")) - { - optimizerOptions.m_ReduceFp32ToFp16 = *options_values[i] != '0'; - } - // Process reduce-fp32-to-bf16 option - else if (std::string(options_keys[i]) == std::string("reduce-fp32-to-bf16")) - { - optimizerOptions.m_ReduceFp32ToBf16 = *options_values[i] != '0'; - } - // Process debug-data - else if (std::string(options_keys[i]) == std::string("debug-data")) - { - optimizerOptions.m_Debug = *options_values[i] != '0'; - } - // Process memory-import - else if (std::string(options_keys[i]) == std::string("memory-import")) - { - optimizerOptions.m_ImportEnabled = *options_values[i] != '0'; - } - // Process enable-internal-profiling - else if (std::string(options_keys[i]) == std::string("enable-internal-profiling")) - { - internalProfilingState = *options_values[i] != '0'; - optimizerOptions.m_ProfilingEnabled = internalProfilingState; - } - // Process internal-profiling-detail - else if (std::string(options_keys[i]) == std::string("internal-profiling-detail")) - { - uint32_t detailLevel = static_cast(std::stoul(options_values[i])); - switch (detailLevel) - { - case 1: - internalProfilingDetail = armnn::ProfilingDetailsMethod::DetailsWithEvents; - break; - case 2: - internalProfilingDetail = armnn::ProfilingDetailsMethod::DetailsOnly; - break; - default: - internalProfilingDetail = armnn::ProfilingDetailsMethod::Undefined; - break; - } - } - // Process enable-external-profiling - else if (std::string(options_keys[i]) == std::string("enable-external-profiling")) - { - extProfilingParams.m_EnableProfiling = *options_values[i] != '0'; - } - // Process timeline-profiling - else if (std::string(options_keys[i]) == std::string("timeline-profiling")) - { - extProfilingParams.m_TimelineEnabled = *options_values[i] != '0'; - } - // Process outgoing-capture-file - else if (std::string(options_keys[i]) == std::string("outgoing-capture-file")) - { - extProfilingParams.m_OutgoingCaptureFile = options_values[i]; - } - // Process incoming-capture-file - else if (std::string(options_keys[i]) == std::string("incoming-capture-file")) - { - extProfilingParams.m_IncomingCaptureFile = options_values[i]; - } - // Process file-only-external-profiling - else if (std::string(options_keys[i]) == std::string("file-only-external-profiling")) - { - extProfilingParams.m_FileOnly = *options_values[i] != '0'; - } - // Process counter-capture-period - else if (std::string(options_keys[i]) == std::string("counter-capture-period")) - { - extProfilingParams.m_CapturePeriod = static_cast(std::stoul(options_values[i])); - } - // Process profiling-file-format - else if (std::string(options_keys[i]) == std::string("profiling-file-format")) - { - extProfilingParams.m_FileFormat = options_values[i]; - } - // Process serialize-to-dot - else if (std::string(options_keys[i]) == std::string("serialize-to-dot")) - { - options.SetSerializeToDot(options_values[i]); - } - else - { - throw armnn::Exception("Unknown option for the ArmNN Delegate given: " + std::string(options_keys[i])); - } - } - - options.SetRuntimeOptions(runtimeOptions); - options.SetOptimizerOptions(optimizerOptions); - options.SetInternalProfilingParams(internalProfilingState, internalProfilingDetail); - options.SetExternalProfilingParams(extProfilingParams); + armnnDelegate::DelegateOptions options (options_keys, options_values, num_options, (*report_error)); delegate = TfLiteArmnnDelegateCreate(options); } catch (const std::exception& ex) diff --git a/delegate/src/test/DelegateOptionsTest.cpp b/delegate/src/test/DelegateOptionsTest.cpp index 338772596b..54f9c8f0e3 100644 --- a/delegate/src/test/DelegateOptionsTest.cpp +++ b/delegate/src/test/DelegateOptionsTest.cpp @@ -223,6 +223,90 @@ TEST_CASE ("ArmnnDelegateSerializeToDot") fs::remove(filename); } +void CreateFp16StringParsingTestRun(std::vector& keys, + std::vector& values, + std::stringstream& ss) +{ + StreamRedirector redirect(std::cout, ss.rdbuf()); + + std::vector backends = { armnn::Compute::CpuRef }; + std::vector tensorShape { 1, 2, 2, 1 }; + std::vector inputData = { 1, 2, 3, 4 }; + std::vector divData = { 2, 2, 3, 4 }; + std::vector expectedResult = { 1, 2, 2, 2 }; + + // Create options_keys and options_values char array + size_t num_options = keys.size(); + std::unique_ptr options_keys = + std::unique_ptr(new const char*[num_options + 1]); + std::unique_ptr options_values = + std::unique_ptr(new const char*[num_options + 1]); + for (size_t i=0; i(::tflite::TensorType_FLOAT32, + backends, + tensorShape, + inputData, + inputData, + divData, + expectedResult, + delegateOptions); +} + +TEST_CASE ("ArmnnDelegateStringParsingOptionReduceFp32ToFp16") +{ + SUBCASE("Fp16=1") + { + std::stringstream ss; + std::vector keys { "backends", "debug-data", "reduce-fp32-to-fp16", "logging-severity"}; + std::vector values { "CpuRef", "1", "1", "info"}; + CreateFp16StringParsingTestRun(keys, values, ss); + CHECK(ss.str().find("convert_fp32_to_fp16") != std::string::npos); + CHECK(ss.str().find("convert_fp16_to_fp32") != std::string::npos); + } + SUBCASE("Fp16=true") + { + std::stringstream ss; + std::vector keys { "backends", "debug-data", "reduce-fp32-to-fp16"}; + std::vector values { "CpuRef", "TRUE", "true"}; + CreateFp16StringParsingTestRun(keys, values, ss); + CHECK(ss.str().find("convert_fp32_to_fp16") != std::string::npos); + CHECK(ss.str().find("convert_fp16_to_fp32") != std::string::npos); + } + SUBCASE("Fp16=True") + { + std::stringstream ss; + std::vector keys { "backends", "debug-data", "reduce-fp32-to-fp16"}; + std::vector values { "CpuRef", "true", "True"}; + CreateFp16StringParsingTestRun(keys, values, ss); + CHECK(ss.str().find("convert_fp32_to_fp16") != std::string::npos); + CHECK(ss.str().find("convert_fp16_to_fp32") != std::string::npos); + } + SUBCASE("Fp16=0") + { + std::stringstream ss; + std::vector keys { "backends", "debug-data", "reduce-fp32-to-fp16"}; + std::vector values { "CpuRef", "true", "0"}; + CreateFp16StringParsingTestRun(keys, values, ss); + CHECK(ss.str().find("convert_fp32_to_fp16") == std::string::npos); + CHECK(ss.str().find("convert_fp16_to_fp32") == std::string::npos); + } + SUBCASE("Fp16=false") + { + std::stringstream ss; + std::vector keys { "backends", "debug-data", "reduce-fp32-to-fp16"}; + std::vector values { "CpuRef", "1", "false"}; + CreateFp16StringParsingTestRun(keys, values, ss); + CHECK(ss.str().find("convert_fp32_to_fp16") == std::string::npos); + CHECK(ss.str().find("convert_fp16_to_fp32") == std::string::npos); + } +} + } diff --git a/include/armnn/utility/StringUtils.hpp b/include/armnn/utility/StringUtils.hpp index cc5e8e7349..172b1798c5 100644 --- a/include/armnn/utility/StringUtils.hpp +++ b/include/armnn/utility/StringUtils.hpp @@ -7,6 +7,9 @@ #include #include +#include +#include +#include namespace armnn { @@ -115,6 +118,47 @@ inline void StringReplaceAll(std::string& str, } } +/// +/// Converts a string to bool. +/// Accepts "true", "false" (case-insensitive) and numbers, 1 (true) or 0 (false). +/// +/// \param s String to convert to bool +/// \param throw_on_error Bool variable to suppress error if conversion failed (Will return false in that case) +/// \return bool value +/// +inline bool StringToBool(const std::string& s, bool throw_on_error = true) +{ + // in case of failure to convert returns false + auto result = false; + + // isstringstream fails if parsing didn't work + std::istringstream is(s); + + // try integer conversion first. For the case s is a number + is >> result; + + if (is.fail()) + { + // transform to lower case to make case-insensitive + std::string s_lower = s; + std::transform(s_lower.begin(), + s_lower.end(), + s_lower.begin(), + [](unsigned char c){ return std::tolower(c); }); + is.str(s_lower); + // try boolean -> s="false" or "true" + is.clear(); + is >> std::boolalpha >> result; + } + + if (is.fail() && throw_on_error) + { + throw armnn::InvalidArgumentException(s + " is not convertable to bool"); + } + + return result; +} + } // namespace stringUtils } // namespace armnn \ No newline at end of file diff --git a/src/armnn/test/UtilityTests.cpp b/src/armnn/test/UtilityTests.cpp index 7911fd2608..fba0dd9d52 100644 --- a/src/armnn/test/UtilityTests.cpp +++ b/src/armnn/test/UtilityTests.cpp @@ -3,16 +3,17 @@ // SPDX-License-Identifier: MIT // -#include #define ARMNN_POLYMORPHIC_CAST_TESTABLE #define ARMNN_NUMERIC_CAST_TESTABLE +#include #include #include #include +#include -#include +#include #include @@ -250,4 +251,28 @@ TEST_CASE("NumericCast") } +TEST_CASE("StringToBool") + { + CHECK(true == armnn::stringUtils::StringToBool("1")); + CHECK(false == armnn::stringUtils::StringToBool("0")); + // Any number larger than 1 will be a failure. + CHECK_THROWS_AS(armnn::stringUtils::StringToBool("2"), armnn::InvalidArgumentException); + CHECK_THROWS_AS(armnn::stringUtils::StringToBool("23456567"), armnn::InvalidArgumentException); + CHECK_THROWS_AS(armnn::stringUtils::StringToBool("-23456567"), armnn::InvalidArgumentException); + CHECK_THROWS_AS(armnn::stringUtils::StringToBool("Not a number"), armnn::InvalidArgumentException); + // Empty string should be a failure. + CHECK_THROWS_AS(armnn::stringUtils::StringToBool(""), armnn::InvalidArgumentException); + CHECK(true == armnn::stringUtils::StringToBool("true")); + CHECK(false == armnn::stringUtils::StringToBool("false")); + // Should be case agnostic. + CHECK(true == armnn::stringUtils::StringToBool("TrUe")); + CHECK(false == armnn::stringUtils::StringToBool("fAlSe")); + + // Same negative test cases with throw_on_error set to false. + CHECK(false == armnn::stringUtils::StringToBool("2", false)); + CHECK(false == armnn::stringUtils::StringToBool("23456567", false)); + CHECK(false == armnn::stringUtils::StringToBool("-23456567", false)); + CHECK(false == armnn::stringUtils::StringToBool("Not a number", false)); + CHECK(false == armnn::stringUtils::StringToBool("", false)); + } } -- cgit v1.2.1