From 110a0ebe690f83a21c87f1ef1a6a51af67e2a5fc Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 10 Nov 2022 03:56:02 -0800 Subject: [PATCH] perf(core): minimize trivial heap allocations in `resolve_async_ops` (#16584) * Use stack allocated array for 16 promises and spill rest to heap. the exact number can change, maybe 128? (tokio's coop budget limit) * Avoid v8::Global::clone for global context. * Do not open global opresolve when its not needed. --- Cargo.lock | 1 + core/Cargo.toml | 1 + core/runtime.rs | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df49cb67be..f4fa9ce789 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -943,6 +943,7 @@ dependencies = [ "serde", "serde_json", "serde_v8", + "smallvec", "sourcemap", "tokio", "url", diff --git a/core/Cargo.toml b/core/Cargo.toml index d6b8691382..c72497bbfc 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -32,6 +32,7 @@ pin-project = "1.0.11" serde = { version = "1.0.136", features = ["derive"] } serde_json = { version = "1.0.79", features = ["preserve_order"] } serde_v8 = { version = "0.69.0", path = "../serde_v8" } +smallvec = "1.8" sourcemap = "6.1" url = { version = "2.3.1", features = ["serde", "expose_internals"] } v8 = { version = "0.54.0", default-features = false } diff --git a/core/runtime.rs b/core/runtime.rs index b5e2141fb3..21e6e7570b 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -32,6 +32,7 @@ use futures::future::FutureExt; use futures::stream::FuturesUnordered; use futures::stream::StreamExt; use futures::task::AtomicWaker; +use smallvec::SmallVec; use std::any::Any; use std::cell::RefCell; use std::collections::HashMap; @@ -1972,10 +1973,13 @@ impl JsRuntime { // Send finished responses to JS fn resolve_async_ops(&mut self, cx: &mut Context) -> Result<(), Error> { let isolate = self.v8_isolate.as_mut().unwrap(); - - let js_recv_cb_handle = self.state.borrow().js_recv_cb.clone().unwrap(); - let global_realm = self.state.borrow().global_realm.clone().unwrap(); - let scope = &mut global_realm.handle_scope(isolate); + let scope = &mut self + .state + .borrow() + .global_realm + .as_ref() + .unwrap() + .handle_scope(isolate); // We return async responses to JS in unbounded batches (may change), // each batch is a flat vector of tuples: @@ -1984,7 +1988,10 @@ impl JsRuntime { // which contains a value OR an error, encoded as a tuple. // This batch is received in JS via the special `arguments` variable // and then each tuple is used to resolve or reject promises - let mut args: Vec> = vec![]; + // + // This can handle 16 promises (32 / 2) futures in a single batch without heap + // allocations. + let mut args: SmallVec<[v8::Local; 32]> = SmallVec::new(); // Now handle actual ops. { @@ -2010,6 +2017,7 @@ impl JsRuntime { return Ok(()); } + let js_recv_cb_handle = self.state.borrow().js_recv_cb.clone().unwrap(); let tc_scope = &mut v8::TryCatch::new(scope); let js_recv_cb = js_recv_cb_handle.open(tc_scope); let this = v8::undefined(tc_scope).into();