mirror of
https://github.com/denoland/deno.git
synced 2024-12-01 16:51:13 -05:00
feat(ops): relational ops (#18023)
Join two independent ops into one. A fast impl of one + a slow callback of another. Here's an example showing optimized paths for latin-1 via fast call and the next-best fallback using V8 apis. ```rust #[op(v8)] fn op_encoding_encode_into_fallback( scope: &mut v8::HandleScope, input: serde_v8::Value, // ... #[op(fast, slow = op_encoding_encode_into_fallback)] fn op_encoding_encode_into( input: Cow<'_, str>, // ... ``` Benchmark results of the fallback path: ``` time target/release/deno run -A --unstable ./cli/tests/testdata/benches/text_encoder_into_perf.js ________________________________________________________ Executed in 70.90 millis fish external usr time 57.76 millis 0.23 millis 57.53 millis sys time 17.02 millis 1.28 millis 15.74 millis target/release/deno_main run -A --unstable ./cli/tests/testdata/benches/text_encoder_into_perf.js ________________________________________________________ Executed in 154.00 millis fish external usr time 67.14 millis 0.26 millis 66.88 millis sys time 38.82 millis 1.47 millis 37.35 millis ```
This commit is contained in:
parent
dea5de1c45
commit
ed6b60ddbe
3 changed files with 102 additions and 27 deletions
|
@ -350,7 +350,28 @@ impl Resource for TextDecoderResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op]
|
#[op(v8)]
|
||||||
|
fn op_encoding_encode_into_fallback(
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
input: serde_v8::Value,
|
||||||
|
buffer: &mut [u8],
|
||||||
|
out_buf: &mut [u32],
|
||||||
|
) -> Result<(), AnyError> {
|
||||||
|
let s = v8::Local::<v8::String>::try_from(input.v8_value)?;
|
||||||
|
|
||||||
|
let mut nchars = 0;
|
||||||
|
out_buf[1] = s.write_utf8(
|
||||||
|
scope,
|
||||||
|
buffer,
|
||||||
|
Some(&mut nchars),
|
||||||
|
v8::WriteOptions::NO_NULL_TERMINATION
|
||||||
|
| v8::WriteOptions::REPLACE_INVALID_UTF8,
|
||||||
|
) as u32;
|
||||||
|
out_buf[0] = nchars as u32;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op(fast, slow = op_encoding_encode_into_fallback)]
|
||||||
fn op_encoding_encode_into(
|
fn op_encoding_encode_into(
|
||||||
input: Cow<'_, str>,
|
input: Cow<'_, str>,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
|
|
52
ops/attrs.rs
52
ops/attrs.rs
|
@ -1,44 +1,54 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
use syn::parse::Parse;
|
use syn::parse::Parse;
|
||||||
use syn::parse::ParseStream;
|
use syn::parse::ParseStream;
|
||||||
use syn::punctuated::Punctuated;
|
|
||||||
use syn::Error;
|
use syn::Error;
|
||||||
use syn::Ident;
|
use syn::Ident;
|
||||||
use syn::Result;
|
use syn::Result;
|
||||||
use syn::Token;
|
use syn::Token;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Attributes {
|
pub struct Attributes {
|
||||||
pub is_unstable: bool,
|
pub is_unstable: bool,
|
||||||
pub is_v8: bool,
|
pub is_v8: bool,
|
||||||
pub must_be_fast: bool,
|
pub must_be_fast: bool,
|
||||||
pub deferred: bool,
|
pub deferred: bool,
|
||||||
pub is_wasm: bool,
|
pub is_wasm: bool,
|
||||||
|
pub relation: Option<Ident>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for Attributes {
|
impl Parse for Attributes {
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
|
let mut self_ = Self::default();
|
||||||
|
let mut fast = false;
|
||||||
let vars: Vec<_> = vars.iter().map(Ident::to_string).collect();
|
while let Ok(v) = input.parse::<Ident>() {
|
||||||
let vars: Vec<_> = vars.iter().map(String::as_str).collect();
|
match v.to_string().as_str() {
|
||||||
for var in vars.iter() {
|
"unstable" => self_.is_unstable = true,
|
||||||
if !["unstable", "v8", "fast", "deferred", "wasm"].contains(var) {
|
"v8" => self_.is_v8 = true,
|
||||||
return Err(Error::new(
|
"fast" => fast = true,
|
||||||
input.span(),
|
"deferred" => self_.deferred = true,
|
||||||
"invalid attribute, expected one of: unstable, v8, fast, deferred, wasm",
|
"wasm" => self_.is_wasm = true,
|
||||||
));
|
"slow" => {
|
||||||
}
|
if !fast {
|
||||||
|
return Err(Error::new(
|
||||||
|
input.span(),
|
||||||
|
"relational attributes can only be used with fast attribute",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
self_.relation = Some(input.parse()?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::new(
|
||||||
|
input.span(),
|
||||||
|
"invalid attribute, expected one of: unstable, v8, fast, deferred, wasm",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ = input.parse::<Token![,]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_wasm = vars.contains(&"wasm");
|
self_.must_be_fast = self_.is_wasm || fast;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(self_)
|
||||||
is_unstable: vars.contains(&"unstable"),
|
|
||||||
is_v8: vars.contains(&"v8"),
|
|
||||||
deferred: vars.contains(&"deferred"),
|
|
||||||
must_be_fast: is_wasm || vars.contains(&"fast"),
|
|
||||||
is_wasm,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
ops/lib.rs
54
ops/lib.rs
|
@ -111,24 +111,68 @@ impl Op {
|
||||||
active,
|
active,
|
||||||
} = fast_call::generate(&core, &mut optimizer, &item);
|
} = fast_call::generate(&core, &mut optimizer, &item);
|
||||||
|
|
||||||
|
let docline = format!("Use `{name}::decl()` to get an op-declaration");
|
||||||
|
|
||||||
|
let is_v8 = attrs.is_v8;
|
||||||
|
let is_unstable = attrs.is_unstable;
|
||||||
|
|
||||||
|
if let Some(v8_fn) = attrs.relation {
|
||||||
|
return quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[doc="Auto-generated by `deno_ops`, i.e: `#[op]`"]
|
||||||
|
#[doc=""]
|
||||||
|
#[doc=#docline]
|
||||||
|
#[doc="you can include in a `deno_core::Extension`."]
|
||||||
|
pub struct #name;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl #name {
|
||||||
|
pub fn name() -> &'static str {
|
||||||
|
stringify!(#name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn v8_fn_ptr #generics () -> #core::v8::FunctionCallback #where_clause {
|
||||||
|
use #core::v8::MapFnTo;
|
||||||
|
#v8_fn::v8_func::<#type_params>.map_fn_to()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decl #generics () -> #core::OpDecl #where_clause {
|
||||||
|
#core::OpDecl {
|
||||||
|
name: Self::name(),
|
||||||
|
v8_fn_ptr: Self::v8_fn_ptr::<#type_params>(),
|
||||||
|
enabled: true,
|
||||||
|
fast_fn: #decl,
|
||||||
|
is_async: #is_async,
|
||||||
|
is_unstable: #is_unstable,
|
||||||
|
is_v8: #is_v8,
|
||||||
|
argc: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
#orig
|
||||||
|
}
|
||||||
|
|
||||||
|
#impl_and_fn
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let has_fallible_fast_call = active && optimizer.returns_result;
|
let has_fallible_fast_call = active && optimizer.returns_result;
|
||||||
|
|
||||||
let (v8_body, argc) = if is_async {
|
let (v8_body, argc) = if is_async {
|
||||||
|
let deferred = attrs.deferred;
|
||||||
codegen_v8_async(
|
codegen_v8_async(
|
||||||
&core,
|
&core,
|
||||||
&item,
|
&item,
|
||||||
attrs,
|
attrs,
|
||||||
item.sig.asyncness.is_some(),
|
item.sig.asyncness.is_some(),
|
||||||
attrs.deferred,
|
deferred,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
codegen_v8_sync(&core, &item, attrs, has_fallible_fast_call)
|
codegen_v8_sync(&core, &item, attrs, has_fallible_fast_call)
|
||||||
};
|
};
|
||||||
|
|
||||||
let is_v8 = attrs.is_v8;
|
|
||||||
let is_unstable = attrs.is_unstable;
|
|
||||||
|
|
||||||
let docline = format!("Use `{name}::decl()` to get an op-declaration");
|
|
||||||
// Generate wrapper
|
// Generate wrapper
|
||||||
quote! {
|
quote! {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
|
Loading…
Reference in a new issue