Merge #6250: backport: Merge bitcoin#22217, 23025, 22362

bd67f80634 Merge bitcoin/bitcoin#22362: Drop only invalid entries when reading banlist.json (W. J. van der Laan)
8b3a486702 Merge bitcoin/bitcoin#23025: bench: update nanobench add `-min_time` (W. J. van der Laan)
d7a20b3ee6 Merge bitcoin/bitcoin#22217: refactor: Avoid wallet code writing node settings file (Samuel Dobson)

Pull request description:

  btc backport

ACKs for top commit:
  knst:
    utACK  bd67f80634
  UdjinM6:
    utACK bd67f80634

Tree-SHA512: c487760c7b4946c21103978625076dd276344c831e99452031fae9b5c6cf954a1a0d3109725ab10f3d6837a5fd1833886a7e28c88b28429c6638c82f06f54f3d
This commit is contained in:
Konstantin Akimov 2024-09-27 00:02:52 +07:00
commit c200175ebf
No known key found for this signature in database
GPG Key ID: 2176C4A5D01EA524
10 changed files with 118 additions and 40 deletions

View File

@ -4,15 +4,20 @@
#include <bench/bench.h> #include <bench/bench.h>
#include <chainparams.h>
#include <fs.h> #include <fs.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <validation.h>
#include <algorithm> #include <algorithm>
#include <chrono>
#include <fstream> #include <fstream>
#include <functional>
#include <iostream> #include <iostream>
#include <map>
#include <regex> #include <regex>
#include <string>
#include <vector>
using namespace std::chrono_literals;
const std::function<void(const std::string&)> G_TEST_LOG_FUN{}; const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
@ -66,6 +71,12 @@ void benchmark::BenchRunner::RunAll(const Args& args)
Bench bench; Bench bench;
bench.name(p.first); bench.name(p.first);
if (args.min_time > 0ms) {
// convert to nanos before dividing to reduce rounding errors
std::chrono::nanoseconds min_time_ns = args.min_time;
bench.minEpochTime(min_time_ns / bench.epochs());
}
if (args.asymptote.empty()) { if (args.asymptote.empty()) {
p.second(bench); p.second(bench);
} else { } else {

View File

@ -42,11 +42,12 @@ using ankerl::nanobench::Bench;
typedef std::function<void(Bench&)> BenchFunction; typedef std::function<void(Bench&)> BenchFunction;
struct Args { struct Args {
std::string regex_filter;
bool is_list_only; bool is_list_only;
std::chrono::milliseconds min_time;
std::vector<double> asymptote; std::vector<double> asymptote;
fs::path output_csv; fs::path output_csv;
fs::path output_json; fs::path output_json;
std::string regex_filter;
}; };
class BenchRunner class BenchRunner

View File

@ -4,6 +4,7 @@
#include <bench/bench.h> #include <bench/bench.h>
#include <clientversion.h>
#include <crypto/sha256.h> #include <crypto/sha256.h>
#include <fs.h> #include <fs.h>
#include <stacktraces.h> #include <stacktraces.h>
@ -11,16 +12,23 @@
#include <util/system.h> #include <util/system.h>
#include <bls/bls.h> #include <bls/bls.h>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <sstream>
#include <vector>
static const char* DEFAULT_BENCH_FILTER = ".*"; static const char* DEFAULT_BENCH_FILTER = ".*";
static constexpr int64_t DEFAULT_MIN_TIME_MS{10};
static void SetupBenchArgs(ArgsManager& argsman) static void SetupBenchArgs(ArgsManager& argsman)
{ {
SetupHelpOptions(argsman); SetupHelpOptions(argsman);
argsman.AddArg("-asymptote=n1,n2,n3,...", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_BOOL, OptionsCategory::OPTIONS);
argsman.AddArg("-min_time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
} }
@ -50,16 +58,62 @@ int main(int argc, char** argv)
} }
if (HelpRequested(argsman)) { if (HelpRequested(argsman)) {
std::cout << argsman.GetHelpMessage(); std::cout << "Usage: bench_dash [options]\n"
"\n"
<< argsman.GetHelpMessage()
<< "Description:\n"
"\n"
" bench_dash executes microbenchmarks. The quality of the benchmark results\n"
" highly depend on the stability of the machine. It can sometimes be difficult\n"
" to get stable, repeatable results, so here are a few tips:\n"
"\n"
" * Use pyperf [1] to disable frequency scaling, turbo boost etc. For best\n"
" results, use CPU pinning and CPU isolation (see [2]).\n"
"\n"
" * Each call of run() should do exactly the same work. E.g. inserting into\n"
" a std::vector doesn't do that as it will reallocate on certain calls. Make\n"
" sure each run has exactly the same preconditions.\n"
"\n"
" * If results are still not reliable, increase runtime with e.g.\n"
" -min_time=5000 to let a benchmark run for at least 5 seconds.\n"
"\n"
" * bench_dash uses nanobench [3] for which there is extensive\n"
" documentation available online.\n"
"\n"
"Environment Variables:\n"
"\n"
" To attach a profiler you can run a benchmark in endless mode. This can be\n"
" done with the environment variable NANOBENCH_ENDLESS. E.g. like so:\n"
"\n"
" NANOBENCH_ENDLESS=MuHash ./bench_dash -filter=MuHash\n"
"\n"
" In rare cases it can be useful to suppress stability warnings. This can be\n"
" done with the environment variable NANOBENCH_SUPPRESS_WARNINGS, e.g:\n"
"\n"
" NANOBENCH_SUPPRESS_WARNINGS=1 ./bench_dash\n"
"\n"
"Notes:\n"
"\n"
" 1. pyperf\n"
" https://github.com/psf/pyperf\n"
"\n"
" 2. CPU pinning & isolation\n"
" https://pyperf.readthedocs.io/en/latest/system.html\n"
"\n"
" 3. nanobench\n"
" https://github.com/martinus/nanobench\n"
"\n";
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
benchmark::Args args; benchmark::Args args;
args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
args.is_list_only = argsman.GetBoolArg("-list", false);
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", "")); args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
args.is_list_only = argsman.GetBoolArg("-list", false);
args.min_time = std::chrono::milliseconds(argsman.GetArg("-min_time", DEFAULT_MIN_TIME_MS));
args.output_csv = fs::PathFromString(argsman.GetArg("-output_csv", "")); args.output_csv = fs::PathFromString(argsman.GetArg("-output_csv", ""));
args.output_json = fs::PathFromString(argsman.GetArg("-output_json", "")); args.output_json = fs::PathFromString(argsman.GetArg("-output_json", ""));
args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
benchmark::BenchRunner::RunAll(args); benchmark::BenchRunner::RunAll(args);

View File

@ -249,9 +249,9 @@ static void MuHash(benchmark::Bench& bench)
{ {
MuHash3072 acc; MuHash3072 acc;
unsigned char key[32] = {0}; unsigned char key[32] = {0};
int i = 0; uint32_t i = 0;
bench.run([&] { bench.run([&] {
key[0] = ++i; key[0] = ++i & 0xFF;
acc *= MuHash3072(key); acc *= MuHash3072(key);
}); });
} }
@ -273,10 +273,6 @@ static void MuHashDiv(benchmark::Bench& bench)
FastRandomContext rng(true); FastRandomContext rng(true);
MuHash3072 muhash{rng.randbytes(32)}; MuHash3072 muhash{rng.randbytes(32)};
for (size_t i = 0; i < bench.epochIterations(); ++i) {
acc *= muhash;
}
bench.run([&] { bench.run([&] {
acc /= muhash; acc /= muhash;
}); });

View File

@ -20,19 +20,17 @@ static void EvictionProtectionCommon(
{ {
using Candidates = std::vector<NodeEvictionCandidate>; using Candidates = std::vector<NodeEvictionCandidate>;
FastRandomContext random_context{true}; FastRandomContext random_context{true};
bench.warmup(100).epochIterations(1100);
Candidates candidates{GetRandomNodeEvictionCandidates(num_candidates, random_context)}; Candidates candidates{GetRandomNodeEvictionCandidates(num_candidates, random_context)};
for (auto& c : candidates) { for (auto& c : candidates) {
candidate_setup_fn(c); candidate_setup_fn(c);
} }
std::vector<Candidates> copies{
static_cast<size_t>(bench.epochs() * bench.epochIterations()), candidates};
size_t i{0};
bench.run([&] { bench.run([&] {
ProtectEvictionCandidatesByRatio(copies.at(i)); // creating a copy has an overhead of about 3%, so it does not influence the benchmark results much.
++i; auto copy = candidates;
ProtectEvictionCandidatesByRatio(copy);
}); });
} }

View File

@ -13,16 +13,16 @@ static void RollingBloom(benchmark::Bench& bench)
uint32_t count = 0; uint32_t count = 0;
bench.run([&] { bench.run([&] {
count++; count++;
data[0] = count; data[0] = count & 0xFF;
data[1] = count >> 8; data[1] = (count >> 8) & 0xFF;
data[2] = count >> 16; data[2] = (count >> 16) & 0xFF;
data[3] = count >> 24; data[3] = (count >> 24) & 0xFF;
filter.insert(data); filter.insert(data);
data[0] = count >> 24; data[0] = (count >> 24) & 0xFF;
data[1] = count >> 16; data[1] = (count >> 16) & 0xFF;
data[2] = count >> 8; data[2] = (count >> 8) & 0xFF;
data[3] = count; data[3] = count & 0xFF;
filter.contains(data); filter.contains(data);
}); });
} }

View File

@ -285,11 +285,18 @@ public:
//! Run function after given number of seconds. Cancel any previous calls with same name. //! Run function after given number of seconds. Cancel any previous calls with same name.
virtual void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) = 0; virtual void rpcRunLater(const std::string& name, std::function<void()> fn, int64_t seconds) = 0;
//! Get settings value.
virtual util::SettingsValue getSetting(const std::string& arg) = 0;
//! Get list of settings values.
virtual std::vector<util::SettingsValue> getSettingsList(const std::string& arg) = 0;
//! Return <datadir>/settings.json setting value. //! Return <datadir>/settings.json setting value.
virtual util::SettingsValue getRwSetting(const std::string& name) = 0; virtual util::SettingsValue getRwSetting(const std::string& name) = 0;
//! Write a setting to <datadir>/settings.json. //! Write a setting to <datadir>/settings.json. Optionally just update the
virtual bool updateRwSetting(const std::string& name, const util::SettingsValue& value) = 0; //! setting in memory and do not write the file.
virtual bool updateRwSetting(const std::string& name, const util::SettingsValue& value, bool write=true) = 0;
//! Synchronously send transactionAddedToMempool notifications about all //! Synchronously send transactionAddedToMempool notifications about all
//! current mempool transactions to the specified handler and return after //! current mempool transactions to the specified handler and return after

View File

@ -971,6 +971,14 @@ public:
{ {
RPCRunLater(name, std::move(fn), seconds); RPCRunLater(name, std::move(fn), seconds);
} }
util::SettingsValue getSetting(const std::string& name) override
{
return gArgs.GetSetting(name);
}
std::vector<util::SettingsValue> getSettingsList(const std::string& name) override
{
return gArgs.GetSettingsList(name);
}
util::SettingsValue getRwSetting(const std::string& name) override util::SettingsValue getRwSetting(const std::string& name) override
{ {
util::SettingsValue result; util::SettingsValue result;
@ -981,7 +989,7 @@ public:
}); });
return result; return result;
} }
bool updateRwSetting(const std::string& name, const util::SettingsValue& value) override bool updateRwSetting(const std::string& name, const util::SettingsValue& value, bool write) override
{ {
gArgs.LockSettings([&](util::Settings& settings) { gArgs.LockSettings([&](util::Settings& settings) {
if (value.isNull()) { if (value.isNull()) {
@ -990,7 +998,7 @@ public:
settings.rw_settings[name] = value; settings.rw_settings[name] = value;
} }
}); });
return gArgs.WriteSettingsFile(); return !write || gArgs.WriteSettingsFile();
} }
void requestMempoolTransactions(Notifications& notifications) override void requestMempoolTransactions(Notifications& notifications) override
{ {

View File

@ -225,6 +225,7 @@ protected:
*/ */
bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args); bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
public:
/** /**
* Get setting value. * Get setting value.
* *
@ -239,7 +240,6 @@ protected:
*/ */
std::vector<util::SettingsValue> GetSettingsList(const std::string& arg) const; std::vector<util::SettingsValue> GetSettingsList(const std::string& arg) const;
public:
ArgsManager(); ArgsManager();
~ArgsManager(); ~ArgsManager();

View File

@ -57,18 +57,20 @@ bool VerifyWallets(interfaces::Chain& chain)
options.require_existing = true; options.require_existing = true;
options.verify = false; options.verify = false;
if (MakeWalletDatabase("", options, status, error_string)) { if (MakeWalletDatabase("", options, status, error_string)) {
gArgs.LockSettings([&](util::Settings& settings) { util::SettingsValue wallets(util::SettingsValue::VARR);
util::SettingsValue wallets(util::SettingsValue::VARR); wallets.push_back(""); // Default wallet name is ""
wallets.push_back(""); // Default wallet name is "" // Pass write=false because no need to write file and probably
settings.rw_settings["wallet"] = wallets; // better not to. If unnamed wallet needs to be added next startup
}); // and the setting is empty, this code will just run again.
chain.updateRwSetting("wallet", wallets, /* write= */ false);
} }
} }
// Keep track of each wallet absolute path to detect duplicates. // Keep track of each wallet absolute path to detect duplicates.
std::set<fs::path> wallet_paths; std::set<fs::path> wallet_paths;
for (const auto& wallet_file : gArgs.GetArgs("-wallet")) { for (const auto& wallet : chain.getSettingsList("wallet")) {
const auto& wallet_file = wallet.get_str();
const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file)); const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(wallet_file));
if (!wallet_paths.insert(path).second) { if (!wallet_paths.insert(path).second) {
@ -98,7 +100,8 @@ bool LoadWallets(interfaces::Chain& chain, interfaces::CoinJoin::Loader& coinjoi
{ {
try { try {
std::set<fs::path> wallet_paths; std::set<fs::path> wallet_paths;
for (const std::string& name : gArgs.GetArgs("-wallet")) { for (const auto& wallet : chain.getSettingsList("wallet")) {
const auto& name = wallet.get_str();
if (!wallet_paths.insert(fs::PathFromString(name)).second) { if (!wallet_paths.insert(fs::PathFromString(name)).second) {
continue; continue;
} }