Make btree_bench work; compiled & tested on OS X 10.7.5 using gcc 4.7.2 -std=c++11

pull/5/head
josh macdonald 2013-01-13 00:16:02 -08:00
parent 88853fbc9e
commit 2fd7ed36a9
3 changed files with 171 additions and 134 deletions

View File

@ -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()

View File

@ -3,36 +3,160 @@
// Author: pmattis@google.com (Peter Mattis)
#include <stdint.h>
#include <stdlib.h>
#include <algorithm>
#include <functional>
#include <map>
#include <set>
#include <string>
#include <sys/time.h>
#include <type_traits>
#include <vector>
#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 <typename T>
void sink(const T& t0) {
volatile T t = t0;
}
// Benchmark insertion of values into a container.
template <typename T>
void BM_Insert(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::type V;
typename KeyOfValue<typename T::key_type, V>::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 <typename T>
void BM_Lookup(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::type V;
typename KeyOfValue<typename T::key_type, V>::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 <typename T>
void BM_FullLookup(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::type V;
typename KeyOfValue<typename T::key_type, V>::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 <typename T>
void BM_Delete(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::type V;
typename KeyOfValue<typename T::key_type, V>::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 <typename T>
void BM_QueueAddRem(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::type V;
typename KeyOfValue<typename T::key_type, V>::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 <typename T>
void BM_MixedAddRem(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::type V;
typename KeyOfValue<typename T::key_type, V>::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<V> values = GenerateValues<V>(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 <typename T>
void BM_Fifo(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::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 <typename T>
void BM_FwdIter(int n) {
typedef typename base::remove_const<typename T::value_type>::type V;
typedef typename std::remove_const<typename T::value_type>::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<int32> stl_set_int32;
typedef set<int64> stl_set_int64;
typedef set<int32_t> stl_set_int32;
typedef set<int64_t> stl_set_int64;
typedef set<string> stl_set_string;
typedef set<Cord> stl_set_cord;
typedef map<int32, intptr_t> stl_map_int32;
typedef map<int64, intptr_t> stl_map_int64;
typedef map<int32_t, intptr_t> stl_map_int32;
typedef map<int64_t, intptr_t> stl_map_int64;
typedef map<string, intptr_t> stl_map_string;
typedef map<Cord, intptr_t> stl_map_cord;
typedef multiset<int32> stl_multiset_int32;
typedef multiset<int64> stl_multiset_int64;
typedef multiset<int32_t> stl_multiset_int32;
typedef multiset<int64_t> stl_multiset_int64;
typedef multiset<string> stl_multiset_string;
typedef multiset<Cord> stl_multiset_cord;
typedef multimap<int32, intptr_t> stl_multimap_int32;
typedef multimap<int64, intptr_t> stl_multimap_int64;
typedef multimap<int32_t, intptr_t> stl_multimap_int32;
typedef multimap<int64_t, intptr_t> stl_multimap_int64;
typedef multimap<string, intptr_t> stl_multimap_string;
typedef multimap<Cord, intptr_t> stl_multimap_cord;
#define MY_BENCHMARK_TYPES2(value, name, size) \
typedef btree ## _set<value, less<value>, allocator<value>, size> \
@ -405,21 +513,15 @@ typedef multimap<Cord, intptr_t> 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 <type>(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;
}

View File

@ -565,65 +565,6 @@ std::vector<V> GenerateValues(int n) {
return vec;
}
template <typename K>
double ContainerInfo(const std::set<K> &s) {
int sizeof_node = sizeof(std::_Rb_tree_node<K>);
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 <typename K>
double ContainerInfo(const std::multiset<K> &s) {
int sizeof_node = sizeof(std::_Rb_tree_node<K>);
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 <typename K, typename V>
double ContainerInfo(const std::map<K, V> &m) {
int sizeof_node = sizeof(std::_Rb_tree_node<std::pair<K, V> >);
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 <typename K, typename V>
double ContainerInfo(const std::multimap<K, V> &m) {
int sizeof_node = sizeof(std::_Rb_tree_node<std::pair<K, V> >);
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 <typename P>
double ContainerInfo(const btree_container<P> &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 <typename T, typename V>
void DoTest(const char *name, T *b, const std::vector<V> &values) {
typename KeyOfValue<typename T::key_type, V>::type key_of_value;