Skip to content

perf: optimize version script from rustc - #1355

Merged
davidlattimore merged 17 commits into
wild-linker:mainfrom
karolzwolak:version-script-specialization
Dec 6, 2025
Merged

davidlattimore merged 17 commits into
wild-linker:mainfrom
karolzwolak:version-script-specialization

Conversation

@karolzwolak

Copy link
Copy Markdown
Contributor

Fixes #1014.

@karolzwolak

karolzwolak commented Dec 3, 2025

Copy link
Copy Markdown
Contributor Author

Actually this doesn't speed the things up that much.
Benching libbevy_dylib (560k line version script):

Benchmark 1: ./run-with /home/karolz/dev/contrib/wild/target/release/wild --no-fork
  Time (mean ± σ):      1.068 s ±  0.017 s    [User: 7.228 s, System: 1.607 s]
  Range (min … max):    1.042 s …  1.092 s    10 runs
 
Benchmark 2: ./run-with /home/karolz/dev/contrib/wild/target/release/wild-before --no-fork
  Time (mean ± σ):      1.088 s ±  0.032 s    [User: 7.129 s, System: 1.636 s]
  Range (min … max):    1.054 s …  1.148 s    10 runs
 
Summary
  ./run-with /home/karolz/dev/contrib/wild/target/release/wild --no-fork ran
    1.02 ± 0.03 times faster than ./run-with /home/karolz/dev/contrib/wild/target/release/wild-before --no-fork

Did I miss something performance wise or just this doesn't provide any substantial speedup?

EDIT: this actually is slower for compiling smaller libs like librustc_driver (although 300ms is probably too low to be a representative test)

Benchmark 1: ./run-with /home/karolz/dev/contrib/wild/target/release/wild --no-fork
  Time (mean ± σ):     292.5 ms ±   7.9 ms    [User: 1992.1 ms, System: 536.3 ms]
  Range (min … max):   280.4 ms … 306.7 ms    10 runs
 
Benchmark 2: ./run-with /home/karolz/dev/contrib/wild/target/release/wild-before --no-fork
  Time (mean ± σ):     286.2 ms ±   8.9 ms    [User: 1968.3 ms, System: 536.9 ms]
  Range (min … max):   277.6 ms … 305.7 ms    10 runs
 
Summary
  ./run-with /home/karolz/dev/contrib/wild/target/release/wild-before --no-fork ran
    1.02 ± 0.04 times faster than ./run-with /home/karolz/dev/contrib/wild/target/release/wild --no-fork

@karolzwolak
karolzwolak marked this pull request as draft December 3, 2025 21:23

@davidlattimore davidlattimore left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's unfortunate that there wasn't a speedup for librustc-driver. Although, I see a 9.9% speedup for the bevy dylib, which is still definitely something! I just looked at some profiles and it seems that analyze_glob_pattern is taking quite a bit of time.

I wonder if the changes you've made here would be a step towards moving the call to analyze_glob_pattern to run in parallel. e.g. we parse the file from a single thread, but don't identify the kinds of patterns, then from multiple threads, we classify matchers. I wouldn't suggest doing that in this PR, but we could explore doing it afterwards. If you don't want to, then I might.

Comment thread libwild/src/value_flags.rs Outdated
Comment thread libwild/src/version_script.rs Outdated
Comment thread libwild/src/symbol_db.rs Outdated
Comment thread libwild/src/symbol_db.rs
Comment thread libwild/src/symbol_db.rs Outdated
Comment thread libwild/src/symbol_db.rs Outdated
Comment thread libwild/src/symbol_db.rs Outdated
@mati865

mati865 commented Dec 4, 2025

Copy link
Copy Markdown
Member

If you don't want to regress performance in the short term, we could merge it into a new branch, do the optimizations, and then rebase main onto that branch (without squashing).
Although I'm not sure if it's worth risking conflicts.

@davidlattimore

Copy link
Copy Markdown
Member

The performance regression for librustc-driver was pretty small, about 1% and went away completely (at least for me) once the extra phase was run in parallel with dropping a bunch of Vecs.

@karolzwolak

Copy link
Copy Markdown
Contributor Author

I wonder if the changes you've made here would be a step towards moving the call to analyze_glob_pattern to run in parallel. e.g. we parse the file from a single thread, but don't identify the kinds of patterns, then from multiple threads, we classify matchers. I wouldn't suggest doing that in this PR, but we could explore doing it afterwards. If you don't want to, then I might.

Sure, I will look into that when this is merged.

Comment thread libwild/src/symbol_db.rs
let mut per_symbol_flags = PerSymbolFlags::new(per_symbol_flags);

verbose_timing_phase!("Apply linker scripts");
rayon::join(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We should probably benchmark that this doesn't slow things down when the version script isn't coming from rustc. I can do that tomorrow.

Comment thread libwild/src/symbol_db.rs Outdated
for script in linker_scripts {
index.apply_linker_script(script);
}
{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Maybe we should move this scope out of the rayon task and only do the join if the version script is coming from rustc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's not clear to me which is better, this way we are always dropping in parallel but not sure that's actually worth it when the script isn't from rustc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

rayon::join is pretty lightweight. It doesn't even heap-allocate the closures and we're not in a loop here, so this is one extra join during the entire linker execution.

@karolzwolak
karolzwolak marked this pull request as ready for review December 5, 2025 20:41
@karolzwolak

Copy link
Copy Markdown
Contributor Author

That's unfortunate that there wasn't a speedup for librustc-driver. Although, I see a 9.9% speedup for the bevy dylib, which is still definitely something! I just looked at some profiles and it seems that analyze_glob_pattern is taking quite a bit of time.

This is great! The speedup is negligible on my system.

./run-with /home/karolz/dev/contrib/wild/target/release/wild --no-fork ran
    1.01 ± 0.02 times faster than ./run-with /home/karolz/dev/contrib/wild/target/release/wild-before --no-fork

Could you do the benchmark again on the latest revision?

@davidlattimore

Copy link
Copy Markdown
Member

Sure! Still looks good to me:

B=bevy F="--threads=32 --strip-debug" R=/home/david/save/$B/run-with OUT=/run/user/1000/ttt bench poop -d 30000  "$R $HOME/wild-builds/2025-12-06 $F" "$R target/release/wild $F"
  Temperature: 54.8 C
Benchmark 1 (87 runs): /home/david/save/bevy/run-with /home/d/wild-builds/2025-12-06 --threads=32 --strip-debug
  measurement          mean ± σ            min … max           outliers         delta
  wall_time           345ms ± 7.04ms     332ms …  366ms          2 ( 2%)        0%
  peak_rss           7.29MB ± 24.2KB    7.24MB … 7.30MB         16 (18%)        0%
  cpu_cycles         10.5G  ±  136M     10.1G  … 10.7G           0 ( 0%)        0%
  instructions       10.2G  ± 17.2M     10.2G  … 10.2G           0 ( 0%)        0%
  cache_references    237M  ± 1.56M      233M  …  243M           2 ( 2%)        0%
  cache_misses       61.2M  ±  350K     60.3M  … 62.5M           3 ( 3%)        0%
  branch_misses      17.2M  ± 61.0K     17.1M  … 17.4M           0 ( 0%)        0%
Benchmark 2 (99 runs): /home/david/save/bevy/run-with target/release/wild --threads=32 --strip-debug
  measurement          mean ± σ            min … max           outliers         delta
  wall_time           305ms ± 6.35ms     296ms …  326ms          5 ( 5%)        ⚡- 11.4% ±  0.6%
  peak_rss           7.29MB ± 21.4KB    7.20MB … 7.34MB         19 (19%)          +  0.1% ±  0.1%
  cpu_cycles         10.6G  ±  111M     10.2G  … 10.7G           4 ( 4%)          +  0.5% ±  0.3%
  instructions       10.1G  ± 16.8M     10.1G  … 10.2G           1 ( 1%)          -  0.4% ±  0.0%
  cache_references    239M  ± 1.78M      236M  …  246M           4 ( 4%)          +  0.9% ±  0.2%
  cache_misses       61.8M  ±  342K     61.3M  … 63.2M           1 ( 1%)          +  1.0% ±  0.2%
  branch_misses      17.5M  ± 58.8K     17.4M  … 17.7M           5 ( 5%)        💩+  1.6% ±  0.1%
  Temperature: 61.4 C

No significant change on librustc-driver.

Comment thread libwild/src/symbol_db.rs Outdated
@davidlattimore
davidlattimore merged commit 653fdde into wild-linker:main Dec 6, 2025
20 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.

Avoid building a hashmap during version script parsing

3 participants