Skip to content

perf (tsort) : avoid reading the whole input into memory and intern strings #9872

Merged
sylvestre merged 3 commits into
uutils:mainfrom
anastygnome:tsort
Jan 2, 2026
Merged

perf (tsort) : avoid reading the whole input into memory and intern strings #9872
sylvestre merged 3 commits into
uutils:mainfrom
anastygnome:tsort

Conversation

@anastygnome

Copy link
Copy Markdown
Contributor

This PR is a WIP to get more performance out of tsort.

Move to usize is complete. Next step is to move to vec instead of hashmap

@codspeed-hq

codspeed-hq Bot commented Dec 26, 2025

Copy link
Copy Markdown

CodSpeed Performance Report

Merging #9872 will degrade performance by 5.29%

Comparing anastygnome:tsort (4420344) with main (84e6f03)

Summary

⚡ 1 improvement
❌ 1 regression
✅ 128 untouched
⏩ 30 skipped1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Benchmarks breakdown

Benchmark BASE HEAD Efficiency
tsort_input_parsing_heavy[5000] 84.2 ms 71.9 ms +17.13%
tsort_linear_chain[1000000] 1.5 s 1.6 s -5.29%

Footnotes

  1. 30 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/misc/tsort. tests/misc/tsort is passing on 'main'. Maybe you have to rebase?

@anastygnome anastygnome force-pushed the tsort branch 3 times, most recently from 81ece9b to 0a56fe6 Compare December 27, 2025 21:48
@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/cp/cp-mv-enotsup-xattr. tests/cp/cp-mv-enotsup-xattr is passing on 'main'. Maybe you have to rebase?
Note: The gnu test tests/csplit/csplit-io-err was skipped on 'main' but is now failing.

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Congrats! The gnu test tests/tail/follow-name is no longer failing!

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Congrats! The gnu test tests/tail/follow-name is no longer failing!

@anastygnome anastygnome force-pushed the tsort branch 2 times, most recently from ddb2361 to b98950c Compare December 27, 2025 23:43
@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/misc/tsort. tests/misc/tsort is passing on 'main'. Maybe you have to rebase?

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/cp/cp-mv-enotsup-xattr. tests/cp/cp-mv-enotsup-xattr is passing on 'main'. Maybe you have to rebase?
Note: The gnu test tests/csplit/csplit-io-err was skipped on 'main' but is now failing.

@sylvestre

Copy link
Copy Markdown
Contributor

any idea why the perf regressed here?

Comment thread Cargo.toml Outdated
Comment thread Cargo.toml Outdated
@anastygnome

Copy link
Copy Markdown
Contributor Author

Oh, @sylvestre , I just added a few changes :)
The perf regression is most likely due to using Usize symbols. U32 actually improves performance.

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/sort/sort-stale-thread-mem. tests/sort/sort-stale-thread-mem is passing on 'main'. Maybe you have to rebase?

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Congrats! The gnu test tests/tail/assert is no longer failing!

@anastygnome anastygnome marked this pull request as ready for review December 28, 2025 13:24
@anastygnome

Copy link
Copy Markdown
Contributor Author

@sylvestre I believe the regression noticed are due to cache misses because the metadata that goes with the token is just slightly bigger than usize.

Here's another benchmark that maybe of interest:

graph.tsort, 1 Gb random graph 
Massif output: 
Gnu tsort: 

    MB (Memory)
248.6^                                                                       #
     |                                                          ::::::::::@::#
     |                                        ::::::::::::::::::: ::: ::::@: #
     |                               :::::@:::: ::::: : ::::: ::: ::: ::::@: #
     |                            @@:: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |                        ::::@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |                     @@::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |                   ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |               ::::::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |              :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |            :::::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |         :::: :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |       :::: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |      :: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |    :::: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |    : :: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |   :: :: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     |   :: :: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     | @@:: :: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
     | @ :: :: :: : :::: ::@ ::: :@ :: : :@:: : ::::: : ::::: ::: ::: ::::@: #
   0 +----------------------------------------------------------------------->Gi (Giga Instructions) 
     0                                                                   23.47

Number of snapshots: 56
 Detailed snapshots: [1, 2, 16, 21, 26, 52, 55 (peak)]
 
 
 
uutils tsort 

     MB (Memory)
200.0^                                                                    #   
     |                                @::::@:::::::::::@::::@::::@::::@:::#:  
     |              :::::::::::@::::::@::::@::: :::::: @::::@::::@::::@:::#:  
     |           ::::::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:  
     |         :::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:: 
     |        ::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:: 
     |      @:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |     :@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |     :@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |     :@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |     :@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |     :@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |   @::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |  :@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |  :@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |  :@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     |  :@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     | ::@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     | ::@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
     | ::@::@:::::: :::::::::::@:::: :@::::@::: :::::: @::::@::::@::::@:::#:::
   0 +----------------------------------------------------------------------->Gi (Giga Instructions) 
     0                                                                   35.83

Number of snapshots: 98
 Detailed snapshots: [3, 6, 24, 31, 37, 50, 51, 52, 62, 72, 82, 91 (peak)]

Hyperfine : 
 
Benchmark 1: target/release/coreutils tsort /home/bench_vm/graph.tsort
  Time (mean ± σ):      6.044 s ±  0.071 s    [User: 5.602 s, System: 0.394 s]
  Range (min … max):    5.974 s …  6.168 s    10 runs
 
Benchmark 2: tsort /home/bench_vm/graph.tsort 
  Time (mean ± σ):      7.115 s ±  0.147 s    [User: 6.738 s, System: 0.328 s]
  Range (min … max):    6.910 s …  7.390 s    10 runs
`
Summary: 'target/release/coreutils tsort /home/bench_vm/graph.tsort'
ran 1.18 ± 0.03 times faster than 'tsort /home/bench_vm/graph.tsort'
 `

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Congrats! The gnu test tests/tail/assert is no longer failing!

@github-actions

Copy link
Copy Markdown

GNU testsuite comparison:

Congrats! The gnu test tests/tty/tty-eof is no longer failing!

@github-actions

github-actions Bot commented Jan 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

GNU test failed: tests/shuf/shuf-reservoir. tests/shuf/shuf-reservoir is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/sort/sort-stale-thread-mem. tests/sort/sort-stale-thread-mem is passing on 'main'. Maybe you have to rebase?
Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

@anastygnome anastygnome force-pushed the tsort branch 6 times, most recently from 756b865 to b9604fe Compare January 1, 2026 18:16
@github-actions

github-actions Bot commented Jan 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

@anastygnome anastygnome force-pushed the tsort branch 2 times, most recently from 8ac2d2a to 51b542f Compare January 1, 2026 21:07
@sylvestre

Copy link
Copy Markdown
Contributor

windows fails with:


error[E0308]: mismatched types
  --> src\uu\tsort\src\tsort.rs:80:24
   |
80 |             return Err(TsortError::IsDir(input.to_string_lossy().to_string()));
   |                    --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `UIoError`, found `TsortError`
   |                    |
   |                    arguments to this enum variant are incorrect
   |
help: the type constructed contains `TsortError` due to the type of the argument passed
  --> src\uu\tsort\src\tsort.rs:80:20
   |
80 |             return Err(TsortError::IsDir(input.to_string_lossy().to_string()));
   |                    ^^^^------------------------------------------------------^
   |                        |
   |                        this argument influences the type of `Err`
note: tuple variant defined here
  --> /rustc/8d670b93d40737e1b320fd892c6f169ffa35e49e/library\core\src\result.rs:566:4

@anastygnome anastygnome force-pushed the tsort branch 2 times, most recently from 3cd87f8 to a8a1860 Compare January 1, 2026 21:12
@anastygnome

Copy link
Copy Markdown
Contributor Author

@sylvestre fixed, the rest of the work will be put in another PR.

@github-actions

github-actions Bot commented Jan 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

1 similar comment
@github-actions

github-actions Bot commented Jan 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

@github-actions

github-actions Bot commented Jan 1, 2026

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/timeout/timeout (fails in this run but passes in the 'main' branch)

@anastygnome

Copy link
Copy Markdown
Contributor Author

@sylvestre done :)

@anastygnome

Copy link
Copy Markdown
Contributor Author

The performance regression is inevitable here if we want to support the same input range (2^64 -1 unique input tokens). There's also a slight buffering cost at the given bench graph size. We are still way more memory efficient, so I'd still consider it a net positive.

@sylvestre sylvestre merged commit 9086f43 into uutils:main Jan 2, 2026
127 of 129 checks passed
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