Initial commit

Benchmark library builds and runs but only single-threaded. Multithreaded
support needs a bit more love.

Currently requires some C++11 support (g++ 4.6.3 seems to work).
This commit is contained in:
Dominic Hamon 2013-12-18 16:55:45 -08:00
commit 403f354423
21 changed files with 3562 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
CMakeCache.txt
CMakeFiles/
Makefile
bin/
cmake_install.cmake
lib/

43
CMakeLists.txt Normal file
View File

@ -0,0 +1,43 @@
cmake_minimum_required (VERSION 2.8)
project (benchmark)
find_package(Threads)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)
set(CMAKE_CXX_FLAGS "-Wall -Werror --std=c++0x")
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -DDEBUG")
set(CMAKE_CXX_FLAGS_RELEASE "-fno-strict-aliasing -O3 -DNDEBUG")
# Set OS
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_definitions(-DOS_MACOSX)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
add_definitions(-DOS_LINUX)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
add_definitions(-DOS_WINDOWS)
endif()
# Set CPU
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86")
add_definitions(-DARCH_X86)
endif()
# Set up directories
include_directories(${PROJECT_SOURCE_DIR}/include)
include_directories(${PROJECT_SOURCE_DIR}/src)
link_directories(${PROJECT_SOURCE_DIR}/lib)
# Build the targets
FILE(GLOB SOURCE_FILES "src/*.cc")
add_library(benchmark STATIC ${SOURCE_FILES})
add_executable(benchmark_test test/benchmark_test.cc)
target_link_libraries(benchmark_test benchmark ${CMAKE_THREAD_LIBS_INIT})

View File

@ -0,0 +1,540 @@
// Support for registering benchmarks for functions.
/* Example usage:
// Define a function that executes the code to be measured a
// specified number of times:
static void BM_StringCreation(benchmark::State& state) {
while (state.KeepRunning())
std::string empty_string;
}
// Register the function as a benchmark
BENCHMARK(BM_StringCreation);
// Define another benchmark
static void BM_StringCopy(benchmark::State& state) {
std::string x = "hello";
while (state.KeepRunning())
std::string copy(x);
}
BENCHMARK(BM_StringCopy);
// Augment the main() program to invoke benchmarks if specified
// via the --benchmarks command line flag. E.g.,
// my_unittest --benchmarks=all
// my_unittest --benchmarks=BM_StringCreation
// my_unittest --benchmarks=String
// my_unittest --benchmarks='Copy|Creation'
int main(int argc, char** argv) {
Initialize(&argc, argv);
RunSpecifiedBenchmarks();
}
// Sometimes a family of microbenchmarks can be implemented with
// just one routine that takes an extra argument to specify which
// one of the family of benchmarks to run. For example, the following
// code defines a family of microbenchmarks for measuring the speed
// of memcpy() calls of different lengths:
static void BM_memcpy(benchmark::State& state) {
char* src = new char[state.range_x()]; char* dst = new char[state.range_x()];
memset(src, 'x', state.range_x());
while (state.KeepRunning()) {
memcpy(dst, src, state.range_x());
SetBenchmarkBytesProcessed(int64_t_t(state.iterations) * int64(state.range_x()));
delete[] src; delete[] dst;
}
BENCHMARK(BM_memcpy)->Arg(8)->Arg(64)->Arg(512)->Arg(1<<10)->Arg(8<<10);
// The preceding code is quite repetitive, and can be replaced with the
// following short-hand. The following invocation will pick a few
// appropriate arguments in the specified range and will generate a
// microbenchmark for each such argument.
BENCHMARK(BM_memcpy)->Range(8, 8<<10);
// You might have a microbenchmark that depends on two inputs. For
// example, the following code defines a family of microbenchmarks for
// measuring the speed of set insertion.
static void BM_SetInsert(benchmark::State& state) {
while (state.KeepRunning()) {
state.PauseTiming();
set<int> data = ConstructRandomSet(state.range_x());
state.ResumeTiming();
for (int j = 0; j < state.rangeY; ++j)
data.insert(RandomNumber());
}
}
BENCHMARK(BM_SetInsert)
->ArgPair(1<<10, 1)
->ArgPair(1<<10, 8)
->ArgPair(1<<10, 64)
->ArgPair(1<<10, 512)
->ArgPair(8<<10, 1)
->ArgPair(8<<10, 8)
->ArgPair(8<<10, 64)
->ArgPair(8<<10, 512);
// The preceding code is quite repetitive, and can be replaced with
// the following short-hand. The following macro will pick a few
// appropriate arguments in the product of the two specified ranges
// and will generate a microbenchmark for each such pair.
BENCHMARK(BM_SetInsert)->RangePair(1<<10, 8<<10, 1, 512);
// For more complex patterns of inputs, passing a custom function
// to Apply allows programmatic specification of an
// arbitrary set of arguments to run the microbenchmark on.
// The following example enumerates a dense range on
// one parameter, and a sparse range on the second.
static benchmark::internal::Benchmark* CustomArguments(
benchmark::internal::Benchmark* b) {
for (int i = 0; i <= 10; ++i)
for (int j = 32; j <= 1024*1024; j *= 8)
b = b->ArgPair(i, j);
return b;
}
BENCHMARK(BM_SetInsert)->Apply(CustomArguments);
// Templated microbenchmarks work the same way:
// Produce then consume 'size' messages 'iters' times
// Measures throughput in the absence of multiprogramming.
template <class Q> int BM_Sequential(benchmark::State& state) {
Q q;
typename Q::value_type v;
while (state.KeepRunning()) {
for (int i = state.range_x(); i--; )
q.push(v);
for (int e = state.range_x(); e--; )
q.Wait(&v);
}
// actually messages, not bytes:
state.SetBytesProcessed(
static_cast<int64_t>(state.iterations())*state.range_x());
}
BENCHMARK_TEMPLATE(BM_Sequential, WaitQueue<int>)->Range(1<<0, 1<<10);
In a multithreaded test, it is guaranteed that none of the threads will start
until all have called KeepRunning, and all will have finished before KeepRunning
returns false. As such, any global setup or teardown you want to do can be
wrapped in a check against the thread index:
static void BM_MultiThreaded(benchmark::State& state) {
if (state.thread_index == 0) {
// Setup code here.
}
while (state.KeepRunning()) {
// Run the test as normal.
}
if (state.thread_index == 0) {
// Teardown code here.
}
}
*/
#ifndef BENCHMARK_BENCHMARK_H_
#define BENCHMARK_BENCHMARK_H_
#include <stdint.h>
#include <functional>
#include <string>
#include <vector>
#include "macros.h"
namespace benchmark {
// If the --benchmarks flag is empty, do nothing.
//
// Otherwise, run all benchmarks specified by the --benchmarks flag,
// and exit after running the benchmarks.
extern void RunSpecifiedBenchmarks();
// ------------------------------------------------------
// Routines that can be called from within a benchmark
//
// REQUIRES: a benchmark is currently executing
extern void SetLabel(const std::string& label);
// If this routine is called, peak memory allocation past this point in the
// benchmark is reported at the end of the benchmark report line. (It is
// computed by running the benchmark once with a single iteration and a memory
// tracer.)
extern void MemoryUsage();
// If a particular benchmark is I/O bound, or if for some reason CPU
// timings are not representative, call this method from within the
// benchmark routine. If called, the elapsed time will be used to
// control how many iterations are run, and in the printing of
// items/second or MB/seconds values. If not called, the cpu time
// used by the benchmark will be used.
extern void UseRealTime();
namespace internal {
class Benchmark;
}
// State is passed to a running Benchmark and contains state for the
// benchmark to use.
class State {
public:
// Returns true iff the benchmark should continue through another iteration.
bool KeepRunning();
void PauseTiming();
void ResumeTiming();
// Set the number of bytes processed by the current benchmark
// execution. This routine is typically called once at the end of a
// throughput oriented benchmark. If this routine is called with a
// value > 0, the report is printed in MB/sec instead of nanoseconds
// per iteration.
//
// REQUIRES: a benchmark has exited its KeepRunning loop.
void SetBytesProcessed(int64_t bytes);
// If this routine is called with items > 0, then an items/s
// label is printed on the benchmark report line for the currently
// executing benchmark. It is typically called at the end of a processing
// benchmark where a processing items/second output is desired.
//
// REQUIRES: a benchmark has exited its KeepRunning loop.
void SetItemsProcessed(int64_t items);
// If this routine is called, the specified label is printed at the
// end of the benchmark report line for the currently executing
// benchmark. Example:
// static void BM_Compress(int iters) {
// ...
// double compress = input_size / output_size;
// benchmark::SetLabel(StringPrintf("compress:%.1f%%", 100.0*compression));
// }
// Produces output that looks like:
// BM_Compress 50 50 14115038 compress:27.3%
//
// REQUIRES: a benchmark has exited its KeepRunning loop.
void SetLabel(const std::string& label);
// Range arguments for this run. CHECKs if the argument has been set.
int range_x() const;
int range_y() const;
int iterations() const { return total_iterations_; }
const int thread_index;
private:
class FastClock;
struct SharedState;
State(FastClock* clock, SharedState* s, int t);
bool StartRunning();
bool FinishInterval();
bool MaybeStop();
void NewInterval();
bool AllStarting();
bool RunAnotherInterval() const;
void Run();
enum EState {
STATE_INITIAL, // KeepRunning hasn't been called
STATE_STARTING, // KeepRunning called, waiting for other threads
STATE_RUNNING, // Running and being timed
STATE_STOPPING, // Not being timed but waiting for other threads
STATE_STOPPED, // Stopped
} state_;
FastClock* clock_;
// State shared by all BenchmarkRun objects that belong to the same
// BenchmarkInstance
SharedState* shared_;
// Custom label set by the user.
std::string label_;
// Each State object goes through a sequence of measurement intervals. By
// default each interval is approx. 100ms in length. The following stats are
// kept for each interval.
int64_t iterations_;
double start_cpu_;
double start_time_;
int64_t stop_time_micros_;
double start_pause_;
double pause_time_;
// Total number of iterations for all finished runs.
int64_t total_iterations_;
// Approximate time in microseconds for one interval of execution.
// Dynamically adjusted as needed.
int64_t interval_micros_;
// True if the current interval is the continuation of a previous one.
bool is_continuation_;
friend class internal::Benchmark;
DISALLOW_COPY_AND_ASSIGN(State);
};
namespace internal {
class BenchmarkReporter;
typedef std::function<void(State&)> BenchmarkFunction;
// Run all benchmarks whose name is a partial match for the regular
// expression in "spec". The results of benchmark runs are fed to "reporter".
void RunMatchingBenchmarks(const std::string& spec,
BenchmarkReporter* reporter);
// Extract the list of benchmark names that match the specified regular
// expression.
void FindMatchingBenchmarkNames(const std::string& re,
std::vector<std::string>* benchmark_names);
// ------------------------------------------------------
// Benchmark registration object. The BENCHMARK() macro expands
// into an internal::Benchmark* object. Various methods can
// be called on this object to change the properties of the benchmark.
// Each method returns "this" so that multiple method calls can
// chained into one expression.
class Benchmark {
public:
// The Benchmark takes ownership of the Callback pointed to by f.
Benchmark(const char* name, BenchmarkFunction f);
~Benchmark();
// Note: the following methods all return "this" so that multiple
// method calls can be chained together in one expression.
// Run this benchmark once with "x" as the extra argument passed
// to the function.
// REQUIRES: The function passed to the constructor must accept an arg1.
Benchmark* Arg(int x);
// Run this benchmark once for a number of values picked from the
// range [start..limit]. (start and limit are always picked.)
// REQUIRES: The function passed to the constructor must accept an arg1.
Benchmark* Range(int start, int limit);
// Run this benchmark once for every value in the range [start..limit]
// REQUIRES: The function passed to the constructor must accept an arg1.
Benchmark* DenseRange(int start, int limit);
// Run this benchmark once with "x,y" as the extra arguments passed
// to the function.
// REQUIRES: The function passed to the constructor must accept arg1,arg2.
Benchmark* ArgPair(int x, int y);
// Pick a set of values A from the range [lo1..hi1] and a set
// of values B from the range [lo2..hi2]. Run the benchmark for
// every pair of values in the cartesian product of A and B
// (i.e., for all combinations of the values in A and B).
// REQUIRES: The function passed to the constructor must accept arg1,arg2.
Benchmark* RangePair(int lo1, int hi1, int lo2, int hi2);
// Pass this benchmark object to *func, which can customize
// the benchmark by calling various methods like Arg, ArgPair,
// Threads, etc.
Benchmark* Apply(void (*func)(Benchmark* benchmark));
// Support for running multiple copies of the same benchmark concurrently
// in multiple threads. This may be useful when measuring the scaling
// of some piece of code.
// Run one instance of this benchmark concurrently in t threads.
Benchmark* Threads(int t);
// Pick a set of values T from [min_threads,max_threads].
// min_threads and max_threads are always included in T. Run this
// benchmark once for each value in T. The benchmark run for a
// particular value t consists of t threads running the benchmark
// function concurrently. For example, consider:
// BENCHMARK(Foo)->ThreadRange(1,16);
// This will run the following benchmarks:
// Foo in 1 thread
// Foo in 2 threads
// Foo in 4 threads
// Foo in 8 threads
// Foo in 16 threads
Benchmark* ThreadRange(int min_threads, int max_threads);
// Equivalent to ThreadRange(NumCPUs(), NumCPUs())
Benchmark* ThreadPerCpu();
// TODO(dominich): Control whether or not real-time is used for this benchmark
// TODO(dominich): Control the default number of iterations
// -------------------------------
// Following methods are not useful for clients
// Used inside the benchmark implementation
struct Instance;
struct ThreadStats;
// Extract the list of benchmark instances that match the specified
// regular expression.
static void FindBenchmarks(const std::string& re,
std::vector<Instance>* benchmarks);
// Measure the overhead of an empty benchmark to subtract later.
static void MeasureOverhead();
private:
std::vector<Benchmark::Instance> CreateBenchmarkInstances(int rangeXindex,
int rangeYindex);
std::string name_;
BenchmarkFunction function_;
int registration_index_;
std::vector<int> rangeX_;
std::vector<int> rangeY_;
std::vector<int> thread_counts_;
// Special value placed in thread_counts_ to stand for NumCPUs()
static const int kNumCpuMarker = -1;
// Special value used to indicate that no range is required.
static const int kNoRange = -1;
static void AddRange(std::vector<int>* dst, int lo, int hi, int mult);
static double MeasurePeakHeapMemory(const Instance& b);
static void RunInstance(const Instance& b, BenchmarkReporter* br);
friend class ::benchmark::State;
friend struct ::benchmark::internal::Benchmark::Instance;
friend void ::benchmark::internal::RunMatchingBenchmarks(
const std::string&, BenchmarkReporter*);
DISALLOW_COPY_AND_ASSIGN(Benchmark);
};
// ------------------------------------------------------
// Benchmarks reporter interface + data containers.
struct BenchmarkContextData {
int num_cpus;
double mhz_per_cpu;
//std::string cpu_info;
bool cpu_scaling_enabled;
// The number of chars in the longest benchmark name.
int name_field_width;
};
struct BenchmarkRunData {
BenchmarkRunData() :
thread_index(-1),
iterations(1),
real_accumulated_time(0),
cpu_accumulated_time(0),
bytes_per_second(0),
items_per_second(0),
max_heapbytes_used(0) {}
std::string benchmark_name;
std::string report_label;
int thread_index;
int64_t iterations;
double real_accumulated_time;
double cpu_accumulated_time;
// Zero if not set by benchmark.
double bytes_per_second;
double items_per_second;
// This is set to 0.0 if memory tracing is not enabled.
double max_heapbytes_used;
};
// Interface for custom benchmark result printers.
// By default, benchmark reports are printed to stdout. However an application
// can control the destination of the reports by calling
// RunMatchingBenchmarks and passing it a custom reporter object.
// The reporter object must implement the following interface.
class BenchmarkReporter {
public:
// Called once for every suite of benchmarks run.
// The parameter "context" contains information that the
// reporter may wish to use when generating its report, for example the
// platform under which the benchmarks are running. The benchmark run is
// never started if this function returns false, allowing the reporter
// to skip runs based on the context information.
virtual bool ReportContext(const BenchmarkContextData& context) = 0;
// Called once for each group of benchmark runs, gives information about
// cpu-time and heap memory usage during the benchmark run.
// Note that all the grouped benchmark runs should refer to the same
// benchmark, thus have the same name.
virtual void ReportRuns(const std::vector<BenchmarkRunData>& report) = 0;
virtual ~BenchmarkReporter();
};
// ------------------------------------------------------
// Internal implementation details follow; please ignore
// Given a collection of reports, computes their mean and stddev.
// REQUIRES: all runs in "reports" must be from the same benchmark.
void ComputeStats(const std::vector<BenchmarkRunData>& reports,
BenchmarkRunData* mean_data,
BenchmarkRunData* stddev_data);
// Simple reporter that outputs benchmark data to the console. This is the
// default reporter used by RunSpecifiedBenchmarks().
class ConsoleReporter : public BenchmarkReporter {
public:
virtual bool ReportContext(const BenchmarkContextData& context);
virtual void ReportRuns(const std::vector<BenchmarkRunData>& reports);
private:
std::string PrintMemoryUsage(double bytes);
virtual void PrintRunData(const BenchmarkRunData& report);
int name_field_width_;
};
} // end namespace internal
void Initialize(int* argc, const char** argv);
} // end namespace benchmark
// ------------------------------------------------------
// Macro to register benchmarks
// Helpers for generating unique variable names
#define BENCHMARK_CONCAT(a, b, c) BENCHMARK_CONCAT2(a, b, c)
#define BENCHMARK_CONCAT2(a, b, c) a ## b ## c
#define BENCHMARK(n) \
static ::benchmark::internal::Benchmark* \
BENCHMARK_CONCAT(__benchmark_, n, __LINE__) ATTRIBUTE_UNUSED = \
(new ::benchmark::internal::Benchmark(#n, n))
// Old-style macros
#define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a))
#define BENCHMARK_WITH_ARG2(n, a1, a2) BENCHMARK(n)->ArgPair((a1), (a2))
#define BENCHMARK_RANGE(n, lo, hi) BENCHMARK(n)->Range((lo), (hi))
#define BENCHMARK_RANGE2(n, l1, h1, l2, h2) \
BENCHMARK(n)->RangePair((l1), (h1), (l2), (h2))
// This will register a benchmark for a templatized function. For example:
//
// template<int arg>
// void BM_Foo(int iters);
//
// BENCHMARK_TEMPLATE(BM_Foo, 1);
//
// will register BM_Foo<1> as a benchmark.
#define BENCHMARK_TEMPLATE(n, a) \
static ::benchmark::internal::Benchmark* \
BENCHMARK_CONCAT(__benchmark_, n, __LINE__) ATTRIBUTE_UNUSED = \
(new ::benchmark::internal::Benchmark(#n "<" #a ">", n<a>))
#define BENCHMARK_TEMPLATE2(n, a, b) \
static ::benchmark::internal::Benchmark* \
BENCHMARK_CONCAT(__benchmark_, n, __LINE__) ATTRIBUTE_UNUSED = \
(new ::benchmark::internal::Benchmark(#n "<" #a "," #b ">", n<a, b>))
#endif // BENCHMARK_BENCHMARK_H_

120
include/benchmark/macros.h Normal file
View File

@ -0,0 +1,120 @@
#ifndef BENCHMARK_MACROS_H_
#define BENCHMARK_MACROS_H_
#include <assert.h>
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&);
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// One caveat is that, for C++03, arraysize() doesn't accept any array of
// an anonymous type or a type defined inside a function. In these rare
// cases, you have to use the unsafe ARRAYSIZE() macro below. This is
// due to a limitation in C++03's template system. The limitation has
// been removed in C++11.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef COMPILER_MSVC
template <typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
// The STATIC_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// STATIC_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// STATIC_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
template <bool>
struct StaticAssert {
};
#define STATIC_ASSERT(expr, msg) \
typedef StaticAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
// Implementation details of STATIC_ASSERT:
//
// - STATIC_ASSERT works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
// - The simpler definition
//
// #define STATIC_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
//
// does not work, as gcc supports variable-length arrays whose sizes
// are determined at run-time (this is gcc's extension and not part
// of the C++ standard). As a result, gcc fails to reject the
// following code with the simple definition:
//
// int foo;
// STATIC_ASSERT(foo, msg); // not supposed to compile as foo is
// // not a compile-time constant.
//
// - By using the type StaticAssert<(bool(expr))>, we ensures that
// expr is a compile-time constant. (Template arguments must be
// determined at compile-time.)
//
// - The outer parentheses in StaticAssert<(bool(expr))> are necessary
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
//
// StaticAssert<bool(expr)>
//
// instead, these compilers will refuse to compile
//
// STATIC_ASSERT(5 > 0, some_message);
//
// (They seem to think the ">" in "5 > 0" marks the end of the
// template argument list.)
//
// - The array size is (bool(expr) ? 1 : -1), instead of simply
//
// ((expr) ? 1 : -1).
//
// This is to avoid running into a bug in MS VC 7.1, which
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
#define CHECK(b) do { if (!(b)) assert(false); } while(0)
#define CHECK_EQ(a, b) CHECK((a) == (b))
#define CHECK_GE(a, b) CHECK((a) >= (b))
#define CHECK_LE(a, b) CHECK((a) <= (b))
#define CHECK_GT(a, b) CHECK((a) > (b))
#define CHECK_LT(a, b) CHECK((a) < (b))
//
// Prevent the compiler from complaining about or optimizing away variables
// that appear unused.
#define ATTRIBUTE_UNUSED __attribute__ ((unused))
//
// For functions we want to force inline or not inline.
// Introduced in gcc 3.1.
#define ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline))
#define HAVE_ATTRIBUTE_ALWAYS_INLINE 1
#define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
#define HAVE_ATTRIBUTE_NOINLINE 1
#endif // BENCHMARK_MACROS_H_

1197
src/benchmark.cc Normal file

File diff suppressed because it is too large Load Diff

82
src/colorprint.cc Normal file
View File

@ -0,0 +1,82 @@
#include "colorprint.h"
#include <stdarg.h>
#include "commandlineflags.h"
DECLARE_bool(color_print);
namespace {
#ifdef OS_WINDOWS
typedef WORD PlatformColorCode;
#else
typedef const char* PlatformColorCode;
#endif
PlatformColorCode GetPlatformColorCode(LogColor color) {
#ifdef OS_WINDOWS
switch (color) {
case COLOR_RED: return FOREGROUND_RED;
case COLOR_GREEN: return FOREGROUND_GREEN;
case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN;
case COLOR_BLUE: return FOREGROUND_BLUE;
case COLOR_MAGENTA: return FOREGROUND_BLUE | FOREGROUND_RED;
case COLOR_CYAN: return FOREGROUND_BLUE | FOREGROUND_GREEN;
case COLOR_WHITE: // fall through to default
default: return 0;
}
#else
switch (color) {
case COLOR_RED: return "1";
case COLOR_GREEN: return "2";
case COLOR_YELLOW: return "3";
case COLOR_BLUE: return "4";
case COLOR_MAGENTA: return "5";
case COLOR_CYAN: return "6";
case COLOR_WHITE: return "7";
default: return NULL;
};
#endif
}
} // end namespace
void ColorPrintf(LogColor color, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
if (!FLAGS_color_print) {
vprintf(fmt, args);
va_end(args);
return;
}
#ifdef OS_WINDOWS
const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
// Gets the current text color.
CONSOLE_SCREEN_BUFFER_INFO buffer_info;
GetConsoleScreenBufferInfo(stdout_handle, &buffer_info);
const WORD old_color_attrs = buffer_info.wAttributes;
// We need to flush the stream buffers into the console before each
// SetConsoleTextAttribute call lest it affect the text that is already
// printed but has not yet reached the console.
fflush(stdout);
SetConsoleTextAttribute(stdout_handle,
GetPlatformColorCode(color) | FOREGROUND_INTENSITY);
vprintf(fmt, args);
fflush(stdout);
// Restores the text color.
SetConsoleTextAttribute(stdout_handle, old_color_attrs);
#else
const char* color_code = GetPlatformColorCode(color);
if (color_code)
fprintf(stdout, "\033[0;3%sm", color_code);
vprintf(fmt, args);
printf("\033[m"); // Resets the terminal to default.
#endif
va_end(args);
}

17
src/colorprint.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef BENCHMARK_COLORPRINT_H_
#define BENCHMARK_COLORPRINT_H_
enum LogColor {
COLOR_DEFAULT,
COLOR_RED,
COLOR_GREEN,
COLOR_YELLOW,
COLOR_BLUE,
COLOR_MAGENTA,
COLOR_CYAN,
COLOR_WHITE
};
void ColorPrintf(LogColor color, const char* fmt, ...);
#endif // BENCHMARK_COLORPRINT_H_

213
src/commandlineflags.cc Normal file
View File

@ -0,0 +1,213 @@
#include "commandlineflags.h"
#include <string.h>
#include <iostream>
#include <limits>
namespace benchmark {
// Parses 'str' for a 32-bit signed integer. If successful, writes
// the result to *value and returns true; otherwise leaves *value
// unchanged and returns false.
bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) {
// Parses the environment variable as a decimal integer.
char* end = NULL;
const long long_value = strtol(str, &end, 10); // NOLINT
// Has strtol() consumed all characters in the string?
if (*end != '\0') {
// No - an invalid character was encountered.
std::cerr << src_text << " is expected to be a 32-bit integer, "
<< "but actually has value \"" << str << "\".\n";
return false;
}
// Is the parsed value in the range of an Int32?
const int32_t result = static_cast<int32_t>(long_value);
if (long_value == std::numeric_limits<long>::max() ||
long_value == std::numeric_limits<long>::min() ||
// The parsed value overflows as a long. (strtol() returns
// LONG_MAX or LONG_MIN when the input overflows.)
result != long_value
// The parsed value overflows as an Int32.
) {
std::cerr << src_text << " is expected to be a 32-bit integer, "
<< "but actually has value \"" << str << "\", "
<< "which overflows.\n";
return false;
}
*value = result;
return true;
}
// Parses 'str' for a double. If successful, writes the result to *value and
// returns true; otherwise leaves *value unchanged and returns false.
bool ParseDouble(const std::string& src_text, const char* str, double* value) {
// Parses the environment variable as a decimal integer.
char* end = NULL;
const double double_value = strtod(str, &end); // NOLINT
// Has strtol() consumed all characters in the string?
if (*end != '\0') {
// No - an invalid character was encountered.
std::cerr << src_text << " is expected to be a double, "
<< "but actually has value \"" << str << "\".\n";
return false;
}
*value = double_value;
return true;
}
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)
// 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);
return (env != NULL && env[0] != '\0') ? env : NULL;
#else
return getenv(name);
#endif
}
// Returns the name of the environment variable corresponding to the
// given flag. For example, FlagToEnvVar("foo") will return
// "BENCHMARK_FOO" in the open-source version.
static std::string FlagToEnvVar(const char* flag) {
const std::string flag_str(flag);
std::string env_var;
for (size_t i = 0; i != flag_str.length(); ++i)
env_var += ::toupper(flag_str.c_str()[i]);
return "BENCHMARK_" + env_var;
}
// Reads and returns the Boolean environment variable corresponding to
// the given flag; if it's not set, returns default_value.
//
// The value is considered true iff it's not "0".
bool BoolFromEnv(const char* flag, bool default_value) {
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = GetEnv(env_var.c_str());
return string_value == NULL ?
default_value : strcmp(string_value, "0") != 0;
}
// Reads and returns a 32-bit integer stored in the environment
// variable corresponding to the given flag; if it isn't set or
// doesn't represent a valid 32-bit integer, returns default_value.
int32_t Int32FromEnv(const char* flag, int32_t default_value) {
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = GetEnv(env_var.c_str());
if (string_value == NULL) {
// The environment variable is not set.
return default_value;
}
int32_t result = default_value;
if (!ParseInt32(std::string("Environment variable ") + env_var,
string_value, &result)) {
std::cout << "The default value " << default_value << " is used.\n";
return default_value;
}
return result;
}
// Reads and returns the string environment variable corresponding to
// the given flag; if it's not set, returns default_value.
const char* StringFromEnv(const char* flag, const char* default_value) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value = GetEnv(env_var.c_str());
return value == NULL ? default_value : value;
}
// Parses a string as a command line flag. The string should have
// the format "--flag=value". When def_optional is true, the "=value"
// part can be omitted.
//
// Returns the value of the flag, or NULL if the parsing failed.
const char* ParseFlagValue(const char* str,
const char* flag,
bool def_optional) {
// str and flag must not be NULL.
if (str == NULL || flag == NULL) return NULL;
// The flag must start with "--".
const std::string flag_str = std::string("--") + std::string(flag);
const size_t flag_len = flag_str.length();
if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
// Skips the flag name.
const char* flag_end = str + flag_len;
// When def_optional is true, it's OK to not have a "=value" part.
if (def_optional && (flag_end[0] == '\0'))
return flag_end;
// If def_optional is true and there are more characters after the
// flag name, or if def_optional is false, there must be a '=' after
// the flag name.
if (flag_end[0] != '=') return NULL;
// Returns the string after "=".
return flag_end + 1;
}
bool ParseBoolFlag(const char* str, const char* flag, bool* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, true);
// Aborts if the parsing failed.
if (value_str == NULL) return false;
// Converts the string value to a bool.
*value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
return true;
}
bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
if (value_str == NULL) return false;
// Sets *value to the value of the flag.
return ParseInt32(std::string("The value of flag --") + flag,
value_str, value);
}
bool ParseDoubleFlag(const char* str, const char* flag, double* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
if (value_str == NULL) return false;
// Sets *value to the value of the flag.
return ParseDouble(std::string("The value of flag --") + flag,
value_str, value);
}
bool ParseStringFlag(const char* str, const char* flag, std::string* value) {
// Gets the value of the flag as a string.
const char* const value_str = ParseFlagValue(str, flag, false);
// Aborts if the parsing failed.
if (value_str == NULL) return false;
*value = value_str;
return true;
}
bool IsFlag(const char* str, const char* flag) {
return (ParseFlagValue(str, flag, true) != NULL);
}
} // end namespace benchmark

79
src/commandlineflags.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef BENCHMARK_COMMANDLINEFLAGS_H_
#define BENCHMARK_COMMANDLINEFLAGS_H_
#include <stdint.h>
#include <string>
// Macro for referencing flags.
#define FLAG(name) FLAGS_##name
// Macros for declaring flags.
#define DECLARE_bool(name) extern bool FLAG(name)
#define DECLARE_int32(name) extern int32_t FLAG(name)
#define DECLARE_int64(name) extern int64_t FLAG(name)
#define DECLARE_double(name) extern double FLAG(name)
#define DECLARE_string(name) extern std::string FLAG(name)
// Macros for defining flags.
#define DEFINE_bool(name, default_val, doc) bool FLAG(name) = (default_val)
#define DEFINE_int32(name, default_val, doc) int32_t FLAG(name) = (default_val)
#define DEFINE_int64(name, default_val, doc) int64_t FLAG(name) = (default_val)
#define DEFINE_double(name, default_val, doc) double FLAG(name) = (default_val)
#define DEFINE_string(name, default_val, doc) \
std::string FLAG(name) = (default_val)
namespace benchmark {
// Parses 'str' for a 32-bit signed integer. If successful, writes the result
// to *value and returns true; otherwise leaves *value unchanged and returns
// false.
bool ParseInt32(const std::string& src_text, const char* str, int32_t* value);
// Parses a bool/Int32/string from the environment variable
// corresponding to the given Google Test flag.
bool BoolFromEnv(const char* flag, bool default_val);
int32_t Int32FromEnv(const char* flag, int32_t default_val);
double DoubleFromEnv(const char* flag, double default_val);
const char* StringFromEnv(const char* flag, const char* default_val);
// Parses a string for a bool flag, in the form of either
// "--flag=value" or "--flag".
//
// In the former case, the value is taken as true as long as it does
// not start with '0', 'f', or 'F'.
//
// In the latter case, the value is taken as true.
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
bool ParseBoolFlag(const char* str, const char* flag, bool* value);
// Parses a string for an Int32 flag, in the form of
// "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
bool ParseInt32Flag(const char* str, const char* flag, int32_t* value);
// Parses a string for a Double flag, in the form of
// "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
bool ParseDoubleFlag(const char* str, const char* flag, double* value);
// Parses a string for a string flag, in the form of
// "--flag=value".
//
// On success, stores the value of the flag in *value, and returns
// true. On failure, returns false without changing *value.
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
#endif // BENCHMARK_COMMANDLINEFLAGS_H_

129
src/cycleclock.h Normal file
View File

@ -0,0 +1,129 @@
// ----------------------------------------------------------------------
// CycleClock
// A CycleClock tells you the current time in Cycles. The "time"
// is actually time since power-on. This is like time() but doesn't
// involve a system call and is much more precise.
//
// NOTE: Not all cpu/platform/kernel combinations guarantee that this
// clock increments at a constant rate or is synchronized across all logical
// cpus in a system.
//
// If you need the above guarantees, please consider using a different
// API. There are efforts to provide an interface which provides a millisecond
// granularity and implemented as a memory read. A memory read is generally
// cheaper than the CycleClock for many architectures.
//
// Also, in some out of order CPU implementations, the CycleClock is not
// serializing. So if you're trying to count at cycles granularity, your
// data might be inaccurate due to out of order instruction execution.
// ----------------------------------------------------------------------
#ifndef BENCHMARK_CYCLECLOCK_H_
#define BENCHMARK_CYCLECLOCK_H_
#include <stdint.h>
#if defined(OS_MACOSX)
# include <mach/mach_time.h>
#endif
// For MSVC, we want to use '_asm rdtsc' when possible (since it works
// with even ancient MSVC compilers), and when not possible the
// __rdtsc intrinsic, declared in <intrin.h>. Unfortunately, in some
// environments, <windows.h> and <intrin.h> have conflicting
// declarations of some other intrinsics, breaking compilation.
// Therefore, we simply declare __rdtsc ourselves. See also
// http://connect.microsoft.com/VisualStudio/feedback/details/262047
#if defined(COMPILER_MSVC) && !defined(_M_IX86)
extern "C" uint64_t __rdtsc();
#pragma intrinsic(__rdtsc)
#endif
#include <sys/time.h>
// NOTE: only i386 and x86_64 have been well tested.
// PPC, sparc, alpha, and ia64 are based on
// http://peter.kuscsik.com/wordpress/?p=14
// with modifications by m3b. See also
// https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h
struct CycleClock {
// This should return the number of cycles since power-on. Thread-safe.
static inline int64_t Now() {
#if defined(OS_MACOSX)
// this goes at the top because we need ALL Macs, regardless of
// architecture, to return the number of "mach time units" that
// have passed since startup. See sysinfo.cc where
// InitializeSystemInfo() sets the supposed cpu clock frequency of
// macs to the number of mach time units per second, not actual
// CPU clock frequency (which can change in the face of CPU
// frequency scaling). Also note that when the Mac sleeps, this
// counter pauses; it does not continue counting, nor does it
// reset to zero.
return mach_absolute_time();
#elif defined(__i386__)
int64_t ret;
__asm__ volatile ("rdtsc" : "=A" (ret) );
return ret;
#elif defined(__x86_64__) || defined(__amd64__)
uint64_t low, high;
__asm__ volatile ("rdtsc" : "=a" (low), "=d" (high));
return (high << 32) | low;
#elif defined(__powerpc__) || defined(__ppc__)
// This returns a time-base, which is not always precisely a cycle-count.
int64_t tbl, tbu0, tbu1;
asm("mftbu %0" : "=r" (tbu0));
asm("mftb %0" : "=r" (tbl));
asm("mftbu %0" : "=r" (tbu1));
tbl &= -static_cast<int64>(tbu0 == tbu1);
// high 32 bits in tbu1; low 32 bits in tbl (tbu0 is garbage)
return (tbu1 << 32) | tbl;
#elif defined(__sparc__)
int64_t tick;
asm(".byte 0x83, 0x41, 0x00, 0x00");
asm("mov %%g1, %0" : "=r" (tick));
return tick;
#elif defined(__ia64__)
int64_t itc;
asm("mov %0 = ar.itc" : "=r" (itc));
return itc;
#elif defined(COMPILER_MSVC) && defined(_M_IX86)
// Older MSVC compilers (like 7.x) don't seem to support the
// __rdtsc intrinsic properly, so I prefer to use _asm instead
// when I know it will work. Otherwise, I'll use __rdtsc and hope
// the code is being compiled with a non-ancient compiler.
_asm rdtsc
#elif defined(COMPILER_MSVC)
return __rdtsc();
#elif defined(ARMV3)
#if defined(ARMV6) // V6 is the earliest arch that has a standard cyclecount
uint32_t pmccntr;
uint32_t pmuseren;
uint32_t pmcntenset;
// Read the user mode perf monitor counter access permissions.
asm("mrc p15, 0, %0, c9, c14, 0" : "=r" (pmuseren));
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
asm("mrc p15, 0, %0, c9, c12, 1" : "=r" (pmcntenset));
if (pmcntenset & 0x80000000ul) { // Is it counting?
asm("mrc p15, 0, %0, c9, c13, 0" : "=r" (pmccntr));
// The counter is set up to count every 64th cycle
return static_cast<int64>(pmccntr) * 64; // Should optimize to << 6
}
}
#endif
struct timeval tv;
gettimeofday(&tv, NULL);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__mips__)
// mips apparently only allows rdtsc for superusers, so we fall
// back to gettimeofday. It's possible clock_gettime would be better.
struct timeval tv;
gettimeofday(&tv, NULL);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#else
// The soft failover to a generic implementation is automatic only for ARM.
// For other platforms the developer is expected to make an attempt to create
// a fast implementation and use generic version if nothing better is available.
#error You need to define CycleTimer for your OS and CPU
#endif
}
};
#endif // BENCHMARK_CYCLECLOCK_H_

110
src/macros.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef BENCHMARK_MACROS_H_
#define BENCHMARK_MACROS_H_
#include <assert.h>
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&);
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// One caveat is that, for C++03, arraysize() doesn't accept any array of
// an anonymous type or a type defined inside a function. In these rare
// cases, you have to use the unsafe ARRAYSIZE() macro below. This is
// due to a limitation in C++03's template system. The limitation has
// been removed in C++11.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef COMPILER_MSVC
template <typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
// The STATIC_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// STATIC_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// STATIC_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
template <bool>
struct StaticAssert {
};
#define STATIC_ASSERT(expr, msg) \
typedef StaticAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
// Implementation details of STATIC_ASSERT:
//
// - STATIC_ASSERT works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
// - The simpler definition
//
// #define STATIC_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
//
// does not work, as gcc supports variable-length arrays whose sizes
// are determined at run-time (this is gcc's extension and not part
// of the C++ standard). As a result, gcc fails to reject the
// following code with the simple definition:
//
// int foo;
// STATIC_ASSERT(foo, msg); // not supposed to compile as foo is
// // not a compile-time constant.
//
// - By using the type StaticAssert<(bool(expr))>, we ensures that
// expr is a compile-time constant. (Template arguments must be
// determined at compile-time.)
//
// - The outer parentheses in StaticAssert<(bool(expr))> are necessary
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
//
// StaticAssert<bool(expr)>
//
// instead, these compilers will refuse to compile
//
// STATIC_ASSERT(5 > 0, some_message);
//
// (They seem to think the ">" in "5 > 0" marks the end of the
// template argument list.)
//
// - The array size is (bool(expr) ? 1 : -1), instead of simply
//
// ((expr) ? 1 : -1).
//
// This is to avoid running into a bug in MS VC 7.1, which
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
#define CHECK(b) do { if (!(b)) assert(false); } while(0)
#define CHECK_EQ(a, b) CHECK((a) == (b))
#define CHECK_GE(a, b) CHECK((a) >= (b))
#define CHECK_LE(a, b) CHECK((a) <= (b))
#define CHECK_GT(a, b) CHECK((a) > (b))
#define CHECK_LT(a, b) CHECK((a) < (b))
#define ATTRIBUTE_UNUSED __attribute__ ((unused))
#endif // BENCHMARK_MACROS_H_

20
src/mutex_lock.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef BENCHMARK_MUTEX_LOCK_H_
#define BENCHMARK_MUTEX_LOCK_H_
#include <pthread.h>
class mutex_lock {
public:
explicit mutex_lock(pthread_mutex_t* mu) : mu_(mu) {
pthread_mutex_lock(mu_);
}
~mutex_lock() {
pthread_mutex_unlock(mu_);
}
private:
pthread_mutex_t* mu_;
};
#endif // BENCHMARK_MUTEX_LOCK_H_

8
src/port.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef BENCHMARK_PORT_H_
#define BENCHMARK_PORT_H_
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&);
#endif // BENCHMARK_PORT_H_

42
src/sleep.cc Normal file
View File

@ -0,0 +1,42 @@
#include "sleep.h"
#include <time.h>
#include <errno.h>
#ifdef OS_WINDOWS
// Window's _sleep takes milliseconds argument.
void SleepForMilliseconds(int milliseconds) {
_sleep(milliseconds);
}
void SleepForSeconds(double seconds) {
SleepForMilliseconds(static_cast<int>(seconds * 1000));
}
#else // OS_WINDOWS
static const int64_t kNumMillisPerSecond = 1000LL;
static const int64_t kNumMicrosPerMilli = 1000LL;
static const int64_t kNumMicrosPerSecond = kNumMillisPerSecond * 1000LL;
static const int64_t kNumNanosPerMicro = 1000LL;
void SleepForMicroseconds(int64_t microseconds) {
struct timespec sleep_time;
sleep_time.tv_sec = microseconds / kNumMicrosPerSecond;
sleep_time.tv_nsec = (microseconds % kNumMicrosPerSecond) * kNumNanosPerMicro;
while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR)
; // Ignore signals and wait for the full interval to elapse.
}
void SleepForMilliseconds(int milliseconds) {
SleepForMicroseconds(static_cast<int64_t>(milliseconds) * kNumMicrosPerMilli);
}
void SleepForSeconds(double seconds) {
SleepForMicroseconds(static_cast<int64_t>(seconds * kNumMicrosPerSecond));
}
#endif // OS_WINDOWS

10
src/sleep.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef BENCHMARK_SLEEP_H_
#define BENCHMARK_SLEEP_H_
#include <stdint.h>
void SleepForMicroseconds(int64_t microseconds);
void SleepForMilliseconds(int milliseconds);
void SleepForSeconds(double seconds);
#endif // BENCHMARK_SLEEP_H_

306
src/stat.h Normal file
View File

@ -0,0 +1,306 @@
#ifndef BENCHMARK_STAT_H_
#define BENCHMARK_STAT_H_
#include <math.h>
#include <iostream>
#include <limits>
template <typename VType, typename NumType>
class Stat1;
template <typename VType, typename NumType>
class Stat1MinMax;
typedef Stat1<float, float> Stat1_f;
typedef Stat1<double, double> Stat1_d;
typedef Stat1MinMax<float, float> Stat1MinMax_f;
typedef Stat1MinMax<double, double> Stat1MinMax_d;
template <typename VType> class Vector2;
template <typename VType> class Vector3;
template <typename VType> class Vector4;
template <typename VType, typename NumType>
class Stat1 {
public:
typedef Stat1<VType, NumType> Self;
Stat1() {
Clear();
}
void Clear() {
numsamples_ = NumType();
sum_squares_ = sum_ = VType();
}
// Create a sample of value dat and weight 1
explicit Stat1(const VType &dat) {
sum_ = dat;
sum_squares_ = Sqr(dat);
numsamples_ = 1;
}
// Create statistics for all the samples between begin (included)
// and end(excluded)
explicit Stat1(const VType *begin, const VType *end) {
Clear();
for ( const VType *item = begin; item < end; ++item ) {
(*this) += Stat1(*item);
}
}
// Create a sample of value dat and weight w
Stat1(const VType &dat, const NumType &w) {
sum_ = w * dat;
sum_squares_ = w * Sqr(dat);
numsamples_ = w;
}
// Copy operator
Stat1(const Self &stat) {
sum_ = stat.sum_;
sum_squares_ = stat.sum_squares_;
numsamples_ = stat.numsamples_;
}
inline Self &operator =(const Self &stat) {
sum_ = stat.sum_;
sum_squares_ = stat.sum_squares_;
numsamples_ = stat.numsamples_;
return (*this);
}
// Merge statistics from two sample sets.
inline Self &operator +=(const Self &stat) {
sum_ += stat.sum_;
sum_squares_+= stat.sum_squares_;
numsamples_ += stat.numsamples_;
return (*this);
}
// The operation opposite to +=
inline Self &operator -=(const Self &stat) {
sum_ -= stat.sum_;
sum_squares_-= stat.sum_squares_;
numsamples_ -= stat.numsamples_;
return (*this);
}
// Multiply the weight of the set of samples by a factor k
inline Self &operator *=(const VType &k) {
sum_ *= k;
sum_squares_*= k;
numsamples_ *= k;
return (*this);
}
// Merge statistics from two sample sets.
inline Self operator + (const Self &stat) const {
return Self(*this) += stat;
}
// The operation opposite to +
inline Self operator - (const Self &stat) const {
return Self(*this) -= stat;
}
// Multiply the weight of the set of samples by a factor k
inline Self operator * (const VType &k) const {
return Self(*this) *= k;
}
// Return the total weight of this sample set
NumType NumSamples() const {
return numsamples_;
}
// Return the sum of this sample set
VType Sum() const {
return sum_;
}
// Return the mean of this sample set
VType Mean() const {
if (numsamples_ == 0) return VType();
return sum_ * (1.0 / numsamples_);
}
// Return the mean of this sample set and compute the standard deviation at
// the same time.
VType Mean(VType *stddev) const {
if (numsamples_ == 0) return VType();
VType mean = sum_ * (1.0 / numsamples_);
if (stddev) {
VType avg_squares = sum_squares_ * (1.0 / numsamples_);
*stddev = Sqrt(avg_squares - Sqr(mean));
}
return mean;
}
// Return the standard deviation of the sample set
VType StdDev() const {
if (numsamples_ == 0) return VType();
VType mean = Mean();
VType avg_squares = sum_squares_ * (1.0 / numsamples_);
return Sqrt(avg_squares - Sqr(mean));
}
private:
// Let i be the index of the samples provided (using +=)
// and weight[i],value[i] be the data of sample #i
// then the variables have the following meaning:
NumType numsamples_; // sum of weight[i];
VType sum_; // sum of weight[i]*value[i];
VType sum_squares_; // sum of weight[i]*value[i]^2;
// Template function used to square a number.
// For a vector we square all components
template <typename SType>
static inline SType Sqr(const SType &dat) {
return dat * dat;
}
template <typename SType>
static inline Vector2<SType> Sqr(const Vector2<SType> &dat) {
return dat.MulComponents(dat);
}
template <typename SType>
static inline Vector3<SType> Sqr(const Vector3<SType> &dat) {
return dat.MulComponents(dat);
}
template <typename SType>
static inline Vector4<SType> Sqr(const Vector4<SType> &dat) {
return dat.MulComponents(dat);
}
// Template function used to take the square root of a number.
// For a vector we square all components
template <typename SType>
static inline SType Sqrt(const SType &dat) {
// Avoid NaN due to imprecision in the calculations
if ( dat < 0 )
return 0;
return sqrt(dat);
}
template <typename SType>
static inline Vector2<SType> Sqrt(const Vector2<SType> &dat) {
// Avoid NaN due to imprecision in the calculations
return Max(dat, Vector2<SType>()).Sqrt();
}
template <typename SType>
static inline Vector3<SType> Sqrt(const Vector3<SType> &dat) {
// Avoid NaN due to imprecision in the calculations
return Max(dat, Vector3<SType>()).Sqrt();
}
template <typename SType>
static inline Vector4<SType> Sqrt(const Vector4<SType> &dat) {
// Avoid NaN due to imprecision in the calculations
return Max(dat, Vector4<SType>()).Sqrt();
}
};
// Useful printing function
template <typename VType, typename NumType>
inline std::ostream& operator<<(std::ostream& out,
const Stat1<VType, NumType>& s) {
out << "{ avg = " << s.Mean()
<< " std = " << s.StdDev()
<< " nsamples = " << s.NumSamples() << "}";
return out;
}
// Stat1MinMax: same as Stat1, but it also
// keeps the Min and Max values; the "-"
// operator is disabled because it cannot be implemented
// efficiently
template <typename VType, typename NumType>
class Stat1MinMax : public Stat1<VType, NumType> {
public:
typedef Stat1MinMax<VType, NumType> Self;
Stat1MinMax() {
Clear();
}
void Clear() {
Stat1<VType, NumType>::Clear();
if (std::numeric_limits<VType>::has_infinity) {
min_ = std::numeric_limits<VType>::infinity();
max_ = -std::numeric_limits<VType>::infinity();
} else {
min_ = std::numeric_limits<VType>::max();
max_ = std::numeric_limits<VType>::min();
}
}
// Create a sample of value dat and weight 1
explicit Stat1MinMax(const VType &dat) : Stat1<VType, NumType>(dat) {
max_ = dat;
min_ = dat;
}
// Create statistics for all the samples between begin (included)
// and end(excluded)
explicit Stat1MinMax(const VType *begin, const VType *end) {
Clear();
for ( const VType *item = begin; item < end; ++item ) {
(*this) += Stat1MinMax(*item);
}
}
// Create a sample of value dat and weight w
Stat1MinMax(const VType &dat, const NumType &w)
: Stat1<VType, NumType>(dat, w) {
max_ = dat;
min_ = dat;
}
// Copy operator
Stat1MinMax(const Self &stat) : Stat1<VType, NumType>(stat) {
max_ = stat.max_;
min_ = stat.min_;
}
inline Self &operator =(const Self &stat) {
this->Stat1<VType, NumType>::operator=(stat);
max_ = stat.max_;
min_ = stat.min_;
return (*this);
}
// Merge statistics from two sample sets.
inline Self &operator +=(const Self &stat) {
this->Stat1<VType, NumType>::operator+=(stat);
if (stat.max_ > max_) max_ = stat.max_;
if (stat.min_ < min_) min_ = stat.min_;
return (*this);
}
// Multiply the weight of the set of samples by a factor k
inline Self &operator *=(const VType &stat) {
this->Stat1<VType, NumType>::operator*=(stat);
return (*this);
}
// Merge statistics from two sample sets.
inline Self operator + (const Self &stat) const {
return Self(*this) += stat;
}
// Multiply the weight of the set of samples by a factor k
inline Self operator * (const VType &k) const {
return Self(*this) *= k;
}
private:
// The - operation makes no sense with Min/Max
// unless we keep the full list of values (but we don't)
// make it private, and let it undefined so nobody can call it
Self &operator -=(const Self &stat); // senseless. let it undefined.
// The operation opposite to -
Self operator - (const Self &stat) const; // senseless. let it undefined.
public:
// Return the maximal value in this sample set
VType Max() const {
return max_;
}
// Return the minimal value in this sample set
VType Min() const {
return min_;
}
private:
// Let i be the index of the samples provided (using +=)
// and weight[i],value[i] be the data of sample #i
// then the variables have the following meaning:
VType max_; // max of value[i]
VType min_; // min of value[i]
};
// Useful printing function
template <typename VType, typename NumType>
inline std::ostream& operator <<(std::ostream& out,
const Stat1MinMax<VType, NumType>& s) {
out << "{ avg = " << s.Mean()
<< " std = " << s.StdDev()
<< " nsamples = " << s.NumSamples()
<< " min = " << s.Min()
<< " max = " << s.Max() << "}";
return out;
}
#endif // BENCHMARK_STAT_H_

337
src/sysinfo.cc Normal file
View File

@ -0,0 +1,337 @@
#include "sysinfo.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <iostream>
#include <limits>
#include "cycleclock.h"
#include "macros.h"
#include "mutex_lock.h"
#include "sleep.h"
namespace {
pthread_once_t cpuinfo_init = PTHREAD_ONCE_INIT;
double cpuinfo_cycles_per_second = 1.0;
int cpuinfo_num_cpus = 1; // Conservative guess
static pthread_mutex_t cputimens_mutex;
// Helper function estimates cycles/sec by observing cycles elapsed during
// sleep(). Using small sleep time decreases accuracy significantly.
int64_t EstimateCyclesPerSecond(const int estimate_time_ms) {
CHECK(estimate_time_ms > 0);
double multiplier = 1000.0 / (double)estimate_time_ms; // scale by this much
const int64_t start_ticks = CycleClock::Now();
SleepForMilliseconds(estimate_time_ms);
const int64_t guess = int64_t(multiplier * (CycleClock::Now() - start_ticks));
return guess;
}
// Helper function for reading an int from a file. Returns true if successful
// and the memory location pointed to by value is set to the value read.
bool ReadIntFromFile(const char *file, int *value) {
bool ret = false;
int fd = open(file, O_RDONLY);
if (fd != -1) {
char line[1024];
char* err;
memset(line, '\0', sizeof(line));
CHECK(read(fd, line, sizeof(line) - 1));
const int temp_value = strtol(line, &err, 10);
if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {
*value = temp_value;
ret = true;
}
close(fd);
}
return ret;
}
void InitializeSystemInfo() {
bool saw_mhz = false;
// TODO: destroy this
pthread_mutex_init(&cputimens_mutex, NULL);
#if defined OS_LINUX || defined OS_CYGWIN
char line[1024];
char* err;
int freq;
// If the kernel is exporting the tsc frequency use that. There are issues
// where cpuinfo_max_freq cannot be relied on because the BIOS may be
// exporintg an invalid p-state (on x86) or p-states may be used to put the
// processor in a new mode (turbo mode). Essentially, those frequencies
// cannot always be relied upon. The same reasons apply to /proc/cpuinfo as
// well.
if (!saw_mhz &&
ReadIntFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) {
// The value is in kHz (as the file name suggests). For example, on a
// 2GHz warpstation, the file contains the value "2000000".
cpuinfo_cycles_per_second = freq * 1000.0;
saw_mhz = true;
}
// If CPU scaling is in effect, we want to use the *maximum* frequency,
// not whatever CPU speed some random processor happens to be using now.
if (!saw_mhz &&
ReadIntFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
&freq)) {
// The value is in kHz. For example, on a 2GHz warpstation, the file
// contains the value "2000000".
cpuinfo_cycles_per_second = freq * 1000.0;
saw_mhz = true;
}
// Read /proc/cpuinfo for other values, and if there is no cpuinfo_max_freq.
const char* pname = "/proc/cpuinfo";
int fd = open(pname, O_RDONLY);
if (fd == -1) {
perror(pname);
if (!saw_mhz) {
cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
}
return; // TODO: use generic tester instead?
}
double bogo_clock = 1.0;
bool saw_bogo = false;
int max_cpu_id = 0;
int num_cpus = 0;
line[0] = line[1] = '\0';
int chars_read = 0;
do { // we'll exit when the last read didn't read anything
// Move the next line to the beginning of the buffer
const int oldlinelen = strlen(line);
if (sizeof(line) == oldlinelen + 1) // oldlinelen took up entire line
line[0] = '\0';
else // still other lines left to save
memmove(line, line + oldlinelen+1, sizeof(line) - (oldlinelen+1));
// Terminate the new line, reading more if we can't find the newline
char* newline = strchr(line, '\n');
if (newline == NULL) {
const int linelen = strlen(line);
const int bytes_to_read = sizeof(line)-1 - linelen;
CHECK(bytes_to_read > 0); // because the memmove recovered >=1 bytes
chars_read = read(fd, line + linelen, bytes_to_read);
line[linelen + chars_read] = '\0';
newline = strchr(line, '\n');
}
if (newline != NULL)
*newline = '\0';
// When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only
// accept postive values. Some environments (virtual machines) report zero,
// which would cause infinite looping in WallTime_Init.
if (!saw_mhz && strncasecmp(line, "cpu MHz", sizeof("cpu MHz")-1) == 0) {
const char* freqstr = strchr(line, ':');
if (freqstr) {
cpuinfo_cycles_per_second = strtod(freqstr+1, &err) * 1000000.0;
if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0)
saw_mhz = true;
}
} else if (strncasecmp(line, "bogomips", sizeof("bogomips")-1) == 0) {
const char* freqstr = strchr(line, ':');
if (freqstr) {
bogo_clock = strtod(freqstr+1, &err) * 1000000.0;
if (freqstr[1] != '\0' && *err == '\0' && bogo_clock > 0)
saw_bogo = true;
}
} else if (strncasecmp(line, "processor", sizeof("processor")-1) == 0) {
num_cpus++; // count up every time we see an "processor :" entry
const char* freqstr = strchr(line, ':');
if (freqstr) {
const int cpu_id = strtol(freqstr+1, &err, 10);
if (freqstr[1] != '\0' && *err == '\0' && max_cpu_id < cpu_id)
max_cpu_id = cpu_id;
}
}
} while (chars_read > 0);
close(fd);
if (!saw_mhz) {
if (saw_bogo) {
// If we didn't find anything better, we'll use bogomips, but
// we're not happy about it.
cpuinfo_cycles_per_second = bogo_clock;
} else {
// If we don't even have bogomips, we'll use the slow estimation.
cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
}
}
if (num_cpus == 0) {
fprintf(stderr, "Failed to read num. CPUs correctly from /proc/cpuinfo\n");
} else {
if ((max_cpu_id + 1) != num_cpus) {
fprintf(stderr,
"CPU ID assignments in /proc/cpuinfo seems messed up."
" This is usually caused by a bad BIOS.\n");
}
cpuinfo_num_cpus = num_cpus;
}
#elif defined OS_FREEBSD
// For this sysctl to work, the machine must be configured without
// SMP, APIC, or APM support. hz should be 64-bit in freebsd 7.0
// and later. Before that, it's a 32-bit quantity (and gives the
// wrong answer on machines faster than 2^32 Hz). See
// http://lists.freebsd.org/pipermail/freebsd-i386/2004-November/001846.html
// But also compare FreeBSD 7.0:
// http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG70#L223
// 231 error = sysctl_handle_quad(oidp, &freq, 0, req);
// To FreeBSD 6.3 (it's the same in 6-STABLE):
// http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG6#L131
// 139 error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
#if __FreeBSD__ >= 7
uint64_t hz = 0;
#else
unsigned int hz = 0;
#endif
size_t sz = sizeof(hz);
const char *sysctl_path = "machdep.tsc_freq";
if ( sysctlbyname(sysctl_path, &hz, &sz, NULL, 0) != 0 ) {
fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n",
sysctl_path, strerror(errno));
cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
} else {
cpuinfo_cycles_per_second = hz;
}
// TODO: also figure out cpuinfo_num_cpus
#elif defined OS_WINDOWS
# pragma comment(lib, "shlwapi.lib") // for SHGetValue()
// In NT, read MHz from the registry. If we fail to do so or we're in win9x
// then make a crude estimate.
OSVERSIONINFO os;
os.dwOSVersionInfoSize = sizeof(os);
DWORD data, data_size = sizeof(data);
if (GetVersionEx(&os) &&
os.dwPlatformId == VER_PLATFORM_WIN32_NT &&
SUCCEEDED(SHGetValueA(HKEY_LOCAL_MACHINE,
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
"~MHz", NULL, &data, &data_size)))
cpuinfo_cycles_per_second = (int64)data * (int64)(1000 * 1000); // was mhz
else
cpuinfo_cycles_per_second = EstimateCyclesPerSecond(500); // TODO <500?
// TODO: also figure out cpuinfo_num_cpus
#elif defined OS_MACOSX
// returning "mach time units" per second. the current number of elapsed
// mach time units can be found by calling uint64 mach_absolute_time();
// while not as precise as actual CPU cycles, it is accurate in the face
// of CPU frequency scaling and multi-cpu/core machines.
// Our mac users have these types of machines, and accuracy
// (i.e. correctness) trumps precision.
// See cycleclock.h: CycleClock::Now(), which returns number of mach time
// units on Mac OS X.
mach_timebase_info_data_t timebase_info;
mach_timebase_info(&timebase_info);
double mach_time_units_per_nanosecond =
static_cast<double>(timebase_info.denom) /
static_cast<double>(timebase_info.numer);
cpuinfo_cycles_per_second = mach_time_units_per_nanosecond * 1e9;
int num_cpus = 0;
size_t size = sizeof(num_cpus);
int numcpus_name[] = { CTL_HW, HW_NCPU };
if (::sysctl(numcpus_name, arraysize(numcpus_name), &num_cpus, &size, 0, 0)
== 0
&& (size == sizeof(num_cpus)))
cpuinfo_num_cpus = num_cpus;
#else
// Generic cycles per second counter
cpuinfo_cycles_per_second = EstimateCyclesPerSecond(1000);
#endif
}
} // end namespace
#ifndef OS_WINDOWS
// getrusage() based implementation of MyCPUUsage
static double MyCPUUsageRUsage() {
struct rusage ru;
if (getrusage(RUSAGE_SELF, &ru) == 0) {
return (static_cast<double>(ru.ru_utime.tv_sec) +
static_cast<double>(ru.ru_utime.tv_usec)*1e-6 +
static_cast<double>(ru.ru_stime.tv_sec) +
static_cast<double>(ru.ru_stime.tv_usec)*1e-6);
} else {
return 0.0;
}
}
static bool MyCPUUsageCPUTimeNsLocked(double *cputime) {
static int cputime_fd = -1;
if (cputime_fd == -1) {
cputime_fd = open("/proc/self/cputime_ns", O_RDONLY);
if (cputime_fd < 0) {
cputime_fd = -1;
return false;
}
}
char buff[64];
memset(buff, 0, sizeof(buff));
if (pread(cputime_fd, buff, sizeof(buff)-1, 0) <= 0) {
close(cputime_fd);
cputime_fd = -1;
return false;
}
unsigned long long result = strtoull(buff, NULL, 0);
if (result == (std::numeric_limits<unsigned long long>::max)()) {
close(cputime_fd);
cputime_fd = -1;
return false;
}
*cputime = static_cast<double>(result) / 1e9;
return true;
}
double MyCPUUsage() {
{
mutex_lock l(&cputimens_mutex);
static bool use_cputime_ns = true;
if (use_cputime_ns) {
double value;
if (MyCPUUsageCPUTimeNsLocked(&value)) {
return value;
}
// Once MyCPUUsageCPUTimeNsLocked fails once fall back to getrusage().
std::cout << "Reading /proc/self/cputime_ns failed. Using getrusage().\n";
use_cputime_ns = false;
}
}
return MyCPUUsageRUsage();
}
double ChildrenCPUUsage() {
struct rusage ru;
if (getrusage(RUSAGE_CHILDREN, &ru) == 0) {
return (static_cast<double>(ru.ru_utime.tv_sec) +
static_cast<double>(ru.ru_utime.tv_usec)*1e-6 +
static_cast<double>(ru.ru_stime.tv_sec) +
static_cast<double>(ru.ru_stime.tv_usec)*1e-6);
} else {
return 0.0;
}
}
#endif // OS_WINDOWS
double CyclesPerSecond(void) {
pthread_once(&cpuinfo_init, &InitializeSystemInfo);
return cpuinfo_cycles_per_second;
}
int NumCPUs(void) {
pthread_once(&cpuinfo_init, &InitializeSystemInfo);
return cpuinfo_num_cpus;
}

9
src/sysinfo.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef BENCHMARK_SYSINFO_H_
#define BENCHMARK_SYSINFO_H_
double MyCPUUsage();
double ChildrenCPUUsage();
int NumCPUs();
double CyclesPerSecond();
#endif // BENCHMARK_SYSINFO_H_

137
src/walltime.cc Normal file
View File

@ -0,0 +1,137 @@
#include "walltime.h"
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <atomic>
#include <limits>
#include "cycleclock.h"
#include "macros.h"
#include "sysinfo.h"
namespace walltime {
namespace {
const double kMaxErrorInterval = 100e-6;
std::atomic<bool> initialized(false);
WallTime base_walltime = 0.0;
int64_t base_cycletime = 0;
int64_t cycles_per_second;
double seconds_per_cycle;
uint32_t last_adjust_time = 0;
std::atomic<int32_t> drift_adjust(0);
int64_t max_interval_cycles = 0;
// Helper routines to load/store a float from an AtomicWord. Required because
// g++ < 4.7 doesn't support std::atomic<float> correctly. I cannot wait to get
// rid of this horror show.
inline void SetDrift(float f) {
int32_t w;
memcpy(&w, &f, sizeof(f));
std::atomic_store(&drift_adjust, w);
}
inline float GetDrift() {
float f;
int32_t w = std::atomic_load(&drift_adjust);
memcpy(&f, &w, sizeof(f));
return f;
}
static_assert(sizeof(float) <= sizeof(int32_t),
"type sizes don't allow the drift_adjust hack");
WallTime Slow() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec * 1e-6;
}
bool SplitTimezone(WallTime value, bool local, struct tm* t,
double* subsecond) {
memset(t, 0, sizeof(*t));
if ((value < 0) || (value > std::numeric_limits<time_t>::max())) {
*subsecond = 0.0;
return false;
}
const time_t whole_time = static_cast<time_t>(value);
*subsecond = value - whole_time;
if (local)
localtime_r(&whole_time, t);
else
gmtime_r(&whole_time, t);
return true;
}
} // end namespace
// This routine should be invoked to initialize walltime.
// It is not intended for general purpose use.
void Initialize() {
CHECK(!std::atomic_load(&initialized));
cycles_per_second = static_cast<int64_t>(CyclesPerSecond());
CHECK(cycles_per_second != 0);
seconds_per_cycle = 1.0 / cycles_per_second;
max_interval_cycles = static_cast<int64_t>(
cycles_per_second * kMaxErrorInterval);
do {
base_cycletime = CycleClock::Now();
base_walltime = Slow();
} while (CycleClock::Now() - base_cycletime > max_interval_cycles);
// We are now sure that "base_walltime" and "base_cycletime" were produced
// within kMaxErrorInterval of one another.
SetDrift(0.0);
last_adjust_time = static_cast<uint32_t>(uint64_t(base_cycletime) >> 32);
std::atomic_store(&initialized, true);
}
WallTime Now() {
if (!std::atomic_load(&initialized))
return Slow();
WallTime now = 0.0;
WallTime result = 0.0;
int64_t ct = 0;
uint32_t top_bits = 0;
do {
ct = CycleClock::Now();
int64_t cycle_delta = ct - base_cycletime;
result = base_walltime + cycle_delta * seconds_per_cycle;
top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32);
// Recompute drift no more often than every 2^32 cycles.
// I.e., @2GHz, ~ every two seconds
if (top_bits == last_adjust_time) { // don't need to recompute drift
return result + GetDrift();
}
now = Slow();
} while (CycleClock::Now() - ct > max_interval_cycles);
// We are now sure that "now" and "result" were produced within
// kMaxErrorInterval of one another.
SetDrift(now - result);
last_adjust_time = top_bits;
return now;
}
const char* Print(WallTime time, const char *format, bool local,
char* storage, int *remainder_us) {
struct tm split;
double subsecond;
if (!SplitTimezone(time, local, &split, &subsecond)) {
snprintf(storage, sizeof(storage), "Invalid time: %f", time);
} else {
if (remainder_us != NULL) {
*remainder_us = static_cast<int>((subsecond * 1000000) + 0.5);
if (*remainder_us > 999999) *remainder_us = 999999;
if (*remainder_us < 0) *remainder_us = 0;
}
strftime(storage, sizeof(storage), format, &split);
}
return storage;
}
} // end namespace walltime

19
src/walltime.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef BENCHMARK_WALLTIME_H_
#define BENCHMARK_WALLTIME_H_
typedef double WallTime;
namespace walltime {
void Initialize();
WallTime Now();
// GIVEN: walltime, generic format string (as understood by strftime),
// a boolean flag specifying if the time is local or UTC (true=local).
// RETURNS: the formatted string. ALSO RETURNS: the storage printbuffer
// passed and the remaining number of microseconds (never printed in
// the string since strftime does not understand it)
const char* Print(WallTime time, const char *format, bool local,
char* storage, int *remainder_us);
} // end namespace walltime
#endif // BENCHMARK_WALLTIME_H_

138
test/benchmark_test.cc Normal file
View File

@ -0,0 +1,138 @@
#include "benchmark/benchmark.h"
#include <math.h>
#include <stdint.h>
#include <limits>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <vector>
namespace {
int ATTRIBUTE_NOINLINE Factorial(uint32_t n) {
return (n == 1) ? 1 : n * Factorial(n - 1);
}
double CalculatePi(int depth) {
double pi = 0.0;
for (int i = 0; i < depth; ++i) {
double numerator = static_cast<double>(((i % 2) * 2) - 1);
double denominator = static_cast<double>((2 * i) - 1);
pi += numerator / denominator;
}
return (pi - 1.0) * 4;
}
std::set<int> ConstructRandomSet(int size) {
std::set<int> s;
for (int i = 0; i < size; ++i)
s.insert(i);
return s;
}
static std::vector<int>* test_vector = NULL;
} // end namespace
#ifdef DEBUG
static void BM_Factorial(benchmark::State& state) {
int fac_42 = 0;
while (state.KeepRunning())
fac_42 = Factorial(8);
// Prevent compiler optimizations
CHECK(fac_42 != std::numeric_limits<int>::max());
}
BENCHMARK(BM_Factorial);
#endif
static void BM_CalculatePiRange(benchmark::State& state) {
double pi = 0.0;
while (state.KeepRunning())
pi = CalculatePi(state.range_x());
std::stringstream ss;
ss << pi;
state.SetLabel(ss.str());
}
BENCHMARK_RANGE(BM_CalculatePiRange, 1, 1024 * 1024);
static void BM_CalculatePi(benchmark::State& state) {
static const int depth = 1024;
double pi ATTRIBUTE_UNUSED = 0.0;
while (state.KeepRunning()) {
pi = CalculatePi(depth);
}
}
BENCHMARK(BM_CalculatePi)->Threads(8);
BENCHMARK(BM_CalculatePi)->ThreadRange(1, 32);
BENCHMARK(BM_CalculatePi)->ThreadPerCpu();
static void BM_SetInsert(benchmark::State& state) {
while (state.KeepRunning()) {
state.PauseTiming();
std::set<int> data = ConstructRandomSet(state.range_x());
state.ResumeTiming();
for (int j = 0; j < state.range_y(); ++j)
data.insert(rand());
}
}
BENCHMARK(BM_SetInsert)->RangePair(1<<10,8<<10, 1,10);
template<typename Q>
static void BM_Sequential(benchmark::State& state) {
Q q;
typename Q::value_type v;
while (state.KeepRunning())
for (int i = state.range_x(); --i; )
q.push_back(v);
const int64_t items_processed =
static_cast<int64_t>(state.iterations()) * state.range_x();
state.SetItemsProcessed(items_processed);
state.SetBytesProcessed(items_processed * sizeof(v));
}
BENCHMARK_TEMPLATE(BM_Sequential, std::vector<int>)->Range(1 << 0, 1 << 10);
BENCHMARK_TEMPLATE(BM_Sequential, std::list<int>)->Range(1 << 0, 1 << 10);
static void BM_StringCompare(benchmark::State& state) {
std::string s1(state.range_x(), '-');
std::string s2(state.range_x(), '-');
int r = 0;
while (state.KeepRunning())
r |= s1.compare(s2);
// Prevent compiler optimizations
CHECK(r != std::numeric_limits<int>::max());
}
BENCHMARK(BM_StringCompare)->Range(1, 1<<20);
static void BM_SetupTeardown(benchmark::State& state) {
if (state.thread_index == 0)
test_vector = new std::vector<int>();
while (state.KeepRunning())
test_vector->push_back(0);
if (state.thread_index == 0) {
delete test_vector;
test_vector = NULL;
}
}
BENCHMARK(BM_SetupTeardown);
static void BM_LongTest(benchmark::State& state) {
double tracker = 0.0;
while (state.KeepRunning())
for (int i = 0; i < state.range_x(); ++i)
tracker += i;
CHECK(tracker != 0.0);
}
BENCHMARK(BM_LongTest)->Range(1<<16,1<<28);
int main(int argc, const char* argv[]) {
benchmark::Initialize(&argc, argv);
CHECK(Factorial(8) == 40320);
CHECK(CalculatePi(1) == 0.0);
benchmark::RunSpecifiedBenchmarks();
}