Skip to content

Commit f639c68

Browse files
committed
perf(fs): use std::fs instead of tokio::fs in FileSystemOs
tokio::fs offloads every syscall to the blocking threadpool (semaphore + task harness + park/unpark). Resolution issues a large number of tiny stat/read calls whose kernel time is dwarfed by that per-call scheduling overhead (~14% of total instructions in the resolver callgrind bench). std::fs runs them inline, matching the existing wasm impl and the upstream synchronous resolver. The async FileSystem trait is unchanged; callers wanting resolution off the reactor should spawn_blocking the whole resolve, not each syscall.
1 parent 02dbd01 commit f639c68

1 file changed

Lines changed: 18 additions & 14 deletions

File tree

src/file_system.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,19 +147,27 @@ impl FileSystemOs {
147147
#[cfg(not(target_arch = "wasm32"))]
148148
#[async_trait::async_trait]
149149
impl FileSystem for FileSystemOs {
150+
// The FS trait is async, but resolution issues a large number of tiny
151+
// `stat`/`read` syscalls whose kernel time is dwarfed by the per-call cost of
152+
// tokio's `spawn_blocking` machinery (blocking-pool semaphore + task harness +
153+
// park/unpark). Calling `std::fs` directly removes that scheduling overhead
154+
// (~14% of total instructions in the resolver bench) and matches both the
155+
// wasm impl below and the upstream synchronous resolver. Callers that need
156+
// resolution off the async reactor should `spawn_blocking` the whole resolve,
157+
// not each syscall.
150158
async fn read(&self, path: &Path) -> io::Result<Vec<u8>> {
151159
cfg_if! {
152160
if #[cfg(feature = "yarn_pnp")] {
153161
if self.options.enable_pnp {
154162
return match VPath::from(path)? {
155163
VPath::Zip(info) => self.pnp_lru.read(info.physical_base_path(), info.zip_path),
156-
VPath::Virtual(info) => tokio::fs::read(info.physical_base_path()).await,
157-
VPath::Native(path) => tokio::fs::read(&path).await,
164+
VPath::Virtual(info) => fs::read(info.physical_base_path()),
165+
VPath::Native(path) => fs::read(&path),
158166
}
159167
}
160168
}}
161169

162-
tokio::fs::read(path).await
170+
fs::read(path)
163171
}
164172

165173
async fn read_to_string(&self, path: &Path) -> io::Result<String> {
@@ -168,13 +176,13 @@ impl FileSystem for FileSystemOs {
168176
if self.options.enable_pnp {
169177
return match VPath::from(path)? {
170178
VPath::Zip(info) => self.pnp_lru.read_to_string(info.physical_base_path(), info.zip_path),
171-
VPath::Virtual(info) => tokio::fs::read_to_string(info.physical_base_path()).await,
172-
VPath::Native(path) => tokio::fs::read_to_string(&path).await,
179+
VPath::Virtual(info) => fs::read_to_string(info.physical_base_path()),
180+
VPath::Native(path) => fs::read_to_string(&path),
173181
}
174182
}
175183
}
176184
}
177-
tokio::fs::read_to_string(path).await
185+
fs::read_to_string(path)
178186
}
179187

180188
async fn metadata(&self, path: &Path) -> io::Result<FileMetadata> {
@@ -187,23 +195,19 @@ impl FileSystem for FileSystemOs {
187195
.file_type(info.physical_base_path(), info.zip_path)
188196
.map(FileMetadata::from),
189197
VPath::Virtual(info) => {
190-
tokio::fs::metadata(info.physical_base_path())
191-
.await
192-
.map(FileMetadata::from)
198+
fs::metadata(info.physical_base_path()).map(FileMetadata::from)
193199
}
194-
VPath::Native(path) => tokio::fs::metadata(path).await.map(FileMetadata::from),
200+
VPath::Native(path) => fs::metadata(path).map(FileMetadata::from),
195201
}
196202
}
197203
}
198204
}
199205

200-
tokio::fs::metadata(path).await.map(FileMetadata::from)
206+
fs::metadata(path).map(FileMetadata::from)
201207
}
202208

203209
async fn symlink_metadata(&self, path: &Path) -> io::Result<FileMetadata> {
204-
tokio::fs::symlink_metadata(path)
205-
.await
206-
.map(FileMetadata::from)
210+
fs::symlink_metadata(path).map(FileMetadata::from)
207211
}
208212

209213
async fn canonicalize(&self, path: &Path) -> io::Result<PathBuf> {

0 commit comments

Comments
 (0)