diff options
author | Martin Leitner-Ankerl <martin.ankerl@gmail.com> | 2022-03-23 07:08:32 +0100 |
---|---|---|
committer | Martin Leitner-Ankerl <martin.ankerl@gmail.com> | 2022-04-17 14:29:52 +0200 |
commit | 5e61532e72c1021fda9c7b213bd9cf397cb3a802 (patch) | |
tree | 2457af2105ec1c74211160f07e6b057352392182 /src/util | |
parent | 4e2b99f72a90b956f3050095abed4949aff9b516 (diff) | |
download | bitcoin-5e61532e72c1021fda9c7b213bd9cf397cb3a802.tar.xz |
util: optimizes HexStr
In my benchmark, this rewrite improves runtime 27% (g++) to 46% (clang++) for the benchmark `HexStrBench`:
g++ 11.2.0
| ns/byte | byte/s | err% | ins/byte | cyc/byte | IPC | bra/byte | miss% | total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
| 0.94 | 1,061,381,310.36 | 0.7% | 12.00 | 3.01 | 3.990 | 1.00 | 0.0% | 0.01 | `HexStrBench` master
| 0.68 | 1,465,366,544.25 | 1.7% | 6.00 | 2.16 | 2.778 | 1.00 | 0.0% | 0.01 | `HexStrBench` branch
clang++ 13.0.1
| ns/byte | byte/s | err% | ins/byte | cyc/byte | IPC | bra/byte | miss% | total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
| 0.80 | 1,244,713,415.92 | 0.9% | 10.00 | 2.56 | 3.913 | 0.50 | 0.0% | 0.01 | `HexStrBench` master
| 0.43 | 2,324,188,940.72 | 0.2% | 4.00 | 1.37 | 2.914 | 0.25 | 0.0% | 0.01 | `HexStrBench` branch
Note that the idea for this change comes from denis2342 in PR 23364. This is a rewrite so no unaligned accesses occur.
Also, the lookup table is now calculated at compile time, which hopefully makes the code a bit easier to review.
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/strencodings.cpp | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 940fa90da2..84549bf0f1 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -9,6 +9,7 @@ #include <tinyformat.h> #include <algorithm> +#include <array> #include <cstdlib> #include <cstring> #include <limits> @@ -508,17 +509,37 @@ std::string Capitalize(std::string str) return str; } +namespace { + +using ByteAsHex = std::array<char, 2>; + +constexpr std::array<ByteAsHex, 256> CreateByteToHexMap() +{ + constexpr char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + std::array<ByteAsHex, 256> byte_to_hex{}; + for (size_t i = 0; i < byte_to_hex.size(); ++i) { + byte_to_hex[i][0] = hexmap[i >> 4]; + byte_to_hex[i][1] = hexmap[i & 15]; + } + return byte_to_hex; +} + +} // namespace + std::string HexStr(const Span<const uint8_t> s) { std::string rv(s.size() * 2, '\0'); - static constexpr char hexmap[16] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - auto it = rv.begin(); + static constexpr auto byte_to_hex = CreateByteToHexMap(); + static_assert(sizeof(byte_to_hex) == 512); + + char* it = rv.data(); for (uint8_t v : s) { - *it++ = hexmap[v >> 4]; - *it++ = hexmap[v & 15]; + std::memcpy(it, byte_to_hex[v].data(), 2); + it += 2; } - assert(it == rv.end()); + + assert(it == rv.data() + rv.size()); return rv; } |