diff --git a/.jules/bolt.md b/.jules/bolt.md index 541c0ab51..a30549526 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. + +## 2025-06-05 - Avoid extra stat calls during directory traversal +**Learning:** Calling `entry.path().is_file()` during directory traversal with crates like `ignore` performs an unnecessary `stat` syscall. Using `entry.file_type().is_some_and(|ft| ft.is_file())` utilizes metadata already retrieved during `readdir`, which is significantly faster. +**Action:** When filtering files during traversal, always use the metadata provided by the directory entry (`file_type()`) rather than querying the filesystem again using the path. diff --git a/crates/mill-ast/src/import_updater/file_scanner.rs b/crates/mill-ast/src/import_updater/file_scanner.rs index 99edef07e..8b1735b0f 100644 --- a/crates/mill-ast/src/import_updater/file_scanner.rs +++ b/crates/mill-ast/src/import_updater/file_scanner.rs @@ -359,7 +359,8 @@ pub async fn find_project_files( for entry in walker.flatten() { let path = entry.path(); - if path.is_file() { + // ⚡ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { if let Some(ext) = path.extension() { let ext_str = ext.to_str().unwrap_or(""); // Check if any plugin handles this extension diff --git a/crates/mill-handlers/src/handlers/common/checksums.rs b/crates/mill-handlers/src/handlers/common/checksums.rs index 79a48a327..b30b9d5f3 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() { + // ⚡ Bolt: Use entry.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/lsp_adapter.rs b/crates/mill-handlers/src/handlers/lsp_adapter.rs index b40923003..7ec6a71e4 100644 --- a/crates/mill-handlers/src/handlers/lsp_adapter.rs +++ b/crates/mill-handlers/src/handlers/lsp_adapter.rs @@ -773,7 +773,8 @@ impl LspImportFinder for DirectLspAdapter { let mut files_in_dir = Vec::new(); for entry in walker.flatten() { let path = entry.path(); - if path.is_file() { + // ⚡ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { if let Some(ext) = path.extension().and_then(|e| e.to_str()) { if self.extensions.contains(&ext.to_string()) { files_in_dir.push(path.to_path_buf()); diff --git a/crates/mill-handlers/src/handlers/prune_ops.rs b/crates/mill-handlers/src/handlers/prune_ops.rs index 2f9ca3a91..05cb1a4b0 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()) + // ⚡ Bolt: Use entry.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..3b6ab8697 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() { + // ⚡ Bolt: Use entry.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..3f319b01f 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() { + // ⚡ Bolt: Use entry.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()); } } diff --git a/crates/mill-services/src/services/move_service/planner.rs b/crates/mill-services/src/services/move_service/planner.rs index 9616a3d38..d5392eb0a 100644 --- a/crates/mill-services/src/services/move_service/planner.rs +++ b/crates/mill-services/src/services/move_service/planner.rs @@ -143,7 +143,10 @@ async fn append_svelte_import_edits( for entry in walker.flatten() { let path = entry.path(); - if path.is_file() && path.extension().and_then(|e| e.to_str()) == Some("svelte") { + // ⚡ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) + && path.extension().and_then(|e| e.to_str()) == Some("svelte") + { files.push(path.to_path_buf()); } } @@ -420,7 +423,8 @@ async fn plan_documentation_and_config_edits( for entry in moved_walker.flatten() { let path = entry.path(); - if path.is_file() { + // ⚡ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { if let Ok(relative) = path.strip_prefix(old_path) { moved_files.push((path.to_path_buf(), new_path.join(relative))); } @@ -455,7 +459,8 @@ async fn plan_documentation_and_config_edits( for entry in walker.flatten() { let path = entry.path(); - if path.is_file() { + // ⚡ Bolt: Use entry.file_type() to avoid unnecessary stat syscalls + if entry.file_type().is_some_and(|ft| ft.is_file()) { if let Some(ext) = path.extension().and_then(|e| e.to_str()) { if file_extensions.contains(ext) { files_by_extension