1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 00:29:09 -05:00

perf(ext/node): optimize fs.exists[Sync] (#24613)

Use `access` on *nix and `GetFileAttributesW` on Windows.

[Benchmark](https://paste.divy.work/p/-gq8Ark.js):
```
$ deno run -A bench.mjs # main (568dd)
existsSync: 8980.636629ms

$ target/release/deno run -A bench.mjs # this PR
existsSync: 6448.7604519999995ms

$ bun bench.mjs
existsSync: 6562.88671ms

$ node bench.mjs
existsSync: 7740.064653ms
```

Ref https://github.com/denoland/deno/pull/24434#discussion_r1679777912
This commit is contained in:
Divy Srivastava 2024-07-17 05:35:51 -07:00 committed by Bartek Iwańczuk
parent 35cbe2937d
commit 3628895471
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
5 changed files with 63 additions and 3 deletions

View file

@ -315,6 +315,9 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
fn exists_sync(&self, path: &Path) -> bool { fn exists_sync(&self, path: &Path) -> bool {
self.stat_sync(path).is_ok() self.stat_sync(path).is_ok()
} }
async fn exists_async(&self, path: PathBuf) -> FsResult<bool> {
Ok(self.stat_async(path).await.is_ok())
}
fn read_text_file_lossy_sync( fn read_text_file_lossy_sync(
&self, &self,

View file

@ -173,6 +173,15 @@ impl FileSystem for RealFs {
spawn_blocking(move || lstat(&path)).await?.map(Into::into) spawn_blocking(move || lstat(&path)).await?.map(Into::into)
} }
fn exists_sync(&self, path: &Path) -> bool {
exists(path)
}
async fn exists_async(&self, path: PathBuf) -> FsResult<bool> {
spawn_blocking(move || exists(&path))
.await
.map_err(Into::into)
}
fn realpath_sync(&self, path: &Path) -> FsResult<PathBuf> { fn realpath_sync(&self, path: &Path) -> FsResult<PathBuf> {
realpath(path) realpath(path)
} }
@ -837,6 +846,32 @@ fn stat_extra(
} }
} }
fn exists(path: &Path) -> bool {
#[cfg(unix)]
{
use nix::unistd::access;
use nix::unistd::AccessFlags;
access(path, AccessFlags::F_OK).is_ok()
}
#[cfg(windows)]
{
use std::os::windows::ffi::OsStrExt;
use winapi::um::fileapi::GetFileAttributesW;
use winapi::um::fileapi::INVALID_FILE_ATTRIBUTES;
let path = path
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect::<Vec<_>>();
// Safety: `path` is a null-terminated string
let attrs = unsafe { GetFileAttributesW(path.as_ptr()) };
attrs != INVALID_FILE_ATTRIBUTES
}
}
fn realpath(path: &Path) -> FsResult<PathBuf> { fn realpath(path: &Path) -> FsResult<PathBuf> {
Ok(deno_core::strip_unc_prefix(path.canonicalize()?)) Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
} }

View file

@ -313,6 +313,7 @@ deno_core::extension!(deno_node,
ops::crypto::x509::op_node_x509_get_serial_number, ops::crypto::x509::op_node_x509_get_serial_number,
ops::crypto::x509::op_node_x509_key_usage, ops::crypto::x509::op_node_x509_key_usage,
ops::fs::op_node_fs_exists_sync<P>, ops::fs::op_node_fs_exists_sync<P>,
ops::fs::op_node_fs_exists<P>,
ops::fs::op_node_cp_sync<P>, ops::fs::op_node_cp_sync<P>,
ops::fs::op_node_cp<P>, ops::fs::op_node_cp<P>,
ops::fs::op_node_lchown_sync<P>, ops::fs::op_node_lchown_sync<P>,

View file

@ -26,7 +26,28 @@ where
.borrow_mut::<P>() .borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.existsSync()"))?; .check_read_with_api_name(&path, Some("node:fs.existsSync()"))?;
let fs = state.borrow::<FileSystemRc>(); let fs = state.borrow::<FileSystemRc>();
Ok(fs.lstat_sync(&path).is_ok()) Ok(fs.exists_sync(&path))
}
#[op2(async)]
pub async fn op_node_fs_exists<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
) -> Result<bool, AnyError>
where
P: NodePermissions + 'static,
{
let path = PathBuf::from(path);
let fs = {
let mut state = state.borrow_mut();
state
.borrow_mut::<P>()
.check_read_with_api_name(&path, Some("node:fs.exists()"))?;
state.borrow::<FileSystemRc>().clone()
};
Ok(fs.exists_async(path).await?)
} }
#[op2(fast)] #[op2(fast)]

View file

@ -3,7 +3,7 @@
// TODO(petamoriken): enable prefer-primordials for node polyfills // TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials // deno-lint-ignore-file prefer-primordials
import { op_node_fs_exists_sync } from "ext:core/ops"; import { op_node_fs_exists, op_node_fs_exists_sync } from "ext:core/ops";
import { pathFromURL } from "ext:deno_web/00_infra.js"; import { pathFromURL } from "ext:deno_web/00_infra.js";
@ -16,7 +16,7 @@ type ExistsCallback = (exists: boolean) => void;
*/ */
export function exists(path: string | URL, callback: ExistsCallback) { export function exists(path: string | URL, callback: ExistsCallback) {
path = path instanceof URL ? pathFromURL(path) : path; path = path instanceof URL ? pathFromURL(path) : path;
Deno.lstat(path).then(() => callback(true), () => callback(false)); op_node_fs_exists(path).then(callback);
} }
// The callback of fs.exists doesn't have standard callback signature. // The callback of fs.exists doesn't have standard callback signature.