diff --git a/.jules/bolt.md b/.jules/bolt.md index 541c0ab51..28b6d5894 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -17,3 +17,7 @@ ## 2025-05-19 - File Discovery Allocations **Learning:** In `discover_importing_files`, `WalkBuilder` results were being converted to `PathBuf` via `.map(|e| e.into_path())` *before* filtering. This caused allocations for every single file in the workspace (including excluded files and directories). **Action:** Filter `ignore::DirEntry` directly using `entry.file_type()` and `entry.path()` before mapping to `PathBuf`. This avoids allocations for non-matching files. + +## 2024-06-06 - Directory Traversal Stat Syscalls +**Learning:** Calling `entry.path().is_file()` on `ignore::DirEntry` triggers an unnecessary `stat` syscall for every entry, becoming a bottleneck during directory traversals. +**Action:** Always use `entry.file_type().is_some_and(|ft| ft.is_file())` instead to reuse metadata from `readdir`. diff --git a/crates/mill-handlers/src/handlers/common/checksums.rs b/crates/mill-handlers/src/handlers/common/checksums.rs index 79a48a327..b39ac7d6d 100644 --- a/crates/mill-handlers/src/handlers/common/checksums.rs +++ b/crates/mill-handlers/src/handlers/common/checksums.rs @@ -79,7 +79,8 @@ pub async fn calculate_checksums_for_directory_rename( .build(); for entry in walker.flatten() { - if entry.path().is_file() { + // Use file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { if let Ok(content) = context.app_state.file_service.read_file(entry.path()).await { file_checksums.insert( entry.path().to_string_lossy().to_string(), diff --git a/crates/mill-handlers/src/handlers/prune_ops.rs b/crates/mill-handlers/src/handlers/prune_ops.rs index 2f9ca3a91..a07afbffc 100644 --- a/crates/mill-handlers/src/handlers/prune_ops.rs +++ b/crates/mill-handlers/src/handlers/prune_ops.rs @@ -665,7 +665,8 @@ impl PrunePlanner { let walker = ignore::WalkBuilder::new(&abs_dir).hidden(false).build(); let files = walker .flatten() - .filter(|entry| entry.path().is_file()) + // Use file_type() to avoid unnecessary stat syscalls + .filter(|entry| entry.file_type().is_some_and(|ft| ft.is_file())) .map(|entry| entry.path().to_path_buf()) .collect(); Ok((files, abs_dir)) diff --git a/crates/mill-handlers/src/handlers/rename_ops/directory_rename.rs b/crates/mill-handlers/src/handlers/rename_ops/directory_rename.rs index 68aead607..f7b4030e6 100644 --- a/crates/mill-handlers/src/handlers/rename_ops/directory_rename.rs +++ b/crates/mill-handlers/src/handlers/rename_ops/directory_rename.rs @@ -287,7 +287,8 @@ impl RenameService { let mut files_to_move = 0; let walker = ignore::WalkBuilder::new(&old_path).hidden(false).build(); for entry in walker.flatten() { - if entry.path().is_file() { + // Use file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { files_to_move += 1; } } diff --git a/crates/mill-services/src/services/filesystem/file_service/rename.rs b/crates/mill-services/src/services/filesystem/file_service/rename.rs index b8b364ffa..e3e4b6536 100644 --- a/crates/mill-services/src/services/filesystem/file_service/rename.rs +++ b/crates/mill-services/src/services/filesystem/file_service/rename.rs @@ -469,7 +469,8 @@ impl FileService { let mut files = Vec::new(); let walker = ignore::WalkBuilder::new(dir).hidden(false).build(); for entry in walker.flatten() { - if entry.path().is_file() { + // Use file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { files.push(entry.path().to_path_buf()); } }