From a187aa080377f4707bb28690d51f206c0d49f436 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Fri, 6 Mar 2015 17:01:05 -0500 Subject: [PATCH] Third step in moving towards adopting new timer semantics --- src/CMakeLists.txt | 3 +- src/benchmark.cc | 107 +-------------------------- src/colorprint.cc | 5 +- src/commandlineflags.cc | 8 +- src/commandlineflags.h | 5 +- src/cycleclock.h | 7 +- src/re.h | 2 +- src/re_posix.cc | 2 +- src/re_std.cc | 3 +- src/sleep.cc | 8 +- src/sleep.h | 2 +- src/stat.h | 11 +-- src/string_util.cc | 157 ++++++++++++++++++++++++++++++++++++++++ src/string_util.h | 48 ++++++++++++ src/sysinfo.cc | 12 +-- test/re_test.cc | 2 +- 16 files changed, 244 insertions(+), 138 deletions(-) create mode 100644 src/string_util.cc create mode 100644 src/string_util.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 944c499c..5f22510d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,8 @@ include_directories(${PROJECT_SOURCE_DIR}/src) # Define the source files set(SOURCE_FILES "benchmark.cc" "colorprint.cc" "commandlineflags.cc" - "log.cc" "sleep.cc" "sysinfo.cc" "walltime.cc") + "log.cc" "sleep.cc" "string_util.cc" "sysinfo.cc" + "walltime.cc") # Determine the correct regular expression engine to use if(HAVE_STD_REGEX) set(RE_FILES "re_std.cc") diff --git a/src/benchmark.cc b/src/benchmark.cc index 97464c0a..3ce37fd3 100644 --- a/src/benchmark.cc +++ b/src/benchmark.cc @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ #include "re.h" #include "sleep.h" #include "stat.h" +#include "string_util.h" #include "sysinfo.h" #include "walltime.h" @@ -82,108 +83,6 @@ DECLARE_string(heap_check); namespace benchmark { namespace { -// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta. -const char kBigSIUnits[] = "kMGTPEZY"; -// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi. -const char kBigIECUnits[] = "KMGTPEZY"; -// milli, micro, nano, pico, femto, atto, zepto, yocto. -const char kSmallSIUnits[] = "munpfazy"; - -// We require that all three arrays have the same size. -static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits), - "SI and IEC unit arrays must be the same size"); -static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits), - "Small SI and Big SI unit arrays must be the same size"); -static const size_t kUnitsSize = arraysize(kBigSIUnits); - -void ToExponentAndMantissa(double val, double thresh, int precision, - double one_k, std::string* mantissa, size_t* exponent) { - std::stringstream mantissa_stream; - - if (val < 0) { - mantissa_stream << "-"; - val = -val; - } - - // Adjust threshold so that it never excludes things which can't be rendered - // in 'precision' digits. - const double adjusted_threshold = - std::max(thresh, 1.0 / pow(10.0, precision)); - const double big_threshold = adjusted_threshold * one_k; - const double small_threshold = adjusted_threshold; - - if (val > big_threshold) { - // Positive powers - double scaled = val; - for (size_t i = 0; i < arraysize(kBigSIUnits); ++i) { - scaled /= one_k; - if (scaled <= big_threshold) { - mantissa_stream << scaled; - *exponent = i + 1; - *mantissa = mantissa_stream.str(); - return; - } - } - mantissa_stream << val; - *exponent = 0; - } else if (val < small_threshold) { - // Negative powers - double scaled = val; - for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) { - scaled *= one_k; - if (scaled >= small_threshold) { - mantissa_stream << scaled; - *exponent = -i - 1; - *mantissa = mantissa_stream.str(); - return; - } - } - mantissa_stream << val; - *exponent = 0; - } else { - mantissa_stream << val; - *exponent = 0; - } - *mantissa = mantissa_stream.str(); -} - -std::string ExponentToPrefix(size_t exponent, bool iec) { - if (exponent == 0) return ""; - - const size_t index = (exponent > 0 ? exponent - 1 : -exponent - 1); - if (index >= kUnitsSize) return ""; - - const char* array = - (exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits); - if (iec) - return array[index] + std::string("i"); - else - return std::string(1, array[index]); -} - -std::string ToBinaryStringFullySpecified(double value, double threshold, - int precision) { - std::string mantissa; - size_t exponent; - ToExponentAndMantissa(value, threshold, precision, 1024.0, &mantissa, - &exponent); - return mantissa + ExponentToPrefix(exponent, false); -} - -inline void AppendHumanReadable(int n, std::string* str) { - std::stringstream ss; - // Round down to the nearest SI prefix. - ss << "/" << ToBinaryStringFullySpecified(n, 1.0, 0); - *str += ss.str(); -} - -inline std::string HumanReadableNumber(double n) { - // 1.1 means that figures up to 1.1k should be shown with the next unit down; - // this softens edge effects. - // 1 means that we should show one decimal place of precision. - return ToBinaryStringFullySpecified(n, 1.1, 1); -} - // For non-dense Range, intermediate values are powers of kRangeMultiplier. static const int kRangeMultiplier = 8; @@ -271,7 +170,7 @@ void ComputeStats(const std::vector& reports, mean_data->iterations; mean_data->bytes_per_second = bytes_per_second_stat.Mean(); mean_data->items_per_second = items_per_second_stat.Mean(); - mean_data->max_heapbytes_used = max_heapbytes_used_stat.max(); + mean_data->max_heapbytes_used = max_heapbytes_used_stat.Max(); // Only add label to mean/stddev if it is same for all runs mean_data->report_label = reports[0].report_label; diff --git a/src/colorprint.cc b/src/colorprint.cc index 3a84f6ac..783797b8 100644 --- a/src/colorprint.cc +++ b/src/colorprint.cc @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,9 +14,10 @@ #include "colorprint.h" -#include +#include #include "commandlineflags.h" +#include "internal_macros.h" DECLARE_bool(color_print); diff --git a/src/commandlineflags.cc b/src/commandlineflags.cc index 57a75f5a..f2b19134 100644 --- a/src/commandlineflags.cc +++ b/src/commandlineflags.cc @@ -14,8 +14,7 @@ #include "commandlineflags.h" -#include - +#include #include #include @@ -75,10 +74,7 @@ bool ParseDouble(const std::string& src_text, const char* str, double* value) { } inline const char* GetEnv(const char* name) { -#if GTEST_OS_WINDOWS_MOBILE - // We are on Windows CE, which has no environment variables. - return NULL; -#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) +#if defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) // Environment variables which we programmatically clear will be set to the // empty string rather than unset (NULL). Handle that case. const char* const env = getenv(name); diff --git a/src/commandlineflags.h b/src/commandlineflags.h index 001c8bac..34b9c6f3 100644 --- a/src/commandlineflags.h +++ b/src/commandlineflags.h @@ -1,8 +1,7 @@ #ifndef BENCHMARK_COMMANDLINEFLAGS_H_ #define BENCHMARK_COMMANDLINEFLAGS_H_ -#include - +#include #include // Macro for referencing flags. @@ -72,6 +71,6 @@ bool ParseStringFlag(const char* str, const char* flag, std::string* value); // Returns true if the string matches the flag. bool IsFlag(const char* str, const char* flag); -} // end namespace gbenchmark +} // end namespace benchmark #endif // BENCHMARK_COMMANDLINEFLAGS_H_ diff --git a/src/cycleclock.h b/src/cycleclock.h index 0ad430fd..5b284171 100644 --- a/src/cycleclock.h +++ b/src/cycleclock.h @@ -21,7 +21,10 @@ #ifndef BENCHMARK_CYCLECLOCK_H_ #define BENCHMARK_CYCLECLOCK_H_ -#include +#include + +#include "benchmark/macros.h" +#include "internal_macros.h" #if defined(OS_MACOSX) #include @@ -39,8 +42,6 @@ extern "C" uint64_t __rdtsc(); #endif #include -#include "benchmark/macros.h" - namespace benchmark { // NOTE: only i386 and x86_64 have been well tested. // PPC, sparc, alpha, and ia64 are based on diff --git a/src/re.h b/src/re.h index a2e9fdfd..54117b10 100644 --- a/src/re.h +++ b/src/re.h @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/re_posix.cc b/src/re_posix.cc index 30e248c0..e8fe1fc9 100644 --- a/src/re_posix.cc +++ b/src/re_posix.cc @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/src/re_std.cc b/src/re_std.cc index 30883fda..cfd7a218 100644 --- a/src/re_std.cc +++ b/src/re_std.cc @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include "re.h" namespace benchmark { diff --git a/src/sleep.cc b/src/sleep.cc index 572d04d9..4dfb4d90 100644 --- a/src/sleep.cc +++ b/src/sleep.cc @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,8 +14,10 @@ #include "sleep.h" -#include -#include +#include +#include + +#include "internal_macros.h" namespace benchmark { #ifdef OS_WINDOWS diff --git a/src/sleep.h b/src/sleep.h index 4e877bf0..f1e515ca 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -1,7 +1,7 @@ #ifndef BENCHMARK_SLEEP_H_ #define BENCHMARK_SLEEP_H_ -#include +#include namespace benchmark { const int64_t kNumMillisPerSecond = 1000LL; diff --git a/src/stat.h b/src/stat.h index b556309e..435743c6 100644 --- a/src/stat.h +++ b/src/stat.h @@ -1,11 +1,12 @@ #ifndef BENCHMARK_STAT_H_ #define BENCHMARK_STAT_H_ -#include -#include +#include +#include #include namespace benchmark { + template class Stat1; @@ -103,7 +104,7 @@ class Stat1 { NumType numSamples() const { return numsamples_; } // Return the sum of this sample set - VType sum() const { return sum_; } + VType Sum() const { return sum_; } // Return the mean of this sample set VType Mean() const { @@ -267,9 +268,9 @@ class Stat1MinMax : public Stat1 { Self operator*(const VType &k) const { return Self(*this) *= k; } // Return the maximal value in this sample set - VType max() const { return max_; } + VType Max() const { return max_; } // Return the minimal value in this sample set - VType min() const { return min_; } + VType Min() const { return min_; } private: // The - operation makes no sense with Min/Max diff --git a/src/string_util.cc b/src/string_util.cc new file mode 100644 index 00000000..1be15341 --- /dev/null +++ b/src/string_util.cc @@ -0,0 +1,157 @@ +#include "string_util.h" + +#include +#include +#include +#include +#include + +#include "arraysize.h" + +namespace benchmark { +namespace { + +// kilo, Mega, Giga, Tera, Peta, Exa, Zetta, Yotta. +const char kBigSIUnits[] = "kMGTPEZY"; +// Kibi, Mebi, Gibi, Tebi, Pebi, Exbi, Zebi, Yobi. +const char kBigIECUnits[] = "KMGTPEZY"; +// milli, micro, nano, pico, femto, atto, zepto, yocto. +const char kSmallSIUnits[] = "munpfazy"; + +// We require that all three arrays have the same size. +static_assert(arraysize(kBigSIUnits) == arraysize(kBigIECUnits), + "SI and IEC unit arrays must be the same size"); +static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits), + "Small SI and Big SI unit arrays must be the same size"); + +static const int kUnitsSize = arraysize(kBigSIUnits); + +} // end anonymous namespace + +void ToExponentAndMantissa(double val, double thresh, int precision, + double one_k, std::string* mantissa, + int* exponent) { + std::stringstream mantissa_stream; + + if (val < 0) { + mantissa_stream << "-"; + val = -val; + } + + // Adjust threshold so that it never excludes things which can't be rendered + // in 'precision' digits. + const double adjusted_threshold = + std::max(thresh, 1.0 / std::pow(10.0, precision)); + const double big_threshold = adjusted_threshold * one_k; + const double small_threshold = adjusted_threshold; + + if (val > big_threshold) { + // Positive powers + double scaled = val; + for (size_t i = 0; i < arraysize(kBigSIUnits); ++i) { + scaled /= one_k; + if (scaled <= big_threshold) { + mantissa_stream << scaled; + *exponent = i + 1; + *mantissa = mantissa_stream.str(); + return; + } + } + mantissa_stream << val; + *exponent = 0; + } else if (val < small_threshold) { + // Negative powers + double scaled = val; + for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) { + scaled *= one_k; + if (scaled >= small_threshold) { + mantissa_stream << scaled; + *exponent = -i - 1; + *mantissa = mantissa_stream.str(); + return; + } + } + mantissa_stream << val; + *exponent = 0; + } else { + mantissa_stream << val; + *exponent = 0; + } + *mantissa = mantissa_stream.str(); +} + +std::string ExponentToPrefix(int exponent, bool iec) { + if (exponent == 0) return ""; + + const int index = (exponent > 0 ? exponent - 1 : -exponent - 1); + if (index >= kUnitsSize) return ""; + + const char* array = + (exponent > 0 ? (iec ? kBigIECUnits : kBigSIUnits) : kSmallSIUnits); + if (iec) + return array[index] + std::string("i"); + else + return std::string(1, array[index]); +} + +std::string ToBinaryStringFullySpecified(double value, double threshold, + int precision) { + std::string mantissa; + int exponent; + ToExponentAndMantissa(value, threshold, precision, 1024.0, &mantissa, + &exponent); + return mantissa + ExponentToPrefix(exponent, false); +} + +void AppendHumanReadable(int n, std::string* str) { + std::stringstream ss; + // Round down to the nearest SI prefix. + ss << "/" << ToBinaryStringFullySpecified(n, 1.0, 0); + *str += ss.str(); +} + +std::string HumanReadableNumber(double n) { + // 1.1 means that figures up to 1.1k should be shown with the next unit down; + // this softens edge effects. + // 1 means that we should show one decimal place of precision. + return ToBinaryStringFullySpecified(n, 1.1, 1); +} + +std::string StringPrintFImp(const char *msg, va_list args) +{ + // we might need a second shot at this, so pre-emptivly make a copy + va_list args_cp; + va_copy(args_cp, args); + + // TODO(ericwf): use std::array for first attempt to avoid one memory + // allocation guess what the size might be + std::array local_buff; + std::size_t size = local_buff.size(); + auto ret = std::vsnprintf(local_buff.data(), size, msg, args_cp); + + va_end(args_cp); + + // handle empty expansion + if (ret == 0) + return std::string{}; + if (static_cast(ret) < size) + return std::string(local_buff.data()); + + // we did not provide a long enough buffer on our first attempt. + // add 1 to size to account for null-byte in size cast to prevent overflow + size = static_cast(ret) + 1; + auto buff_ptr = std::unique_ptr(new char[size]); + ret = std::vsnprintf(buff_ptr.get(), size, msg, args); + return std::string(buff_ptr.get()); +} + +std::string StringPrintF(const char* format, ...) +{ + va_list args; + va_start(args, format); + std::string tmp = StringPrintFImp(format, args); + va_end(args); + return tmp; +} + +} // end namespace benchmark diff --git a/src/string_util.h b/src/string_util.h new file mode 100644 index 00000000..56018d55 --- /dev/null +++ b/src/string_util.h @@ -0,0 +1,48 @@ +#ifndef BENCHMARK_STRING_UTIL_H +#define BENCHMARK_STRING_UTIL_H + +#include +#include +#include + +namespace benchmark { + +void ToExponentAndMantissa(double val, double thresh, int precision, + double one_k, std::string* mantissa, int* exponent); + +std::string ExponentToPrefix(int exponent, bool iec); + +std::string ToBinaryStringFullySpecified(double value, double threshold, + int precision); + +void AppendHumanReadable(int n, std::string* str); + +std::string HumanReadableNumber(double n); + +std::string StringPrintF(const char* format, ...); + +inline std::ostream& +StringCatImp(std::ostream& out) noexcept +{ + return out; +} + +template +inline std::ostream& +StringCatImp(std::ostream& out, First&& f, Rest&&... rest) +{ + out << std::forward(f); + return StringCatImp(out, std::forward(rest)...); +} + +template +inline std::string StrCat(Args&&... args) +{ + std::ostringstream ss; + StringCatImp(ss, std::forward(args)...); + return ss.str(); +} + +} // end namespace benchmark + +#endif // BENCHMARK_STRING_UTIL_H \ No newline at end of file diff --git a/src/sysinfo.cc b/src/sysinfo.cc index a67662c8..521acb0e 100644 --- a/src/sysinfo.cc +++ b/src/sysinfo.cc @@ -14,24 +14,26 @@ #include "sysinfo.h" -#include #include -#include -#include -#include -#include #include #include // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD #include #include #include +#include +#include +#include +#include +#include #include #include #include +#include "arraysize.h" #include "check.h" #include "cycleclock.h" +#include "internal_macros.h" #include "sleep.h" namespace benchmark { diff --git a/test/re_test.cc b/test/re_test.cc index 27bf6072..1bba661e 100644 --- a/test/re_test.cc +++ b/test/re_test.cc @@ -1,4 +1,4 @@ -// Copyright 2014 Google Inc. All rights reserved. +// Copyright 2015 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.