mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
perf(ext/web): Add fast path for non-streaming TextDecoder (#14217)
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
This commit is contained in:
parent
8879244f72
commit
e58f77e431
4 changed files with 134 additions and 11 deletions
|
@ -95,16 +95,6 @@
|
||||||
context: "Argument 2",
|
context: "Argument 2",
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(lucacasonato): add fast path for non-streaming decoder & decode
|
|
||||||
|
|
||||||
if (this.#rid === null) {
|
|
||||||
this.#rid = core.opSync("op_encoding_new_decoder", {
|
|
||||||
label: this.#encoding,
|
|
||||||
fatal: this.#fatal,
|
|
||||||
ignoreBom: this.#ignoreBOM,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
if (ArrayBufferIsView(input)) {
|
if (ArrayBufferIsView(input)) {
|
||||||
|
@ -132,12 +122,28 @@
|
||||||
// with a TypedArray argument copies the data.
|
// with a TypedArray argument copies the data.
|
||||||
input = new Uint8Array(input);
|
input = new Uint8Array(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options.stream && this.#rid === null) {
|
||||||
|
return core.opSync("op_encoding_decode_single", input, {
|
||||||
|
label: this.#encoding,
|
||||||
|
fatal: this.#fatal,
|
||||||
|
ignoreBom: this.#ignoreBOM,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#rid === null) {
|
||||||
|
this.#rid = core.opSync("op_encoding_new_decoder", {
|
||||||
|
label: this.#encoding,
|
||||||
|
fatal: this.#fatal,
|
||||||
|
ignoreBom: this.#ignoreBOM,
|
||||||
|
});
|
||||||
|
}
|
||||||
return core.opSync("op_encoding_decode", input, {
|
return core.opSync("op_encoding_decode", input, {
|
||||||
rid: this.#rid,
|
rid: this.#rid,
|
||||||
stream: options.stream,
|
stream: options.stream,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
if (!options.stream) {
|
if (!options.stream && this.#rid) {
|
||||||
core.close(this.#rid);
|
core.close(this.#rid);
|
||||||
this.#rid = null;
|
this.#rid = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ deno_bench_util = { version = "0.46.0", path = "../../bench_util" }
|
||||||
deno_url = { version = "0.52.0", path = "../url" }
|
deno_url = { version = "0.52.0", path = "../url" }
|
||||||
deno_webidl = { version = "0.52.0", path = "../webidl" }
|
deno_webidl = { version = "0.52.0", path = "../webidl" }
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "encoding"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "timers_ops"
|
name = "timers_ops"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
54
ext/web/benches/encoding.rs
Normal file
54
ext/web/benches/encoding.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use deno_core::Extension;
|
||||||
|
|
||||||
|
use deno_bench_util::bench_js_sync;
|
||||||
|
use deno_bench_util::bench_or_profile;
|
||||||
|
use deno_bench_util::bencher::{benchmark_group, Bencher};
|
||||||
|
use deno_web::BlobStore;
|
||||||
|
|
||||||
|
struct Permissions;
|
||||||
|
|
||||||
|
impl deno_web::TimersPermission for Permissions {
|
||||||
|
fn allow_hrtime(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn check_unstable(
|
||||||
|
&self,
|
||||||
|
_state: &deno_core::OpState,
|
||||||
|
_api_name: &'static str,
|
||||||
|
) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup() -> Vec<Extension> {
|
||||||
|
vec![
|
||||||
|
deno_webidl::init(),
|
||||||
|
deno_url::init(),
|
||||||
|
deno_web::init::<Permissions>(BlobStore::default(), None),
|
||||||
|
Extension::builder()
|
||||||
|
.js(vec![(
|
||||||
|
"setup",
|
||||||
|
Box::new(|| {
|
||||||
|
Ok(
|
||||||
|
r#"
|
||||||
|
const { TextDecoder } = globalThis.__bootstrap.encoding;
|
||||||
|
const hello12k = Deno.core.encode("hello world\n".repeat(1e3));
|
||||||
|
"#
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)])
|
||||||
|
.state(|state| {
|
||||||
|
state.put(Permissions {});
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_encode_12kb(b: &mut Bencher) {
|
||||||
|
bench_js_sync(b, r#"new TextDecoder().decode(hello12k);"#, setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmark_group!(benches, bench_encode_12kb);
|
||||||
|
bench_or_profile!(benches);
|
|
@ -90,6 +90,7 @@ pub fn init<P: TimersPermission + 'static>(
|
||||||
op_base64_atob::decl(),
|
op_base64_atob::decl(),
|
||||||
op_base64_btoa::decl(),
|
op_base64_btoa::decl(),
|
||||||
op_encoding_normalize_label::decl(),
|
op_encoding_normalize_label::decl(),
|
||||||
|
op_encoding_decode_single::decl(),
|
||||||
op_encoding_new_decoder::decl(),
|
op_encoding_new_decoder::decl(),
|
||||||
op_encoding_decode::decl(),
|
op_encoding_decode::decl(),
|
||||||
op_encoding_encode_into::decl(),
|
op_encoding_encode_into::decl(),
|
||||||
|
@ -215,6 +216,64 @@ fn op_encoding_normalize_label(label: String) -> Result<String, AnyError> {
|
||||||
Ok(encoding.name().to_lowercase())
|
Ok(encoding.name().to_lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
fn op_encoding_decode_single(
|
||||||
|
data: ZeroCopyBuf,
|
||||||
|
options: DecoderOptions,
|
||||||
|
) -> Result<U16String, AnyError> {
|
||||||
|
let DecoderOptions {
|
||||||
|
label,
|
||||||
|
ignore_bom,
|
||||||
|
fatal,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| {
|
||||||
|
range_error(format!(
|
||||||
|
"The encoding label provided ('{}') is invalid.",
|
||||||
|
label
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut decoder = if ignore_bom {
|
||||||
|
encoding.new_decoder_without_bom_handling()
|
||||||
|
} else {
|
||||||
|
encoding.new_decoder_with_bom_removal()
|
||||||
|
};
|
||||||
|
|
||||||
|
let max_buffer_length = decoder
|
||||||
|
.max_utf16_buffer_length(data.len())
|
||||||
|
.ok_or_else(|| range_error("Value too large to decode."))?;
|
||||||
|
|
||||||
|
let mut output = vec![0; max_buffer_length];
|
||||||
|
|
||||||
|
if fatal {
|
||||||
|
let (result, _, written) =
|
||||||
|
decoder.decode_to_utf16_without_replacement(&data, &mut output, true);
|
||||||
|
match result {
|
||||||
|
DecoderResult::InputEmpty => {
|
||||||
|
output.truncate(written);
|
||||||
|
Ok(output.into())
|
||||||
|
}
|
||||||
|
DecoderResult::OutputFull => {
|
||||||
|
Err(range_error("Provided buffer too small."))
|
||||||
|
}
|
||||||
|
DecoderResult::Malformed(_, _) => {
|
||||||
|
Err(type_error("The encoded data is not valid."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (result, _, written, _) =
|
||||||
|
decoder.decode_to_utf16(&data, &mut output, true);
|
||||||
|
match result {
|
||||||
|
CoderResult::InputEmpty => {
|
||||||
|
output.truncate(written);
|
||||||
|
Ok(output.into())
|
||||||
|
}
|
||||||
|
CoderResult::OutputFull => Err(range_error("Provided buffer too small.")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[op]
|
#[op]
|
||||||
fn op_encoding_new_decoder(
|
fn op_encoding_new_decoder(
|
||||||
state: &mut OpState,
|
state: &mut OpState,
|
||||||
|
|
Loading…
Add table
Reference in a new issue