0
0
Fork 0
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:
randomicon00 2022-05-17 14:52:48 +01:00 committed by GitHub
parent 8879244f72
commit e58f77e431
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 11 deletions

View file

@ -95,16 +95,6 @@
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 {
if (ArrayBufferIsView(input)) {
@ -132,12 +122,28 @@
// with a TypedArray argument copies the data.
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, {
rid: this.#rid,
stream: options.stream,
});
} finally {
if (!options.stream) {
if (!options.stream && this.#rid) {
core.close(this.#rid);
this.#rid = null;
}

View file

@ -28,6 +28,10 @@ deno_bench_util = { version = "0.46.0", path = "../../bench_util" }
deno_url = { version = "0.52.0", path = "../url" }
deno_webidl = { version = "0.52.0", path = "../webidl" }
[[bench]]
name = "encoding"
harness = false
[[bench]]
name = "timers_ops"
harness = false

View 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);

View file

@ -90,6 +90,7 @@ pub fn init<P: TimersPermission + 'static>(
op_base64_atob::decl(),
op_base64_btoa::decl(),
op_encoding_normalize_label::decl(),
op_encoding_decode_single::decl(),
op_encoding_new_decoder::decl(),
op_encoding_decode::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())
}
#[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]
fn op_encoding_new_decoder(
state: &mut OpState,