diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2022-10-27 06:40:48 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-27 19:10:48 +0530 |
commit | 02187966c108fd50354c8de57bab443a5b262373 (patch) | |
tree | 8076ec580cef65797897303a6bb3866f74227bd3 /core/bindings.rs | |
parent | bfd9912e1faa30a92472252b77878714e668a3d4 (diff) |
perf(core): generate inlined wrappers for async ops (#16428)
V8's JIT can do a better job knowing the argument count and also enable
fast call path (in future).
This also lets us call async ops without `opAsync`:
```js
const { ops } = Deno.core;
await ops.op_void_async();
```
this patch: 4405286 ops/sec
main: 3508771 ops/sec
Diffstat (limited to 'core/bindings.rs')
-rw-r--r-- | core/bindings.rs | 92 |
1 files changed, 89 insertions, 3 deletions
diff --git a/core/bindings.rs b/core/bindings.rs index 741ab6336..60d15f745 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -97,6 +97,7 @@ pub fn initialize_context<'s>( scope: &mut v8::HandleScope<'s, ()>, op_ctxs: &[OpCtx], snapshot_loaded: bool, + will_snapshot: bool, ) -> v8::Local<'s, v8::Context> { let scope = &mut v8::EscapableHandleScope::new(scope); @@ -116,7 +117,9 @@ pub fn initialize_context<'s>( let ops_obj = JsRuntime::grab_global::<v8::Object>(scope, "Deno.core.ops") .expect("Deno.core.ops to exist"); initialize_ops(scope, ops_obj, op_ctxs, snapshot_loaded); - + if !will_snapshot { + initialize_async_ops_info(scope, ops_obj, op_ctxs); + } return scope.escape(context); } @@ -128,8 +131,10 @@ pub fn initialize_context<'s>( // Bind functions to Deno.core.ops.* let ops_obj = JsRuntime::ensure_objs(scope, global, "Deno.core.ops").unwrap(); - - initialize_ops(scope, ops_obj, op_ctxs, snapshot_loaded); + if !will_snapshot { + initialize_async_ops_info(scope, ops_obj, op_ctxs); + } + initialize_ops(scope, ops_obj, op_ctxs, !will_snapshot); scope.escape(context) } @@ -590,3 +595,84 @@ pub fn throw_type_error(scope: &mut v8::HandleScope, message: impl AsRef<str>) { let exception = v8::Exception::type_error(scope, message); scope.throw_exception(exception); } + +struct AsyncOpsInfo { + ptr: *const OpCtx, + len: usize, +} + +impl<'s> IntoIterator for &'s AsyncOpsInfo { + type Item = &'s OpCtx; + type IntoIter = AsyncOpsInfoIterator<'s>; + + fn into_iter(self) -> Self::IntoIter { + AsyncOpsInfoIterator { + // SAFETY: OpCtx slice is valid for the lifetime of the Isolate + info: unsafe { std::slice::from_raw_parts(self.ptr, self.len) }, + index: 0, + } + } +} + +struct AsyncOpsInfoIterator<'s> { + info: &'s [OpCtx], + index: usize, +} + +impl<'s> Iterator for AsyncOpsInfoIterator<'s> { + type Item = &'s OpCtx; + + fn next(&mut self) -> Option<Self::Item> { + loop { + match self.info.get(self.index) { + Some(ctx) if ctx.decl.is_async => { + self.index += 1; + return Some(ctx); + } + Some(_) => { + self.index += 1; + } + None => return None, + } + } + } +} + +fn async_ops_info( + scope: &mut v8::HandleScope, + args: v8::FunctionCallbackArguments, + mut rv: v8::ReturnValue, +) { + let async_op_names = v8::Object::new(scope); + let external: v8::Local<v8::External> = args.data().try_into().unwrap(); + let info: &AsyncOpsInfo = + // SAFETY: external is guaranteed to be a valid pointer to AsyncOpsInfo + unsafe { &*(external.value() as *const AsyncOpsInfo) }; + for ctx in info { + let name = v8::String::new(scope, ctx.decl.name).unwrap(); + let argc = v8::Integer::new(scope, ctx.decl.argc as i32); + async_op_names.set(scope, name.into(), argc.into()); + } + rv.set(async_op_names.into()); +} + +fn initialize_async_ops_info( + scope: &mut v8::HandleScope, + ops_obj: v8::Local<v8::Object>, + op_ctxs: &[OpCtx], +) { + let key = v8::String::new(scope, "asyncOpsInfo").unwrap(); + let external = v8::External::new( + scope, + Box::into_raw(Box::new(AsyncOpsInfo { + ptr: op_ctxs as *const [OpCtx] as _, + len: op_ctxs.len(), + })) as *mut c_void, + ); + let val = v8::Function::builder(async_ops_info) + .data(external.into()) + .build(scope) + .unwrap(); + val.set_name(key); + ops_obj.set(scope, key.into(), val.into()); +} |