Skip to content

Commit 535ed92

Browse files
committed
Simple benchmarking framework
Benchmarking framework, loosely based on google's micro-benchmarking library (https://github.com/google/benchmark) Wny not use the Google Benchmark framework? Because adding Even More Dependencies isn't worth it. If we get a dozen or three benchmarks and need nanosecond-accurate timings of threaded code then switching to the full-blown Google Benchmark library should be considered. The benchmark framework is hard-coded to run each benchmark for one wall-clock second, and then spits out .csv-format timing information to stdout. It is left as an exercise for later (or maybe never) to add command-line arguments to specify which benchmark(s) to run, how long to run them for, how to format results, etc etc etc. Again, see the Google Benchmark framework for where that might end up. See src/bench/MilliSleep.cpp for a sanity-test benchmark that just benchmarks 'sleep 100 milliseconds.' To compile and run benchmarks: cd src; make bench Sample output: Benchmark,count,min,max,average Sleep100ms,10,0.101854,0.105059,0.103881
1 parent 1a9f19a commit 535ed92

File tree

7 files changed

+222
-0
lines changed

7 files changed

+222
-0
lines changed

configure.ac

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ AC_ARG_ENABLE(tests,
9191
[use_tests=$enableval],
9292
[use_tests=yes])
9393

94+
AC_ARG_ENABLE(bench,
95+
AS_HELP_STRING([--enable-bench],[compile benchmarks (default is yes)]),
96+
[use_bench=$enableval],
97+
[use_bench=yes])
98+
9499
AC_ARG_WITH([comparison-tool],
95100
AS_HELP_STRING([--with-comparison-tool],[path to java comparison tool (requires --enable-tests)]),
96101
[use_comparison_tool=$withval],
@@ -881,6 +886,7 @@ AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes])
881886
AM_CONDITIONAL([ENABLE_TESTS],[test x$use_tests = xyes])
882887
AM_CONDITIONAL([ENABLE_QT],[test x$bitcoin_enable_qt = xyes])
883888
AM_CONDITIONAL([ENABLE_QT_TESTS],[test x$use_tests$bitcoin_enable_qt_test = xyesyes])
889+
AM_CONDITIONAL([ENABLE_BENCH],[test x$use_bench = xyes])
884890
AM_CONDITIONAL([USE_QRCODE], [test x$use_qr = xyes])
885891
AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes])
886892
AM_CONDITIONAL([USE_COMPARISON_TOOL],[test x$use_comparison_tool != xno])

src/Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ endif
6161

6262
bin_PROGRAMS =
6363
TESTS =
64+
BENCHMARKS =
6465

6566
if BUILD_BITCOIND
6667
bin_PROGRAMS += bitcoind
@@ -445,6 +446,10 @@ if ENABLE_TESTS
445446
include Makefile.test.include
446447
endif
447448

449+
if ENABLE_BENCH
450+
include Makefile.bench.include
451+
endif
452+
448453
if ENABLE_QT
449454
include Makefile.qt.include
450455
endif

src/Makefile.bench.include

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
bin_PROGRAMS += bench/bench_bitcoin
2+
BENCH_SRCDIR = bench
3+
BENCH_BINARY = bench/bench_bitcoin$(EXEEXT)
4+
5+
6+
bench_bench_bitcoin_SOURCES = \
7+
bench/bench_bitcoin.cpp \
8+
bench/bench.cpp \
9+
bench/bench.h \
10+
bench/MilliSleep.cpp
11+
12+
bench_bench_bitcoin_CPPFLAGS = $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
13+
bench_bench_bitcoin_LDADD = \
14+
$(LIBBITCOIN_SERVER) \
15+
$(LIBBITCOIN_COMMON) \
16+
$(LIBBITCOIN_UNIVALUE) \
17+
$(LIBBITCOIN_UTIL) \
18+
$(LIBBITCOIN_CRYPTO) \
19+
$(LIBLEVELDB) \
20+
$(LIBMEMENV) \
21+
$(LIBSECP256K1)
22+
23+
if ENABLE_ZMQ
24+
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
25+
endif
26+
27+
if ENABLE_WALLET
28+
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
29+
endif
30+
31+
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
32+
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
33+
34+
35+
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno
36+
37+
CLEANFILES += $(CLEAN_BITCOIN_BENCH)
38+
39+
bitcoin_bench: $(BENCH_BINARY)
40+
41+
bench: $(BENCH_BINARY) FORCE
42+
$(BENCH_BINARY)
43+
44+
bitcoin_bench_clean : FORCE
45+
rm -f $(CLEAN_BITCOIN_BENCH) $(bench_bench_bitcoin_OBJECTS) $(BENCH_BINARY)

src/bench/MilliSleep.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2015 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "bench.h"
6+
#include "main.h"
7+
#include "utiltime.h"
8+
9+
static void Sleep100ms(benchmark::State& state)
10+
{
11+
while (state.KeepRunning()) {
12+
MilliSleep(100);
13+
}
14+
}
15+
16+
BENCHMARK(Sleep100ms);

src/bench/bench.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) 2015 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
#include "bench.h"
5+
#include <iostream>
6+
#include <sys/time.h>
7+
8+
using namespace benchmark;
9+
10+
std::map<std::string, BenchFunction> BenchRunner::benchmarks;
11+
12+
static double gettimedouble(void) {
13+
struct timeval tv;
14+
gettimeofday(&tv, NULL);
15+
return tv.tv_usec * 0.000001 + tv.tv_sec;
16+
}
17+
18+
BenchRunner::BenchRunner(std::string name, BenchFunction func)
19+
{
20+
benchmarks.insert(std::make_pair(name, func));
21+
}
22+
23+
void
24+
BenchRunner::RunAll(double elapsedTimeForOne)
25+
{
26+
std::cout << "Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n";
27+
28+
for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin();
29+
it != benchmarks.end(); ++it) {
30+
31+
State state(it->first, elapsedTimeForOne);
32+
BenchFunction& func = it->second;
33+
func(state);
34+
}
35+
}
36+
37+
bool State::KeepRunning()
38+
{
39+
double now = gettimedouble();
40+
if (count == 0) {
41+
beginTime = now;
42+
}
43+
else {
44+
double elapsedOne = now - lastTime;
45+
if (elapsedOne < minTime) minTime = elapsedOne;
46+
if (elapsedOne > maxTime) maxTime = elapsedOne;
47+
}
48+
lastTime = now;
49+
++count;
50+
51+
if (now - beginTime < maxElapsed) return true; // Keep going
52+
53+
--count;
54+
55+
// Output results
56+
double average = (now-beginTime)/count;
57+
std::cout << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n";
58+
59+
return false;
60+
}

src/bench/bench.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) 2015 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
#ifndef BITCOIN_BENCH_H
5+
#define BITCOIN_BENCH_H
6+
7+
// Simple micro-benchmarking framework; API mostly matches a subset of the Google Benchmark
8+
// framework (see https://github.com/google/benchmark)
9+
// Wny not use the Google Benchmark framework? Because adding Yet Another Dependency
10+
// (that uses cmake as its build system and has lots of features we don't need) isn't
11+
// worth it.
12+
13+
/*
14+
* Usage:
15+
16+
static void CODE_TO_TIME(benchmark::State& state)
17+
{
18+
... do any setup needed...
19+
while (state.KeepRunning()) {
20+
... do stuff you want to time...
21+
}
22+
... do any cleanup needed...
23+
}
24+
25+
BENCHMARK(CODE_TO_TIME);
26+
27+
*/
28+
29+
30+
#include <boost/function.hpp>
31+
#include <boost/preprocessor/cat.hpp>
32+
#include <boost/preprocessor/stringize.hpp>
33+
#include <map>
34+
#include <string>
35+
36+
namespace benchmark {
37+
38+
class State {
39+
std::string name;
40+
double maxElapsed;
41+
double beginTime;
42+
double lastTime, minTime, maxTime;
43+
int64_t count;
44+
public:
45+
State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) {
46+
minTime = std::numeric_limits<double>::max();
47+
maxTime = std::numeric_limits<double>::min();
48+
}
49+
bool KeepRunning();
50+
};
51+
52+
typedef boost::function<void(State&)> BenchFunction;
53+
54+
class BenchRunner
55+
{
56+
static std::map<std::string, BenchFunction> benchmarks;
57+
58+
public:
59+
BenchRunner(std::string name, BenchFunction func);
60+
61+
static void RunAll(double elapsedTimeForOne=1.0);
62+
};
63+
}
64+
65+
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
66+
#define BENCHMARK(n) \
67+
benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))(BOOST_PP_STRINGIZE(n), n);
68+
69+
#endif // BITCOIN_BENCH_H

src/bench/bench_bitcoin.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) 2015 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "bench.h"
6+
7+
#include "key.h"
8+
#include "main.h"
9+
#include "util.h"
10+
11+
int
12+
main(int argc, char** argv)
13+
{
14+
ECC_Start();
15+
SetupEnvironment();
16+
fPrintToDebugLog = false; // don't want to write to debug.log file
17+
18+
benchmark::BenchRunner::RunAll();
19+
20+
ECC_Stop();
21+
}

0 commit comments

Comments
 (0)