Skip to content

Commit 6f00392

Browse files
committed
more efficient way to track subdirs
1 parent 8443c49 commit 6f00392

6 files changed

Lines changed: 230 additions & 140 deletions

File tree

crates/fff-core/src/file_picker.rs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -729,19 +729,6 @@ impl FilePicker {
729729
&self.sync_data.dirs
730730
}
731731

732-
/// Whether the absolute `path` is a directory known to the index
733-
/// (including dirs that were empty at scan time).
734-
pub(crate) fn has_indexed_dir(&self, path: &Path) -> bool {
735-
let Ok(rel) = path.strip_prefix(&self.base_path) else {
736-
return false;
737-
};
738-
let mut rel = crate::path_utils::to_canonical_slashes(&rel.to_string_lossy()).into_owned();
739-
if !rel.is_empty() && !rel.ends_with('/') {
740-
rel.push('/');
741-
}
742-
self.sync_data.find_dir_index(&rel).is_some()
743-
}
744-
745732
/// Actual heap bytes used: (chunked_path_store, 0, 0).
746733
/// The second element is 0 because leaked overflow stores aren't tracked.
747734
pub fn arena_bytes(&self) -> (usize, usize, usize) {
@@ -2047,9 +2034,7 @@ impl FileSync {
20472034
)?;
20482035
let ignore_rules = ignore_rules.map(Arc::new);
20492036

2050-
// Sort files by (dir_part, filename) — grouping them into contiguous
2051-
// per-dir runs — and the walked dirs by the same '/'-terminated form,
2052-
// so the build pass below can merge both in a single sweep.
2037+
// group walked dirs and files with a dir part to the same order
20532038
BACKGROUND_THREAD_POOL.install(|| {
20542039
rayon::join(
20552040
|| {
@@ -2200,35 +2185,45 @@ fn populates_dirs_files_chunked_storage<'a>(
22002185
let mut dirs: Vec<DirItem> = Vec::with_capacity(walked_dirs.len() + 1);
22012186
let mut dir_iter = walked_dirs.iter().peekable();
22022187

2188+
// Root-level files sort first and their "" parent is never a walker dir.
2189+
if pairs
2190+
.first()
2191+
.is_some_and(|(f, _)| f.path.filename_offset == 0)
2192+
{
2193+
push_dir_item(&mut dirs, chunk_storage, "");
2194+
}
2195+
2196+
// Detects contiguous same-dir runs (pairs are sorted by dir) so the
2197+
// merge below runs once per directory, not once per file.
22032198
let mut prev_dir: &'a str = "";
2204-
let mut prev_dir_valid = false;
22052199
let mut current_dir_idx: u32 = 0;
22062200

22072201
for (file, rel) in pairs.iter_mut() {
22082202
let rel: &'a str = rel;
22092203
let dir_part: &'a str = &rel[..file.path.filename_offset as usize];
22102204

2211-
if !prev_dir_valid || prev_dir != dir_part {
2205+
if prev_dir != dir_part {
22122206
// Flush walked dirs up to and including this file's parent,
22132207
// keeping the table sorted for the find_dir_index binary search.
2214-
let mut matched = false;
22152208
while let Some(dir) = dir_iter.peek()
2216-
&& dir.as_str() <= dir_part
2209+
&& dir.as_str() < dir_part
22172210
{
2218-
matched = dir.as_str() == dir_part;
22192211
push_dir_item(&mut dirs, chunk_storage, dir);
22202212
dir_iter.next();
22212213
}
22222214

2223-
// Root-level files ("" dir part) and parents the walker reported
2224-
// with a non-dir kind (e.g. followed symlinks) aren't in the list.
2225-
if !matched {
2226-
push_dir_item(&mut dirs, chunk_storage, dir_part);
2215+
match dir_iter.peek() {
2216+
Some(dir) if dir.as_str() == dir_part => {
2217+
push_dir_item(&mut dirs, chunk_storage, dir);
2218+
dir_iter.next();
2219+
}
2220+
// Parents the walker reported with a non-dir kind
2221+
// (e.g. followed symlinks) aren't in the list.
2222+
_ => push_dir_item(&mut dirs, chunk_storage, dir_part),
22272223
}
2228-
current_dir_idx = (dirs.len() - 1) as u32;
22292224

2225+
current_dir_idx = (dirs.len() - 1) as u32;
22302226
prev_dir = dir_part;
2231-
prev_dir_valid = true;
22322227
}
22332228

22342229
file.path = chunk_storage.add_file_immediate(rel, file.path.filename_offset);

crates/fff-core/src/walk/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ pub(crate) use ripgrep::walk_collect_files;
2020

2121
pub(crate) struct WalkOutput {
2222
pub(crate) pairs: Vec<(FileItem, String)>,
23-
/// Every non-ignored directory the walk visited, as '/'-canonical
24-
/// '/'-terminated paths relative to the base (base itself excluded).
25-
/// This is the source of the picker's dir table; unsorted (parallel walk).
23+
/// Every non-ignored directory the walk visited, relative, ending with /
2624
pub(crate) dirs: Vec<String>,
2725
pub(crate) ignore_rules: Option<WalkIgnoreRules>,
2826
}

crates/fff-core/src/walk/ripgrep.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ pub(crate) fn walk_collect_files(
3434

3535
let walker = walk_builder.build_parallel();
3636

37-
let pairs = parking_lot::Mutex::new(Vec::<(FileItem, String)>::new());
38-
let dirs = parking_lot::Mutex::new(Vec::<String>::new());
37+
// Single lock for both collections: every entry is either a file or a
38+
// dir, so this keeps one mutex acquisition per entry.
39+
let collected =
40+
parking_lot::Mutex::new((Vec::<(FileItem, String)>::new(), Vec::<String>::new()));
3941
walker.run(|| {
40-
let pairs = &pairs;
41-
let dirs = &dirs;
42+
let collected = &collected;
4243
let counter = Arc::clone(synced_files_count);
4344
let base_path = base_path.to_path_buf();
4445

@@ -60,7 +61,7 @@ pub(crate) fn walk_collect_files(
6061
let (file_item, rel_path) =
6162
FileItem::new_from_walk(path, &base_path, None, metadata.as_ref());
6263

63-
pairs.lock().push((file_item, rel_path));
64+
collected.lock().0.push((file_item, rel_path));
6465
counter.fetch_add(1, Ordering::Relaxed);
6566
} else if entry.depth() > 0 && entry.file_type().is_some_and(|ft| ft.is_dir()) {
6667
let path = entry.path();
@@ -70,16 +71,17 @@ pub(crate) fn walk_collect_files(
7071
let mut rel = crate::path_utils::to_canonical_slashes(&rel.to_string_lossy())
7172
.into_owned();
7273
rel.push('/');
73-
dirs.lock().push(rel);
74+
collected.lock().1.push(rel);
7475
}
7576
}
7677
ignore::WalkState::Continue
7778
})
7879
});
7980

81+
let (pairs, dirs) = collected.into_inner();
8082
Ok(WalkOutput {
81-
pairs: pairs.into_inner(),
82-
dirs: dirs.into_inner(),
83+
pairs,
84+
dirs,
8385
ignore_rules: None,
8486
})
8587
}

crates/fff-core/src/walk/zlob.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,19 @@ pub(crate) fn walk_collect_files(
4646
tracing::warn!(?e, "zlob extra_ignore rejected; walking without it");
4747
}
4848

49-
let pairs = Mutex::new(Vec::new());
50-
let dirs = Mutex::new(Vec::new());
49+
// Single lock for both collections: every entry is either a file or a
50+
// dir, so this keeps one mutex acquisition per entry.
51+
let collected = Mutex::new((Vec::new(), Vec::new()));
5152

5253
let outcome = match builder.run(|entry| {
5354
if !entry.is_file() {
54-
// `.git` and ignored dirs are pruned by zlob itself (GITIGNORE
55-
// flag); empty rel path is the walk root, covered separately.
55+
// unlike ripgrep walker zlob doesnt show .git files
5656
if entry.is_dir() {
5757
let rel_bytes = entry.relative_path_bytes();
5858
if !rel_bytes.is_empty() {
5959
let mut rel = String::from_utf8_lossy(rel_bytes).into_owned();
6060
rel.push('/');
61-
dirs.lock().push(rel);
61+
collected.lock().1.push(rel);
6262
}
6363
}
6464

@@ -84,9 +84,9 @@ pub(crate) fn walk_collect_files(
8484
let rel_str = String::from_utf8_lossy(rel_bytes).into_owned();
8585
let item = FileItem::new_raw(basename_offset, size, modified, None, is_binary);
8686

87-
let mut guard = pairs.lock();
88-
guard.push((item, rel_str));
89-
let n = guard.len();
87+
let mut guard = collected.lock();
88+
guard.0.push((item, rel_str));
89+
let n = guard.0.len();
9090
drop(guard);
9191

9292
if n % PROGRESS_STEP == 0 {
@@ -104,7 +104,7 @@ pub(crate) fn walk_collect_files(
104104
}
105105
};
106106

107-
let pairs = pairs.into_inner();
107+
let (pairs, dirs) = collected.into_inner();
108108
// Always report the exact final total regardless of the last step.
109109
synced_files_count.store(pairs.len(), Ordering::Relaxed);
110110

@@ -117,7 +117,7 @@ pub(crate) fn walk_collect_files(
117117

118118
Ok(WalkOutput {
119119
pairs,
120-
dirs: dirs.into_inner(),
120+
dirs,
121121
ignore_rules,
122122
})
123123
}

0 commit comments

Comments
 (0)