Skip to content

Conversation

LarryRuane
Copy link
Contributor

This PR is strictly a performance improvement; there is no functional change. The CBufferedFile::FindByte() method searches for the next occurrence of the given byte in the file. Currently, this is done by explicitly inspecting each byte in turn. This PR takes advantage of std::find() to do the same more efficiently, improving its CPU runtime by a factor of about 25 in typical use.

@LarryRuane
Copy link
Contributor Author

This PR was suggested by @hebasto #16981 (comment) (thank you!)

Copy link
Contributor

@promag promag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The performance gain looks substantial - didn't verified.

src/streams.h Outdated
nReadPos++;
size_t n = vchBuf.size() - start;
if (n > nSrcPos - nReadPos)
n = nSrcPos - nReadPos;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, join with above line.

@hebasto
Copy link
Member

hebasto commented Aug 10, 2020

@LarryRuane

... improving its CPU runtime by a factor of about 25 in typical use.

How was it measured?

@laanwj
Copy link
Member

laanwj commented Aug 10, 2020

Any idea why this is so much faster? As far as I know, there is no faster algorithm to look for the first occurrence of a single byte in a memory array than a linear iteration over it. I'd expect std::find of a byte to simply unroll into a loop.

@LarryRuane
Copy link
Contributor Author

How was it measured?

I ran:

time src/test/test_bitcoin --run_test=streams_tests/streams_findbyte

with master (1m20s) and with master + PR (3s).

Any idea why this is so much faster?

I think you're correct that there's no faster way than a loop with runtime proportional to the number of bytes to scan, but I assume std::find() on a char vector is highly optimized, probably using memchr() or memcmp(), which are implemented in assembly language. Also, the master version does a few things each byte (testing nReadPos == nSrcPos, remainder calculation (%), incrementing nReadPos) that the PR does once for each large run of bytes.

I just noticed that the repetition count on the test is set to a large number (50000000) and I meant to reduce it for the commit (3 seconds is too long to add to the unit test suite). I'll reduce that number in force push in a minute. This test doesn't really need to be in this PR (FindByte()'s functionality is tested very well in another test), but it helps reviewers verify the performance improvement.

@LarryRuane
Copy link
Contributor Author

LarryRuane commented Aug 10, 2020

Force-push a small fix to the test, so it doesn't take 3 seconds to run.

@laanwj
Copy link
Member

laanwj commented Aug 10, 2020

but I assume std::find() on a char vector is highly optimized, probably using memchr() or memcmp(), which are implemented in assembly language

That's true, it's possible to optimize that with assembly language (definitely with specific instruction sets).

it still surprises me because you'd expect the I/O, to read the data from disk, to dominate greatly in the block importing. Not looking for a character already in memory! It just seems out of proportion.

fwrite(&b, 1, 1, file);
rewind(file);
CBufferedFile bf(file, fileSize * 2, fileSize, 0, 0);
for (int rep = 0; rep < 100; ++rep) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand how the performance increase is so significant, but why not a bench if you're worried about burdening the unit tests? I tried to do this but I must be doing something wrong because I can't seem to reproduce the speedup. 😞

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The performance impact may be very compiler/architecture/stdlib dependent. I'm kind of surprised std::find has optimizations beyond the naive loop implementation in the first place on some platforms, so I certainly wouldn't be surprised if others don't have it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gzhao408, thank you, I wasn't aware of bench. I suspect the iteration count, 100, is far too low and the difference is swamped by the noise. I increased the iteration count to 10m (1e7) and it showed the expected difference, master: 1,659.30 ns/op, PR: 52.66 ns/op (ratio is about 31).

I just force-pushed (diff) to remove the unit test (which was only for benchmarking, not really testing anything) and cherry-pick Gloria's benchmark. I added one more commit to increase the iteration count.

@LarryRuane
Copy link
Contributor Author

it still surprises me because you'd expect the I/O, to read the data from disk, to dominate greatly in the block importing.

FindByte() only reads from disk by calling Fill() (when the buffer is empty), which is rare. In this test, Fill() gets called only once, the first time FindByte() runs, because I wanted to isolate the modified part of the code.

@sipa
Copy link
Member

sipa commented Aug 11, 2020

@gmaxwell pointed out to me why this is so much faster: it's not that std::find is amazing, but that the original code (which I wrote in 2012, it seems!) is doing a modulus operation for every character (which is often orders of magnitude slower than the byte comparison or addition/subtraction).

Thinking about this a bit more high level: the end goal is just to scan quickly for the 4-byte network magic in a file. If this is relevant for performance (and it seems it may be), it may be better to have a function that does exactly that in CBufferedFile, rather than a search for one byte + memcmp. std::search is probably what you want.

@LarryRuane
Copy link
Contributor Author

LarryRuane commented Aug 12, 2020

Here's a version that's very close in implementation to master that eliminates the % operation on every character:

    void FindByte(char ch) {
        size_t start = nReadPos % vchBuf.size();
        while (true) {
            if (nReadPos == nSrcPos)
                Fill();
            if (vchBuf[start] == ch)
                break;
            nReadPos++;
            start++;
            if (start >= vchBuf.size()) start = 0;
        }

and that does make a significant difference; bench_bitcoin reports 314 ns/op for this version. (My laptop isn't set up for very accurate CPU benchmarking, but my results are pretty consistent, varying by less than 1% across multiple runs.) That's about 5.3 times as fast as master (1,659.30 ns/op, as mentioned above). The PR (52.66 ns/op) is still about 6 times faster than this version. So this version is right about in the middle (in terms of ratios) between master and this PR.

I do like @sipa's suggestion to generalize FindByte() to search for a given sequence of bytes (maybe called FindBytes() or Search(), suggestions welcome); that's a much nicer interface. I'll push a commit to do that within the next few hours.

@LarryRuane
Copy link
Contributor Author

I just added a new commit (25413ab, can squash later) to implement @sipa's suggestion to add a method to CBufferedFile to find a sequence of bytes, rather than just one byte, as FindByte() does. This simplifies LoadExternalBlockFile(); the overall code base isn't simpler, but it encapsulates complexity nicely within the CBufferedFile class.

It didn't work out to use std::search() because that function requires the bytes being searched to be consecutive in memory -- that works if the entire file is read into contiguous memory. But CBufferedFile implements a circular buffer. The bytes we're searching for might be partially in and partially out of memory (bytes are read on demand), and even if all are in memory, they may be split between the end of the buffer and the beginning (because of wraparound), so trying to use std::search gets complicated. FindByte() doesn't have these problems since it's looking for a single byte.

@adamjonas
Copy link
Member

adamjonas commented Aug 14, 2020

Looks like one of the sanitizers is finding an implicit-integer-sign-change problem in the latest update:

�[0;34m node0 stderr streams.h:857:22: runtime error: implicit conversion from type 'unsigned char' of value 250 (8-bit, unsigned) to type 'char' changed the value to -6 (8-bit, signed)
    #0 0x55f6d4cc6285 in CBufferedFile::Search(unsigned char const*, unsigned long) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/./streams.h:857:22
    #1 0x55f6d4c967a0 in LoadExternalBlockFile(CChainParams const&, _IO_FILE*, FlatFilePos*) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/validation.cpp:4682:24
    #2 0x55f6d45071f6 in ThreadImport(ChainstateManager&, std::vector<boost::filesystem::path, std::allocator<boost::filesystem::path> >) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/init.cpp:705:13
    #3 0x55f6d4506814 in AppInitMain(util::Ref const&, NodeContext&)::$_10::operator()() const /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/init.cpp:1853:96
    #4 0x55f6d4506814 in std::_Function_handler<void (), AppInitMain(util::Ref const&, NodeContext&)::$_10>::_M_invoke(std::_Any_data const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:300:2
    #5 0x55f6d4585b29 in std::function<void ()>::operator()() const /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/std_function.h:688:14
    #6 0x55f6d45181bb in void TraceThread<std::function<void ()> >(char const*, std::function<void ()>) /tmp/cirrus-ci-build/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/./util/system.h:438:9
    #7 0x55f6d4506447 in void std::__invoke_impl<void, void (*)(char const*, std::function<void ()>), char const*, AppInitMain(util::Ref const&, NodeContext&)::$_10>(std::__invoke_other, void (*&&)(char const*, std::function<void ()>), char const*&&, AppInitMain(util::Ref const&, NodeContext&)::$_10&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/invoke.h:60:14
    #8 0x55f6d450616a in std::__invoke_result<void (*)(char const*, std::function<void ()>), char const*, AppInitMain(util::Ref const&, NodeContext&)::$_10>::type std::__invoke<void (*)(char const*, std::function<void ()>), char const*, AppInitMain(util::Ref const&, NodeContext&)::$_10>(void (*&&)(char const*, std::function<void ()>), char const*&&, AppInitMain(util::Ref const&, NodeContext&)::$_10&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/invoke.h:95:14
    #9 0x55f6d4505db2 in void std::thread::_Invoker<std::tuple<void (*)(char const*, std::function<void ()>), char const*, AppInitMain(util::Ref const&, NodeContext&)::$_10> >::_M_invoke<0ul, 1ul, 2ul>(std::_Index_tuple<0ul, 1ul, 2ul>) /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/thread:244:13
    #10 0x55f6d4505db2 in std::thread::_Invoker<std::tuple<void (*)(char const*, std::function<void ()>), char const*, AppInitMain(util::Ref const&, NodeContext&)::$_10> >::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/thread:251:11
    #11 0x55f6d4505db2 in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(char const*, std::function<void ()>), char const*, AppInitMain(util::Ref const&, NodeContext&)::$_10> > >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/thread:195:13
    #12 0x7f20f2f18cb3  (/lib/x86_64-linux-gnu/libstdc++.so.6+0xd6cb3)
    #13 0x7f20f3317608 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x9608)
    #14 0x7f20f2bf5102 in clone (/lib/x86_64-linux-gnu/libc.so.6+0x122102)

SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change streams.h:857:22

@LarryRuane
Copy link
Contributor Author

LarryRuane commented Aug 14, 2020

Thanks, @adamjonas, force-pushed a fix for that signed-unsigned CI failure, added another unit test, some improvements to Search(), some clang-format-diff.py cleanups.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 20, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK stickies-v, achow101
Stale ACK laanwj, sipa, hebasto, john-moffett

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

No conflicts as of last run.

static void FindByte(benchmark::Bench& bench)
{
// Setup
FILE* file = fsbridge::fopen("streams_tmp", "w+b");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we maybe use something like memfd_create(2) for the benchmark, to decrease I/O noise?
Anyone knows if there is a windows equivalent or a higher abstraction in boost::filesystem?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fmemopen(3) also looks interesting

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions, but I don't think this matters, because the file IO (read) occurs only on the very first benchmark loop iteration; after that, the data is in memory and there is no IO at all. Each iteration repositions the stream pointer to zero (bf.SetPos(0)) and then searches forward for the value 1 (bf.FindByte(1)), which is 200 bytes away. But after the first iteration, all of these bytes are in memory, and we're just moving the position between 0 and 200.

The reason for the 200, by the way, is that with random data, searching for a random byte value the average distance would be half of 256. But the data in blk.dat files (which is the only use of this code currently) isn't quite random; zero and 0xff occur more often (and we never search for those values). So I guessed that 200 is close to a typical distance that this function would move through before finding the requested byte. Maybe I should explain these points in a comment in bench/streams_findbyte.cpp.

@laanwj
Copy link
Member

laanwj commented Nov 20, 2020

Code review ACK 8f2e8c2, this looks like a better abstraction, and it's an improvement not to do a modulus for every byte.

@LarryRuane
Copy link
Contributor Author

LarryRuane commented Dec 4, 2020

Force-pushed to rationalize the commits, so I think it's in a good state for merging. I reordered the commit that adds the benchmark test, b576994, to be first, so it's easy for reviewers to checkout that commit and run the new benchmark test without the improvements:

src/bench/bench_bitcoin -filter=FindByte

I re-ran those tests just now, and on my laptop, the ns/op without the PR is 1,840, and with the PR it's 55 (an improvement of more than a factor of 33).

@sipa
Copy link
Member

sipa commented Dec 4, 2020

Code review ACK 566c2e2. I think it'd be good to move the refactoring to FindByte in the last commit to a separate commit.

@LarryRuane
Copy link
Contributor Author

Force-pushed to implement latest review suggestion, changes are more cleanly separated among the commits (no code changes overall), thanks @sipa.

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK.

@LarryRuane
Copy link
Contributor Author

@john-moffett - Good idea to bring back the earlier commit (the condition branch instruction theory makes sense); I just restored (force-pushed) it as you suggested. On my x86 (ns/op, lower is better):

master: 307
previous version of this PR (08cf6a9): 135
current version (using std::find): 34

@achow101
Copy link
Member

ACK dacd331

@DrahtBot DrahtBot requested review from hebasto, laanwj and sipa March 15, 2023 16:13
Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach ACK dacd331

I'm now seeing bench performance improvement on my M1 again, from ~150ns/op -> ~85ns/op.

src/streams.h Outdated
@@ -744,13 +744,22 @@ class CBufferedFile
//! search for a given byte in the stream, and remain positioned on it
void FindByte(uint8_t ch)
{
const std::byte byte{static_cast<std::byte>(ch)};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that there is only a single non-test callsite of FindByte(), would it make sense to just update the fn signature to take std::byte directly?

src/streams.h Outdated
@@ -744,13 +744,22 @@ class CBufferedFile
//! search for a given byte in the stream, and remain positioned on it
void FindByte(uint8_t ch)
{
const std::byte byte{static_cast<std::byte>(ch)};
size_t buf_offset = m_read_pos % vchBuf.size();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: and a bunch more of those

Suggested change
size_t buf_offset = m_read_pos % vchBuf.size();
size_t buf_offset{m_read_pos % vchBuf.size()};

@@ -744,13 +744,22 @@ class CBufferedFile
//! search for a given byte in the stream, and remain positioned on it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some of the rationale of this implementation should be in the docs so future contributors don't simplify the code again to unintentionally undo the performance gains, e.g. why the modulo operator is kept outside of the while loop seems quite important and non-trivial?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, but I think it would also be helpful to improve the docs to specify that if ch is not found, std::ios_base::failure is thrown (from Fill()). It's an essential part of the interface imo.

Perhaps worth improving on this behaviour, and have FindByte() throw its own error, by wrapping the Fill() command in a try/catch? Orthogonal to this PR, though. (And I also don't like that we're catching a general std::exception for a FindByte() failure, but again, orthogonal.)

@LarryRuane
Copy link
Contributor Author

Force-pushed for review comments (thanks, @stickies-v), verified benchmark performance is unchanged (ns/op with PR: 34.76, without PR: 302). Summary of force-push changes:

  • change FindByte() argument type from uint8_t to std::byte)
  • add "exception" comment before call to Fill()
  • add comment suggesting to avoid mod (%) operator within loop
  • changed assignment statements to use more modern braces syntax

@LarryRuane LarryRuane force-pushed the FindByte-speedup branch 2 times, most recently from acef167 to 0fe832c Compare March 20, 2023 17:03
@LarryRuane
Copy link
Contributor Author

Force-pushed again to fix CI failures.

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 0fe832c

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of changing the callsites of FindByte(), how about adding a uint8_t overload? I think it keeps the implementation clean, but since it can easily be argued that uint8_t also is a byte, this keeps the callsites straightforward and reduces the diff.

git diff
diff --git a/src/bench/streams_findbyte.cpp b/src/bench/streams_findbyte.cpp
index 175564fe9..7b2e65da2 100644
--- a/src/bench/streams_findbyte.cpp
+++ b/src/bench/streams_findbyte.cpp
@@ -20,7 +20,7 @@ static void FindByte(benchmark::Bench& bench)
 
     bench.run([&] {
         bf.SetPos(0);
-        bf.FindByte(std::byte(1));
+        bf.FindByte(1);
     });
 
     // Cleanup
diff --git a/src/streams.h b/src/streams.h
index 2558bd830..9280fa013 100644
--- a/src/streams.h
+++ b/src/streams.h
@@ -763,6 +763,8 @@ public:
             if (buf_offset >= vchBuf.size()) buf_offset = 0;
         }
     }
+
+    void FindByte(uint8_t byte) { return FindByte(static_cast<std::byte>(byte)); }
 };
 
 #endif // BITCOIN_STREAMS_H
diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp
index 2f7ce60c7..67cac8fa4 100644
--- a/src/test/fuzz/buffered_file.cpp
+++ b/src/test/fuzz/buffered_file.cpp
@@ -53,7 +53,7 @@ FUZZ_TARGET(buffered_file)
                         return;
                     }
                     try {
-                        opt_buffered_file->FindByte(std::byte(fuzzed_data_provider.ConsumeIntegral<uint8_t>()));
+                        opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral<uint8_t>());
                     } catch (const std::ios_base::failure&) {
                     }
                 },
diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp
index 79bc7b7c0..1db5b61f1 100644
--- a/src/test/streams_tests.cpp
+++ b/src/test/streams_tests.cpp
@@ -462,7 +462,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
                 size_t find = currentPos + InsecureRandRange(8);
                 if (find >= fileSize)
                     find = fileSize - 1;
-                bf.FindByte(std::byte(find));
+                bf.FindByte(find);
                 // The value at each offset is the offset.
                 BOOST_CHECK_EQUAL(bf.GetPos(), find);
                 currentPos = find;
diff --git a/src/validation.cpp b/src/validation.cpp
index a79b81add..b42b39861 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -4438,7 +4438,7 @@ void Chainstate::LoadExternalBlockFile(
             try {
                 // locate a header
                 unsigned char buf[CMessageHeader::MESSAGE_START_SIZE];
-                blkdat.FindByte(std::byte(params.MessageStart()[0]));
+                blkdat.FindByte(params.MessageStart()[0]);
                 nRewind = blkdat.GetPos() + 1;
                 blkdat >> buf;
                 if (memcmp(buf, params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE)) {

if (m_read_pos == nSrcPos) {
// No more bytes available; read from the file into the buffer,
// setting nSrcPos to one beyond the end of the new data.
// Throws exception if end-of-file reached.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that it's part of the interface, I think this needs to be documented on the function level so devs wanting to use FindByte know how it behaves when the byte isn't found - they shouldn't need to dive into the implementation.

{
// For best performance, avoid mod operation within the loop.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
// For best performance, avoid mod operation within the loop.
// The modulus operation is much more expensive than byte
// comparison and addition, so we keep it out of the loop to
// improve performance (see #19690 for discussion).

@DrahtBot DrahtBot requested a review from achow101 March 22, 2023 13:38
@achow101
Copy link
Member

ACK 0fe832c

@DrahtBot DrahtBot removed the request for review from achow101 April 21, 2023 18:35
Copy link
Contributor

@john-moffett john-moffett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 0fe832c

@achow101
Copy link
Member

Silent merge conflict with master:

../../../src/bench/streams_findbyte.cpp:7:10: fatal error: fs.h: No such file or directory
    7 | #include <fs.h>
      |          ^~~~~~
compilation terminated.

glozow and others added 2 commits May 5, 2023 06:03
Avoid use of the expensive mod operator (%) when calculating the
buffer offset. No functional difference.

Co-authored-by: Hennadii Stepanov <32963518+hebasto@users.noreply.github.com>
@LarryRuane
Copy link
Contributor Author

Force pushed rebase to fix hidden merge conflict, thanks @achow101

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-ACK 72efc26

Verified that the only difference is to include <util/fs.h> instead of <fs.h> (introduced by 00e9b97)

% git range-diff HEAD~2 0fe832c4a4b2049fdf967bca375468d5ac285563 HEAD
1:  5842d92c8 ! 1:  604df63f6 [bench] add streams findbyte
    @@ src/bench/streams_findbyte.cpp (new)
     +
     +#include <bench/bench.h>
     +
    -+#include <fs.h>
    ++#include <util/fs.h>
     +#include <streams.h>
     +
     +static void FindByte(benchmark::Bench& bench)
2:  0fe832c4a = 2:  72efc2643 util: improve streams.h:FindByte() performance

@DrahtBot DrahtBot requested review from achow101 and john-moffett May 5, 2023 13:16
@achow101
Copy link
Member

re-ACK 72efc26

@DrahtBot DrahtBot removed the request for review from achow101 May 10, 2023 21:40
@achow101 achow101 merged commit 3ff67f7 into bitcoin:master May 10, 2023
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request May 11, 2023
72efc26 util: improve streams.h:FindByte() performance (Larry Ruane)
604df63 [bench] add streams findbyte (gzhao408)

Pull request description:

  This PR is strictly a performance improvement; there is no functional change. The `CBufferedFile::FindByte()` method searches for the next occurrence of the given byte in the file. Currently, this is done by explicitly inspecting each byte in turn. This PR takes advantage of `std::find()` to do the same more efficiently, improving its CPU runtime by a factor of about 25 in typical use.

ACKs for top commit:
  achow101:
    re-ACK 72efc26
  stickies-v:
    re-ACK 72efc26

Tree-SHA512: ddf0bff335cc8aa34f911aa4e0558fa77ce35d963d602e4ab1c63090b4a386faf074548daf06ee829c7f2c760d06eed0125cf4c34e981c6129cea1804eb3b719
@bitcoin bitcoin locked and limited conversation to collaborators May 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.