IVGCVSW-5335 Documentation for fast_math

 * Changed documentation for fast_math to add warning about possibly reduction in precision.
 * Added -h,--help option to display command line help for the driver.
 * Added -V,--version option to display ArmNN version information for the driver.
 * Changed driver to display an error and the command line help if it cannot start for any reason.
 * Backend no longer defaults to GpuAcc.

Signed-off-by: Mike Kelly <mike.kelly@arm.com>
Change-Id: I270b10ec9d485fd25e25680fc29ea1fc2b0e8e1d
diff --git a/DriverOptions.cpp b/DriverOptions.cpp
index f7b00f8..414fc93 100644
--- a/DriverOptions.cpp
+++ b/DriverOptions.cpp
@@ -8,6 +8,7 @@
 #include "DriverOptions.hpp"
 #include "Utils.hpp"
 
+#include <armnn/Version.hpp>
 #include <log/log.h>
 #include "SystemPropertiesUtils.hpp"
 
@@ -56,40 +57,39 @@
     , m_EnableGpuProfiling(false)
     , m_fp16Enabled(false)
     , m_FastMathEnabled(false)
+    , m_ShouldExit(false)
 {
     std::string unsupportedOperationsAsString;
     std::string clTunedParametersModeAsString;
     std::string clTuningLevelAsString;
     std::vector<std::string> backends;
+    bool showHelp;
+    bool showVersion;
 
-    cxxopts::Options optionsDesc("Options");
+    cxxopts::Options optionsDesc(argv[0], "ArmNN Android NN driver for the Android Neural Networks API. The Android NN "
+                                          "driver will convert Android NNAPI requests and delegate them to available "
+                                          "ArmNN backends.");
     try
     {
         optionsDesc.add_options()
+
+        ("a,enable-fast-math", "Enables fast_math options in backends that support it. Using the fast_math flag can "
+                               "lead to performance improvements but may result in reduced or different precision.",
+         cxxopts::value<bool>(m_FastMathEnabled)->default_value("false"))
+
         ("c,compute",
          "Comma separated list of backends to run layers on. Examples of possible values are: CpuRef, CpuAcc, GpuAcc",
          cxxopts::value<std::vector<std::string>>(backends))
 
-        ("v,verbose-logging", "Turns verbose logging on",
-         cxxopts::value<bool>(m_VerboseLogging)->default_value("false"))
-
         ("d,request-inputs-and-outputs-dump-dir",
          "If non-empty, the directory where request inputs and outputs should be dumped",
          cxxopts::value<std::string>(m_RequestInputsAndOutputsDumpDir)->default_value(""))
 
-        ("n,service-name",
-         "If non-empty, the driver service name to be registered",
-         cxxopts::value<std::string>(m_ServiceName)->default_value("armnn"))
+        ("f,fp16-enabled", "Enables support for relaxed computation from Float32 to Float16",
+         cxxopts::value<bool>(m_fp16Enabled)->default_value("false"))
 
-        ("u,unsupported-operations",
-         "If non-empty, a comma-separated list of operation indices which the driver will forcibly "
-         "consider unsupported",
-         cxxopts::value<std::string>(unsupportedOperationsAsString)->default_value(""))
-
-        ("t,cl-tuned-parameters-file",
-         "If non-empty, the given file will be used to load/save CL tuned parameters. "
-         "See also --cl-tuned-parameters-mode",
-         cxxopts::value<std::string>(m_ClTunedParametersFile)->default_value(""))
+        ("h,help", "Show this help",
+         cxxopts::value<bool>(showHelp)->default_value("false"))
 
         ("m,cl-tuned-parameters-mode",
          "If 'UseTunedParameters' (the default), will read CL tuned parameters from the file specified by "
@@ -98,6 +98,10 @@
          "the file accordingly.",
          cxxopts::value<std::string>(clTunedParametersModeAsString)->default_value("UseTunedParameters"))
 
+        ("n,service-name",
+         "If non-empty, the driver service name to be registered",
+         cxxopts::value<std::string>(m_ServiceName)->default_value("armnn"))
+
         ("o,cl-tuning-level",
          "exhaustive: all lws values are tested "
          "normal: reduced number of lws values but enough to still have the performance really close to the "
@@ -105,40 +109,79 @@
          "rapid: only 3 lws values should be tested for each kernel ",
          cxxopts::value<std::string>(clTuningLevelAsString)->default_value("rapid"))
 
-        ("a,fast-math", "Turns FastMath on",
-         cxxopts::value<bool>(m_FastMathEnabled)->default_value("false"))
-
         ("p,gpu-profiling", "Turns GPU profiling on",
          cxxopts::value<bool>(m_EnableGpuProfiling)->default_value("false"))
 
-        ("f,fp16-enabled", "Enables support for relaxed computation from Float32 to Float16",
-         cxxopts::value<bool>(m_fp16Enabled)->default_value("false"));
+        ("t,cl-tuned-parameters-file",
+         "If non-empty, the given file will be used to load/save CL tuned parameters. "
+         "See also --cl-tuned-parameters-mode",
+         cxxopts::value<std::string>(m_ClTunedParametersFile)->default_value(""))
+
+        ("u,unsupported-operations",
+         "If non-empty, a comma-separated list of operation indices which the driver will forcibly "
+         "consider unsupported",
+         cxxopts::value<std::string>(unsupportedOperationsAsString)->default_value(""))
+
+        ("v,verbose-logging", "Turns verbose logging on",
+         cxxopts::value<bool>(m_VerboseLogging)->default_value("false"))
+
+        ("V,version", "Show version information",
+         cxxopts::value<bool>(showVersion)->default_value("false"));
     }
     catch (const std::exception& e)
     {
-        ALOGW("An error occurred attempting to construct options: %s", e.what());
+        ALOGE("An error occurred attempting to construct options: %s", e.what());
+        std::cout << "An error occurred attempting to construct options: %s" << std::endl;
+        m_ExitCode = EXIT_FAILURE;
+        return;
     }
 
-
     try
     {
         cxxopts::ParseResult result = optionsDesc.parse(argc, argv);
-        // If no backends have been specified then the default value is GpuAcc.
-        if (backends.empty())
-        {
-            backends.push_back("GpuAcc");
-        }
     }
     catch (const cxxopts::OptionException& e)
     {
-        ALOGW("An error occurred attempting to parse program options: %s", e.what());
+        ALOGW("An exception occurred attempting to parse program options: %s", e.what());
+        std::cout << optionsDesc.help() << std::endl
+                  << "An exception occurred while parsing program options: " << std::endl
+                  << e.what() << std::endl;
+    }
+    if (showHelp)
+    {
+        ALOGW("Showing help and exiting");
+        std::cout << optionsDesc.help() << std::endl;
+        m_ShouldExit = true;
+        m_ExitCode = EXIT_SUCCESS;
+        return;
+    }
+    if (showVersion)
+    {
+        ALOGW("Showing version and exiting");
+        std::cout << "ArmNN Android NN driver for the Android Neural Networks API.\n"
+                     "ArmNN v" << ARMNN_VERSION << std::endl;
+        m_ShouldExit = true;
+        m_ExitCode = EXIT_SUCCESS;
+        return;
     }
 
     // Convert the string backend names into backendId's.
     m_Backends.reserve(backends.size());
     for (auto&& backend : backends)
     {
-            m_Backends.emplace_back(backend);
+        m_Backends.emplace_back(backend);
+    }
+
+    // If no backends have been specified then the default value is GpuAcc.
+    if (backends.empty())
+    {
+        ALOGE("No backends have been specified:");
+        std::cout << optionsDesc.help() << std::endl
+                  << "Unable to start:" << std::endl
+                  << "No backends have been specified" << std::endl;
+        m_ShouldExit = true;
+        m_ExitCode = EXIT_FAILURE;
+        return;
     }
 
     if (!unsupportedOperationsAsString.empty())
diff --git a/DriverOptions.hpp b/DriverOptions.hpp
index ba0919c..1523652 100644
--- a/DriverOptions.hpp
+++ b/DriverOptions.hpp
@@ -34,6 +34,8 @@
     bool IsFastMathEnabled() const { return m_FastMathEnabled; }
     bool GetFp16Enabled() const { return m_fp16Enabled; }
     void SetBackends(const std::vector<armnn::BackendId>& backends) { m_Backends = backends; }
+    bool ShouldExit() const { return m_ShouldExit; }
+    int GetExitCode() const { return m_ExitCode; }
 
 private:
     std::vector<armnn::BackendId> m_Backends;
@@ -47,6 +49,8 @@
     bool m_EnableGpuProfiling;
     bool m_fp16Enabled;
     bool m_FastMathEnabled;
+    bool m_ShouldExit;
+    int m_ExitCode;
 };
 
 } // namespace armnn_driver
diff --git a/service.cpp b/service.cpp
index 823f0c7..5191fbf 100644
--- a/service.cpp
+++ b/service.cpp
@@ -19,6 +19,11 @@
 {
     android::sp<ArmnnDriver> driver;
     DriverOptions driverOptions(argc, argv);
+
+    if (driverOptions.ShouldExit())
+    {
+        return driverOptions.GetExitCode();
+    }
     try
     {
         driver = new ArmnnDriver(DriverOptions(argc, argv));
@@ -26,6 +31,8 @@
     catch (const std::exception& e)
     {
         ALOGE("Could not create driver: %s", e.what());
+        std::cout << "Unable to start:" << std::endl
+                  << "Could not create driver: " << e.what() << std::endl;
         return EXIT_FAILURE;
     }
 
@@ -38,15 +45,19 @@
     catch (const std::exception& e)
     {
         ALOGE("Could not register service: %s", e.what());
-        return EXIT_FAILURE;
-    }
-    if (status != android::OK)
-    {
-        ALOGE("Could not register service");
+        std::cout << "Unable to start:" << std::endl
+                  << "Could not register service: " << e.what() << std::endl;
         return EXIT_FAILURE;
     }
 
+    if (status != android::OK)
+    {
+        ALOGE("Could not register service");
+        std::cout << "Unable to start:" << std::endl
+                  << "Could not register service" << std::endl;
+        return EXIT_FAILURE;
+    }
     android::hardware::joinRpcThreadpool();
-    ALOGE("Service exited!");
-    return EXIT_FAILURE;
+    ALOGW("Service exited!");
+    return EXIT_SUCCESS;
 }