Skip to content

Commit 40b6070

Browse files
committed
Merge #16805: logs: add timing information to FlushStateToDisk()
dcef9a2 logs: add timing information to FlushStateToDisk() (James O'Beirne) 41edaf2 logs: add BCLog::Timer and related macros (James O'Beirne) Pull request description: It's currently annoying to detect FlushStateToDisk() calls when benchmarking since they have to be inferred from a drop in coins count from the `UpdateTip: ` log messages. This adds a new logging utility, `BCLog::Timer`, and some related macros that are generally useful for printing timing-related logging messages, and a message that is unconditionally written when the coins cache is flushed to disk. ``` 2019-09-04T20:17:51Z FlushStateToDisk: write block and undo data to disk completed (3ms) 2019-09-04T20:17:51Z FlushStateToDisk: write block index to disk completed (370ms) 2019-09-04T20:17:51Z FlushStateToDisk: write coins cache to disk (2068451 coins, 294967kB) completed (21481ms) ``` ACKs for top commit: laanwj: Thanks, ACK dcef9a2 ryanofsky: Code review ACK dcef9a2. No changes since last review other than moving code to new timer.h header Tree-SHA512: 6d61e48a062d3edb48d0e056a6f0b1f8031773cc99289ee4544f8349d24526b88519e1e304009d56e428f1eaf76c857bf8e7e1c0b6873a6f270306accb5edc3d
2 parents d9a4550 + dcef9a2 commit 40b6070

File tree

5 files changed

+168
-3
lines changed

5 files changed

+168
-3
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ BITCOIN_CORE_H = \
146146
dbwrapper.h \
147147
limitedmap.h \
148148
logging.h \
149+
logging/timer.h \
149150
memusage.h \
150151
merkleblock.h \
151152
miner.h \

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ BITCOIN_TESTS =\
127127
test/key_io_tests.cpp \
128128
test/key_tests.cpp \
129129
test/limitedmap_tests.cpp \
130+
test/logging_tests.cpp \
130131
test/dbwrapper_tests.cpp \
131132
test/validation_tests.cpp \
132133
test/mempool_tests.cpp \

src/logging/timer.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2018 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#ifndef BITCOIN_LOGGING_TIMER_H
7+
#define BITCOIN_LOGGING_TIMER_H
8+
9+
#include <logging.h>
10+
#include <util/macros.h>
11+
#include <util/time.h>
12+
13+
#include <chrono>
14+
#include <string>
15+
16+
17+
namespace BCLog {
18+
19+
//! RAII-style object that outputs timing information to logs.
20+
template <typename TimeType>
21+
class Timer
22+
{
23+
public:
24+
//! If log_category is left as the default, end_msg will log unconditionally
25+
//! (instead of being filtered by category).
26+
Timer(
27+
std::string prefix,
28+
std::string end_msg,
29+
BCLog::LogFlags log_category = BCLog::LogFlags::ALL) :
30+
m_prefix(std::move(prefix)),
31+
m_title(std::move(end_msg)),
32+
m_log_category(log_category)
33+
{
34+
this->Log(strprintf("%s started", m_title));
35+
m_start_t = GetTime<std::chrono::microseconds>();
36+
}
37+
38+
~Timer()
39+
{
40+
this->Log(strprintf("%s completed", m_title));
41+
}
42+
43+
void Log(const std::string& msg)
44+
{
45+
const std::string full_msg = this->LogMsg(msg);
46+
47+
if (m_log_category == BCLog::LogFlags::ALL) {
48+
LogPrintf("%s\n", full_msg);
49+
} else {
50+
LogPrint(m_log_category, "%s\n", full_msg);
51+
}
52+
}
53+
54+
std::string LogMsg(const std::string& msg)
55+
{
56+
const auto end_time = GetTime<std::chrono::microseconds>() - m_start_t;
57+
if (m_start_t.count() <= 0) {
58+
return strprintf("%s: %s", m_prefix, msg);
59+
}
60+
61+
std::string units = "";
62+
float divisor = 1;
63+
64+
if (std::is_same<TimeType, std::chrono::microseconds>::value) {
65+
units = "μs";
66+
} else if (std::is_same<TimeType, std::chrono::milliseconds>::value) {
67+
units = "ms";
68+
divisor = 1000.;
69+
} else if (std::is_same<TimeType, std::chrono::seconds>::value) {
70+
units = "s";
71+
divisor = 1000. * 1000.;
72+
}
73+
74+
const float time_ms = end_time.count() / divisor;
75+
return strprintf("%s: %s (%.2f%s)", m_prefix, msg, time_ms, units);
76+
}
77+
78+
private:
79+
std::chrono::microseconds m_start_t{};
80+
81+
//! Log prefix; usually the name of the function this was created in.
82+
const std::string m_prefix{};
83+
84+
//! A descriptive message of what is being timed.
85+
const std::string m_title{};
86+
87+
//! Forwarded on to LogPrint if specified - has the effect of only
88+
//! outputing the timing log when a particular debug= category is specified.
89+
const BCLog::LogFlags m_log_category{};
90+
91+
};
92+
93+
} // namespace BCLog
94+
95+
96+
#define LOG_TIME_MICROS(end_msg, ...) \
97+
BCLog::Timer<std::chrono::microseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, ## __VA_ARGS__)
98+
#define LOG_TIME_MILLIS(end_msg, ...) \
99+
BCLog::Timer<std::chrono::milliseconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, ## __VA_ARGS__)
100+
#define LOG_TIME_SECONDS(end_msg, ...) \
101+
BCLog::Timer<std::chrono::seconds> PASTE2(logging_timer, __COUNTER__)(__func__, end_msg, ## __VA_ARGS__)
102+
103+
104+
#endif // BITCOIN_LOGGING_TIMER_H

src/test/logging_tests.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2019 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 <logging.h>
6+
#include <logging/timer.h>
7+
#include <test/setup_common.h>
8+
9+
#include <chrono>
10+
11+
#include <boost/test/unit_test.hpp>
12+
13+
BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
14+
15+
BOOST_AUTO_TEST_CASE(logging_timer)
16+
{
17+
18+
SetMockTime(1);
19+
auto sec_timer = BCLog::Timer<std::chrono::seconds>("tests", "end_msg");
20+
SetMockTime(2);
21+
BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
22+
23+
SetMockTime(1);
24+
auto ms_timer = BCLog::Timer<std::chrono::milliseconds>("tests", "end_msg");
25+
SetMockTime(2);
26+
BOOST_CHECK_EQUAL(ms_timer.LogMsg("test ms"), "tests: test ms (1000.00ms)");
27+
28+
SetMockTime(1);
29+
auto micro_timer = BCLog::Timer<std::chrono::microseconds>("tests", "end_msg");
30+
SetMockTime(2);
31+
BOOST_CHECK_EQUAL(micro_timer.LogMsg("test micros"), "tests: test micros (1000000.00μs)");
32+
33+
SetMockTime(0);
34+
}
35+
36+
BOOST_AUTO_TEST_SUITE_END()

src/validation.cpp

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <flatfile.h>
1919
#include <hash.h>
2020
#include <index/txindex.h>
21+
#include <logging.h>
22+
#include <logging/timer.h>
2123
#include <policy/fees.h>
2224
#include <policy/policy.h>
2325
#include <policy/settings.h>
@@ -2199,15 +2201,23 @@ bool CChainState::FlushStateToDisk(
21992201
static int64_t nLastFlush = 0;
22002202
std::set<int> setFilesToPrune;
22012203
bool full_flush_completed = false;
2204+
2205+
const size_t coins_count = CoinsTip().GetCacheSize();
2206+
const size_t coins_mem_usage = CoinsTip().DynamicMemoryUsage();
2207+
22022208
try {
22032209
{
22042210
bool fFlushForPrune = false;
22052211
bool fDoFullFlush = false;
22062212
LOCK(cs_LastBlockFile);
22072213
if (fPruneMode && (fCheckForPruning || nManualPruneHeight > 0) && !fReindex) {
22082214
if (nManualPruneHeight > 0) {
2215+
LOG_TIME_MILLIS("find files to prune (manual)", BCLog::BENCH);
2216+
22092217
FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight);
22102218
} else {
2219+
LOG_TIME_MILLIS("find files to prune", BCLog::BENCH);
2220+
22112221
FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight());
22122222
fCheckForPruning = false;
22132223
}
@@ -2246,10 +2256,17 @@ bool CChainState::FlushStateToDisk(
22462256
if (!CheckDiskSpace(GetBlocksDir())) {
22472257
return AbortNode(state, "Disk space is too low!", _("Error: Disk space is too low!").translated, CClientUIInterface::MSG_NOPREFIX);
22482258
}
2249-
// First make sure all block and undo data is flushed to disk.
2250-
FlushBlockFile();
2259+
{
2260+
LOG_TIME_MILLIS("write block and undo data to disk", BCLog::BENCH);
2261+
2262+
// First make sure all block and undo data is flushed to disk.
2263+
FlushBlockFile();
2264+
}
2265+
22512266
// Then update all block file information (which may refer to block and undo files).
22522267
{
2268+
LOG_TIME_MILLIS("write block index to disk", BCLog::BENCH);
2269+
22532270
std::vector<std::pair<int, const CBlockFileInfo*> > vFiles;
22542271
vFiles.reserve(setDirtyFileInfo.size());
22552272
for (std::set<int>::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) {
@@ -2267,12 +2284,18 @@ bool CChainState::FlushStateToDisk(
22672284
}
22682285
}
22692286
// Finally remove any pruned files
2270-
if (fFlushForPrune)
2287+
if (fFlushForPrune) {
2288+
LOG_TIME_MILLIS("unlink pruned files", BCLog::BENCH);
2289+
22712290
UnlinkPrunedFiles(setFilesToPrune);
2291+
}
22722292
nLastWrite = nNow;
22732293
}
22742294
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
22752295
if (fDoFullFlush && !CoinsTip().GetBestBlock().IsNull()) {
2296+
LOG_TIME_SECONDS(strprintf("write coins cache to disk (%d coins, %.2fkB)",
2297+
coins_count, coins_mem_usage / 1000));
2298+
22762299
// Typical Coin structures on disk are around 48 bytes in size.
22772300
// Pushing a new one to the database can cause it to be written
22782301
// twice (once in the log, and once in the tables). This is already

0 commit comments

Comments
 (0)