0bed7b4702 Merge bitcoin/bitcoin#22292: bench, doc: benchmarking updates and fixups (fanquake)
c95df68637 Merge bitcoin/bitcoin#22388: ci: use Ubuntu 20.04 as the default Docker container (MarcoFalke)
c586ca5b56 Merge bitcoin/bitcoin#22334: wallet: do not spam about non-existent spk managers (fanquake)
62f9394374 Merge bitcoin/bitcoin#22308: wallet: Add missing BlockUntilSyncedToCurrentChain (MarcoFalke)
240d8efb82 Merge bitcoin/bitcoin#22358: Remove unused wallet pointer from wallet signals (fanquake)
9a1500ab47 Merge bitcoin/bitcoin#22118: test: check anchors.dat when node starts for the first time (MarcoFalke)
262c8b6f44 Merge bitcoin/bitcoin#22082: test: update nanobench from release 4.0.0 to 4.3.4 (MarcoFalke)
3d2cea667b Merge bitcoin-core/gui#311: Peers Window rename 'Peer id' to 'Peer' (Hennadii Stepanov)
dc498be3be Merge bitcoin-core/gui#271: Don't clear console prompt when font resizing (W. J. van der Laan)
1ed2d2d891 Merge #21053: rpc, test: document {previous,next}blockhash as optional (MarcoFalke)

Pull request description:

  ## Issue being fixed or feature implemented
  Regular backports from bitcoin v22

  ## What was done?
   - bitcoin/bitcoin#21053
   - bitcoin-core/gui#271
   - bitcoin-core/gui#311
   - bitcoin/bitcoin#22082
   - bitcoin/bitcoin#22118
   - bitcoin/bitcoin#22358
   - bitcoin/bitcoin#22308
   - bitcoin/bitcoin#22334
   - bitcoin/bitcoin#22388
   - bitcoin/bitcoin#22292

  ## How Has This Been Tested?
  Run unit/functional tests

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have commented my code, particularly in hard-to-understand areas
  - [ ] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  PastaPastaPasta:
    utACK 0bed7b4702

Tree-SHA512: 8354297857516ddf94b242f2e50e1a28d999e613da2a7eb90c603e1fee7212e46d6e8a20ad42aa2945b48137e98fc7f589c9c77469c71cc01d032a33fa6da517
This commit is contained in:
pasta 2024-05-18 21:07:59 -05:00
commit 1fc62c81a0
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
16 changed files with 307 additions and 131 deletions

View File

@ -50,7 +50,7 @@ export RUN_SYMBOL_TESTS=${RUN_SYMBOL_TESTS:-false}
export EXPECTED_TESTS_DURATION_IN_SECONDS=${EXPECTED_TESTS_DURATION_IN_SECONDS:-1000}
export CONTAINER_NAME=${CONTAINER_NAME:-ci_unnamed}
export DOCKER_NAME_TAG=${DOCKER_NAME_TAG:-ubuntu:focal}
export DOCKER_NAME_TAG=${DOCKER_NAME_TAG:-ubuntu:20.04}
# Randomize test order.
# See https://www.boost.org/doc/libs/1_71_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/random.html
export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1}

View File

@ -8,8 +8,10 @@ thread queue, wallet balance.
Running
---------------------
For benchmarks purposes you only need to compile `dash_bench`. Beware of configuring without `--enable-debug` as this would impact
benchmarking by unlatching log printers and lock analysis.
For benchmarking, you only need to compile `dash_bench`. The bench runner
warns if you configure with `--enable-debug`, but consider if building without
it will impact the benchmark(s) you are interested in by unlatching log printers
and lock analysis.
make -C src dash_bench
@ -19,19 +21,28 @@ After compiling Dash Core, the benchmarks can be run with:
The output will look similar to:
```
| ns/byte | byte/s | error % | benchmark
|--------------------:|--------------------:|--------:|:----------------------------------------------
| 64.13 | 15,592,356.01 | 0.1% | `Base58CheckEncode`
| 24.56 | 40,722,672.68 | 0.2% | `Base58Decode`
| ns/op | op/s | err% | total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
| 57,927,463.00 | 17.26 | 3.6% | 0.66 | `AddrManAdd`
| 677,816.00 | 1,475.33 | 4.9% | 0.01 | `AddrManGetAddr`
...
| ns/byte | byte/s | err% | total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
| 127.32 | 7,854,302.69 | 0.3% | 0.00 | `Base58CheckEncode`
| 31.95 | 31,303,226.99 | 0.2% | 0.00 | `Base58Decode`
...
```
Help
---------------------
src/bench/bench_dash --help
src/bench/bench_dash -?
To print options like scaling factor or per-benchmark filter.
To print the various options, like listing the benchmarks without running them
or using a regex filter to only run certain benchmarks.
Notes
---------------------

View File

@ -18,16 +18,19 @@
/*
* Usage:
static void CODE_TO_TIME(benchmark::Bench& bench)
static void NameOfYourBenchmarkFunction(benchmark::Bench& bench)
{
... do any setup needed...
nanobench::Config().run([&] {
... do stuff you want to time...
...do any setup needed...
bench.run([&] {
...do stuff you want to time; refer to src/bench/nanobench.h
for more information and the options that can be passed here...
});
... do any cleanup needed...
...do any cleanup needed...
}
BENCHMARK(CODE_TO_TIME);
BENCHMARK(NameOfYourBenchmarkFunction);
*/
@ -55,7 +58,8 @@ public:
static void RunAll(const Args& args);
};
}
} // namespace benchmark
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
#define BENCHMARK(n) \
benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n);

View File

@ -17,11 +17,11 @@ static void SetupBenchArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
argsman.AddArg("-list", "List benchmarks without executing them", 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("-asymptote=n1,n2,n3,...", strprintf("Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark"), 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("-list", "List benchmarks without executing them", 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);
}
// parses a comma separated list like "10,20,30,50"

View File

@ -7,7 +7,7 @@
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019-2020 Martin Ankerl <martin.ankerl@gmail.com>
// Copyright (c) 2019-2021 Martin Ankerl <martin.ankerl@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@ -32,8 +32,8 @@
// see https://semver.org/
#define ANKERL_NANOBENCH_VERSION_MAJOR 4 // incompatible API changes
#define ANKERL_NANOBENCH_VERSION_MINOR 0 // backwards-compatible changes
#define ANKERL_NANOBENCH_VERSION_PATCH 0 // backwards-compatible bug fixes
#define ANKERL_NANOBENCH_VERSION_MINOR 3 // backwards-compatible changes
#define ANKERL_NANOBENCH_VERSION_PATCH 4 // backwards-compatible bug fixes
///////////////////////////////////////////////////////////////////////////////////////////////////
// public facing api - as minimal as possible
@ -78,12 +78,20 @@
#if defined(ANKERL_NANOBENCH_LOG_ENABLED)
# include <iostream>
# define ANKERL_NANOBENCH_LOG(x) std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl
# define ANKERL_NANOBENCH_LOG(x) \
do { \
std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl; \
} while (0)
#else
# define ANKERL_NANOBENCH_LOG(x)
# define ANKERL_NANOBENCH_LOG(x) \
do { \
} while (0)
#endif
#if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
#if defined(__linux__) && defined(PERF_EVENT_IOC_ID) && defined(PERF_COUNT_HW_REF_CPU_CYCLES) && defined(PERF_FLAG_FD_CLOEXEC) && \
!defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
// only enable perf counters on kernel 3.14 which seems to have all the necessary defines. The three PERF_... defines are not in
// kernel 2.6.32 (all others are).
# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
#else
# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
@ -173,7 +181,7 @@ class BigO;
* `contextswitches`, `instructions`, `branchinstructions`, and `branchmisses`. All the measuers (except `iterations`) are
* provided for a single iteration (so `elapsed` is the time a single iteration took). The following tags are available:
*
* * `{{median(<name>>)}}` Calculate median of a measurement data set, e.g. `{{median(elapsed)}}`.
* * `{{median(<name>)}}` Calculate median of a measurement data set, e.g. `{{median(elapsed)}}`.
*
* * `{{average(<name>)}}` Average (mean) calculation.
*
@ -181,10 +189,11 @@ class BigO;
* metric for the variation of measurements. It is more robust to outliers than the
* [Mean absolute percentage error (M-APE)](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error).
* @f[
* \mathrm{medianAbsolutePercentError}(e) = \mathrm{median}\{| \frac{e_i - \mathrm{median}\{e\}}{e_i}| \}
* \mathrm{MdAPE}(e) = \mathrm{med}\{| \frac{e_i - \mathrm{med}\{e\}}{e_i}| \}
* @f]
* E.g. for *elapsed*: First, @f$ \mathrm{median}\{elapsed\} @f$ is calculated. This is used to calculate the absolute percentage
* error to this median for each measurement, as in @f$ | \frac{e_i - \mathrm{median}\{e\}}{e_i}| @f$. All these results
* E.g. for *elapsed*: First, @f$ \mathrm{med}\{e\} @f$ calculates the median by sorting and then taking the middle element
* of all *elapsed* measurements. This is used to calculate the absolute percentage
* error to this median for each measurement, as in @f$ | \frac{e_i - \mathrm{med}\{e\}}{e_i}| @f$. All these results
* are sorted, and the middle value is chosen as the median absolute percent error.
*
* This measurement is a bit hard to interpret, but it is very robust against outliers. E.g. a value of 5% means that half of the
@ -207,7 +216,7 @@ class BigO;
*
* * `{{#measurement}}` To access individual measurement results, open the begin tag for measurements.
*
* * `{{elapsed}}` Average elapsed time per iteration, in seconds.
* * `{{elapsed}}` Average elapsed wall clock time per iteration, in seconds.
*
* * `{{iterations}}` Number of iterations in the measurement. The number of iterations will fluctuate due
* to some applied randomness, to enhance accuracy.
@ -261,6 +270,7 @@ class BigO;
* :cpp:func:`templates::csv() <ankerl::nanobench::templates::csv()>`
* :cpp:func:`templates::json() <ankerl::nanobench::templates::json()>`
* :cpp:func:`templates::htmlBoxplot() <ankerl::nanobench::templates::htmlBoxplot()>`
* :cpp:func:`templates::pyperf() <ankerl::nanobench::templates::pyperf()>`
@endverbatim
*
@ -269,6 +279,7 @@ class BigO;
* @param out Output for the generated output.
*/
void render(char const* mustacheTemplate, Bench const& bench, std::ostream& out);
void render(std::string const& mustacheTemplate, Bench const& bench, std::ostream& out);
/**
* Same as render(char const* mustacheTemplate, Bench const& bench, std::ostream& out), but for when
@ -279,6 +290,7 @@ void render(char const* mustacheTemplate, Bench const& bench, std::ostream& out)
* @param out Output for the generated output.
*/
void render(char const* mustacheTemplate, std::vector<Result> const& results, std::ostream& out);
void render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out);
// Contains mustache-like templates
namespace templates {
@ -297,7 +309,7 @@ char const* csv() noexcept;
/*!
@brief HTML output that uses plotly to generate an interactive boxplot chart. See the tutorial for an example output.
The output uses only the elapsed time, and displays each epoch as a single dot.
The output uses only the elapsed wall clock time, and displays each epoch as a single dot.
@verbatim embed:rst
See the tutorial at :ref:`tutorial-template-html` for an example.
@endverbatim
@ -306,6 +318,14 @@ char const* csv() noexcept;
*/
char const* htmlBoxplot() noexcept;
/*!
@brief Output in pyperf compatible JSON format, which can be used for more analyzations.
@verbatim embed:rst
See the tutorial at :ref:`tutorial-template-pyperf` for an example how to further analyze the output.
@endverbatim
*/
char const* pyperf() noexcept;
/*!
@brief Template to generate JSON data.
@ -369,6 +389,8 @@ struct Config {
uint64_t mEpochIterations{0}; // If not 0, run *exactly* these number of iterations per epoch.
uint64_t mWarmup = 0;
std::ostream* mOut = nullptr;
std::chrono::duration<double> mTimeUnit = std::chrono::nanoseconds{1};
std::string mTimeUnitName = "ns";
bool mShowPerformanceCounters = true;
bool mIsRelative = false;
@ -504,6 +526,7 @@ public:
*/
explicit Rng(uint64_t seed) noexcept;
Rng(uint64_t x, uint64_t y) noexcept;
Rng(std::vector<uint64_t> const& data);
/**
* Creates a copy of the Rng, thus the copy provides exactly the same random sequence as the original.
@ -558,6 +581,14 @@ public:
template <typename Container>
void shuffle(Container& container) noexcept;
/**
* Extracts the full state of the generator, e.g. for serialization. For this RNG this is just 2 values, but to stay API compatible
* with future implementations that potentially use more state, we use a vector.
*
* @return Vector containing the full state:
*/
std::vector<uint64_t> state() const;
private:
static constexpr uint64_t rotl(uint64_t x, unsigned k) noexcept;
@ -666,6 +697,19 @@ public:
Bench& unit(std::string const& unit);
ANKERL_NANOBENCH(NODISCARD) std::string const& unit() const noexcept;
/**
* @brief Sets the time unit to be used for the default output.
*
* Nanobench defaults to using ns (nanoseconds) as output in the markdown. For some benchmarks this is too coarse, so it is
* possible to configure this. E.g. use `timeUnit(1ms, "ms")` to show `ms/op` instead of `ns/op`.
*
* @param tu Time unit to display the results in, default is 1ns.
* @param tuName Name for the time unit, default is "ns"
*/
Bench& timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName);
ANKERL_NANOBENCH(NODISCARD) std::string const& timeUnitName() const noexcept;
ANKERL_NANOBENCH(NODISCARD) std::chrono::duration<double> const& timeUnit() const noexcept;
/**
* @brief Set the output stream where the resulting markdown table will be printed to.
*
@ -916,6 +960,7 @@ public:
@endverbatim
*/
Bench& render(char const* templateContent, std::ostream& os);
Bench& render(std::string const& templateContent, std::ostream& os);
Bench& config(Config const& benchmarkConfig);
ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept;
@ -945,23 +990,24 @@ void doNotOptimizeAway(T const& val);
#else
// see folly's Benchmark.h
// These assembly magic is directly from what Google Benchmark is doing. I have previously used what facebook's folly was doing, but
// this seemd to have compilation problems in some cases. Google Benchmark seemed to be the most well tested anyways.
// see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark.h#L307
template <typename T>
constexpr bool doNotOptimizeNeedsIndirect() {
using Decayed = typename std::decay<T>::type;
return !ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(Decayed) || sizeof(Decayed) > sizeof(long) || std::is_pointer<Decayed>::value;
void doNotOptimizeAway(T const& val) {
// NOLINTNEXTLINE(hicpp-no-assembler)
asm volatile("" : : "r,m"(val) : "memory");
}
template <typename T>
typename std::enable_if<!doNotOptimizeNeedsIndirect<T>()>::type doNotOptimizeAway(T const& val) {
void doNotOptimizeAway(T& val) {
# if defined(__clang__)
// NOLINTNEXTLINE(hicpp-no-assembler)
asm volatile("" ::"r"(val));
}
template <typename T>
typename std::enable_if<doNotOptimizeNeedsIndirect<T>()>::type doNotOptimizeAway(T const& val) {
asm volatile("" : "+r,m"(val) : : "memory");
# else
// NOLINTNEXTLINE(hicpp-no-assembler)
asm volatile("" ::"m"(val) : "memory");
asm volatile("" : "+m,r"(val) : : "memory");
# endif
}
#endif
@ -1067,7 +1113,7 @@ constexpr uint64_t(Rng::max)() {
return (std::numeric_limits<uint64_t>::max)();
}
ANKERL_NANOBENCH_NO_SANITIZE("integer")
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
uint64_t Rng::operator()() noexcept {
auto x = mX;
@ -1077,7 +1123,7 @@ uint64_t Rng::operator()() noexcept {
return x;
}
ANKERL_NANOBENCH_NO_SANITIZE("integer")
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
uint32_t Rng::bounded(uint32_t range) noexcept {
uint64_t r32 = static_cast<uint32_t>(operator()());
auto multiresult = r32 * range;
@ -1103,6 +1149,7 @@ void Rng::shuffle(Container& container) noexcept {
}
}
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
constexpr uint64_t Rng::rotl(uint64_t x, unsigned k) noexcept {
return (x << k) | (x >> (64U - k));
}
@ -1306,6 +1353,30 @@ char const* htmlBoxplot() noexcept {
</html>)DELIM";
}
char const* pyperf() noexcept {
return R"DELIM({
"benchmarks": [
{
"runs": [
{
"values": [
{{#measurement}} {{elapsed}}{{^-last}},
{{/last}}{{/measurement}}
]
}
]
}
],
"metadata": {
"loops": {{sum(iterations)}},
"inner_loops": {{batch}},
"name": "{{title}}",
"unit": "second"
},
"version": "1.0"
})DELIM";
}
char const* json() noexcept {
return R"DELIM({
"results": [
@ -1410,6 +1481,7 @@ static std::vector<Node> parseMustacheTemplate(char const** tpl) {
}
static bool generateFirstLast(Node const& n, size_t idx, size_t size, std::ostream& out) {
ANKERL_NANOBENCH_LOG("n.type=" << static_cast<int>(n.type));
bool matchFirst = n == "-first";
bool matchLast = n == "-last";
if (!matchFirst && !matchLast) {
@ -1632,6 +1704,7 @@ namespace detail {
char const* getEnv(char const* name);
bool isEndlessRunning(std::string const& name);
bool isWarningsEnabled();
template <typename T>
T parseFile(std::string const& filename);
@ -1770,25 +1843,49 @@ void render(char const* mustacheTemplate, std::vector<Result> const& results, st
for (size_t i = 0; i < nbResults; ++i) {
generateResult(n.children, i, results, out);
}
} else if (n == "measurement") {
if (results.size() != 1) {
throw std::runtime_error(
"render: can only use section 'measurement' here if there is a single result, but there are " +
detail::fmt::to_s(results.size()));
}
// when we only have a single result, we can immediately go into its measurement.
auto const& r = results.front();
for (size_t i = 0; i < r.size(); ++i) {
generateResultMeasurement(n.children, i, r, out);
}
} else {
throw std::runtime_error("unknown section '" + std::string(n.begin, n.end) + "'");
throw std::runtime_error("render: unknown section '" + std::string(n.begin, n.end) + "'");
}
break;
case templates::Node::Type::tag:
// This just uses the last result's config.
if (!generateConfigTag(n, results.back().config(), out)) {
throw std::runtime_error("unknown tag '" + std::string(n.begin, n.end) + "'");
if (results.size() == 1) {
// result & config are both supported there
generateResultTag(n, results.front(), out);
} else {
// This just uses the last result's config.
if (!generateConfigTag(n, results.back().config(), out)) {
throw std::runtime_error("unknown tag '" + std::string(n.begin, n.end) + "'");
}
}
break;
}
}
}
void render(std::string const& mustacheTemplate, std::vector<Result> const& results, std::ostream& out) {
render(mustacheTemplate.c_str(), results, out);
}
void render(char const* mustacheTemplate, const Bench& bench, std::ostream& out) {
render(mustacheTemplate, bench.results(), out);
}
void render(std::string const& mustacheTemplate, const Bench& bench, std::ostream& out) {
render(mustacheTemplate.c_str(), bench.results(), out);
}
namespace detail {
PerformanceCounters& performanceCounters() {
@ -1837,6 +1934,12 @@ bool isEndlessRunning(std::string const& name) {
return nullptr != endless && endless == name;
}
// True when environment variable NANOBENCH_SUPPRESS_WARNINGS is either not set at all, or set to "0"
bool isWarningsEnabled() {
auto suppression = getEnv("NANOBENCH_SUPPRESS_WARNINGS");
return nullptr == suppression || suppression == std::string("0");
}
void gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<std::string>& recommendations) {
warnings.clear();
recommendations.clear();
@ -1889,13 +1992,13 @@ void gatherStabilityInformation(std::vector<std::string>& warnings, std::vector<
recommendations.emplace_back("Make sure you compile for Release");
}
if (recommendPyPerf) {
recommendations.emplace_back("Use 'pyperf system tune' before benchmarking. See https://github.com/vstinner/pyperf");
recommendations.emplace_back("Use 'pyperf system tune' before benchmarking. See https://github.com/psf/pyperf");
}
}
void printStabilityInformationOnce(std::ostream* outStream) {
static bool shouldPrint = true;
if (shouldPrint && outStream) {
if (shouldPrint && outStream && isWarningsEnabled()) {
auto& os = *outStream;
shouldPrint = false;
std::vector<std::string> warnings;
@ -1923,16 +2026,7 @@ uint64_t& singletonHeaderHash() noexcept {
return sHeaderHash;
}
ANKERL_NANOBENCH_NO_SANITIZE("integer")
inline uint64_t fnv1a(std::string const& str) noexcept {
auto val = UINT64_C(14695981039346656037);
for (auto c : str) {
val = (val ^ static_cast<uint8_t>(c)) * UINT64_C(1099511628211);
}
return val;
}
ANKERL_NANOBENCH_NO_SANITIZE("integer")
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
inline uint64_t hash_combine(uint64_t seed, uint64_t val) {
return seed ^ (val + UINT64_C(0x9e3779b9) + (seed << 6U) + (seed >> 2U));
}
@ -2010,7 +2104,7 @@ struct IterationLogic::Impl {
return static_cast<uint64_t>(doubleNewIters + 0.5);
}
ANKERL_NANOBENCH_NO_SANITIZE("integer") void upscale(std::chrono::nanoseconds elapsed) {
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined") void upscale(std::chrono::nanoseconds elapsed) {
if (elapsed * 10 < mTargetRuntimePerEpoch) {
// we are far below the target runtime. Multiply iterations by 10 (with overflow check)
if (mNumIters * 10 < mNumIters) {
@ -2108,7 +2202,8 @@ struct IterationLogic::Impl {
columns.emplace_back(14, 0, "complexityN", "", mBench.complexityN());
}
columns.emplace_back(22, 2, "ns/" + mBench.unit(), "", 1e9 * rMedian / mBench.batch());
columns.emplace_back(22, 2, mBench.timeUnitName() + "/" + mBench.unit(), "",
rMedian / (mBench.timeUnit().count() * mBench.batch()));
columns.emplace_back(22, 2, mBench.unit() + "/s", "", rMedian <= 0.0 ? 0.0 : mBench.batch() / rMedian);
double rErrorMedian = mResult.medianAbsolutePercentError(Result::Measure::elapsed);
@ -2140,16 +2235,19 @@ struct IterationLogic::Impl {
}
}
columns.emplace_back(12, 2, "total", "", mResult.sum(Result::Measure::elapsed));
columns.emplace_back(12, 2, "total", "", mResult.sumProduct(Result::Measure::iterations, Result::Measure::elapsed));
// write everything
auto& os = *mBench.output();
// combine all elements that are relevant for printing the header
uint64_t hash = 0;
hash = hash_combine(fnv1a(mBench.unit()), hash);
hash = hash_combine(fnv1a(mBench.title()), hash);
hash = hash_combine(mBench.relative(), hash);
hash = hash_combine(mBench.performanceCounters(), hash);
hash = hash_combine(std::hash<std::string>{}(mBench.unit()), hash);
hash = hash_combine(std::hash<std::string>{}(mBench.title()), hash);
hash = hash_combine(std::hash<std::string>{}(mBench.timeUnitName()), hash);
hash = hash_combine(std::hash<double>{}(mBench.timeUnit().count()), hash);
hash = hash_combine(std::hash<bool>{}(mBench.relative()), hash);
hash = hash_combine(std::hash<bool>{}(mBench.performanceCounters()), hash);
if (hash != singletonHeaderHash()) {
singletonHeaderHash() = hash;
@ -2177,7 +2275,7 @@ struct IterationLogic::Impl {
os << col.value();
}
os << "| ";
auto showUnstable = rErrorMedian >= 0.05;
auto showUnstable = isWarningsEnabled() && rErrorMedian >= 0.05;
if (showUnstable) {
os << ":wavy_dash: ";
}
@ -2305,7 +2403,7 @@ public:
}
template <typename Op>
ANKERL_NANOBENCH_NO_SANITIZE("integer")
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
void calibrate(Op&& op) {
// clear current calibration data,
for (auto& v : mCalibratedOverhead) {
@ -2411,7 +2509,7 @@ bool LinuxPerformanceCounters::monitor(perf_hw_id hwId, LinuxPerformanceCounters
}
// overflow is ok, it's checked
ANKERL_NANOBENCH_NO_SANITIZE("integer")
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
void LinuxPerformanceCounters::updateResults(uint64_t numIters) {
// clear old data
for (auto& id_value : mIdToTarget) {
@ -2963,6 +3061,20 @@ std::string const& Bench::unit() const noexcept {
return mConfig.mUnit;
}
Bench& Bench::timeUnit(std::chrono::duration<double> const& tu, std::string const& tuName) {
mConfig.mTimeUnit = tu;
mConfig.mTimeUnitName = tuName;
return *this;
}
std::string const& Bench::timeUnitName() const noexcept {
return mConfig.mTimeUnitName;
}
std::chrono::duration<double> const& Bench::timeUnit() const noexcept {
return mConfig.mTimeUnit;
}
// If benchmarkTitle differs from currently set title, the stored results will be cleared.
Bench& Bench::title(const char* benchmarkTitle) {
if (benchmarkTitle != mConfig.mBenchmarkTitle) {
@ -3083,6 +3195,11 @@ Bench& Bench::render(char const* templateContent, std::ostream& os) {
return *this;
}
Bench& Bench::render(std::string const& templateContent, std::ostream& os) {
::ankerl::nanobench::render(templateContent, *this, os);
return *this;
}
std::vector<BigO> Bench::complexityBigO() const {
std::vector<BigO> bigOs;
auto rangeMeasure = BigO::collectRangeMeasure(mResults);
@ -3119,7 +3236,7 @@ Rng::Rng()
} while (mX == 0 && mY == 0);
}
ANKERL_NANOBENCH_NO_SANITIZE("integer")
ANKERL_NANOBENCH_NO_SANITIZE("integer", "undefined")
uint64_t splitMix64(uint64_t& state) noexcept {
uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15));
z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9);
@ -3145,6 +3262,24 @@ Rng Rng::copy() const noexcept {
return Rng{mX, mY};
}
Rng::Rng(std::vector<uint64_t> const& data)
: mX(0)
, mY(0) {
if (data.size() != 2) {
throw std::runtime_error("ankerl::nanobench::Rng::Rng: needed exactly 2 entries in data, but got " +
detail::fmt::to_s(data.size()));
}
mX = data[0];
mY = data[1];
}
std::vector<uint64_t> Rng::state() const {
std::vector<uint64_t> data(2);
data[0] = mX;
data[1] = mY;
return data;
}
BigO::RangeMeasure BigO::collectRangeMeasure(std::vector<Result> const& results) {
BigO::RangeMeasure rangeMeasure;
for (auto const& result : results) {

View File

@ -83,7 +83,28 @@ public Q_SLOTS:
private:
interfaces::Node& m_node;
const QStringList columns{tr("Peer Id"), tr("Address"), tr("Network"), tr("Ping"), tr("Sent"), tr("Received"), tr("User Agent")};
const QStringList columns{
/*: Title of Peers Table column which contains a
unique number used to identify a connection. */
tr("Peer"),
/*: Title of Peers Table column which contains the
IP/Onion/I2P address of the connected peer. */
tr("Address"),
/*: Title of Peers Table column which states the network the peer
connected through. */
tr("Network"),
/*: Title of Peers Table column which indicates the current latency
of the connection with the peer. */
tr("Ping"),
/*: Title of Peers Table column which indicates the total amount of
network information we have sent to the peer. */
tr("Sent"),
/*: Title of Peers Table column which indicates the total amount of
network information we have received from the peer. */
tr("Received"),
/*: Title of Peers Table column which contains the peer's
User Agent string. */
tr("User Agent")};
std::unique_ptr<PeerTablePriv> priv;
QTimer *timer;
};

View File

@ -489,7 +489,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
ui->lineEdit->setMaxLength(16 * 1024 * 1024);
ui->messagesWidget->installEventFilter(this);
connect(ui->clearButton, &QPushButton::clicked, this, &RPCConsole::clear);
connect(ui->clearButton, &QPushButton::clicked, [this] { clear(); });
connect(ui->fontBiggerButton, &QPushButton::clicked, this, &RPCConsole::fontBigger);
connect(ui->fontSmallerButton, &QPushButton::clicked, this, &RPCConsole::fontSmaller);
connect(ui->btnClearTrafficGraph, &QPushButton::clicked, ui->trafficGraph, &TrafficGraphWidget::clear);
@ -822,7 +822,7 @@ void RPCConsole::setFontSize(int newSize)
// clear console (reset icon sizes, default stylesheet) and re-add the content
float oldPosFactor = 1.0 / ui->messagesWidget->verticalScrollBar()->maximum() * ui->messagesWidget->verticalScrollBar()->value();
clear(false);
clear(/* keep_prompt */ true);
ui->messagesWidget->setHtml(str);
ui->messagesWidget->verticalScrollBar()->setValue(oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
}
@ -873,15 +873,10 @@ void RPCConsole::buildParameterlist(QString arg)
Q_EMIT handleRestart(args);
}
void RPCConsole::clear(bool clearHistory)
void RPCConsole::clear(bool keep_prompt)
{
ui->messagesWidget->clear();
if(clearHistory)
{
history.clear();
historyPtr = 0;
}
ui->lineEdit->clear();
if (!keep_prompt) ui->lineEdit->clear();
ui->lineEdit->setFocus();
// Set default style sheet
@ -1232,7 +1227,7 @@ void RPCConsole::updateDetailWidget()
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected_rows.first().row());
// update the detail ui with latest node information
QString peerAddrDetails(QString::fromStdString(stats->nodeStats.m_addr_name) + " ");
peerAddrDetails += tr("(peer id: %1)").arg(QString::number(stats->nodeStats.nodeid));
peerAddrDetails += tr("(peer: %1)").arg(QString::number(stats->nodeStats.nodeid));
if (!stats->nodeStats.addrLocal.empty())
peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
ui->peerHeading->setText(peerAddrDetails);

View File

@ -103,7 +103,7 @@ private Q_SLOTS:
void updateDetailWidget();
public Q_SLOTS:
void clear(bool clearHistory = true);
void clear(bool keep_prompt = false);
void fontBigger();
void fontSmaller();
void setFontSize(int newSize);

View File

@ -64,7 +64,7 @@ uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDe
entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address)));
entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
uint256 txid;
boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {
boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](const uint256& hash, ChangeType status) {
if (status == CT_NEW) txid = hash;
}));
ConfirmSend();

View File

@ -524,13 +524,13 @@ public:
std::unique_ptr<Handler> handleAddressBookChanged(AddressBookChangedFn fn) override
{
return MakeHandler(m_wallet->NotifyAddressBookChanged.connect(
[fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine,
const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
[fn](const CTxDestination& address, const std::string& label, bool is_mine,
const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); }));
}
std::unique_ptr<Handler> handleTransactionChanged(TransactionChangedFn fn) override
{
return MakeHandler(m_wallet->NotifyTransactionChanged.connect(
[fn](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); }));
[fn](const uint256& txid, ChangeType status) { fn(txid, status); }));
}
std::unique_ptr<Handler> handleInstantLockReceived(InstantLockReceivedFn fn) override
{

View File

@ -4381,9 +4381,13 @@ RPCHelpMan walletprocesspsbt()
{
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
const CWallet* const pwallet = wallet.get();
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
const CWallet& wallet{*pwallet};
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
// Unserialize the transaction
PartiallySignedTransaction psbtx;
@ -4403,7 +4407,7 @@ RPCHelpMan walletprocesspsbt()
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool complete = true;
const TransactionError err = pwallet->FillPSBT(psbtx, complete, nHashType, sign, bip32derivs);
const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs)};
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}
@ -4500,9 +4504,13 @@ static RPCHelpMan walletcreatefundedpsbt()
}, true
);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;
CWallet& wallet{*pwallet};
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
wallet.BlockUntilSyncedToCurrentChain();
CAmount fee;
int change_position;
@ -4511,7 +4519,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Automatically select coins, unless at least one is manually selected. Can
// be overridden by options.add_inputs.
coin_control.m_add_inputs = rawTx.vin.size() == 0;
FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control);
FundTransaction(&wallet, rawTx, fee, change_position, request.params[3], coin_control);
// Make a blank psbt
PartiallySignedTransaction psbtx{rawTx};
@ -4519,7 +4527,7 @@ static RPCHelpMan walletcreatefundedpsbt()
// Fill transaction with out data but don't sign
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
bool complete = true;
const TransactionError err = pwallet->FillPSBT(psbtx, complete, 1, false, bip32derivs);
const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, false, bip32derivs)};
if (err != TransactionError::OK) {
throw JSONRPCTransactionError(err);
}

View File

@ -918,7 +918,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
wtx.MarkDirty();
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED);
#if HAVE_SYSTEM
// notify an external script when a wallet transaction comes in or is updated
@ -1101,7 +1101,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
wtx.setAbandoned();
wtx.MarkDirty();
batch.WriteTx(wtx);
NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED);
NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.hash == now) {
@ -3804,7 +3804,7 @@ void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
CWalletTx &coin = mapWallet.at(txin.prevout.hash);
coin.MarkDirty();
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
NotifyTransactionChanged(txin.prevout.hash, CT_UPDATED);
updated_hahes.insert(txin.prevout.hash);
}
// Get the inserted-CWalletTx from mapWallet so that the
@ -3901,7 +3901,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
for (const auto& txin : it->second.tx->vin)
mapTxSpends.erase(txin.prevout);
mapWallet.erase(it);
NotifyTransactionChanged(this, hash, CT_DELETED);
NotifyTransactionChanged(hash, CT_DELETED);
}
if (nZapSelectTxRet == DBErrors::NEED_REWRITE)
@ -3936,8 +3936,8 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].purpose = strPurpose;
is_mine = IsMine(address) != ISMINE_NO;
}
NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
NotifyAddressBookChanged(address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW));
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false;
return batch.WriteName(EncodeDestination(address), strName);
@ -3972,7 +3972,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
is_mine = IsMine(address) != ISMINE_NO;
}
NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
NotifyAddressBookChanged(address, "", is_mine, "", CT_DELETED);
batch.ErasePurpose(EncodeDestination(address));
return batch.EraseName(EncodeDestination(address));
@ -5156,7 +5156,7 @@ void CWallet::notifyTransactionLock(const CTransactionRef &tx, const std::shared
uint256 txHash = tx->GetHash();
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txHash);
if (mi != mapWallet.end()){
NotifyTransactionChanged(this, txHash, CT_UPDATED);
NotifyTransactionChanged(txHash, CT_UPDATED);
NotifyISLockReceived();
#if HAVE_SYSTEM
// notify an external script
@ -5477,7 +5477,6 @@ ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(bool internal) const
{
const auto spk_manager = internal ? m_internal_spk_managers : m_external_spk_managers;
if (spk_manager == nullptr) {
WalletLogPrintf("%s scriptPubKey Manager does not exist\n", internal ? "Internal" : "External");
return nullptr;
}
return spk_manager;

View File

@ -1223,19 +1223,18 @@ public:
/**
* Address book entry changed.
* @note called with lock cs_wallet held.
* @note called without lock cs_wallet held.
*/
boost::signals2::signal<void (CWallet *wallet, const CTxDestination
&address, const std::string &label, bool isMine,
const std::string &purpose,
ChangeType status)> NotifyAddressBookChanged;
boost::signals2::signal<void(const CTxDestination& address,
const std::string& label, bool isMine,
const std::string& purpose, ChangeType status)>
NotifyAddressBookChanged;
/**
* Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx,
ChangeType status)> NotifyTransactionChanged;
boost::signals2::signal<void(const uint256& hashTx, ChangeType status)> NotifyTransactionChanged;
/** Show progress e.g. for rescan */
boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress;

View File

@ -2,7 +2,7 @@
# Copyright (c) 2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test Anchors functionality"""
"""Test block-relay-only anchors functionality"""
import os
@ -10,6 +10,9 @@ from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
INBOUND_CONNECTIONS = 5
BLOCK_RELAY_CONNECTIONS = 2
def check_node_connections(*, node, num_in, num_out):
info = node.getnetworkinfo()
@ -25,19 +28,26 @@ class AnchorsTest(BitcoinTestFramework):
self.setup_nodes()
def run_test(self):
self.log.info("Add 2 block-relay-only connections to node 0")
for i in range(2):
node_anchors_path = os.path.join(
self.nodes[0].datadir, "regtest", "anchors.dat"
)
self.log.info("When node starts, check if anchors.dat doesn't exist")
assert not os.path.exists(node_anchors_path)
self.log.info(f"Add {BLOCK_RELAY_CONNECTIONS} block-relay-only connections to node")
for i in range(BLOCK_RELAY_CONNECTIONS):
self.log.debug(f"block-relay-only: {i}")
self.nodes[0].add_outbound_p2p_connection(
P2PInterface(), p2p_idx=i, connection_type="block-relay-only"
)
self.log.info("Add 5 inbound connections to node 0")
for i in range(5):
self.log.info(f"Add {INBOUND_CONNECTIONS} inbound connections to node")
for i in range(INBOUND_CONNECTIONS):
self.log.debug(f"inbound: {i}")
self.nodes[0].add_p2p_connection(P2PInterface())
self.log.info("Check node 0 connections")
self.log.info("Check node connections")
check_node_connections(node=self.nodes[0], num_in=5, num_out=2)
# 127.0.0.1
@ -57,14 +67,10 @@ class AnchorsTest(BitcoinTestFramework):
self.log.info("Stop node 0")
self.stop_node(0)
node0_anchors_path = os.path.join(
self.nodes[0].datadir, "regtest", "anchors.dat"
)
# It should contain only the block-relay-only addresses
self.log.info("Check the addresses in anchors.dat")
with open(node0_anchors_path, "rb") as file_handler:
with open(node_anchors_path, "rb") as file_handler:
anchors = file_handler.read().hex()
for port in block_relay_nodes_port:
@ -74,11 +80,11 @@ class AnchorsTest(BitcoinTestFramework):
ip_port = ip + port
assert ip_port not in anchors
self.log.info("Start node 0")
self.log.info("Start node")
self.start_node(0)
self.log.info("When node starts, check if anchors.dat doesn't exist anymore")
assert not os.path.exists(node0_anchors_path)
assert not os.path.exists(node_anchors_path)
if __name__ == "__main__":

View File

@ -456,6 +456,9 @@ class BlockchainTest(BitcoinTestFramework):
# Restore chain state
move_block_file('rev_wrong', 'rev00000.dat')
assert 'previousblockhash' not in node.getblock(node.getblockhash(0))
assert 'nextblockhash' not in node.getblock(node.getbestblockhash())
if __name__ == '__main__':
BlockchainTest().main()

View File

@ -223,11 +223,6 @@ def create_raw_transaction(node, txid, to_address, *, amount):
signed_psbt = wrpc.walletprocesspsbt(psbt)
psbt = signed_psbt['psbt']
final_psbt = node.finalizepsbt(psbt)
if not final_psbt["complete"]:
node.log.info(f'final_psbt={final_psbt}')
for w in node.listwallets():
wrpc = node.get_wallet_rpc(w)
node.log.info(f'listunspent={wrpc.listunspent()}')
assert_equal(final_psbt["complete"], True)
return final_psbt['hex']