Skip to content

Commit 39b6253

Browse files
committed
Auto merge of #70837 - nnethercote:speed-up-find_library_crate, r=petrochenkov
Speed up path searching with `find_library_crate`. By doing prefix and suffix checking on a `String` copy of each relevant `PathBuf`, rather than the `PathBuf` itself.
2 parents 209b2be + a932616 commit 39b6253

File tree

3 files changed

+45
-21
lines changed

3 files changed

+45
-21
lines changed

src/librustc_metadata/locator.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,8 @@ impl<'a> CrateLocator<'a> {
543543
// of the crate id (path/name/id).
544544
//
545545
// The goal of this step is to look at as little metadata as possible.
546-
self.filesearch.search(|path, kind| {
547-
let file = match path.file_name().and_then(|s| s.to_str()) {
546+
self.filesearch.search(|spf, kind| {
547+
let file = match &spf.file_name_str {
548548
None => return FileDoesntMatch,
549549
Some(file) => file,
550550
};
@@ -556,20 +556,18 @@ impl<'a> CrateLocator<'a> {
556556
(&file[(dylib_prefix.len())..(file.len() - dypair.1.len())], CrateFlavor::Dylib)
557557
} else {
558558
if file.starts_with(&staticlib_prefix) && file.ends_with(&staticpair.1) {
559-
staticlibs.push(CrateMismatch {
560-
path: path.to_path_buf(),
561-
got: "static".to_string(),
562-
});
559+
staticlibs
560+
.push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() });
563561
}
564562
return FileDoesntMatch;
565563
};
566564

567-
info!("lib candidate: {}", path.display());
565+
info!("lib candidate: {}", spf.path.display());
568566

569567
let hash_str = hash.to_string();
570568
let slot = candidates.entry(hash_str).or_default();
571569
let (ref mut rlibs, ref mut rmetas, ref mut dylibs) = *slot;
572-
fs::canonicalize(path)
570+
fs::canonicalize(&spf.path)
573571
.map(|p| {
574572
if seen_paths.contains(&p) {
575573
return FileDoesntMatch;

src/librustc_session/filesearch.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::env;
77
use std::fs;
88
use std::path::{Path, PathBuf};
99

10-
use crate::search_paths::{PathKind, SearchPath};
10+
use crate::search_paths::{PathKind, SearchPath, SearchPathFile};
1111
use log::debug;
1212
use rustc_fs_util::fix_windows_verbatim_for_gcc;
1313

@@ -43,28 +43,28 @@ impl<'a> FileSearch<'a> {
4343

4444
pub fn search<F>(&self, mut pick: F)
4545
where
46-
F: FnMut(&Path, PathKind) -> FileMatch,
46+
F: FnMut(&SearchPathFile, PathKind) -> FileMatch,
4747
{
4848
for search_path in self.search_paths() {
4949
debug!("searching {}", search_path.dir.display());
50-
fn is_rlib(p: &Path) -> bool {
51-
p.extension() == Some("rlib".as_ref())
50+
fn is_rlib(spf: &SearchPathFile) -> bool {
51+
if let Some(f) = &spf.file_name_str { f.ends_with(".rlib") } else { false }
5252
}
5353
// Reading metadata out of rlibs is faster, and if we find both
5454
// an rlib and a dylib we only read one of the files of
5555
// metadata, so in the name of speed, bring all rlib files to
5656
// the front of the search list.
57-
let files1 = search_path.files.iter().filter(|p| is_rlib(p));
58-
let files2 = search_path.files.iter().filter(|p| !is_rlib(p));
59-
for path in files1.chain(files2) {
60-
debug!("testing {}", path.display());
61-
let maybe_picked = pick(path, search_path.kind);
57+
let files1 = search_path.files.iter().filter(|spf| is_rlib(&spf));
58+
let files2 = search_path.files.iter().filter(|spf| !is_rlib(&spf));
59+
for spf in files1.chain(files2) {
60+
debug!("testing {}", spf.path.display());
61+
let maybe_picked = pick(spf, search_path.kind);
6262
match maybe_picked {
6363
FileMatches => {
64-
debug!("picked {}", path.display());
64+
debug!("picked {}", spf.path.display());
6565
}
6666
FileDoesntMatch => {
67-
debug!("rejected {}", path.display());
67+
debug!("rejected {}", spf.path.display());
6868
}
6969
}
7070
}

src/librustc_session/search_paths.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,31 @@ use std::path::{Path, PathBuf};
66
pub struct SearchPath {
77
pub kind: PathKind,
88
pub dir: PathBuf,
9-
pub files: Vec<PathBuf>,
9+
pub files: Vec<SearchPathFile>,
10+
}
11+
12+
// The obvious implementation of `SearchPath::files` is a `Vec<PathBuf>`. But
13+
// it is searched repeatedly by `find_library_crate`, and the searches involve
14+
// checking the prefix and suffix of the filename of each `PathBuf`. This is
15+
// doable, but very slow, because it involves calls to `file_name` and
16+
// `extension` that are themselves slow.
17+
//
18+
// This type augments the `PathBuf` with an `Option<String>` containing the
19+
// `PathBuf`'s filename. The prefix and suffix checking is much faster on the
20+
// `Option<String>` than the `PathBuf`. (It's an `Option` because
21+
// `Path::file_name` can fail; if that happens then all subsequent checking
22+
// will also fail, which is fine.)
23+
#[derive(Clone, Debug)]
24+
pub struct SearchPathFile {
25+
pub path: PathBuf,
26+
pub file_name_str: Option<String>,
27+
}
28+
29+
impl SearchPathFile {
30+
fn new(path: PathBuf) -> SearchPathFile {
31+
let file_name_str = path.file_name().and_then(|f| f.to_str()).map(|s| s.to_string());
32+
SearchPathFile { path, file_name_str }
33+
}
1034
}
1135

1236
#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, RustcEncodable, RustcDecodable)]
@@ -60,7 +84,9 @@ impl SearchPath {
6084
fn new(kind: PathKind, dir: PathBuf) -> Self {
6185
// Get the files within the directory.
6286
let files = match std::fs::read_dir(&dir) {
63-
Ok(files) => files.filter_map(|p| p.ok().map(|s| s.path())).collect::<Vec<_>>(),
87+
Ok(files) => files
88+
.filter_map(|e| e.ok().map(|e| SearchPathFile::new(e.path())))
89+
.collect::<Vec<_>>(),
6490
Err(..) => vec![],
6591
};
6692

0 commit comments

Comments
 (0)