From 431e81571daa1de174e46ee53e81ce174c18c5c1 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 4 Oct 2026 17:02:11 -0400 Subject: [PATCH 1/3] Hash pool id columns in place on any endianness. --- .../query/consensus/consensus_compact.ipp | 59 ++++--------------- 1 file changed, 12 insertions(+), 47 deletions(-) diff --git a/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp b/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp index c9d0d328c..1057fe530 100644 --- a/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp +++ b/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp @@ -94,15 +94,11 @@ bool CLASS::get_compact_matches(compact_matches& out, return false; const auto rows = possible_narrow_cast(store_.pool.count().value); - if constexpr (is_little_endian) - { - const auto bytes = rows * sizeof(lane_t); - if (is_lesser(ptr0.size(), bytes) || is_lesser(ptr1.size(), bytes) || - is_lesser(ptr2.size(), bytes) || is_lesser(ptr3.size(), bytes)) - return false; - } + const auto bytes = rows * sizeof(lane_t); + if (is_lesser(ptr0.size(), bytes) || is_lesser(ptr1.size(), bytes) || + is_lesser(ptr2.size(), bytes) || is_lesser(ptr3.size(), bytes)) + return false; - std::atomic_bool fail{}; std::vector found(ceilinged_divide(rows, chunk_rows)); std::vector chunks(found.size()); std::iota(chunks.begin(), chunks.end(), zero); @@ -114,47 +110,19 @@ bool CLASS::get_compact_matches(compact_matches& out, const auto first = chunk * chunk_rows; std::vector ids(std::min(chunk_rows, rows - first)); - if constexpr (is_little_endian) + const auto column = [&](const memory& ptr) NOEXCEPT { - const auto column = [&](const memory& ptr) NOEXCEPT + const auto data = pointer_cast(ptr.data()); + return std::span { - const auto data = pointer_cast(ptr.data()); - return std::span - { - std::next(data, first), ids.size() - }; + std::next(data, first), ids.size() }; + }; - siphash(ids, key, siphash_columns - { - column(ptr0), column(ptr1), column(ptr2), column(ptr3) - }); - } - else + siphash(ids, key, siphash_columns { - table::pool_word id0{}, id1{}, id2{}, id3{}; - for (size_t row{}; row < ids.size(); ++row) - { - const table::pool::link link - { - possible_narrow_cast(first + row) - }; - - if (!store_.pool.id0.get(ptr0, link, id0) || - !store_.pool.id1.get(ptr1, link, id1) || - !store_.pool.id2.get(ptr2, link, id2) || - !store_.pool.id3.get(ptr3, link, id3)) - { - fail.store(true, std::memory_order_relaxed); - return; - } - - ids[row] = siphash(key, siphash_words - { - id0.word, id1.word, id2.word, id3.word - }); - } - } + column(ptr0), column(ptr1), column(ptr2), column(ptr3) + }); auto& matches = found.at(chunk); for (size_t row{}; row < ids.size(); ++row) @@ -166,9 +134,6 @@ bool CLASS::get_compact_matches(compact_matches& out, } }); - if (fail.load(std::memory_order_relaxed)) - return false; - for (const auto& matches: found) out.insert(out.end(), matches.cbegin(), matches.cend()); From ce2c0d3305a002b78dfe5e23d7593612784f0e27 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 4 Oct 2026 17:02:16 -0400 Subject: [PATCH 2/3] Size short id scan chunks by cores. --- .../database/impl/query/consensus/consensus_compact.ipp | 3 ++- include/bitcoin/database/query.hpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp b/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp index 1057fe530..232a84bc2 100644 --- a/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp +++ b/include/bitcoin/database/impl/query/consensus/consensus_compact.ipp @@ -85,7 +85,6 @@ bool CLASS::get_compact_matches(compact_matches& out, using link_t = table::pool::link::integer; static_assert(is_same_type>); - constexpr auto chunk_rows = short_id_chunk_rows; const auto ptr0 = store_.pool.id0.get_memory(); const auto ptr1 = store_.pool.id1.get_memory(); const auto ptr2 = store_.pool.id2.get_memory(); @@ -99,6 +98,8 @@ bool CLASS::get_compact_matches(compact_matches& out, is_lesser(ptr2.size(), bytes) || is_lesser(ptr3.size(), bytes)) return false; + const auto chunk_rows = std::max(short_id_minimum_rows, + ceilinged_divide(rows, two * cores())); std::vector found(ceilinged_divide(rows, chunk_rows)); std::vector chunks(found.size()); std::iota(chunks.begin(), chunks.end(), zero); diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp index 644ed4324..742436b40 100644 --- a/include/bitcoin/database/query.hpp +++ b/include/bitcoin/database/query.hpp @@ -1105,8 +1105,8 @@ class query // This value should never be read, but may be useful in debugging. static constexpr uint32_t unspecified_timestamp = max_uint32; - // Pool rows hashed by one task of the compact block short id scan. - static constexpr size_t short_id_chunk_rows = system::power2(14u); + // Fewest pool rows hashed by one task of the compact block short id scan. + static constexpr size_t short_id_minimum_rows = system::power2(10u); // Chain objects. template From 04246dc48050719926c6148e6dcf7c5311ab8c28 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Sun, 4 Oct 2026 22:32:38 -0400 Subject: [PATCH 3/3] Index block transactions for silent payments sequentially. --- .../database/impl/query/batch/silent.ipp | 40 +++++-------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/include/bitcoin/database/impl/query/batch/silent.ipp b/include/bitcoin/database/impl/query/batch/silent.ipp index 334342bbc..0c5351414 100644 --- a/include/bitcoin/database/impl/query/batch/silent.ipp +++ b/include/bitcoin/database/impl/query/batch/silent.ipp @@ -107,26 +107,14 @@ bool CLASS::set_silent(const header_link& link, const block& block) NOEXCEPT return false; const auto first = links.front(); - - stopper fail{}; - std::vector it(sub1(count)); - std::iota(it.begin(), it.end(), one); - constexpr auto parallel = poolstl::execution::par; - constexpr auto relaxed = std::memory_order_relaxed; - - // TODO: parallel may or may not be optimal. - // TODO: alternatively could accumulate block results and write once. - std::for_each(parallel, it.cbegin(), it.cend(), [&](size_t index) NOEXCEPT + for (auto index = one; index < count; ++index) { - if (fail.load(relaxed)) - return; - const auto& fk = links.at(index); if (fk >= first && !set_silent(fk, *txs->at(index))) - fail.store(true, relaxed); - }); - - return !fail.load(relaxed); + return false; + } + + return true; } TEMPLATE @@ -143,24 +131,14 @@ bool CLASS::set_silent(const header_link& link, return false; const auto first = links.front(); - - stopper fail{}; - std::vector it(sub1(count)); - std::iota(it.begin(), it.end(), one); - constexpr auto parallel = poolstl::execution::par; - constexpr auto relaxed = std::memory_order_relaxed; - - std::for_each(parallel, it.cbegin(), it.cend(), [&](size_t index) NOEXCEPT + for (auto index = one; index < count; ++index) { - if (fail.load(relaxed)) - return; - const auto& fk = links.at(index); if (fk >= first && !set_silent(fk, txs.at(index))) - fail.store(true, relaxed); - }); + return false; + } - return !fail.load(relaxed); + return true; } // Ineligible txs have no records.