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:
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 {
|
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,
|
||||||
|
|
|
@ -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()?))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue