mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
perf(runtime): optimize Deno.file open & stream (#15496)
This commit is contained in:
parent
8bdcec1c84
commit
9e576dff7c
5 changed files with 82 additions and 35 deletions
|
@ -23,6 +23,11 @@ Deno.bench("perf_now", { n: 5e5 }, () => {
|
||||||
performance.now();
|
performance.now();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Deno.bench("open_file_sync", () => {
|
||||||
|
const file = Deno.openSync("./cli/bench/testdata/128k.bin");
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
|
||||||
// A common "language feature", that should be fast
|
// A common "language feature", that should be fast
|
||||||
// also a decent representation of a non-trivial JSON-op
|
// also a decent representation of a non-trivial JSON-op
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,7 @@ const { serve } = Deno;
|
||||||
const path = new URL("../testdata/128k.bin", import.meta.url).pathname;
|
const path = new URL("../testdata/128k.bin", import.meta.url).pathname;
|
||||||
|
|
||||||
function handler() {
|
function handler() {
|
||||||
const file = Deno.openSync(path, { read: true });
|
const file = Deno.openSync(path);
|
||||||
return new Response(file.readable);
|
return new Response(file.readable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -658,7 +658,9 @@
|
||||||
* @returns {ReadableStream<Uint8Array>}
|
* @returns {ReadableStream<Uint8Array>}
|
||||||
*/
|
*/
|
||||||
function readableStreamForRid(rid, unrefCallback) {
|
function readableStreamForRid(rid, unrefCallback) {
|
||||||
const stream = new ReadableStream({
|
const stream = webidl.createBranded(ReadableStream);
|
||||||
|
stream[_maybeRid] = rid;
|
||||||
|
const underlyingSource = {
|
||||||
type: "bytes",
|
type: "bytes",
|
||||||
async pull(controller) {
|
async pull(controller) {
|
||||||
const v = controller.byobRequest.view;
|
const v = controller.byobRequest.view;
|
||||||
|
@ -685,9 +687,15 @@
|
||||||
core.tryClose(rid);
|
core.tryClose(rid);
|
||||||
},
|
},
|
||||||
autoAllocateChunkSize: DEFAULT_CHUNK_SIZE,
|
autoAllocateChunkSize: DEFAULT_CHUNK_SIZE,
|
||||||
});
|
};
|
||||||
|
initializeReadableStream(stream);
|
||||||
|
setUpReadableByteStreamControllerFromUnderlyingSource(
|
||||||
|
stream,
|
||||||
|
underlyingSource,
|
||||||
|
underlyingSource,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
stream[_maybeRid] = rid;
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,7 +722,6 @@
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {{ [_queue]: Array<ValueWithSize<T | _close>>, [_queueTotalSize]: number }} container
|
* @param {{ [_queue]: Array<ValueWithSize<T | _close>>, [_queueTotalSize]: number }} container
|
||||||
|
|
|
@ -33,12 +33,14 @@
|
||||||
|
|
||||||
function openSync(
|
function openSync(
|
||||||
path,
|
path,
|
||||||
options = { read: true },
|
options,
|
||||||
) {
|
) {
|
||||||
checkOpenOptions(options);
|
if (options) checkOpenOptions(options);
|
||||||
const mode = options?.mode;
|
const mode = options?.mode;
|
||||||
const rid = ops.op_open_sync(
|
const rid = ops.op_open_sync(
|
||||||
{ path: pathFromURL(path), options, mode },
|
pathFromURL(path),
|
||||||
|
options,
|
||||||
|
mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
return new FsFile(rid);
|
return new FsFile(rid);
|
||||||
|
@ -46,13 +48,15 @@
|
||||||
|
|
||||||
async function open(
|
async function open(
|
||||||
path,
|
path,
|
||||||
options = { read: true },
|
options,
|
||||||
) {
|
) {
|
||||||
checkOpenOptions(options);
|
if (options) checkOpenOptions(options);
|
||||||
const mode = options?.mode;
|
const mode = options?.mode;
|
||||||
const rid = await core.opAsync(
|
const rid = await core.opAsync(
|
||||||
"op_open_async",
|
"op_open_async",
|
||||||
{ path: pathFromURL(path), options, mode },
|
pathFromURL(path),
|
||||||
|
options,
|
||||||
|
mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
return new FsFile(rid);
|
return new FsFile(rid);
|
||||||
|
|
|
@ -128,15 +128,18 @@ pub struct OpenOptions {
|
||||||
create_new: bool,
|
create_new: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn open_helper(
|
fn open_helper(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
args: &OpenArgs,
|
path: &str,
|
||||||
|
mode: Option<u32>,
|
||||||
|
options: Option<&OpenOptions>,
|
||||||
) -> Result<(PathBuf, std::fs::OpenOptions), AnyError> {
|
) -> Result<(PathBuf, std::fs::OpenOptions), AnyError> {
|
||||||
let path = Path::new(&args.path).to_path_buf();
|
let path = Path::new(path).to_path_buf();
|
||||||
|
|
||||||
let mut open_options = std::fs::OpenOptions::new();
|
let mut open_options = std::fs::OpenOptions::new();
|
||||||
|
|
||||||
if let Some(mode) = args.mode {
|
if let Some(mode) = mode {
|
||||||
// mode only used if creating the file on Unix
|
// mode only used if creating the file on Unix
|
||||||
// if not specified, defaults to 0o666
|
// if not specified, defaults to 0o666
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -149,8 +152,19 @@ fn open_helper(
|
||||||
}
|
}
|
||||||
|
|
||||||
let permissions = state.borrow_mut::<Permissions>();
|
let permissions = state.borrow_mut::<Permissions>();
|
||||||
let options = &args.options;
|
|
||||||
|
|
||||||
|
match options {
|
||||||
|
None => {
|
||||||
|
permissions.read.check(&path)?;
|
||||||
|
open_options
|
||||||
|
.read(true)
|
||||||
|
.create(false)
|
||||||
|
.write(false)
|
||||||
|
.truncate(false)
|
||||||
|
.append(false)
|
||||||
|
.create_new(false);
|
||||||
|
}
|
||||||
|
Some(options) => {
|
||||||
if options.read {
|
if options.read {
|
||||||
permissions.read.check(&path)?;
|
permissions.read.check(&path)?;
|
||||||
}
|
}
|
||||||
|
@ -166,6 +180,8 @@ fn open_helper(
|
||||||
.truncate(options.truncate)
|
.truncate(options.truncate)
|
||||||
.append(options.append)
|
.append(options.append)
|
||||||
.create_new(options.create_new);
|
.create_new(options.create_new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok((path, open_options))
|
Ok((path, open_options))
|
||||||
}
|
}
|
||||||
|
@ -173,9 +189,11 @@ fn open_helper(
|
||||||
#[op]
|
#[op]
|
||||||
fn op_open_sync(
|
fn op_open_sync(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
args: OpenArgs,
|
path: String,
|
||||||
|
options: Option<OpenOptions>,
|
||||||
|
mode: Option<u32>,
|
||||||
) -> Result<ResourceId, AnyError> {
|
) -> Result<ResourceId, AnyError> {
|
||||||
let (path, open_options) = open_helper(state, &args)?;
|
let (path, open_options) = open_helper(state, &path, mode, options.as_ref())?;
|
||||||
let std_file = open_options.open(&path).map_err(|err| {
|
let std_file = open_options.open(&path).map_err(|err| {
|
||||||
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
||||||
})?;
|
})?;
|
||||||
|
@ -187,9 +205,12 @@ fn op_open_sync(
|
||||||
#[op]
|
#[op]
|
||||||
async fn op_open_async(
|
async fn op_open_async(
|
||||||
state: Rc<RefCell<OpState>>,
|
state: Rc<RefCell<OpState>>,
|
||||||
args: OpenArgs,
|
path: String,
|
||||||
|
options: Option<OpenOptions>,
|
||||||
|
mode: Option<u32>,
|
||||||
) -> Result<ResourceId, AnyError> {
|
) -> Result<ResourceId, AnyError> {
|
||||||
let (path, open_options) = open_helper(&mut state.borrow_mut(), &args)?;
|
let (path, open_options) =
|
||||||
|
open_helper(&mut state.borrow_mut(), &path, mode, options.as_ref())?;
|
||||||
let std_file = tokio::task::spawn_blocking(move || {
|
let std_file = tokio::task::spawn_blocking(move || {
|
||||||
open_options.open(path.clone()).map_err(|err| {
|
open_options.open(path.clone()).map_err(|err| {
|
||||||
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
Error::new(err.kind(), format!("{}, open '{}'", err, path.display()))
|
||||||
|
@ -238,7 +259,12 @@ fn op_write_file_sync(
|
||||||
args: WriteFileArgs,
|
args: WriteFileArgs,
|
||||||
) -> Result<(), AnyError> {
|
) -> Result<(), AnyError> {
|
||||||
let (open_args, data) = args.into_open_args_and_data();
|
let (open_args, data) = args.into_open_args_and_data();
|
||||||
let (path, open_options) = open_helper(state, &open_args)?;
|
let (path, open_options) = open_helper(
|
||||||
|
state,
|
||||||
|
&open_args.path,
|
||||||
|
open_args.mode,
|
||||||
|
Some(&open_args.options),
|
||||||
|
)?;
|
||||||
write_file(&path, open_options, &open_args, data)
|
write_file(&path, open_options, &open_args, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +282,12 @@ async fn op_write_file_async(
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
let (open_args, data) = args.into_open_args_and_data();
|
let (open_args, data) = args.into_open_args_and_data();
|
||||||
let (path, open_options) = open_helper(&mut *state.borrow_mut(), &open_args)?;
|
let (path, open_options) = open_helper(
|
||||||
|
&mut *state.borrow_mut(),
|
||||||
|
&open_args.path,
|
||||||
|
open_args.mode,
|
||||||
|
Some(&open_args.options),
|
||||||
|
)?;
|
||||||
let write_future = tokio::task::spawn_blocking(move || {
|
let write_future = tokio::task::spawn_blocking(move || {
|
||||||
write_file(&path, open_options, &open_args, data)
|
write_file(&path, open_options, &open_args, data)
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue