Skip to content

Commit 6dda050

Browse files
committed
Merge bitcoin/bitcoin#29458: refactor: Preallocate result in TryParseHex to avoid resizing
a19235c Preallocate result in `TryParseHex` to avoid resizing (Lőrinc) b7489ec Add benchmark for TryParseHex (Lőrinc) Pull request description: This pull request introduces optimizations to the `TryParseHex` function, focusing primarily on the ideal case (valid hexadecimal input without spaces). A new benchmark, `HexParse` was introduced in a separate commit. The main optimization preallocates the result vector based on the input string's length. This aims to completely avoid costly dynamic reallocations when no spaces are present. ------------ Before: ``` | ns/base16 | base16/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 1.60 | 623,238,893.11 | 0.3% | 0.01 | `HexParse` | 1.65 | 606,747,566.34 | 0.6% | 0.01 | `HexParse` | 1.60 | 626,149,544.07 | 0.3% | 0.01 | `HexParse` ``` After: ``` | ns/base16 | base16/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 0.68 | 1,465,555,976.27 | 0.8% | 0.01 | `HexParse` | 0.68 | 1,472,962,920.18 | 0.3% | 0.01 | `HexParse` | 0.68 | 1,476,159,423.00 | 0.3% | 0.01 | `HexParse` ``` ACKs for top commit: achow101: ACK a19235c hebasto: ACK a19235c. andrewtoth: Re-ACK a19235c Empact: Re-ACK bitcoin/bitcoin@a19235c Tree-SHA512: e09a59791104be3fd1026862ce98de9efafa1f949626fa01e3b7d58e6a2ef02a11f0de55ddba5c43230a53effd24e6d368c1e12848b17e8ce91d7908a59333f0
2 parents 02c7fd8 + a19235c commit 6dda050

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

src/Makefile.bench.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ bench_bench_bitcoin_SOURCES = \
4242
bench/merkle_root.cpp \
4343
bench/nanobench.cpp \
4444
bench/nanobench.h \
45+
bench/parse_hex.cpp \
4546
bench/peer_eviction.cpp \
4647
bench/poly1305.cpp \
4748
bench/pool.cpp \

src/bench/parse_hex.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2024- 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/bench.h>
6+
#include <random.h>
7+
#include <stddef.h>
8+
#include <util/strencodings.h>
9+
#include <cassert>
10+
#include <optional>
11+
#include <vector>
12+
13+
std::string generateHexString(size_t length) {
14+
const auto hex_digits = "0123456789ABCDEF";
15+
FastRandomContext rng(/*fDeterministic=*/true);
16+
17+
std::string data;
18+
while (data.size() < length) {
19+
auto digit = hex_digits[rng.randbits(4)];
20+
data.push_back(digit);
21+
}
22+
return data;
23+
}
24+
25+
static void HexParse(benchmark::Bench& bench)
26+
{
27+
auto data = generateHexString(130); // Generates 678B0EDA0A1FD30904D5A65E3568DB82DB2D918B0AD8DEA18A63FECCB877D07CAD1495C7157584D877420EF38B8DA473A6348B4F51811AC13C786B962BEE5668F9 by default
28+
29+
bench.batch(data.size()).unit("base16").run([&] {
30+
auto result = TryParseHex(data);
31+
assert(result != std::nullopt); // make sure we're measuring the successful case
32+
ankerl::nanobench::doNotOptimizeAway(result);
33+
});
34+
}
35+
36+
BENCHMARK(HexParse, benchmark::PriorityLevel::HIGH);

src/util/strencodings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ template <typename Byte>
8181
std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
8282
{
8383
std::vector<Byte> vch;
84+
vch.reserve(str.size() / 2); // two hex characters form a single byte
85+
8486
auto it = str.begin();
8587
while (it != str.end()) {
8688
if (IsSpace(*it)) {

0 commit comments

Comments
 (0)