Skip to content

uucore: make Range::merge linear instead of quadratic - #14359

Open
haydonryan wants to merge 1 commit into
uutils:mainfrom
haydonryan:core-hr
Open

uucore: make Range::merge linear instead of quadratic#14359
haydonryan wants to merge 1 commit into
uutils:mainfrom
haydonryan:core-hr

Conversation

@haydonryan

Copy link
Copy Markdown
Contributor

Deepseek found another optimization.

This one converts the loop from O(n^2) to O(n) for ranges.

I ran a few options for this PR, including one that was smaller in size by 96 bytes, but this is faster overall.

LLM generated below here:

Range::merge (src/uucore/src/lib/features/ranges.rs) merged overlapping
ranges with ranges.remove(j) inside a while loop. Because Vec::remove
shifts the tail, heavily-overlapping range lists were O(n²). Only caller is
cut (via Range::from_list).

Replaced with a single Vec::dedup_by pass that extends the bucket's high
on overlap. Output is unchanged — still sorted, disjoint, and adjacent ranges
are not merged. The only subtlety is that dedup_by(a, b) passes a = new
element, b = bucket, so the closure must extend b.high (the kept element),
not a.high (the dropped one).

Measurement (same harness, release)

Both versions measured in one harness, same input, realistic overlapping ranges:

ranges before (O(n²)) after (dedup_by) speedup
10 ~0 µs ~0 µs same
100 2 µs 0.3 µs 8x
1,000 94 µs 2 µs 47x
30,000 123 ms 226 µs 543x

The O(n) pass is dramatically faster at 30k+ ranges, but real cut invocations
use a handful of fields (argv caps ~30k, and a typical -f list is 1-100), so
the merge is sub-microsecond, once-at-startup work there. The value of this
change is not a user-visible speedup: it removes the O(n²) blowup (a large
overlapping field list would otherwise hang cut) and shrinks the binary by
256 B. dedup_by was also the smallest/fastest of the four O(n) variants A/B'd
(baseline, extra-Vec, in-place swap, dedup_by).

dedup_by was also the fastest of the four O(n) variants A/B'd (baseline,
extra-Vec, in-place swap, dedup_by) in every all-overlap case; the in
place variants are not measurably faster and compile to a larger binary.

Verification

  • cargo fmt clean, cargo clippy --release --bin coreutils clean (0 warnings)
  • uucore lib tests: 75/75 pass
  • cut integration tests: 103/103 pass

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skipping an intermittent issue tests/misc/io-errors (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/retry (passes in this run but fails in the 'main' branch)
Skipping an intermittent issue tests/tail/tail-n0f (passes in this run but fails in the 'main' branch)

@sylvestre

Copy link
Copy Markdown
Contributor

any reason why codspeed don't detect the improvements?
also, please provide a hyperfine example with before this change, after and against GNU

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.

2 participants