cluster_linearize: add tests and benchmarks for chain and tree-shaped clusters #34625

pull HowHsu wants to merge 3 commits into bitcoin:master from HowHsu:linearize-tests changing 2 files +261 −0
  1. HowHsu commented at 2:55 pm on February 19, 2026: none

    Summary

    This PR adds correctness tests and benchmarks specifically targeting chain and tree-shaped cluster topologies, which are both common in real-world mempools and structurally special with respect to PostLinearize.

    Commits:

    • a11760f9 cluster_linearize: add chain cluster benchmarks for PostLinearize and Linearize
    • 4b7e2b28 cluster_linearize: test PostLinearize optimality on random tree-shaped clusters
    • 0725aa05 cluster_linearize: test PostLinearize optimality on random chain clusters

    Motivation

    PostLinearize gives an optimality guarantee for clusters with a tree shape: if every transaction has at most one direct parent, or every transaction has at most one direct child, the result is provably optimal. A chain (tx_0 → tx_1 → … → tx_{N-1}) satisfies both conditions simultaneously.

    These structural guarantees lacked dedicated test coverage and chain-specific benchmarks, making it hard to observe the effect of topology-aware optimizations on linearization performance.


    Changes

    Tests (src/test/cluster_linearize_tests.cpp)

    postlinearize_tree_optimal_tests

    Adds MakeRandomTreeGraph(), which generates random tree-shaped dependency graphs in two orientations:

    • Upright tree: transaction i picks a random parent from [0, i), so every node has at most one direct parent.
    • Reverse tree: transaction i picks a random child from [0, i), so every node has at most one direct child.

    For random trees of sizes 5..30, the test verifies:

    1. PostLinearize produces a linearization of equal quality to the exhaustive Linearize() result (as guaranteed by PostLinearize’s interface for tree-shaped inputs).
    2. Idempotency: a second PostLinearize pass produces no change.

    postlinearize_chain_optimal_tests

    Adds MakeRandomChainGraph(), which generates random linear chain dependency graphs (tx_0 → tx_1 → … → tx_{N-1}) with randomized feerates.

    For chains of sizes 5..50, the test verifies:

    1. PostLinearize produces a linearization of equal quality to the exhaustive Linearize() result. A chain satisfies both tree-shape conditions simultaneously, so optimality is guaranteed.
    2. Idempotency: a second PostLinearize pass produces no change.

    Benchmarks (src/bench/cluster_linearize.cpp)

    Adds MakeChainGraph() to build deterministic random chain dependency graphs
    (tx_0 → tx_1 → … → tx_{N-1}) with randomized feerates seeded deterministically from the cluster size, and two sets of chain-specific benchmarks:

    • PostLinearizeChain{16,32,48,64,75,99}Tx: measure PostLinearize throughput on chain-shaped clusters across a range of sizes.
    • LinearizeOptimallyChainTotal_{9,16,32,48,63}tx: measure the total time for Linearize to reach an optimal result on chain-shaped clusters.

    Chain clusters are a common real-world mempool pattern (each transaction spending an output of the previous one). Dedicated benchmarks make it easy to observe the effect of topology-specific linearization optimizations as they are developed.

  2. DrahtBot commented at 2:55 pm on February 19, 2026: contributor

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

    Reviews

    See the guideline for information on the review process.

  3. HowHsu force-pushed on Feb 19, 2026
  4. DrahtBot added the label CI failed on Feb 19, 2026
  5. fanquake commented at 3:29 pm on February 19, 2026: member

    https://github.com/bitcoin/bitcoin/actions/runs/22186932377/job/64163346820?pr=34625#step:11:2598:

     0[97](https://github.com/bitcoin/bitcoin/actions/runs/22186932377/job/64163346820?pr=34625#step:11:2598)
     1stderr:
     2/home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:130:79: runtime error: shift exponent 32 is too large for 32-bit type 'DepGraphIndex' (aka 'unsigned int')
     3    [#0](/bitcoin-bitcoin/0/) 0x5803c8353259 in (anonymous namespace)::BenchLinearizeOptimallyChainTotal(ankerl::nanobench::Bench&, std::vector<unsigned int, std::allocator<unsigned int>> const&) /home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:130:79
     4    [#1](/bitcoin-bitcoin/1/) 0x5803c8353259 in LinearizeOptimallyChainTotal(ankerl::nanobench::Bench&) /home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:216:5
     5    [#2](/bitcoin-bitcoin/2/) 0x5803c8280446 in std::function<void (ankerl::nanobench::Bench&)>::operator()(ankerl::nanobench::Bench&) const /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591:9
     6    [#3](/bitcoin-bitcoin/3/) 0x5803c8280446 in benchmark::BenchRunner::RunAll(benchmark::Args const&) /home/admin/actions-runner/_work/_temp/src/bench/bench.cpp:121:13
     7    [#4](/bitcoin-bitcoin/4/) 0x5803c8271b85 in main /home/admin/actions-runner/_work/_temp/src/bench/bench_bitcoin.cpp:135:9
     8    [#5](/bitcoin-bitcoin/5/) 0x74f0319481c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
     9    [#6](/bitcoin-bitcoin/6/) 0x74f03194828a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    10    [#7](/bitcoin-bitcoin/7/) 0x5803c8184974 in _start (/home/admin/actions-runner/_work/_temp/build/bin/bench_bitcoin+0x142e974) (BuildId: 18390638316178c3c31ae8dc10b5258eb5361226)
    11
    12SUMMARY: UndefinedBehaviorSanitizer: invalid-shift-exponent /home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:130:79
    
  6. HowHsu commented at 4:02 pm on February 19, 2026: none

    https://github.com/bitcoin/bitcoin/actions/runs/22186932377/job/64163346820?pr=34625#step:11:2598:

     0
     1[97](https://github.com/bitcoin/bitcoin/actions/runs/22186932377/job/64163346820?pr=34625#step:11:2598)
     2
     3stderr:
     4
     5/home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:130:79: runtime error: shift exponent 32 is too large for 32-bit type 'DepGraphIndex' (aka 'unsigned int')
     6
     7    [#0](/bitcoin-bitcoin/0/) 0x5803c8353259 in (anonymous namespace)::BenchLinearizeOptimallyChainTotal(ankerl::nanobench::Bench&, std::vector<unsigned int, std::allocator<unsigned int>> const&) /home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:130:79
     8
     9    [#1](/bitcoin-bitcoin/1/) 0x5803c8353259 in LinearizeOptimallyChainTotal(ankerl::nanobench::Bench&) /home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:216:5
    10
    11    [#2](/bitcoin-bitcoin/2/) 0x5803c8280446 in std::function<void (ankerl::nanobench::Bench&)>::operator()(ankerl::nanobench::Bench&) const /usr/lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591:9
    12
    13    [#3](/bitcoin-bitcoin/3/) 0x5803c8280446 in benchmark::BenchRunner::RunAll(benchmark::Args const&) /home/admin/actions-runner/_work/_temp/src/bench/bench.cpp:121:13
    14
    15    [#4](/bitcoin-bitcoin/4/) 0x5803c8271b85 in main /home/admin/actions-runner/_work/_temp/src/bench/bench_bitcoin.cpp:135:9
    16
    17    [#5](/bitcoin-bitcoin/5/) 0x74f0319481c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    18
    19    [#6](/bitcoin-bitcoin/6/) 0x74f03194828a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    20
    21    [#7](/bitcoin-bitcoin/7/) 0x5803c8184974 in _start (/home/admin/actions-runner/_work/_temp/build/bin/bench_bitcoin+0x142e974) (BuildId: 18390638316178c3c31ae8dc10b5258eb5361226)
    22
    23
    24
    25SUMMARY: UndefinedBehaviorSanitizer: invalid-shift-exponent /home/admin/actions-runner/_work/_temp/src/bench/cluster_linearize.cpp:130:79
    

    Thanks, I’ll look into it tomorrow.

  7. HowHsu force-pushed on Feb 20, 2026
  8. cluster_linearize: add chain cluster benchmarks for PostLinearize and Linearize
    Add MakeChainGraph() to build deterministic random chain dependency graphs
    (tx0->tx1->...->tx_{n-1}) with randomized feerates, and two sets of
    chain-specific benchmarks:
    
    - PostLinearizeChain{16,32,48,64,75,99}Tx: measure PostLinearize throughput
      on chain-shaped clusters across a range of sizes.
    - LinearizeOptimallyChainTotal_{9,16,32,48,63}tx: measure the total time for
      Linearize to reach an optimal result on chain-shaped clusters.
    
    Chain clusters are a common real-world mempool pattern (each transaction
    spending an output of the previous one). Dedicated benchmarks make it easy
    to observe the effect of topology-specific linearization optimizations.
    386f6084cc
  9. cluster_linearize: test PostLinearize optimality on random tree-shaped clusters
    Add MakeRandomTreeGraph() which generates random tree-shaped dependency graphs
    in two orientations:
    - Upright tree: each transaction i picks a random parent from [0, i), giving
      a DAG where every node has at most one direct parent.
    - Reverse tree: each transaction i picks a random child from [0, i), giving
      a DAG where every node has at most one direct child.
    
    Add postlinearize_tree_optimal_tests which verifies, for random trees of
    various sizes (5..30 transactions), that PostLinearize matches the quality of
    the exhaustive Linearize() result, as guaranteed by PostLinearize's interface.
    Also verifies idempotency: a second PostLinearize pass produces no change.
    e25f63ba6e
  10. cluster_linearize: test PostLinearize optimality on random chain clusters
    Add MakeRandomChainGraph() which generates random linear chain dependency
    graphs (tx0->tx1->...->tx_{n-1}) with randomized feerates.
    
    Add postlinearize_chain_optimal_tests which verifies, for chains of various
    sizes (5..50 transactions), that PostLinearize matches the quality of the
    exhaustive Linearize() result. A chain satisfies both tree-shape conditions
    simultaneously (each transaction has at most one parent and at most one
    child), so PostLinearize is guaranteed to produce an optimal linearization.
    Also verifies idempotency: a second PostLinearize pass produces no change.
    0a3aacde22
  11. HowHsu force-pushed on Feb 20, 2026
  12. DrahtBot removed the label CI failed on Feb 20, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-03-09 21:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me