test: Remove polling loop from test_runner (take 2) #33141

pull maflcko wants to merge 1 commits into bitcoin:master from maflcko:2508-ci changing 1 files +46 −33
  1. maflcko commented at 7:56 AM on August 6, 2025: member

    (This picks up my prior attempt from #13384)

    Currently, the test_runner is using a time.sleep before polling to check if any tests have completed. This is largely fine when running a few tests, or when the tests take a long time.

    However, when running many fast tests, this can accumulate and leave the CPU idle for no reason.

    A trivial improvement would be to only sleep when really needed:

    diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
    index 7c8c15f391..1d9f28cee4 100755
    --- a/test/functional/test_runner.py
    +++ b/test/functional/test_runner.py
    @@ -747,7 +747,6 @@ class TestHandler:
             dot_count = 0
             while True:
                 # Return all procs that have finished, if any. Otherwise sleep until there is one.
    -            time.sleep(.5)
                 ret = []
                 for job in self.jobs:
                     (name, start_time, proc, testdir, log_out, log_err) = job
    @@ -771,6 +770,7 @@ class TestHandler:
                         ret.append((TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr, skip_reason))
                 if ret:
                     return ret
    +            time.sleep(.5)
                 if self.use_term_control:
                     print('.', end='', flush=True)
                 dot_count += 1
    

    However, ideally there is no sleep at all. So do that by using a ThreadPoolExecutor.

    This can be tested via something like:

    time ./bld-cmake/test/functional/test_runner.py $(for i in {1..200}; do echo -n "tool_rpcauth "; done) -j 200
    

    The result should show:

    • Current master is the slowest
    • The "sleep patch" from above is a bit faster (1.5x improvement)
    • This pull request is the fastest (2x improvement)
  2. DrahtBot added the label Tests on Aug 6, 2025
  3. DrahtBot commented at 7:56 AM on August 6, 2025: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33141.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK l0rinc, Eunovo, achow101

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #33313 (test/refactor: use test deque to avoid quadratic iteration by l0rinc)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. l0rinc commented at 3:59 AM on August 7, 2025: contributor

    I ran the 3 versions you mentioned with hyperfine, 10 runs each:

    COMMITS="d767503b6a2618e0c99407acf98f3bd19fb7defd 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7 35e91b899dac689b24a8a95fcef083affc695d1e"; \
    PARALLEL=200; RUNS=10; \
    CC=gcc; CXX=g++; \
    BUILD='Release'; \
    TEST_LIST=$(python3 -c "print('tool_rpcauth ' * $PARALLEL)"); \
    (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done; echo "") && \
    hyperfine \
      --sort command \
      --runs $RUNS \
      --parameter-list COMMIT ${COMMITS// /,} \
      --prepare "git checkout {COMMIT}; git clean -fxd; git reset --hard && cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD} && ninja -C build" \
      "build/test/functional/test_runner.py $TEST_LIST -j$PARALLEL"
    

    d767503b6a Merge bitcoin/bitcoin#33039: refactor,test: follow-ups to multi-byte block obfuscation 5bb380ec87 test: sleep patch 35e91b899d test: Remove polling loop from test_runner

    <details> <summary>runs</summary>

    Benchmark 1: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth too
    l_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_r
    pcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpca
    uth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):      7.233 s ±  0.057 s    [User: 41.885 s, System: 4.270 s]
      Range (min … max):    7.085 s …  7.286 s    10 runs
    
    Benchmark 2: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth too
    l_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_r
    pcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpca
    uth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):      3.827 s ±  0.127 s    [User: 41.717 s, System: 4.300 s]
      Range (min … max):    3.726 s …  4.174 s    10 runs
    
    Benchmark 3: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth too
    l_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_r
    pcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpca
    uth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):      3.696 s ±  0.026 s    [User: 41.489 s, System: 4.324 s]
      Range (min … max):    3.681 s …  3.768 s    10 runs
    

    </details>

    And I'm getting slightly different results than you:

    Relative speed comparison
        1.96 ±  0.02  ./build/test/functional/test_runner.py tool_rpcauth [...] tool_rpcauth -j200 (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
        1.04 ±  0.04  ./build/test/functional/test_runner.py tool_rpcauth [...] tool_rpcauth -j200 (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
        1.00          ./build/test/functional/test_runner.py tool_rpcauth [...] tool_rpcauth -j200 (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
    

    Tried a few different combinations:

    <details> <summary>gcc with debug (3 runs)</summary>

    COMMITS="d767503b6a2618e0c99407acf98f3bd19fb7defd 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7 35e91b899dac689b24a8a95fcef083affc695d1e"; \
    PARALLEL=200; RUNS=3; \
    CC=gcc; CXX=g++; \
    BUILD='Debug'; \
    TEST_LIST=$(python3 -c "print('tool_rpcauth ' * $PARALLEL)"); \
    (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done; echo "") && \
    hyperfine \
      --sort command \
      --runs $RUNS \
      --parameter-list COMMIT ${COMMITS// /,} \
      --prepare "git checkout {COMMIT}; git clean -fxd; git reset --hard && cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD} && ninja -C build" \
      "./build/test/functional/test_runner.py $TEST_LIST -j$PARALLEL"
    
    d767503b6a Merge bitcoin/bitcoin#33039: refactor,test: follow-ups to multi-byte block obfuscation
    5bb380ec87 test: sleep patch
    35e91b899d test: Remove polling loop from test_runner
    
    Benchmark 1: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):      7.716 s ±  0.013 s    [User: 42.081 s, System: 4.335 s]
      Range (min … max):    7.704 s …  7.731 s    3 runs
    
    Benchmark 2: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):      4.301 s ±  0.034 s    [User: 42.486 s, System: 4.310 s]
      Range (min … max):    4.263 s …  4.328 s    3 runs
    
    Benchmark 3: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):      4.169 s ±  0.028 s    [User: 41.859 s, System: 4.345 s]
      Range (min … max):    4.140 s …  4.195 s    3 runs
    
    Relative speed comparison
            1.85 ±  0.01  ./build/test/functional/test_runner.py tool_rpcauth [...] tool_rpcauth  -j200 (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.03 ±  0.01  ./build/test/functional/test_runner.py tool_rpcauth [...] tool_rpcauth  -j200 (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.00          ./build/test/functional/test_runner.py tool_rpcauth [...] tool_rpcauth  -j200 (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
    

    </details>

    <details> <summary>clang with release (10 runs, different machine)</summary>

    COMMITS="d767503b6a2618e0c99407acf98f3bd19fb7defd 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7 35e91b899dac689b24a8a95fcef083affc695d1e"; \
    PARALLEL=200; RUNS=10; \
    CC=clang; CXX=clang++;
    BUILD='Release'; \
    TEST_LIST=$(python3 -c "print('tool_rpcauth ' * $PARALLEL)"); \
    (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done; echo "") && \
    hyperfine \
      --sort command \
      --runs $RUNS \
      --parameter-list COMMIT ${COMMITS// /,} \
      --prepare "git checkout {COMMIT}; git clean -fxd; git reset --hard && cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD} && ninja -C build" \
      "build/test/functional/test_runner.py $TEST_LIST -j$PARALLEL"
    
    d767503b6a Merge bitcoin/bitcoin#33039: refactor,test: follow-ups to multi-byte block obfuscation
    5bb380ec87 test: sleep patch
    35e91b899d test: Remove polling loop from test_runner
    
    Benchmark 1: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):      9.829 s ±  0.183 s    [User: 38.943 s, System: 4.407 s]
      Range (min … max):    9.694 s … 10.320 s    10 runs
    
    Benchmark 2: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):      7.063 s ±  0.193 s    [User: 38.883 s, System: 4.408 s]
      Range (min … max):    6.796 s …  7.267 s    10 runs
    
    Benchmark 3: ./build/test/functional/test_runner.py tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth tool_rpcauth  -j200 (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):      6.904 s ±  0.213 s    [User: 38.949 s, System: 4.386 s]
      Range (min … max):    6.649 s …  7.267 s    10 runs
    
    Relative speed comparison
            1.42 ±  0.05  ./build/test/functional/test_runner.py [...] tool_rpcauth  -j200 (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.02 ±  0.04  ./build/test/functional/test_runner.py [...] tool_rpcauth tool_rpcauth  -j200 (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.00          ./build/test/functional/test_runner.py [...] tool_rpcauth tool_rpcauth  -j200 (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
    

    </details>

    So this is obviously faster for many tiny tests, will run the same for the full test suite and do a full code review later. Concept ACK

  5. maflcko commented at 6:29 AM on August 7, 2025: member

    So this is obviously faster for many tiny tests, will run the same for the full test suite and do a full code review later.

    Nice. Thanks for testing and confirming that the code without any sleep is the fastest of the 3 versions. I think benchmarking the full test suite may be difficult, because it has internal noise higher than the savings here. At least back in #13384 (comment), it was hard to measure, but maybe there is less noise now and this can be measured now.

  6. l0rinc commented at 4:56 PM on August 7, 2025: contributor

    it has internal noise higher than the savings here

    Sure, but I'm just running them a lot of times, the trends are obvious this way.

    will run the same for the full test suite

    I have measured running the tests with the 3 setups with 1-6x the nproc for parallelism. The 3 solutions all scale well beyond the number of cpus (I'm now running the same until 8x on a different platform to confirm the findings).

    <details> <summary>Details</summary>

    COMMITS="d767503b6a2618e0c99407acf98f3bd19fb7defd 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7 35e91b899dac689b24a8a95fcef083affc695d1e"; \
    RUNS=3; \
    CC=gcc; CXX=g++; \
    BUILD='Release'; \
    (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done; echo "") && \
    hyperfine \
      --sort command \
      --runs $RUNS \
      --parameter-list COMMIT ${COMMITS// /,} \
      --parameter-list FACTOR $(seq -s, 1 6) \
      --prepare "git checkout {COMMIT}; git clean -fxd; git reset --hard &&  cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD} && ninja -C build " \
      "build/test/functional/test_runner.py -j\$(({FACTOR} * $(nproc))) || true"
    
    d767503b6a Merge bitcoin/bitcoin#33039: refactor,test: follow-ups to multi-byte block obfuscation
    5bb380ec87 test: sleep patch
    35e91b899d test: Remove polling loop from test_runner
    
    Benchmark 1: build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     161.044 s ± 14.549 s    [User: 727.494 s, System: 95.580 s]
      Range (min … max):   151.727 s … 177.809 s    3 runs
     
    Benchmark 2: build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     162.738 s ±  4.422 s    [User: 745.107 s, System: 95.168 s]
      Range (min … max):   157.762 s … 166.217 s    3 runs
     
    Benchmark 3: build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     164.559 s ±  2.955 s    [User: 740.193 s, System: 96.168 s]
      Range (min … max):   161.401 s … 167.256 s    3 runs
     
    Benchmark 4: build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     123.475 s ±  0.815 s    [User: 881.995 s, System: 112.519 s]
      Range (min … max):   122.815 s … 124.385 s    3 runs
     
    Benchmark 5: build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     123.129 s ±  2.608 s    [User: 881.362 s, System: 112.607 s]
      Range (min … max):   121.520 s … 126.137 s    3 runs
     
      Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
     
    Benchmark 6: build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     122.078 s ±  0.827 s    [User: 857.404 s, System: 113.242 s]
      Range (min … max):   121.211 s … 122.858 s    3 runs
     
    Benchmark 7: build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     111.159 s ±  1.883 s    [User: 900.148 s, System: 123.057 s]
      Range (min … max):   109.349 s … 113.108 s    3 runs
     
    Benchmark 8: build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     110.060 s ±  0.637 s    [User: 909.104 s, System: 123.203 s]
      Range (min … max):   109.374 s … 110.634 s    3 runs
     
    Benchmark 9: build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     107.698 s ±  0.202 s    [User: 908.138 s, System: 123.326 s]
      Range (min … max):   107.530 s … 107.922 s    3 runs
     
    Benchmark 10: build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     99.439 s ±  0.353 s    [User: 876.245 s, System: 123.568 s]
      Range (min … max):   99.055 s … 99.750 s    3 runs
     
    Benchmark 11: build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     100.665 s ±  0.930 s    [User: 878.436 s, System: 123.433 s]
      Range (min … max):   99.654 s … 101.483 s    3 runs
     
    Benchmark 12: build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     98.818 s ±  0.490 s    [User: 891.573 s, System: 122.992 s]
      Range (min … max):   98.278 s … 99.234 s    3 runs
     
    Benchmark 13: build/test/functional/test_runner.py -j$((5 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     96.018 s ±  1.705 s    [User: 847.227 s, System: 123.800 s]
      Range (min … max):   95.008 s … 97.986 s    3 runs
     
      Warning: The first benchmarking run for this command was significantly slower than the rest (97.986 s). This could be caused by (filesystem) caches that were not filled until after the first run. You are already using the '--prepare' option which can be used to clear caches. If you did not use a cache-clearing command with '--prepare', you can either try that or consider using the '--warmup' option to fill those caches before the actual benchmark.
     
    Benchmark 14: build/test/functional/test_runner.py -j$((5 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     97.786 s ±  1.161 s    [User: 854.971 s, System: 123.509 s]
      Range (min … max):   96.573 s … 98.888 s    3 runs
     
    Benchmark 15: build/test/functional/test_runner.py -j$((5 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     97.968 s ±  1.246 s    [User: 876.036 s, System: 123.396 s]
      Range (min … max):   96.862 s … 99.317 s    3 runs
     
    Benchmark 16: build/test/functional/test_runner.py -j$((6 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     97.557 s ±  0.795 s    [User: 864.526 s, System: 124.178 s]
      Range (min … max):   96.685 s … 98.242 s    3 runs
     
    Benchmark 17: build/test/functional/test_runner.py -j$((6 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     98.179 s ±  1.145 s    [User: 863.995 s, System: 124.434 s]
      Range (min … max):   97.295 s … 99.473 s    3 runs
     
    Benchmark 18: build/test/functional/test_runner.py -j$((6 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     96.079 s ±  0.906 s    [User: 857.862 s, System: 124.003 s]
      Range (min … max):   95.159 s … 96.970 s    3 runs
     
    Relative speed comparison
            1.68 ±  0.15  build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.69 ±  0.06  build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.71 ±  0.04  build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
            1.29 ±  0.02  build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.28 ±  0.04  build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.27 ±  0.02  build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
            1.16 ±  0.03  build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.15 ±  0.02  build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.12 ±  0.02  build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
            1.04 ±  0.02  build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.05 ±  0.02  build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.03 ±  0.02  build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
            1.00          build/test/functional/test_runner.py -j$((5 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.02 ±  0.02  build/test/functional/test_runner.py -j$((5 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.02 ±  0.02  build/test/functional/test_runner.py -j$((5 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
            1.02 ±  0.02  build/test/functional/test_runner.py -j$((6 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
            1.02 ±  0.02  build/test/functional/test_runner.py -j$((6 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
            1.00 ±  0.02  build/test/functional/test_runner.py -j$((6 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
    

    </details>

    <img width="1000" alt="image" src="https://github.com/user-attachments/assets/baf5da85-751f-4bf5-bc28-629eadc3b0a1" />

    Which indicates to me that the new solution scales a bit better - but since we haven't even hit the bottom yet (surprisingly), I'll post those in a follow-up.

  7. maflcko commented at 5:09 PM on August 7, 2025: member

    Which indicates to me that the new solution scales a bit better - but since we haven't even hit the bottom yet (surprisingly), I'll post those in a follow-up.

    Thanks for the benchmarks, but honestly, I can't see that the new solution scales better. You only did 3 runs for each point, and the error bars overlap with the other points each time. Also the hyperfine output has warnings about outliers. I guess you'd have to run at least 100 times for each data point (not saying you should :sweat_smile:, I am happy with the previous benchmarks already).

    Also, I am thinking you are already saturated at 100 seconds, because the slowest test likely takes this amount of time. No amount of parallelism can make it faster.

  8. l0rinc commented at 6:13 AM on August 13, 2025: contributor

    Redid only 1-4x, with 20 runs for stability to see if these speedups apply to our test suite as well.

    Running it on a more powerful i9 with SSD: <img width="1200" alt="image" src="https://github.com/user-attachments/assets/0f6baa59-2b6a-4981-a739-1b08b1093eaf" />

    <details> <summary>Details</summary>

    d767503b6a Merge bitcoin/bitcoin#33039: refactor,test: follow-ups to multi-byte block obfuscation
    5bb380ec87 test: sleep patch
    35e91b899d test: Remove polling loop from test_runner
    
    Benchmark 1: build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     162.617 s ±  4.057 s    [User: 742.563 s, System: 95.831 s]
      Range (min … max):   153.539 s … 169.594 s    20 runs
     
    Benchmark 2: build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     166.120 s ±  1.751 s    [User: 746.994 s, System: 95.786 s]
      Range (min … max):   162.345 s … 170.193 s    20 runs
     
    Benchmark 3: build/test/functional/test_runner.py -j$((1 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     165.285 s ±  1.679 s    [User: 743.584 s, System: 96.084 s]
      Range (min … max):   162.273 s … 168.493 s    20 runs
     
    Benchmark 4: build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     123.485 s ±  1.311 s    [User: 861.322 s, System: 112.273 s]
      Range (min … max):   121.381 s … 125.718 s    20 runs
     
    Benchmark 5: build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     123.578 s ±  1.149 s    [User: 865.509 s, System: 112.239 s]
      Range (min … max):   121.861 s … 126.296 s    20 runs
     
    Benchmark 6: build/test/functional/test_runner.py -j$((2 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     122.229 s ±  1.581 s    [User: 867.603 s, System: 112.883 s]
      Range (min … max):   119.546 s … 124.407 s    20 runs
     
    Benchmark 7: build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     110.005 s ±  1.110 s    [User: 904.411 s, System: 122.869 s]
      Range (min … max):   108.654 s … 111.472 s    20 runs
     
    Benchmark 8: build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     111.199 s ±  1.291 s    [User: 902.330 s, System: 122.718 s]
      Range (min … max):   108.685 s … 114.161 s    20 runs
     
    Benchmark 9: build/test/functional/test_runner.py -j$((3 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     109.771 s ±  1.615 s    [User: 897.538 s, System: 122.906 s]
      Range (min … max):   107.295 s … 113.223 s    20 runs
     
    Benchmark 10: build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     100.538 s ±  1.568 s    [User: 890.148 s, System: 123.298 s]
      Range (min … max):   97.325 s … 103.165 s    20 runs
     
    Benchmark 11: build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     100.561 s ±  1.307 s    [User: 881.061 s, System: 123.444 s]
      Range (min … max):   98.469 s … 103.547 s    20 runs
     
    Benchmark 12: build/test/functional/test_runner.py -j$((4 * 16)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     100.220 s ±  1.769 s    [User: 876.089 s, System: 123.136 s]
      Range (min … max):   97.393 s … 103.823 s    20 runs
    

    </details>

    And on a slowe2 i7 with HDD: <img width="1200" alt="image" src="https://github.com/user-attachments/assets/f3f219d5-afa5-4850-8361-540ea6d100ba" />

    <details> <summary>Details</summary>

    COMMITS="d767503b6a2618e0c99407acf98f3bd19fb7defd 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7 35e91b899dac689b24a8a95fcef083affc695d1e"; RUNS=20; CC=gcc; CXX=g++; BUILD='Release'; (echo ""; for c in $COMMITS; do git fetch -q origin $c && git log -1 --pretty='%h %s' $c || exit 1; done; echo "") && hyperfine   --sor
    t command   --runs $RUNS   --parameter-list COMMIT ${COMMITS// /,}   --parameter-list FACTOR $(seq -s, 1 4)   --prepare "git checkout {COMMIT}; git clean -fxd; git reset --hard &&  cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD} && ninja -C build "   "build/test/functional/test_runner.py -j\$(({FACTOR} * $(nproc))) || true"                                           
                                                                                                                                                                                                                                                                                                                                                                                        
    d767503b6a Merge bitcoin/bitcoin#33039: refactor,test: follow-ups to multi-byte block obfuscation                                                                                                                                                                                                                                                                                   
    5bb380ec87 test: sleep patch                                                                 
    35e91b899d test: Remove polling loop from test_runner                                        
    
    Benchmark 1: build/test/functional/test_runner.py -j$((1 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)                                                                
      Time (mean ± σ):     536.561 s ± 10.213 s    [User: 741.217 s, System: 100.037 s]          
      Range (min … max):   518.267 s … 556.539 s    20 runs                                      
                                                                                                 
    Benchmark 2: build/test/functional/test_runner.py -j$((1 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                                
      Time (mean ± σ):     543.853 s ±  7.098 s    [User: 747.414 s, System: 100.004 s]
      Range (min … max):   532.029 s … 556.266 s    20 runs
                                                                                                 
    Benchmark 3: build/test/functional/test_runner.py -j$((1 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     534.225 s ± 14.423 s    [User: 743.823 s, System: 100.001 s]
      Range (min … max):   502.162 s … 552.954 s    20 runs
                                                                                                 
    Benchmark 4: build/test/functional/test_runner.py -j$((2 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)
      Time (mean ± σ):     398.433 s ± 13.789 s    [User: 816.850 s, System: 108.760 s]
      Range (min … max):   368.803 s … 420.022 s    20 runs
                                                                                                 
    Benchmark 5: build/test/functional/test_runner.py -j$((2 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)
      Time (mean ± σ):     405.140 s ±  8.846 s    [User: 817.834 s, System: 109.110 s]
      Range (min … max):   388.287 s … 422.856 s    20 runs
                                                                                                 
    Benchmark 6: build/test/functional/test_runner.py -j$((2 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)
      Time (mean ± σ):     397.259 s ±  8.343 s    [User: 814.290 s, System: 108.749 s]
      Range (min … max):   378.060 s … 412.245 s    20 runs
                                                                                                 
    Benchmark 7: build/test/functional/test_runner.py -j$((3 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)     
      Time (mean ± σ):     351.179 s ±  8.515 s    [User: 855.037 s, System: 113.145 s]          
      Range (min … max):   336.721 s … 368.320 s    20 runs                                      
                                                                                                 
    Benchmark 8: build/test/functional/test_runner.py -j$((3 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                                
      Time (mean ± σ):     350.855 s ±  9.233 s    [User: 862.762 s, System: 113.541 s]          
      Range (min … max):   334.352 s … 366.530 s    20 runs                                      
                                                                                                 
    Benchmark 9: build/test/functional/test_runner.py -j$((3 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)                                                                
      Time (mean ± σ):     357.291 s ±  8.876 s    [User: 854.476 s, System: 114.151 s]          
      Range (min … max):   340.714 s … 369.699 s    20 runs                                      
                                                                                                 
    Benchmark 10: build/test/functional/test_runner.py -j$((4 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)                                                               
      Time (mean ± σ):     338.878 s ±  5.938 s    [User: 881.142 s, System: 116.883 s]          
      Range (min … max):   328.316 s … 348.831 s    20 runs                                      
                                                                                                 
    Benchmark 11: build/test/functional/test_runner.py -j$((4 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                               
      Time (mean ± σ):     330.988 s ±  8.537 s    [User: 889.021 s, System: 116.644 s]          
      Range (min … max):   313.327 s … 344.076 s    20 runs                                      
                                                                                                 
    Benchmark 12: build/test/functional/test_runner.py -j$((4 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)                                                               
      Time (mean ± σ):     324.608 s ±  8.837 s    [User: 875.886 s, System: 116.579 s]          
      Range (min … max):   304.882 s … 341.161 s    20 runs                                      
                                                                                                 
    Relative speed comparison                                                                    
            1.65 ±  0.05  build/test/functional/test_runner.py -j$((1 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)                                                       
            1.68 ±  0.05  build/test/functional/test_runner.py -j$((1 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                       
            1.65 ±  0.06  build/test/functional/test_runner.py -j$((1 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)                                                       
            1.23 ±  0.05  build/test/functional/test_runner.py -j$((2 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)                                                       
            1.25 ±  0.04  build/test/functional/test_runner.py -j$((2 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                       
            1.22 ±  0.04  build/test/functional/test_runner.py -j$((2 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)                                                       
            1.08 ±  0.04  build/test/functional/test_runner.py -j$((3 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)                                                       
            1.08 ±  0.04  build/test/functional/test_runner.py -j$((3 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                       
            1.10 ±  0.04  build/test/functional/test_runner.py -j$((3 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)                                                       
            1.04 ±  0.03  build/test/functional/test_runner.py -j$((4 * 8)) || true (COMMIT = d767503b6a2618e0c99407acf98f3bd19fb7defd)                                                       
            1.02 ±  0.04  build/test/functional/test_runner.py -j$((4 * 8)) || true (COMMIT = 5bb380ec87af93ed5543ba10e1f8465aca7bcfe7)                                                       
            1.00          build/test/functional/test_runner.py -j$((4 * 8)) || true (COMMIT = 35e91b899dac689b24a8a95fcef083affc695d1e)      
    

    </details>

    Not sure we can claim that it's faster, but makes more sense conceptually, maybe we can blow up the heavy tests and scale better.

  9. in test/functional/test_runner.py:762 in faaab70684 outdated
     760 |  
     761 |          dot_count = 0
     762 |          while True:
     763 |              # Return all procs that have finished, if any. Otherwise sleep until there is one.
     764 | -            time.sleep(.5)
     765 | +            procs = futures.wait(self.jobs, timeout=.5, return_when=futures.FIRST_COMPLETED)
    


    Eunovo commented at 10:10 AM on August 15, 2025:

    https://github.com/bitcoin/bitcoin/pull/33141/commits/faaab70684779b81b2f6adac319bbf2d3b77165d: I thought the timeout here might be unnecessary, but I ran some benchmarks comparing .5s timeout to no timeout and this commit was always sligthly faster.


    maflcko commented at 1:23 PM on August 18, 2025:

    I ran some benchmarks comparing .5s timeout to no timeout and this commit was always sligthly faster.

    The reason why .5 is picked here is to preserve the observable behavior when print('.', end='', flush=True) is hit: During the sleep/wait you'll only want a dot every half second, not in a busy-loop without delay. Using a busy-loop also sounds like a plausible explanation for your observed slow down, when decreasing it to 0.

    Other than that, I don't think the exact value here should matter, as long as it is not zero.

  10. Eunovo commented at 10:11 AM on August 15, 2025: contributor

    ACK https://github.com/bitcoin/bitcoin/pull/33141/commits/faaab70684779b81b2f6adac319bbf2d3b77165d

    I ran some simple benchmarks on my 8-core laptop

    hyperfine \
    --sort command \
    --runs 3 \
    --parameter-list COMMIT "master,faaab706" \
    --prepare "git checkout {COMMIT}; cmake --build build -j 8" \
    "build/test/functional/test_runner.py $(python3  -c "print('tool_wallet ' * 100)") -j 8"
    Benchmark 1: build/test/functional/test_runner.py tool_wallet tool_wallet tool_wallet tool_wallet [...] tool_wallet  -j 8 (COMMIT = master)
      Time (mean ± σ):     107.620 s ±  1.261 s    [User: 568.243 s, System: 45.749 s]
      Range (min … max):   106.286 s … 108.791 s    3 runs
     
    Benchmark 2: build/test/functional/test_runner.py tool_wallet [...] tool_wallet  -j 8 (COMMIT = faaab706)
      Time (mean ± σ):     102.151 s ±  0.840 s    [User: 567.913 s, System: 45.192 s]
      Range (min … max):   101.257 s … 102.925 s    3 runs
     
    Relative speed comparison
            1.05 ±  0.02  build/test/functional/test_runner.py tool_wallet [...] tool_wallet  -j 8 (COMMIT = master)
            1.00          build/test/functional/test_runner.py tool_wallet [...] tool_wallet  -j 8 (COMMIT = faaab706)
    
    
    hyperfine \                                          
    --sort command \
    --runs 3 \
    --parameter-list COMMIT "master,faaab706" \
    --prepare "git checkout {COMMIT}; cmake --build build -j 8" \
    "build/test/functional/test_runner.py -j 8"
    Benchmark 1: build/test/functional/test_runner.py -j 8 (COMMIT = master)
      Time (mean ± σ):     240.953 s ±  3.510 s    [User: 458.626 s, System: 96.167 s]
      Range (min … max):   238.456 s … 244.967 s    3 runs
     
    Benchmark 2: build/test/functional/test_runner.py -j 8 (COMMIT = faaab706)
      Time (mean ± σ):     232.209 s ± 10.131 s    [User: 441.218 s, System: 96.422 s]
      Range (min … max):   222.179 s … 242.437 s    3 runs
     
    Relative speed comparison
            1.04 ±  0.05  build/test/functional/test_runner.py -j 8 (COMMIT = master)
            1.00          build/test/functional/test_runner.py -j 8 (COMMIT = faaab706)
    

    https://github.com/bitcoin/bitcoin/pull/33141/commits/faaab70684779b81b2f6adac319bbf2d3b77165d is slightly faster in both cases.

  11. DrahtBot requested review from l0rinc on Aug 15, 2025
  12. maflcko removed review request from l0rinc on Aug 28, 2025
  13. maflcko requested review from l0rinc on Aug 28, 2025
  14. in test/functional/test_runner.py:750 in faaab70684 outdated
     759 | +            print("Remaining jobs: [{}]".format(", ".join(j.debug_name for j in self.jobs)))
     760 |  
     761 |          dot_count = 0
     762 |          while True:
     763 |              # Return all procs that have finished, if any. Otherwise sleep until there is one.
     764 | -            time.sleep(.5)
    


    l0rinc commented at 10:38 PM on August 29, 2025:

    Removing this changes the scheduling overlap to be more unpredictable since the new tasks will be scheduled immediately. That's what we want it to do to avoid wasted cycles, but I can imagine this will result in new test failures we haven't seen before.


    maflcko commented at 6:19 AM on September 1, 2025:

    I can imagine this will result in new test failures we haven't seen before.

    I'd doubt that test will fail due to scheduling them more tightly without sleeps in-between. Though, if they do, that is a good thing, because it hints at a bug that we should be aware of and ideally should fix.

  15. in test/functional/test_runner.py:765 in faaab70684 outdated
     768 | -            for job in self.jobs:
     769 | -                (name, start_time, proc, testdir, log_out, log_err) = job
     770 | -                if proc.poll() is not None:
     771 | +            for job in procs.done:
     772 | +                    (name, start_time, proc, testdir, log_out, log_err) = job.result()
     773 |                      log_out.seek(0), log_err.seek(0)
    


    l0rinc commented at 10:43 PM on August 29, 2025:

    what's the reason for keeping the old indentation here?


    maflcko commented at 6:19 AM on September 1, 2025:

    thx, formatted

  16. in test/functional/test_runner.py:709 in faaab70684 outdated
     703 | @@ -703,9 +704,9 @@ class TestHandler:
     704 |      """
     705 |      Trigger the test scripts passed in via the list.
     706 |      """
     707 | -
     708 |      def __init__(self, *, num_tests_parallel, tests_dir, tmpdir, test_list, flags, use_term_control):
     709 |          assert num_tests_parallel >= 1
     710 | +        self.executor = futures.ThreadPoolExecutor(max_workers=num_tests_parallel)
    


    l0rinc commented at 10:49 PM on August 29, 2025:

    It's likely not strictly needed, but if you edit again consider self.executor.shutdown(wait=True) once all tests are done


    maflcko commented at 6:10 AM on September 1, 2025:

    Yeah, maybe. Though,

    • we can't really use the return code anyway (there is none), so the only difference would be that we have to explicitly shutdown the executor somewhere.
    • There is no natural place to put it, because doing it in get_next seems confusing. Similarly, doing it before sys.exit seems like a layer violation.
    • putting an explicit shutdown somewhere is inconsistent, because the code path isn't hit on exceptions (base exceptions)

    In practise it doesn't matter, because if it is empty, no shutdown is needed and if it isn't empty, a shutdown isn't needed either, becaue python will wait on it anyway.

    leaving as-is for now.

  17. in test/functional/test_runner.py:743 in faaab70684 outdated
     740 |                                time.time(),
     741 |                                subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg,
     742 |                                                 text=True,
     743 |                                                 stdout=log_stdout,
     744 | -                                               stderr=log_stderr),
     745 | +                                               stderr=log_stderr,
    


    l0rinc commented at 10:51 PM on August 29, 2025:

    these extra commas make reformatted code look funny

                                     stderr=log_stderr,
                                     ),
                    testdir,
    

    maflcko commented at 5:48 AM on September 1, 2025:

    thx, formatted

  18. in test/functional/test_runner.py:769 in faaab70684 outdated
     773 |                      log_out.seek(0), log_err.seek(0)
     774 |                      [stdout, stderr] = [log_file.read().decode('utf-8') for log_file in (log_out, log_err)]
     775 |                      log_out.close(), log_err.close()
     776 |                      skip_reason = None
     777 | -                    if proc.returncode == TEST_EXIT_PASSED and stderr == "":
     778 | +                    if proc == TEST_EXIT_PASSED and stderr == "":
    


    l0rinc commented at 10:55 PM on August 29, 2025:

    this is a bit confusing now, this isn't the proc anymore, it's the return value of the proc now


    maflcko commented at 6:20 AM on September 1, 2025:

    thx, restored original code

  19. in test/functional/test_runner.py:738 in faaab70684 outdated
     734 | +            def proc_wait(task):
     735 | +                task[2] = task[2].wait()
     736 | +                return task
     737 | +
     738 | +            self.jobs.append(self.executor.submit(proc_wait, task=[
     739 | +                              test,
    


    l0rinc commented at 10:57 PM on August 29, 2025:

    formatting is off now that the leading ( was removed


    maflcko commented at 6:11 AM on September 1, 2025:

    thx, ran formatter

  20. in test/functional/test_runner.py:734 in faaab70684 outdated
     728 | @@ -728,42 +729,50 @@ def get_next(self):
     729 |              test_argv = test.split()
     730 |              testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)
     731 |              tmpdir_arg = ["--tmpdir={}".format(testdir)]
     732 | -            self.jobs.append((test,
     733 | +
     734 | +            def proc_wait(task):
     735 | +                task[2] = task[2].wait()
    


    l0rinc commented at 11:49 PM on August 29, 2025:

    I find this confusing - as mentioned below -, we're replacing a value with a different typed one. Is the mutation strictly necessary or would something like

    def proc_wait(task):
        name, start_time, proc, testdir, log_out, log_err = task
        return_code = proc.wait()
        return [name, start_time, return_code, testdir, log_out, log_err]
    

    also work?


    maflcko commented at 6:11 AM on September 1, 2025:

    thx, kept the type

  21. l0rinc approved
  22. DrahtBot requested review from l0rinc on Aug 29, 2025
  23. maflcko force-pushed on Sep 1, 2025
  24. maflcko commented at 6:31 AM on September 1, 2025: member

    force pushed with small formatting changes. Should be trivial to re-review via --ignore-all-space.

  25. in test/functional/test_runner.py:751 in fa7a3b5fb0 outdated
     755 | +                testdir,
     756 | +                log_stdout,
     757 | +                log_stderr,
     758 | +            ]
     759 | +            self.jobs.append(self.executor.submit(proc_wait, task))
     760 | +            self.jobs[-1].debug_name = test
    


    l0rinc commented at 5:30 PM on September 1, 2025:

    I’m not exactly sure how this works; my IDEs break here. Is my understanding correct that we’re just piggybacking a new value onto a Future’s per-instance __dict__? Would it be cleaner to use a Future-to-string dictionary instead?

    self.job_name = {}
    ...
    future = self.executor.submit(proc_wait, task)
    self.job_name[future] = test
    self.jobs.append(future)
    ...
    print("Remaining jobs: [{}]".format(", ".join(self.job_name[f] for f in self.jobs)))
    

    (I checked, and it seems dicts are keyable by Futures, the Remaining jobs messages are the same this way)

    It's also what the https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example is doing to connect the Futures back to their inputs


    maflcko commented at 8:32 AM on September 3, 2025:

    The example doesn't use futures.wait, but I guess futures.wait does not create copies of the futures, which would map differently as keys?

    So I went ahead and implemented something like this.


    l0rinc commented at 6:04 PM on September 4, 2025:

    Even better than what I suggested.

    This is now sorted by name instead of insertion order if I understand it correctly, but it's just a log so I'm fine with either - this seems more stable.

    Alternatively, if we don't like future keys, we could also use the names as keys and Futures as values - but I'm fine with it as-is.


    maflcko commented at 6:40 PM on September 4, 2025:

    Alternatively, if we don't like future keys, we could also use the names as keys and Futures as values - but I'm fine with it as-is.

    I don't think this works, because names could be duplicate and a dict can not have duplicate keys.

  26. in test/functional/test_runner.py:762 in fa7a3b5fb0 outdated
     768 |  
     769 |          dot_count = 0
     770 |          while True:
     771 |              # Return all procs that have finished, if any. Otherwise sleep until there is one.
     772 | -            time.sleep(.5)
     773 | +            procs = futures.wait(self.jobs, timeout=.5, return_when=futures.FIRST_COMPLETED)
    


    l0rinc commented at 5:33 PM on September 1, 2025:

    nit: since this is new code, maybe we could decompose done and not_done here already

    done, not_done = futures.wait(self.jobs, timeout=.5, return_when=futures.FIRST_COMPLETED)
    self.jobs = list(not_done)
    ret = []
    for fut in done:
    

    maflcko commented at 8:34 AM on September 3, 2025:

    I prefer named fields with less floating symbols in the scope, so I'll leave as-is for now.

  27. in test/functional/test_runner.py:761 in fa7a3b5fb0 outdated
     766 | -            print("Remaining jobs: [{}]".format(", ".join(j[0] for j in self.jobs)))
     767 | +            print("Remaining jobs: [{}]".format(", ".join(j.debug_name for j in self.jobs)))
     768 |  
     769 |          dot_count = 0
     770 |          while True:
     771 |              # Return all procs that have finished, if any. Otherwise sleep until there is one.
    


    l0rinc commented at 5:56 PM on September 1, 2025:

    is it still correct to claim that we're sleeping?


    maflcko commented at 8:33 AM on September 3, 2025:

    Yes, my understanding is that "sleep" and "wait" are words to describe a similar concept in this context. Leaving as-is for now.

  28. l0rinc approved
  29. l0rinc commented at 6:10 PM on September 1, 2025: contributor

    tested ACK fa7a3b5fb04465a3fbadf1ca62eac26827c1ef1d

    there are still a few things I'd prefer before we merge, but won't block

  30. DrahtBot requested review from Eunovo on Sep 1, 2025
  31. test: Remove polling loop from test_runner fa4885ef2f
  32. maflcko force-pushed on Sep 3, 2025
  33. maflcko commented at 8:58 AM on September 3, 2025: member

    Force pushed with a tiny refactor turning a list into a dict. Should be easy to re-review.

  34. in test/functional/test_runner.py:753 in fa4885ef2f
     757 | +                log_stderr,
     758 | +            ]
     759 | +            fut = self.executor.submit(proc_wait, task)
     760 | +            self.jobs[fut] = test
     761 |          if not self.jobs:
     762 |              raise IndexError('pop from empty list')
    


    l0rinc commented at 6:03 PM on September 4, 2025:

    My understanding is that in Python a list self.test_list.pop(0) is O(n) so consuming all elements is quadratic this way. A collections.deque with popleft might describe the desired behavior better (performance-wise it would likely be the same for a few hundred elements)

    nit: jobs isn't a "list" anymore


    edit: https://docs.python.org/3/tutorial/datastructures.html#using-lists-as-queues

    While appends and pops from the end of list are fast, doing inserts or pops from the beginning of a list is slow (because all of the other elements have to be shifted by one).


    maflcko commented at 6:43 PM on September 4, 2025:

    I don't think pop was ever used, it was remove. Though, this is just a sanity-check, so I don't think it matters. Will leave as-is for now.


    l0rinc commented at 6:56 PM on September 4, 2025:

    maflcko commented at 7:24 PM on September 4, 2025:

    We have self.test_list.pop(0) at the beginning:

    This will already throw an IndexError. There is no need to check it manually twice for it. The check here is for self.jobs, to avoid an infinite while True loop in case of a coding error.


    l0rinc commented at 7:28 PM on September 4, 2025:

    yes, but that's quadratic (n * O(n)) and self.jobs is not a list anymore, so the error is wrong - isn't it?


    maflcko commented at 7:47 PM on September 4, 2025:

    quadratic

    Ok, I see. Though, that seems unrelated? I am not touching test_list in this pull request (and also not in the line you commented on), so you should be able to create a separate pull (happy to review).

    so the error is wrong - isn't it?

    Yes, the message is "wrong". Though, it always was "wrong" (intentionally). The only thing that matters is the raise, the message is mostly irrelevant.


    l0rinc commented at 7:57 PM on September 4, 2025:

    so you should be able to create a separate pull

    yes, I think it should be done in a separate PR, should have clarified that.

    Yes, the message is "wrong".

    I'm not blocking, I don't think it's important, but if you edit again, maybe we can remove/update the message to remove the confusion


    l0rinc commented at 11:46 PM on September 4, 2025:

    Pushed to #33313, please resolve the comment

  35. in test/functional/test_runner.py:765 in fa4885ef2f
     791 | -                    if self.use_term_control:
     792 | -                        clearline = '\r' + (' ' * dot_count) + '\r'
     793 | -                        print(clearline, end='', flush=True)
     794 | -                    dot_count = 0
     795 | -                    ret.append((TestResult(name, status, int(time.time() - start_time)), testdir, stdout, stderr, skip_reason))
     796 | +            for job in procs.done:
    


    l0rinc commented at 6:05 PM on September 4, 2025:

    nit: to avoid confusion with previous tuple, since job is a Future now, we might want to name it as fut as well to be consistent with above


    maflcko commented at 6:44 PM on September 4, 2025:

    may do if i have to re-touch

  36. l0rinc approved
  37. l0rinc commented at 6:37 PM on September 4, 2025: contributor

    tested ACK fa4885ef2fde2b9fd9eb7367564a2946a45f20ac

  38. l0rinc referenced this in commit 1500bb359e on Sep 4, 2025
  39. maflcko removed review request from Eunovo on Sep 7, 2025
  40. maflcko requested review from Eunovo on Sep 7, 2025
  41. l0rinc referenced this in commit 8c1f10233d on Sep 7, 2025
  42. achow101 commented at 8:00 PM on September 11, 2025: member

    ACK fa4885ef2fde2b9fd9eb7367564a2946a45f20ac

  43. achow101 merged this on Sep 11, 2025
  44. achow101 closed this on Sep 11, 2025

  45. l0rinc referenced this in commit 75e6984ec8 on Sep 11, 2025
  46. alexanderwiederin referenced this in commit 49e068b15b on Sep 16, 2025
  47. alexanderwiederin referenced this in commit 4b0c2f2a8f on Sep 17, 2025
  48. alexanderwiederin referenced this in commit 2edb618ffe on Sep 17, 2025
  49. stringintech referenced this in commit fb8510ba20 on Sep 17, 2025
  50. maflcko deleted the branch on Sep 26, 2025

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-05-01 09:12 UTC

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