Allow setting the default time unit globally (#1337)

* Add option to set the default time unit globally

This commit introduces the `--benchmark_time_unit={ns|us|ms|s}` command line argument. The argument only affects benchmarks where the time unit is not set explicitly.

* Update AUTHORS and CONTRIBUTORS

* Test `SetDefaultTimeUnit`

* clang format

* Use `GetDefaultTimeUnit()` for initializing `TimeUnit` variables

* Review fixes

* Export functions

* Add comment
diff --git a/AUTHORS b/AUTHORS
index 2b8072e..5a39872 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -52,6 +52,7 @@
 Radoslav Yovchev <radoslav.tm@gmail.com>
 Roman Lebedev <lebedev.ri@gmail.com>
 Sayan Bhattacharjee <aero.sayan@gmail.com>
+Shapr3D <google-contributors@shapr3d.com>
 Shuo Chen <chenshuo@chenshuo.com>
 Staffan Tjernstrom <staffantj@gmail.com>
 Steinar H. Gunderson <sgunderson@bigfoot.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 651fbea..35a4cc6 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -27,6 +27,7 @@
 Alex Steele <steelal123@gmail.com>
 Andriy Berestovskyy <berestovskyy@gmail.com>
 Arne Beer <arne@twobeer.de>
+Bátor Tallér <bator.taller@shapr3d.com>
 Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
 Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
 Christian Wassermann <christian_wassermann@web.de>
diff --git a/docs/user_guide.md b/docs/user_guide.md
index 0fe5a4f..7c2ca31 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -927,6 +927,10 @@
 BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
 ```
 
+Additionally the default time unit can be set globally with the
+`--benchmark_time_unit={ns|us|ms|s}` command line argument. The argument only
+affects benchmarks where the time unit is not set explicitly.
+
 <a name="preventing-optimization" />
 
 ## Preventing Optimization
diff --git a/include/benchmark/benchmark.h b/include/benchmark/benchmark.h
index 4d6ce4c..7e146db 100644
--- a/include/benchmark/benchmark.h
+++ b/include/benchmark/benchmark.h
@@ -337,6 +337,16 @@
 RunSpecifiedBenchmarks(BenchmarkReporter* display_reporter,
                        BenchmarkReporter* file_reporter, std::string spec);
 
+// TimeUnit is passed to a benchmark in order to specify the order of magnitude
+// for the measured time.
+enum TimeUnit { kNanosecond, kMicrosecond, kMillisecond, kSecond };
+
+BENCHMARK_EXPORT TimeUnit GetDefaultTimeUnit();
+
+// Sets the default time unit the benchmarks use
+// Has to be called before the benchmark loop to take effect
+BENCHMARK_EXPORT void SetDefaultTimeUnit(TimeUnit unit);
+
 // If a MemoryManager is registered (via RegisterMemoryManager()),
 // it can be used to collect and report allocation metrics for a run of the
 // benchmark.
@@ -524,10 +534,6 @@
 // This is the container for the user-defined counters.
 typedef std::map<std::string, Counter> UserCounters;
 
-// TimeUnit is passed to a benchmark in order to specify the order of magnitude
-// for the measured time.
-enum TimeUnit { kNanosecond, kMicrosecond, kMillisecond, kSecond };
-
 // BigO is passed to a benchmark in order to specify the asymptotic
 // computational
 // complexity for the benchmark. In case oAuto is selected, complexity will be
@@ -1108,6 +1114,8 @@
 
   virtual void Run(State& state) = 0;
 
+  TimeUnit GetTimeUnit() const;
+
  protected:
   explicit Benchmark(const char* name);
   void SetName(const char* name);
@@ -1122,7 +1130,10 @@
   AggregationReportMode aggregation_report_mode_;
   std::vector<std::string> arg_names_;       // Args for all benchmark runs
   std::vector<std::vector<int64_t> > args_;  // Args for all benchmark runs
+
   TimeUnit time_unit_;
+  bool use_default_time_unit_;
+
   int range_multiplier_;
   double min_time_;
   IterationCount iterations_;
@@ -1555,7 +1566,7 @@
           error_occurred(false),
           iterations(1),
           threads(1),
-          time_unit(kNanosecond),
+          time_unit(GetDefaultTimeUnit()),
           real_accumulated_time(0),
           cpu_accumulated_time(0),
           max_heapbytes_used(0),
diff --git a/src/benchmark.cc b/src/benchmark.cc
index d915843..57958b1 100644
--- a/src/benchmark.cc
+++ b/src/benchmark.cc
@@ -121,6 +121,10 @@
 // pairs. Kept internal as it's only used for parsing from env/command line.
 BM_DEFINE_kvpairs(benchmark_context, {});
 
+// Set the default time unit to use for reports
+// Valid values are 'ns', 'us', 'ms' or 's'
+BM_DEFINE_string(benchmark_time_unit, "");
+
 // The level of verbose logging to output
 BM_DEFINE_int32(v, 0);
 
@@ -520,6 +524,15 @@
   return benchmarks.size();
 }
 
+namespace {
+// stores the time unit benchmarks use by default
+TimeUnit default_time_unit = kNanosecond;
+}  // namespace
+
+TimeUnit GetDefaultTimeUnit() { return default_time_unit; }
+
+void SetDefaultTimeUnit(TimeUnit unit) { default_time_unit = unit; }
+
 std::string GetBenchmarkFilter() { return FLAGS_benchmark_filter; }
 
 void RegisterMemoryManager(MemoryManager* manager) {
@@ -559,11 +572,26 @@
             "          [--benchmark_color={auto|true|false}]\n"
             "          [--benchmark_counters_tabular={true|false}]\n"
             "          [--benchmark_context=<key>=<value>,...]\n"
+            "          [--benchmark_time_unit={ns|us|ms|s}]\n"
             "          [--v=<verbosity>]\n");
   }
   exit(0);
 }
 
+void SetDefaultTimeUnitFromFlag(const std::string& time_unit_flag) {
+  if (time_unit_flag == "s") {
+    return SetDefaultTimeUnit(kSecond);
+  } else if (time_unit_flag == "ms") {
+    return SetDefaultTimeUnit(kMillisecond);
+  } else if (time_unit_flag == "us") {
+    return SetDefaultTimeUnit(kMicrosecond);
+  } else if (time_unit_flag == "ns") {
+    return SetDefaultTimeUnit(kNanosecond);
+  } else if (!time_unit_flag.empty()) {
+    PrintUsageAndExit();
+  }
+}
+
 void ParseCommandLineFlags(int* argc, char** argv) {
   using namespace benchmark;
   BenchmarkReporter::Context::executable_name =
@@ -593,6 +621,8 @@
                         &FLAGS_benchmark_perf_counters) ||
         ParseKeyValueFlag(argv[i], "benchmark_context",
                           &FLAGS_benchmark_context) ||
+        ParseStringFlag(argv[i], "benchmark_time_unit",
+                        &FLAGS_benchmark_time_unit) ||
         ParseInt32Flag(argv[i], "v", &FLAGS_v)) {
       for (int j = i; j != *argc - 1; ++j) argv[j] = argv[j + 1];
 
@@ -608,6 +638,7 @@
       PrintUsageAndExit();
     }
   }
+  SetDefaultTimeUnitFromFlag(FLAGS_benchmark_time_unit);
   if (FLAGS_benchmark_color.empty()) {
     PrintUsageAndExit();
   }
diff --git a/src/benchmark_api_internal.cc b/src/benchmark_api_internal.cc
index 4de36e3..6b90cfa 100644
--- a/src/benchmark_api_internal.cc
+++ b/src/benchmark_api_internal.cc
@@ -16,7 +16,7 @@
       per_family_instance_index_(per_family_instance_idx),
       aggregation_report_mode_(benchmark_.aggregation_report_mode_),
       args_(args),
-      time_unit_(benchmark_.time_unit_),
+      time_unit_(benchmark_.GetTimeUnit()),
       measure_process_cpu_time_(benchmark_.measure_process_cpu_time_),
       use_real_time_(benchmark_.use_real_time_),
       use_manual_time_(benchmark_.use_manual_time_),
diff --git a/src/benchmark_register.cc b/src/benchmark_register.cc
index 61a0c26..36b3a0b 100644
--- a/src/benchmark_register.cc
+++ b/src/benchmark_register.cc
@@ -202,7 +202,8 @@
 Benchmark::Benchmark(const char* name)
     : name_(name),
       aggregation_report_mode_(ARM_Unspecified),
-      time_unit_(kNanosecond),
+      time_unit_(GetDefaultTimeUnit()),
+      use_default_time_unit_(true),
       range_multiplier_(kRangeMultiplier),
       min_time_(0),
       iterations_(0),
@@ -235,6 +236,7 @@
 
 Benchmark* Benchmark::Unit(TimeUnit unit) {
   time_unit_ = unit;
+  use_default_time_unit_ = false;
   return this;
 }
 
@@ -462,6 +464,10 @@
   return static_cast<int>(args_.front().size());
 }
 
+TimeUnit Benchmark::GetTimeUnit() const {
+  return use_default_time_unit_ ? GetDefaultTimeUnit() : time_unit_;
+}
+
 //=============================================================================//
 //                            FunctionBenchmark
 //=============================================================================//
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 205be91..d5eb25d 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -219,6 +219,7 @@
   add_gtest(statistics_gtest)
   add_gtest(string_util_gtest)
   add_gtest(perf_counters_gtest)
+  add_gtest(time_unit_gtest)
 endif(BENCHMARK_ENABLE_GTEST_TESTS)
 
 ###############################################################################
diff --git a/test/time_unit_gtest.cc b/test/time_unit_gtest.cc
new file mode 100644
index 0000000..ae53743
--- /dev/null
+++ b/test/time_unit_gtest.cc
@@ -0,0 +1,37 @@
+#include "../include/benchmark/benchmark.h"
+#include "gtest/gtest.h"
+
+namespace benchmark {
+namespace internal {
+
+namespace {
+
+class DummyBenchmark : public Benchmark {
+ public:
+  DummyBenchmark() : Benchmark("dummy") {}
+  virtual void Run(State&) override {}
+};
+
+TEST(DefaultTimeUnitTest, TimeUnitIsNotSet) {
+  DummyBenchmark benchmark;
+  EXPECT_EQ(benchmark.GetTimeUnit(), kNanosecond);
+}
+
+TEST(DefaultTimeUnitTest, DefaultIsSet) {
+  DummyBenchmark benchmark;
+  EXPECT_EQ(benchmark.GetTimeUnit(), kNanosecond);
+  SetDefaultTimeUnit(kMillisecond);
+  EXPECT_EQ(benchmark.GetTimeUnit(), kMillisecond);
+}
+
+TEST(DefaultTimeUnitTest, DefaultAndExplicitUnitIsSet) {
+  DummyBenchmark benchmark;
+  benchmark.Unit(kMillisecond);
+  SetDefaultTimeUnit(kMicrosecond);
+
+  EXPECT_EQ(benchmark.GetTimeUnit(), kMillisecond);
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace benchmark