From 2fd7ed36a9031c794f3247b6952308a35cea8079 Mon Sep 17 00:00:00 2001 From: josh macdonald Date: Sun, 13 Jan 2013 00:16:02 -0800 Subject: [PATCH] Make btree_bench work; compiled & tested on OS X 10.7.5 using gcc 4.7.2 -std=c++11 --- CMakeLists.txt | 6 +- btree_bench.cc | 240 ++++++++++++++++++++++++++++++++++--------------- btree_test.h | 59 ------------ 3 files changed, 171 insertions(+), 134 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e908f4a4..79c67ac7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 2.6) project(cppbtree CXX) option(build_tests "Build B-tree tests" OFF) -add_definitions(-std=c++0x) -set(CMAKE_CXX_FLAGS "-g") +add_definitions(-std=c++11) +set(CMAKE_CXX_FLAGS "-g -O2") # CMake doesn't have a way to pure template library, add_library(cppbtree btree.h btree_map.h btree_set.h safe_btree.h safe_btree_map.h safe_btree_set.h) @@ -19,5 +19,5 @@ if(build_tests) add_executable(btree_bench btree_bench.cc btree_test_flags.cc) target_link_libraries(btree_test gtest_main gflags) target_link_libraries(safe_btree_test gtest_main gflags) - target_link_libraries(btree_bench gflags) + target_link_libraries(btree_bench gflags gtest) endif() diff --git a/btree_bench.cc b/btree_bench.cc index 1b45582a..b803ad38 100644 --- a/btree_bench.cc +++ b/btree_bench.cc @@ -3,36 +3,160 @@ // Author: pmattis@google.com (Peter Mattis) #include +#include #include #include #include #include #include +#include +#include #include -#include "base/commandlineflags.h" -#include "base/init_google.h" -#include "base/integral_types.h" -#include "base/logging.h" -#include "base/stringprintf.h" -#include "base/type_traits.h" -#include "strings/cord.h" -#include "testing/base/public/benchmark.h" -#include "testing/base/public/googletest.h" -#include "util/btree/btree_map.h" -#include "util/btree/btree_set.h" -#include "util/btree/btree_test.h" -#include "util/random/acmrandom.h" +#include "gflags/gflags.h" +#include "btree_map.h" +#include "btree_set.h" +#include "btree_test.h" -DECLARE_int32(benchmark_max_iters); +DEFINE_int32(test_random_seed, 123456789, "Seed for srand()"); +DEFINE_int32(benchmark_max_iters, 10000000, "Maximum test iterations"); +DEFINE_int32(benchmark_min_iters, 100, "Minimum test iterations"); +DEFINE_int32(benchmark_target_seconds, 1, + "Attempt to benchmark for this many seconds"); + +using std::allocator; +using std::less; +using std::map; +using std::max; +using std::min; +using std::multimap; +using std::multiset; +using std::set; +using std::string; +using std::vector; namespace btree { namespace { +struct RandGen { + typedef ptrdiff_t result_type; + RandGen(result_type seed) { + srand(seed); + } + result_type operator()(result_type l) { + return rand() % l; + } +}; + +struct BenchmarkRun { + BenchmarkRun(const char *name, void (*func)(int)); + void Run(); + void Stop(); + void Start(); + void Reset(); + + BenchmarkRun *next_benchmark; + const char *benchmark_name; + void (*benchmark_func)(int); + int64_t accum_micros; + int64_t last_started; +}; + +BenchmarkRun *first_benchmark; +BenchmarkRun *current_benchmark; + +int64_t get_micros () { + timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +BenchmarkRun::BenchmarkRun(const char *name, void (*func)(int)) + : next_benchmark(first_benchmark), + benchmark_name(name), + benchmark_func(func), + accum_micros(0), + last_started(0) { + first_benchmark = this; +} + +#define BTREE_BENCHMARK(name) \ + BTREE_BENCHMARK2(#name, name, __COUNTER__) +#define BTREE_BENCHMARK2(name, func, counter) \ + BTREE_BENCHMARK3(name, func, counter) +#define BTREE_BENCHMARK3(name, func, counter) \ + BenchmarkRun bench ## counter (name, func) + +void StopBenchmarkTiming() { + current_benchmark->Stop(); +} + +void StartBenchmarkTiming() { + current_benchmark->Start(); +} + +void RunBenchmarks() { + for (BenchmarkRun *bench = first_benchmark; bench; + bench = bench->next_benchmark) { + bench->Run(); + } +} + +void BenchmarkRun::Start() { + assert(!last_started); + last_started = get_micros(); +} + +void BenchmarkRun::Stop() { + if (last_started == 0) { + return; + } + accum_micros += get_micros() - last_started; + last_started = 0; +} + +void BenchmarkRun::Reset() { + last_started = 0; + accum_micros = 0; +} + +void BenchmarkRun::Run() { + assert(current_benchmark == NULL); + current_benchmark = this; + int iters = FLAGS_benchmark_min_iters; + for (;;) { + Reset(); + Start(); + benchmark_func(iters); + Stop(); + if (accum_micros > FLAGS_benchmark_target_seconds * 1000000 || + iters >= FLAGS_benchmark_max_iters) { + break; + } else if (accum_micros == 0) { + iters *= 100; + } else { + int64_t target_micros = FLAGS_benchmark_target_seconds * 1000000; + iters = target_micros * iters / accum_micros; + } + iters = min(iters, FLAGS_benchmark_max_iters); + } + fprintf(stdout, "%s\t%qu\t%qu\n", + benchmark_name, + accum_micros * 1000 / iters, + iters); + current_benchmark = NULL; +} + +// Used to avoid compiler optimizations for these benchmarks. +template +void sink(const T& t0) { + volatile T t = t0; +} + // Benchmark insertion of values into a container. template void BM_Insert(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; typename KeyOfValue::type key_of_value; // Disable timing while we perform some initialization. @@ -43,7 +167,6 @@ void BM_Insert(int n) { for (int i = 0; i < values.size(); i++) { container.insert(values[i]); } - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); for (int i = 0; i < n; ) { // Remove and re-insert 10% of the keys @@ -70,7 +193,7 @@ void BM_Insert(int n) { // Benchmark lookup of values in a container. template void BM_Lookup(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; typename KeyOfValue::type key_of_value; // Disable timing while we perform some initialization. @@ -82,7 +205,6 @@ void BM_Lookup(int n) { for (int i = 0; i < values.size(); i++) { container.insert(values[i]); } - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); V r = V(); @@ -95,7 +217,7 @@ void BM_Lookup(int n) { StopBenchmarkTiming(); - VLOG(4) << r; // Keep compiler from optimizing away r. + sink(r); // Keep compiler from optimizing away r. } // Benchmark lookup of values in a full container, meaning that values @@ -103,7 +225,7 @@ void BM_Lookup(int n) { // yields a full tree. template void BM_FullLookup(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; typename KeyOfValue::type key_of_value; // Disable timing while we perform some initialization. @@ -117,7 +239,6 @@ void BM_FullLookup(int n) { for (int i = 0; i < sorted.size(); i++) { container.insert(sorted[i]); } - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); V r = V(); @@ -130,13 +251,13 @@ void BM_FullLookup(int n) { StopBenchmarkTiming(); - VLOG(4) << r; // Keep compiler from optimizing away r. + sink(r); // Keep compiler from optimizing away r. } // Benchmark deletion of values from a container. template void BM_Delete(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; typename KeyOfValue::type key_of_value; // Disable timing while we perform some initialization. @@ -147,7 +268,6 @@ void BM_Delete(int n) { for (int i = 0; i < values.size(); i++) { container.insert(values[i]); } - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); for (int i = 0; i < n; ) { // Remove and re-insert 10% of the keys @@ -179,12 +299,12 @@ void BM_Delete(int n) { // value constructors. template void BM_QueueAddRem(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; typename KeyOfValue::type key_of_value; // Disable timing while we perform some initialization. StopBenchmarkTiming(); - CHECK(FLAGS_benchmark_values % 2 == 0); + assert(FLAGS_benchmark_values % 2 == 0); T container; @@ -197,7 +317,7 @@ void BM_QueueAddRem(int n) { add_keys[i] = i; } - ACMRandom rand(FLAGS_test_random_seed); + RandGen rand(FLAGS_test_random_seed); random_shuffle(remove_keys.begin(), remove_keys.end(), rand); random_shuffle(add_keys.begin(), add_keys.end(), rand); @@ -229,27 +349,25 @@ void BM_QueueAddRem(int n) { } int e = container.erase(key_of_value(g(offset - half + remove_keys[idx]))); - DCHECK(e == 1); + assert(e == 1); container.insert(g(offset + half + add_keys[idx])); } StopBenchmarkTiming(); - - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); } // Mixed insertion and deletion in the same range using pre-constructed values. template void BM_MixedAddRem(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; typename KeyOfValue::type key_of_value; // Disable timing while we perform some initialization. StopBenchmarkTiming(); - CHECK(FLAGS_benchmark_values % 2 == 0); + assert(FLAGS_benchmark_values % 2 == 0); T container; - ACMRandom rand(FLAGS_test_random_seed); + RandGen rand(FLAGS_test_random_seed); vector values = GenerateValues(FLAGS_benchmark_values * 2); @@ -282,20 +400,18 @@ void BM_MixedAddRem(int n) { } int e = container.erase(key_of_value(values[remove_keys[idx]])); - DCHECK(e == 1); + assert(e == 1); container.insert(values[add_keys[idx]]); } StopBenchmarkTiming(); - - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); } // Insertion at end, removal from the beginning. This benchmark // counts two value constructors. template void BM_Fifo(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; // Disable timing while we perform some initialization. StopBenchmarkTiming(); @@ -315,14 +431,12 @@ void BM_Fifo(int n) { } StopBenchmarkTiming(); - - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); } // Iteration (forward) through the tree template void BM_FwdIter(int n) { - typedef typename base::remove_const::type V; + typedef typename std::remove_const::type V; // Disable timing while we perform some initialization. StopBenchmarkTiming(); @@ -352,30 +466,24 @@ void BM_FwdIter(int n) { StopBenchmarkTiming(); - VLOG(4) << r; // Keep compiler from optimizing away r. - - SetBenchmarkLabel(StringPrintf(" %0.2f", ContainerInfo(container))); + sink(r); // Keep compiler from optimizing away r. } -typedef set stl_set_int32; -typedef set stl_set_int64; +typedef set stl_set_int32; +typedef set stl_set_int64; typedef set stl_set_string; -typedef set stl_set_cord; -typedef map stl_map_int32; -typedef map stl_map_int64; +typedef map stl_map_int32; +typedef map stl_map_int64; typedef map stl_map_string; -typedef map stl_map_cord; -typedef multiset stl_multiset_int32; -typedef multiset stl_multiset_int64; +typedef multiset stl_multiset_int32; +typedef multiset stl_multiset_int64; typedef multiset stl_multiset_string; -typedef multiset stl_multiset_cord; -typedef multimap stl_multimap_int32; -typedef multimap stl_multimap_int64; +typedef multimap stl_multimap_int32; +typedef multimap stl_multimap_int64; typedef multimap stl_multimap_string; -typedef multimap stl_multimap_cord; #define MY_BENCHMARK_TYPES2(value, name, size) \ typedef btree ## _set, allocator, size> \ @@ -405,21 +513,15 @@ typedef multimap stl_multimap_cord; MY_BENCHMARK_TYPES2(value, name, 1536); \ MY_BENCHMARK_TYPES2(value, name, 2048) -MY_BENCHMARK_TYPES(int32, int32); -MY_BENCHMARK_TYPES(int64, int64); +MY_BENCHMARK_TYPES(int32_t, int32); +MY_BENCHMARK_TYPES(int64_t, int64); MY_BENCHMARK_TYPES(string, string); -MY_BENCHMARK_TYPES(Cord, cord); #define MY_BENCHMARK4(type, name, func) \ void BM_ ## type ## _ ## name(int n) { BM_ ## func (n); } \ - BENCHMARK(BM_ ## type ## _ ## name) + BTREE_BENCHMARK(BM_ ## type ## _ ## name) -// Define NODESIZE_TESTING when running btree_perf.py. You need to do -// a local build or raise the distcc timeout, it takes about 5 minutes -// to build: -// -// blaze build --copts=-DNODESIZE_TESTING --cc_strategy=local -// --compilation_mode=opt util/btree/btree_test +// Define NODESIZE_TESTING when running btree_perf.py. #ifdef NODESIZE_TESTING #define MY_BENCHMARK3(tree, type, name, func) \ @@ -465,8 +567,6 @@ MY_BENCHMARK(set_int64); MY_BENCHMARK(map_int64); MY_BENCHMARK(set_string); MY_BENCHMARK(map_string); -MY_BENCHMARK(set_cord); -MY_BENCHMARK(map_cord); MY_BENCHMARK(multiset_int32); MY_BENCHMARK(multimap_int32); @@ -474,15 +574,11 @@ MY_BENCHMARK(multiset_int64); MY_BENCHMARK(multimap_int64); MY_BENCHMARK(multiset_string); MY_BENCHMARK(multimap_string); -MY_BENCHMARK(multiset_cord); -MY_BENCHMARK(multimap_cord); } // namespace } // namespace btree int main(int argc, char **argv) { - FLAGS_logtostderr = true; - InitGoogle(argv[0], &argc, &argv, true); - RunSpecifiedBenchmarks(); + btree::RunBenchmarks(); return 0; } diff --git a/btree_test.h b/btree_test.h index c368573e..01ab0fe0 100644 --- a/btree_test.h +++ b/btree_test.h @@ -565,65 +565,6 @@ std::vector GenerateValues(int n) { return vec; } -template -double ContainerInfo(const std::set &s) { - int sizeof_node = sizeof(std::_Rb_tree_node); - int bytes_used = sizeof(s) + s.size() * sizeof_node; - double bytes_per_value = (double) bytes_used / s.size(); - std::cout << " size=" << s.size() - << " bytes-used=" << bytes_used - << " bytes-per-value=" << bytes_per_value; - return bytes_per_value; -} - -template -double ContainerInfo(const std::multiset &s) { - int sizeof_node = sizeof(std::_Rb_tree_node); - int bytes_used = sizeof(s) + s.size() * sizeof_node; - double bytes_per_value = (double) bytes_used / s.size(); - std::cout << " size=" << s.size() - << " bytes-used=" << bytes_used - << " bytes-per-value=" << bytes_per_value; - return bytes_per_value; -} - -template -double ContainerInfo(const std::map &m) { - int sizeof_node = sizeof(std::_Rb_tree_node >); - int bytes_used = sizeof(m) + m.size() * sizeof_node; - double bytes_per_value = (double) bytes_used / m.size(); - std::cout << " size=" << m.size() - << " bytes-used=" << bytes_used - << " bytes-per-value=" << bytes_per_value; - return bytes_per_value; -} - -template -double ContainerInfo(const std::multimap &m) { - int sizeof_node = sizeof(std::_Rb_tree_node >); - int bytes_used = sizeof(m) + m.size() * sizeof_node; - double bytes_per_value = (double) bytes_used / m.size(); - std::cout << " size=" << m.size() - << " bytes-used=" << bytes_used - << " bytes-per-value=" << bytes_per_value; - return bytes_per_value; -} - -template -double ContainerInfo(const btree_container

&b) { - double bytes_used = sizeof(b) + b.bytes_used(); - double bytes_per_value = (double) bytes_used / b.size(); - std::cout << " size=" << b.size() - << " bytes-used=" << bytes_used - << " bytes-per-value=" << bytes_per_value - << " height=" << b.height() - << " internal-nodes=" << b.internal_nodes() - << " leaf-nodes=" << b.leaf_nodes() - << " fullness=" << b.fullness() - << " overhead=" << b.overhead(); - return bytes_per_value; -} - template void DoTest(const char *name, T *b, const std::vector &values) { typename KeyOfValue::type key_of_value;