mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -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:
parent
35cbe2937d
commit
3628895471
5 changed files with 63 additions and 3 deletions
|
@ -315,6 +315,9 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
|
|||
fn exists_sync(&self, path: &Path) -> bool {
|
||||
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(
|
||||
&self,
|
||||
|
|
|
@ -173,6 +173,15 @@ impl FileSystem for RealFs {
|
|||
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> {
|
||||
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> {
|
||||
Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
|
||||
}
|
||||
|
|
|
@ -313,6 +313,7 @@ deno_core::extension!(deno_node,
|
|||
ops::crypto::x509::op_node_x509_get_serial_number,
|
||||
ops::crypto::x509::op_node_x509_key_usage,
|
||||
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<P>,
|
||||
ops::fs::op_node_lchown_sync<P>,
|
||||
|
|
|
@ -26,7 +26,28 @@ where
|
|||
.borrow_mut::<P>()
|
||||
.check_read_with_api_name(&path, Some("node:fs.existsSync()"))?;
|
||||
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)]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// TODO(petamoriken): enable prefer-primordials for node polyfills
|
||||
// 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";
|
||||
|
||||
|
@ -16,7 +16,7 @@ type ExistsCallback = (exists: boolean) => void;
|
|||
*/
|
||||
export function exists(path: string | URL, callback: ExistsCallback) {
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue