Skip to content

Commit 18dacf9

Browse files
ryanofskylaanwj
authored andcommitted
Add microbenchmarks to profile more code paths.
The new benchmarks exercise script validation, CCoinsDBView caching, mempool eviction, and wallet coin selection code. All of the benchmarks added here are extremely simple and don't necessarily mirror common real world conditions or interesting performance edge cases. Details about how specific benchmarks can be improved are noted in comments. Github-Issue: bitcoin#7883
1 parent a7e5cbb commit 18dacf9

File tree

5 files changed

+372
-1
lines changed

5 files changed

+372
-1
lines changed

src/Makefile.bench.include

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ bench_bench_bitcoin_SOURCES = \
1414
bench/Examples.cpp \
1515
bench/rollingbloom.cpp \
1616
bench/crypto_hash.cpp \
17+
bench/ccoins_caching.cpp \
18+
bench/mempool_eviction.cpp \
19+
bench/verify_script.cpp \
1720
bench/base58.cpp
1821

1922
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
@@ -34,7 +37,8 @@ bench_bench_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
3437
endif
3538

3639
if ENABLE_WALLET
37-
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
40+
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
41+
bench_bench_bitcoin_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO)
3842
endif
3943

4044
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)

src/bench/ccoins_caching.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) 2016 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 "coins.h"
7+
#include "policy/policy.h"
8+
#include "wallet/crypter.h"
9+
10+
#include <vector>
11+
12+
// FIXME: Dedup with SetupDummyInputs in test/transaction_tests.cpp.
13+
//
14+
// Helper: create two dummy transactions, each with
15+
// two outputs. The first has 11 and 50 CENT outputs
16+
// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
17+
// paid to a TX_PUBKEYHASH.
18+
//
19+
static std::vector<CMutableTransaction>
20+
SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
21+
{
22+
std::vector<CMutableTransaction> dummyTransactions;
23+
dummyTransactions.resize(2);
24+
25+
// Add some keys to the keystore:
26+
CKey key[4];
27+
for (int i = 0; i < 4; i++) {
28+
key[i].MakeNewKey(i % 2);
29+
keystoreRet.AddKey(key[i]);
30+
}
31+
32+
// Create some dummy input transactions
33+
dummyTransactions[0].vout.resize(2);
34+
dummyTransactions[0].vout[0].nValue = 11 * CENT;
35+
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
36+
dummyTransactions[0].vout[1].nValue = 50 * CENT;
37+
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
38+
coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
39+
40+
dummyTransactions[1].vout.resize(2);
41+
dummyTransactions[1].vout[0].nValue = 21 * CENT;
42+
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
43+
dummyTransactions[1].vout[1].nValue = 22 * CENT;
44+
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
45+
coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
46+
47+
return dummyTransactions;
48+
}
49+
50+
// Microbenchmark for simple accesses to a CCoinsViewCache database. Note from
51+
// laanwj, "replicating the actual usage patterns of the client is hard though,
52+
// many times micro-benchmarks of the database showed completely different
53+
// characteristics than e.g. reindex timings. But that's not a requirement of
54+
// every benchmark."
55+
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
56+
static void CCoinsCaching(benchmark::State& state)
57+
{
58+
CBasicKeyStore keystore;
59+
CCoinsView coinsDummy;
60+
CCoinsViewCache coins(&coinsDummy);
61+
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
62+
63+
CMutableTransaction t1;
64+
t1.vin.resize(3);
65+
t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
66+
t1.vin[0].prevout.n = 1;
67+
t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
68+
t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();
69+
t1.vin[1].prevout.n = 0;
70+
t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
71+
t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();
72+
t1.vin[2].prevout.n = 1;
73+
t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
74+
t1.vout.resize(2);
75+
t1.vout[0].nValue = 90 * CENT;
76+
t1.vout[0].scriptPubKey << OP_1;
77+
78+
// Benchmark.
79+
while (state.KeepRunning()) {
80+
bool success = AreInputsStandard(t1, coins);
81+
assert(success);
82+
CAmount value = coins.GetValueIn(t1);
83+
assert(value == (50 + 21 + 22) * CENT);
84+
}
85+
}
86+
87+
BENCHMARK(CCoinsCaching);

src/bench/coin_selection.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2012-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 "wallet/wallet.h"
7+
8+
#include <boost/foreach.hpp>
9+
#include <set>
10+
11+
using namespace std;
12+
13+
static void addCoin(const CAmount& nValue, const CWallet& wallet, vector<COutput>& vCoins)
14+
{
15+
int nInput = 0;
16+
17+
static int nextLockTime = 0;
18+
CMutableTransaction tx;
19+
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
20+
tx.vout.resize(nInput + 1);
21+
tx.vout[nInput].nValue = nValue;
22+
CWalletTx* wtx = new CWalletTx(&wallet, tx);
23+
24+
int nAge = 6 * 24;
25+
COutput output(wtx, nInput, nAge, true, true);
26+
vCoins.push_back(output);
27+
}
28+
29+
// Simple benchmark for wallet coin selection. Note that it maybe be necessary
30+
// to build up more complicated scenarios in order to get meaningful
31+
// measurements of performance. From laanwj, "Wallet coin selection is probably
32+
// the hardest, as you need a wider selection of scenarios, just testing the
33+
// same one over and over isn't too useful. Generating random isn't useful
34+
// either for measurements."
35+
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
36+
static void CoinSelection(benchmark::State& state)
37+
{
38+
const CWallet wallet;
39+
vector<COutput> vCoins;
40+
LOCK(wallet.cs_wallet);
41+
42+
while (state.KeepRunning()) {
43+
// Empty wallet.
44+
BOOST_FOREACH (COutput output, vCoins)
45+
delete output.tx;
46+
vCoins.clear();
47+
48+
// Add coins.
49+
for (int i = 0; i < 1000; i++)
50+
addCoin(1000 * COIN, wallet, vCoins);
51+
addCoin(3 * COIN, wallet, vCoins);
52+
53+
set<pair<const CWalletTx*, unsigned int> > setCoinsRet;
54+
CAmount nValueRet;
55+
bool success = wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet);
56+
assert(success);
57+
assert(nValueRet == 1003 * COIN);
58+
assert(setCoinsRet.size() == 2);
59+
}
60+
}
61+
62+
BENCHMARK(CoinSelection);

src/bench/mempool_eviction.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (c) 2011-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 "policy/policy.h"
7+
#include "txmempool.h"
8+
9+
#include <list>
10+
#include <vector>
11+
12+
static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool)
13+
{
14+
int64_t nTime = 0;
15+
double dPriority = 10.0;
16+
unsigned int nHeight = 1;
17+
bool spendsCoinbase = false;
18+
unsigned int sigOpCost = 4;
19+
LockPoints lp;
20+
pool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(
21+
tx, nFee, nTime, dPriority, nHeight, pool.HasNoInputsOf(tx),
22+
tx.GetValueOut(), spendsCoinbase, sigOpCost, lp));
23+
}
24+
25+
// Right now this is only testing eviction performance in an extremely small
26+
// mempool. Code needs to be written to generate a much wider variety of
27+
// unique transactions for a more meaningful performance measurement.
28+
static void MempoolEviction(benchmark::State& state)
29+
{
30+
CMutableTransaction tx1 = CMutableTransaction();
31+
tx1.vin.resize(1);
32+
tx1.vin[0].scriptSig = CScript() << OP_1;
33+
tx1.vout.resize(1);
34+
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
35+
tx1.vout[0].nValue = 10 * COIN;
36+
37+
CMutableTransaction tx2 = CMutableTransaction();
38+
tx2.vin.resize(1);
39+
tx2.vin[0].scriptSig = CScript() << OP_2;
40+
tx2.vout.resize(1);
41+
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
42+
tx2.vout[0].nValue = 10 * COIN;
43+
44+
CMutableTransaction tx3 = CMutableTransaction();
45+
tx3.vin.resize(1);
46+
tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
47+
tx3.vin[0].scriptSig = CScript() << OP_2;
48+
tx3.vout.resize(1);
49+
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
50+
tx3.vout[0].nValue = 10 * COIN;
51+
52+
CMutableTransaction tx4 = CMutableTransaction();
53+
tx4.vin.resize(2);
54+
tx4.vin[0].prevout.SetNull();
55+
tx4.vin[0].scriptSig = CScript() << OP_4;
56+
tx4.vin[1].prevout.SetNull();
57+
tx4.vin[1].scriptSig = CScript() << OP_4;
58+
tx4.vout.resize(2);
59+
tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
60+
tx4.vout[0].nValue = 10 * COIN;
61+
tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
62+
tx4.vout[1].nValue = 10 * COIN;
63+
64+
CMutableTransaction tx5 = CMutableTransaction();
65+
tx5.vin.resize(2);
66+
tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
67+
tx5.vin[0].scriptSig = CScript() << OP_4;
68+
tx5.vin[1].prevout.SetNull();
69+
tx5.vin[1].scriptSig = CScript() << OP_5;
70+
tx5.vout.resize(2);
71+
tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
72+
tx5.vout[0].nValue = 10 * COIN;
73+
tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
74+
tx5.vout[1].nValue = 10 * COIN;
75+
76+
CMutableTransaction tx6 = CMutableTransaction();
77+
tx6.vin.resize(2);
78+
tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
79+
tx6.vin[0].scriptSig = CScript() << OP_4;
80+
tx6.vin[1].prevout.SetNull();
81+
tx6.vin[1].scriptSig = CScript() << OP_6;
82+
tx6.vout.resize(2);
83+
tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
84+
tx6.vout[0].nValue = 10 * COIN;
85+
tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
86+
tx6.vout[1].nValue = 10 * COIN;
87+
88+
CMutableTransaction tx7 = CMutableTransaction();
89+
tx7.vin.resize(2);
90+
tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
91+
tx7.vin[0].scriptSig = CScript() << OP_5;
92+
tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
93+
tx7.vin[1].scriptSig = CScript() << OP_6;
94+
tx7.vout.resize(2);
95+
tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
96+
tx7.vout[0].nValue = 10 * COIN;
97+
tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
98+
tx7.vout[1].nValue = 10 * COIN;
99+
100+
CTxMemPool pool(CFeeRate(1000));
101+
102+
while (state.KeepRunning()) {
103+
AddTx(tx1, 10000LL, pool);
104+
AddTx(tx2, 5000LL, pool);
105+
AddTx(tx3, 20000LL, pool);
106+
AddTx(tx4, 7000LL, pool);
107+
AddTx(tx5, 1000LL, pool);
108+
AddTx(tx6, 1100LL, pool);
109+
AddTx(tx7, 9000LL, pool);
110+
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4);
111+
pool.TrimToSize(GetVirtualTransactionSize(tx1));
112+
}
113+
}
114+
115+
BENCHMARK(MempoolEviction);

src/bench/verify_script.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) 2016 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 "key.h"
7+
#if defined(HAVE_CONSENSUS_LIB)
8+
#include "script/bitcoinconsensus.h"
9+
#endif
10+
#include "script/script.h"
11+
#include "script/sign.h"
12+
#include "streams.h"
13+
14+
// FIXME: Dedup with BuildCreditingTransaction in test/script_tests.cpp.
15+
static CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey)
16+
{
17+
CMutableTransaction txCredit;
18+
txCredit.nVersion = 1;
19+
txCredit.nLockTime = 0;
20+
txCredit.vin.resize(1);
21+
txCredit.vout.resize(1);
22+
txCredit.vin[0].prevout.SetNull();
23+
txCredit.vin[0].scriptSig = CScript() << CScriptNum(0) << CScriptNum(0);
24+
txCredit.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
25+
txCredit.vout[0].scriptPubKey = scriptPubKey;
26+
txCredit.vout[0].nValue = 1;
27+
28+
return txCredit;
29+
}
30+
31+
// FIXME: Dedup with BuildSpendingTransaction in test/script_tests.cpp.
32+
static CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMutableTransaction& txCredit)
33+
{
34+
CMutableTransaction txSpend;
35+
txSpend.nVersion = 1;
36+
txSpend.nLockTime = 0;
37+
txSpend.vin.resize(1);
38+
txSpend.vout.resize(1);
39+
txSpend.wit.vtxinwit.resize(1);
40+
txSpend.vin[0].prevout.hash = txCredit.GetHash();
41+
txSpend.vin[0].prevout.n = 0;
42+
txSpend.vin[0].scriptSig = scriptSig;
43+
txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
44+
txSpend.vout[0].scriptPubKey = CScript();
45+
txSpend.vout[0].nValue = txCredit.vout[0].nValue;
46+
47+
return txSpend;
48+
}
49+
50+
// Microbenchmark for verification of a basic P2WPKH script. Can be easily
51+
// modified to measure performance of other types of scripts.
52+
static void VerifyScriptBench(benchmark::State& state)
53+
{
54+
const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH;
55+
const int witnessversion = 0;
56+
57+
// Keypair.
58+
CKey key;
59+
const unsigned char vchKey[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
60+
key.Set(vchKey, vchKey + 32, false);
61+
CPubKey pubkey = key.GetPubKey();
62+
uint160 pubkeyHash;
63+
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin());
64+
65+
// Script.
66+
CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash);
67+
CScript scriptSig;
68+
CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG;
69+
CTransaction txCredit = BuildCreditingTransaction(scriptPubKey);
70+
CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit);
71+
CScriptWitness& witness = txSpend.wit.vtxinwit[0].scriptWitness;
72+
witness.stack.emplace_back();
73+
key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SIGVERSION_WITNESS_V0), witness.stack.back(), 0);
74+
witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
75+
witness.stack.push_back(ToByteVector(pubkey));
76+
77+
// Benchmark.
78+
while (state.KeepRunning()) {
79+
ScriptError err;
80+
bool success = VerifyScript(
81+
txSpend.vin[0].scriptSig,
82+
txCredit.vout[0].scriptPubKey,
83+
&txSpend.wit.vtxinwit[0].scriptWitness,
84+
flags,
85+
MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue),
86+
&err);
87+
assert(err == SCRIPT_ERR_OK);
88+
assert(success);
89+
90+
#if defined(HAVE_CONSENSUS_LIB)
91+
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
92+
stream << txSpend;
93+
int csuccess = bitcoinconsensus_verify_script_with_amount(
94+
begin_ptr(txCredit.vout[0].scriptPubKey),
95+
txCredit.vout[0].scriptPubKey.size(),
96+
txCredit.vout[0].nValue,
97+
(const unsigned char*)&stream[0], stream.size(), 0, flags, nullptr);
98+
assert(csuccess == 1);
99+
#endif
100+
}
101+
}
102+
103+
BENCHMARK(VerifyScriptBench);

0 commit comments

Comments
 (0)