Skip to content

implement xargs -P using rayon and a spin lock - #653

Open
asakatida wants to merge 1 commit into
uutils:mainfrom
asakatida:push-zlxwkxlkpsls
Open

implement xargs -P using rayon and a spin lock#653
asakatida wants to merge 1 commit into
uutils:mainfrom
asakatida:push-zlxwkxlkpsls

Conversation

@asakatida

Copy link
Copy Markdown

if this is accepted then I could do #112 in a similar way

Comment thread src/xargs/mod.rs
batches
.par_bridge()
.map(|batch| batch?.execute_parallel(capacity).map_err(XargsError::from))
.collect()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

this is meat of the change, the other option here to avoid would be to build the thread pool directly which would avoid needing a lock but I feel the impl Iterator is a cleaner abstraction and is in line with rayon advice

@asakatida
asakatida force-pushed the push-zlxwkxlkpsls branch from 61aae5c to e175c3c Compare April 8, 2026 09:40
@asakatida

Copy link
Copy Markdown
Author

if more tests are desired then I have a branch ready to push

@codecov

codecov Bot commented Apr 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.38843% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.90%. Comparing base (f38dbe5) to head (fd8d9c1).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/xargs/mod.rs 93.38% 5 Missing and 3 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #653   +/-   ##
=======================================
  Coverage   91.89%   91.90%           
=======================================
  Files          35       35           
  Lines        7217     7297   +80     
  Branches      376      377    +1     
=======================================
+ Hits         6632     6706   +74     
- Misses        442      446    +4     
- Partials      143      145    +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sylvestre

Copy link
Copy Markdown
Contributor

Did you run some benchmark on it?

@asakatida

Copy link
Copy Markdown
Author

here's a few ad-hoc rough numbers

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n2 -P2 sleep

________________________________________________________
Executed in   32.22 secs    fish           external
   usr time  186.48 secs   69.00 micros  186.48 secs
   sys time    0.38 secs  342.00 micros    0.37 secs

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n2 -P8 sleep

________________________________________________________
Executed in    8.02 secs    fish           external
   usr time   24.01 secs   55.00 micros   24.01 secs
   sys time    0.06 secs  303.00 micros    0.06 secs

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n2 -P32 sleep

________________________________________________________
Executed in    8.01 secs      fish           external
   usr time   12.39 millis   65.00 micros   12.32 millis
   sys time   26.17 millis  313.00 micros   25.85 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n8 -P2 sleep

________________________________________________________
Executed in   32.02 secs    fish           external
   usr time   31.97 secs   52.00 micros   31.97 secs
   sys time    0.06 secs  289.00 micros    0.06 secs

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n8 -P8 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    4.72 millis  101.00 micros    4.62 millis
   sys time    9.61 millis  427.00 micros    9.18 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n8 -P32 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    5.09 millis   68.00 micros    5.03 millis
   sys time    8.87 millis  282.00 micros    8.59 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n32 -P2 sleep

________________________________________________________
Executed in   64.02 secs      fish           external
   usr time    3.42 millis  107.00 micros    3.31 millis
   sys time    7.28 millis  398.00 micros    6.88 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n32 -P8 sleep
 
________________________________________________________
Executed in   64.01 secs      fish           external
   usr time    3.03 millis   61.00 micros    2.97 millis
   sys time    6.12 millis  306.00 micros    5.81 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 | target/release/xargs -n32 -P32 sleep

________________________________________________________
Executed in   64.01 secs      fish           external
   usr time    3.17 millis   53.00 micros    3.11 millis
   sys time    5.61 millis  334.00 micros    5.28 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n2 -P2 sleep

________________________________________________________
Executed in    8.02 secs    fish           external
   usr time    8.01 secs   54.00 micros    8.01 secs
   sys time    0.02 secs  287.00 micros    0.02 secs

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n2 -P8 sleep

________________________________________________________
Executed in    4.04 secs      fish           external
   usr time    7.97 millis   92.00 micros    7.88 millis
   sys time   12.45 millis  474.00 micros   11.98 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n2 -P32 sleep

________________________________________________________
Executed in    4.04 secs      fish           external
   usr time    7.93 millis   91.00 micros    7.84 millis
   sys time   12.31 millis  466.00 micros   11.84 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n8 -P2 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    2.89 millis   53.00 micros    2.84 millis
   sys time    6.38 millis  309.00 micros    6.07 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n32 -P2 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    3.03 millis   64.00 micros    2.96 millis
   sys time    5.32 millis  314.00 micros    5.00 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n8 -P8 sleep

________________________________________________________
Executed in   16.02 secs      fish           external
   usr time    3.03 millis   55.00 micros    2.98 millis
   sys time    7.19 millis  306.00 micros    6.88 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n8 -P32 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    2.92 millis   56.00 micros    2.86 millis
   sys time    6.46 millis  320.00 micros    6.14 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n32 -P8 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    2.95 millis   64.00 micros    2.88 millis
   sys time    5.85 millis  320.00 micros    5.53 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 2 2 2 2 2 2 | target/release/xargs -n32 -P32 sleep

________________________________________________________
Executed in   16.01 secs      fish           external
   usr time    2.98 millis   53.00 micros    2.93 millis
   sys time    5.53 millis  286.00 micros    5.25 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n2 -P2 sleep

________________________________________________________
Executed in    4.02 secs      fish           external
   usr time    3.22 millis   51.00 micros    3.17 millis
   sys time    7.91 millis  318.00 micros    7.59 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n2 -P8 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.12 millis   59.00 micros    3.06 millis
   sys time    6.27 millis  335.00 micros    5.93 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n2 -P32 sleep

________________________________________________________
Executed in    4.02 secs      fish           external
   usr time    3.21 millis   53.00 micros    3.16 millis
   sys time    6.66 millis  313.00 micros    6.34 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n8 -P2 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.04 millis   62.00 micros    2.98 millis
   sys time    6.49 millis  324.00 micros    6.17 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n8 -P8 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.11 millis   58.00 micros    3.05 millis
   sys time    4.80 millis  296.00 micros    4.51 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n8 -P32 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.08 millis   56.00 micros    3.03 millis
   sys time    6.12 millis  308.00 micros    5.81 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n32 -P2 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.04 millis   49.00 micros    2.99 millis
   sys time    6.34 millis  330.00 micros    6.01 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n32 -P8 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.00 millis   63.00 micros    2.94 millis
   sys time    6.41 millis  319.00 micros    6.09 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time echo 2 2 | target/release/xargs -n32 -P32 sleep

________________________________________________________
Executed in    4.01 secs      fish           external
   usr time    3.25 millis   56.00 micros    3.19 millis
   sys time    6.60 millis  342.00 micros    6.26 millis

@asakatida

Copy link
Copy Markdown
Author

a few more that probably have more cpu contention

/Users/asakatida/repo/workspace/uutils-findutils
> time target/release/find ../jj-vcs-jj/ -name Cargo.toml -print0 | target/release/xargs -0 -n1 -P3 cargo build --manifest-path 2>/dev/null

________________________________________________________
Executed in    1.28 secs    fish           external
   usr time    1.85 secs    0.23 millis    1.85 secs
   sys time    0.69 secs    1.12 millis    0.69 secs

/Users/asakatida/repo/workspace/uutils-findutils
> time find ../jj-vcs-jj/ -name Cargo.toml -print0 | xargs -0 -n1 -P3 cargo build --manifest-path 2>/dev/null

________________________________________________________
Executed in    2.71 secs      fish           external
   usr time  952.11 millis   12.30 millis  939.81 millis
   sys time  757.92 millis    4.01 millis  753.91 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time target/release/find ../jj-vcs-jj/ -name Cargo.toml -print0 | xargs -0 -n1 -P3 cargo build --manifest-path 2>/dev/null

________________________________________________________
Executed in    1.20 secs      fish           external
   usr time  744.81 millis    0.18 millis  744.63 millis
   sys time  469.26 millis    1.31 millis  467.95 millis

/Users/asakatida/repo/workspace/uutils-findutils
> time find ../jj-vcs-jj/ -name Cargo.toml -print0 | target/release/xargs -0 -n1 -P3 cargo build --manifest-path 2>/dev/null

________________________________________________________
Executed in  856.59 millis    fish           external
   usr time    1.56 secs      0.23 millis    1.56 secs
   sys time    0.48 secs      1.36 millis    0.48 secs

@sylvestre

Copy link
Copy Markdown
Contributor

@asakatida
please run the benchmark with hyperfine, time is too limited :)
thanks

@sylvestre

Copy link
Copy Markdown
Contributor

  Scenario 1 — sequential default (-n1 true)

  Benchmark 1: target/release/xargs.main -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):      1.907 s ±  0.120 s    [User: 0.494 s, System: 1.429 s]
    Range (min … max):    1.679 s …  2.085 s    10 runs

  Benchmark 2: target/release/xargs.pr -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):      1.782 s ±  0.073 s    [User: 0.487 s, System: 1.310 s]
    Range (min … max):    1.676 s …  1.877 s    10 runs

  Benchmark 3: /usr/bin/xargs -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):      1.620 s ±  0.129 s    [User: 0.447 s, System: 1.178 s]
    Range (min … max):    1.490 s …  1.840 s    10 runs

  Summary
    /usr/bin/xargs -n1 true < /tmp/xargs_input.txt ran
      1.10 ± 0.10 times faster than target/release/xargs.pr -n1 true < /tmp/xargs_input.txt
      1.18 ± 0.12 times faster than target/release/xargs.main -n1 true < /tmp/xargs_input.txt

  Scenario 2 — parallel -P8 -n1 true

  Benchmark 1: target/release/xargs.main -P8 -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):      2.046 s ±  0.107 s    [User: 0.509 s, System: 1.518 s]
    Range (min … max):    1.896 s …  2.178 s    10 runs

  Benchmark 2: target/release/xargs.pr -P8 -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):     532.7 ms ±  71.0 ms    [User: 4003.5 ms, System: 1575.7 ms]
    Range (min … max):   464.0 ms … 672.8 ms    10 runs

  Benchmark 3: /usr/bin/xargs -P8 -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):     314.8 ms ±  29.0 ms    [User: 433.3 ms, System: 1008.6 ms]
    Range (min … max):   286.3 ms … 374.1 ms    10 runs

  Summary
    /usr/bin/xargs -P8 -n1 true < /tmp/xargs_input.txt ran
      1.69 ± 0.27 times faster than target/release/xargs.pr -P8 -n1 true < /tmp/xargs_input.txt
      6.50 ± 0.69 times faster than target/release/xargs.main -P8 -n1 true < /tmp/xargs_input.txt

  Scenario 3 — parallel -P2 -n1 true (spin-lock worst case)

  Benchmark 1: target/release/xargs.pr -P2 -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):      2.254 s ±  0.211 s    [User: 29.493 s, System: 1.750 s]
    Range (min … max):    1.941 s …  2.666 s    10 runs

  Benchmark 2: /usr/bin/xargs -P2 -n1 true < /tmp/xargs_input.txt
    Time (mean ± σ):     904.2 ms ±  39.7 ms    [User: 404.5 ms, System: 1371.9 ms]
    Range (min … max):   851.4 ms … 970.7 ms    10 runs

  Summary
    /usr/bin/xargs -P2 -n1 true < /tmp/xargs_input.txt ran
      2.49 ± 0.26 times faster than target/release/xargs.pr -P2 -n1 true < /tmp/xargs_input.txt

@sylvestre

Copy link
Copy Markdown
Contributor

btw:


echo x | ./target/release/xargs -P0 echo


thread 'main' (216351) panicked at src/xargs/mod.rs:1255:22:
max-procs must be non-zero
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@Pistonight

Copy link
Copy Markdown

Scenario 3 looks a little disappointing - Would be interesting to see how the performance compares against the implementation without the lock and without using multiple threads at all? (Just use one thread to spawn childs up to MAX_PROC and wait for any to finish)

@sylvestre

Copy link
Copy Markdown
Contributor

could you please rebase it? thanks

@asakatida
asakatida force-pushed the push-zlxwkxlkpsls branch from aa1ec44 to fd8d9c1 Compare July 26, 2026 15:59
@codspeed-hq

codspeed-hq Bot commented Jul 26, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 20 untouched benchmarks


Comparing asakatida:push-zlxwkxlkpsls (fd8d9c1) with main (f38dbe5)

Open in CodSpeed

@github-actions

Copy link
Copy Markdown

Commit fd8d9c1 has test result changes:

GNU findutils testsuite:

Test results comparison:
  Current:   TOTAL: 495 / PASSED: 412 / FAILED: 82 / SKIPPED: 1
  Reference: TOTAL: 495 / PASSED: 417 / FAILED: 77 / SKIPPED: 1

Changes from main branch:
  TOTAL: +0
  PASSED: -5
  FAILED: +5

New test failures (5):
  - idef-s26-0
  - s14
  - s14-0
  - s42
  - tests/find/refuse-noop

bfs testsuite:

Test results comparison:
  Current:   TOTAL: 314 / PASSED: 266 / FAILED: 42 / SKIPPED: 6
  Reference: TOTAL: 314 / PASSED: 267 / FAILED: 41 / SKIPPED: 6

Changes from main branch:
  TOTAL: +0
  PASSED: -1
  FAILED: +1

New test failures (2):
  - gnu/files0_from_ok
  - gnu/okdir_path_relative

Test improvements (1):
  + gnu/okdir_path_empty

@sylvestre

Copy link
Copy Markdown
Contributor

@codspeed add benchmarks

@sylvestre

Copy link
Copy Markdown
Contributor

Fails too many tests

New test failures (5):
  - idef-s26-0
  - s14
  - s14-0
  - s42
  - tests/find/refuse-noop

@codspeed-hq

codspeed-hq Bot commented Jul 26, 2026

Copy link
Copy Markdown

@codspeed add benchmarks

Added benchmarks covering the new parallel xargs -P path. Until now the suite in benches/xargs_bench.rs only exercised the serial code path, which is why CodSpeed reported the new parallel logic as untouched.

I added three benchmarks in benches/xargs_bench.rs that drive the rayon-based dispatch in process_input:

  • parallel_p2-n 10 -P 2: low parallelism, i.e. the spin-lock worst case flagged earlier in the review (high contention between two workers).
  • parallel_p8-n 10 -P 8: higher parallelism, exercising the scheduler under more contention.
  • parallel_p8_batched_n-n 50 -P 8: fewer, larger batches, so the balance shifts back toward argument assembly while still going through the parallel bridge.

The parallel benchmarks use a smaller dedicated corpus (400 tokens vs. 4000 for the serial ones): each batch spawns a real true child, so a smaller corpus keeps the spawn count reasonable under the simulation instrument while still producing enough batches to feed the parallel bridge. The existing serial benchmarks are unchanged.

All benchmarks build and run successfully under codspeed run --mode simulation, and the change has been pushed to this PR. Once CI runs, the CodSpeed report should now include the parallel_* benchmarks so the -P path is tracked going forward.

Summary
  • Warming up the performance wizard...
  • Adding xargs -P benchmarks — Added three parallel (-P) benchmarks to benches/xargs_bench.rs covering the new rayon-based dispatch, validated them under CodSpeed simulation, and pushed the change to the PR branch.
  • Handling comment — Comment handling finished successfully

Was this response helpful? React with 👍 or 👎 to help us improve CodSpeedBot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants