From 291def2d70641fa843563842150345ef8aff8a6f Mon Sep 17 00:00:00 2001 From: LongYinan Date: Mon, 21 Jun 2021 23:09:13 +0800 Subject: [PATCH] perf(napi): make FuturePromise static dispatch --- Cargo.toml | 3 +++ bench/Cargo.toml | 5 +---- bench/async.ts | 31 ++++++++++++++++++++----------- bench/buffer.ts | 2 +- bench/query.ts | 4 ++-- bench/src/buffer.rs | 2 +- bench/src/lib.rs | 10 +++++----- napi/src/env.rs | 4 ++-- napi/src/promise.rs | 27 ++++++++++++++------------- napi/src/threadsafe_function.rs | 9 ++++++--- 10 files changed, 55 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c135e1f8..b785fb59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,6 @@ members = [ "./bench", "./memory-testing", ] + +[profile.release] +lto = true diff --git a/bench/Cargo.toml b/bench/Cargo.toml index 8f6f289c..92c3a8a4 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -13,10 +13,7 @@ napi-derive = {path = "../napi-derive"} serde = "1" serde_json = "1" -[target.'cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64"), not(target_arch = "arm")))'.dependencies] -jemallocator = {version = "0.3", features = ["disable_initial_exec_tls"]} - -[target.'cfg(all(windows, not(target_arch = "aarch64")))'.dependencies] +[target.'cfg(all(target_arch = "x86_64", not(target_env = "musl")))'.dependencies] mimalloc = {version = "0.1"} [build-dependencies] diff --git a/bench/async.ts b/bench/async.ts index 464ca6c1..9300201c 100644 --- a/bench/async.ts +++ b/bench/async.ts @@ -1,3 +1,5 @@ +import { cpus } from 'os' + import b from 'benny' const { @@ -8,25 +10,32 @@ const { const buffer = Buffer.from('hello 🚀 rust!') +const ALL_THREADS = Array.from({ length: cpus().length }) + export const benchAsync = () => b.suite( 'Async task', b.add('spawn task', async () => { - await benchAsyncTask(buffer) + await Promise.all(ALL_THREADS.map(() => benchAsyncTask(buffer))) }), b.add('ThreadSafeFunction', async () => { - await new Promise((resolve, reject) => { - benchThreadsafeFunction(buffer, (err?: Error, value?: number) => { - if (err) { - reject(err) - } else { - resolve(value) - } - }) - }) + await Promise.all( + ALL_THREADS.map( + () => + new Promise((resolve, reject) => { + benchThreadsafeFunction(buffer, (err?: Error, value?: number) => { + if (err) { + reject(err) + } else { + resolve(value) + } + }) + }), + ), + ) }), b.add('Tokio future to Promise', async () => { - await benchTokioFuture(buffer) + await Promise.all(ALL_THREADS.map(() => benchTokioFuture(buffer))) }), b.cycle(), b.complete(), diff --git a/bench/buffer.ts b/bench/buffer.ts index 24554c68..705dce80 100644 --- a/bench/buffer.ts +++ b/bench/buffer.ts @@ -3,7 +3,7 @@ import b from 'benny' const { benchCreateBuffer } = require('./index.node') function createBuffer() { - const buf = Buffer.alloc(100000) + const buf = Buffer.allocUnsafe(1024) buf[0] = 1 buf[1] = 2 return buf diff --git a/bench/query.ts b/bench/query.ts index 48f1307c..b9eed04c 100644 --- a/bench/query.ts +++ b/bench/query.ts @@ -7,10 +7,10 @@ const e = engine('model A {}') export const benchQuery = () => b.suite( 'Query', - b.add('napi-rs', async () => { + b.add('query * 100', async () => { await Promise.all(Array.from({ length: 100 }).map(() => query(e))) }), - b.add('neon', async () => { + b.add('query * 1', async () => { await query(e) }), diff --git a/bench/src/buffer.rs b/bench/src/buffer.rs index f92fabff..e7578156 100644 --- a/bench/src/buffer.rs +++ b/bench/src/buffer.rs @@ -2,7 +2,7 @@ use napi::{ContextlessResult, Env, JsBuffer, JsObject, Result}; #[contextless_function] pub fn bench_create_buffer(env: Env) -> ContextlessResult { - let mut output = Vec::with_capacity(100000); + let mut output = Vec::with_capacity(1024); output.push(1); output.push(2); env diff --git a/bench/src/lib.rs b/bench/src/lib.rs index dbf146ec..a6d5a6f6 100644 --- a/bench/src/lib.rs +++ b/bench/src/lib.rs @@ -3,11 +3,11 @@ extern crate napi_derive; use napi::{Env, JsObject, Result}; -#[cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64")))] -#[global_allocator] -static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; - -#[cfg(all(windows, not(target_arch = "aarch64")))] +#[cfg(all( + target_arch = "x86_64", + not(target_env = "musl"), + not(debug_assertions) +))] #[global_allocator] static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; diff --git a/napi/src/env.rs b/napi/src/env.rs index d2c16fdc..41736994 100644 --- a/napi/src/env.rs +++ b/napi/src/env.rs @@ -1132,8 +1132,7 @@ impl Env { })?; let raw_env = self.0; - let future_promise = - promise::FuturePromise::create(raw_env, raw_deferred, Box::from(resolver))?; + let future_promise = promise::FuturePromise::create(raw_env, raw_deferred, resolver)?; let future_to_resolve = promise::resolve_from_future(future_promise.start()?, fut); handle.spawn(future_to_resolve); Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_promise) }) @@ -1286,6 +1285,7 @@ impl Env { /// } /// ``` #[cfg(feature = "serde-json")] + #[allow(clippy::wrong_self_convention)] #[inline] pub fn to_js_value(&self, node: &T) -> Result where diff --git a/napi/src/promise.rs b/napi/src/promise.rs index 45aea7e5..63e538ee 100644 --- a/napi/src/promise.rs +++ b/napi/src/promise.rs @@ -1,26 +1,25 @@ use std::future::Future; +use std::marker::PhantomData; use std::os::raw::{c_char, c_void}; use std::ptr; use crate::{check_status, sys, Env, JsError, NapiRaw, Result}; -pub struct FuturePromise { +pub struct FuturePromise Result> { deferred: sys::napi_deferred, env: sys::napi_env, tsfn: sys::napi_threadsafe_function, async_resource_name: sys::napi_value, - resolver: Box Result>, + resolver: F, + _data: PhantomData, + _value: PhantomData, } -unsafe impl Send for FuturePromise {} +unsafe impl Result> Send for FuturePromise {} -impl FuturePromise { +impl Result> FuturePromise { #[inline] - pub fn create( - env: sys::napi_env, - raw_deferred: sys::napi_deferred, - resolver: Box Result>, - ) -> Result { + pub fn create(env: sys::napi_env, raw_deferred: sys::napi_deferred, resolver: F) -> Result { let mut async_resource_name = ptr::null_mut(); let s = "napi_resolve_promise_from_future"; check_status!(unsafe { @@ -38,6 +37,8 @@ impl FuturePromise { env, tsfn: ptr::null_mut(), async_resource_name, + _data: PhantomData, + _value: PhantomData, }) } @@ -57,8 +58,8 @@ impl FuturePromise { 1, ptr::null_mut(), None, - self_ref as *mut FuturePromise as *mut c_void, - Some(call_js_cb::), + self_ref as *mut FuturePromise as *mut c_void, + Some(call_js_cb::), &mut tsfn_value, ) })?; @@ -94,14 +95,14 @@ pub(crate) async fn resolve_from_future>>( .expect("Failed to release thread safe function"); } -unsafe extern "C" fn call_js_cb( +unsafe extern "C" fn call_js_cb Result>( raw_env: sys::napi_env, _js_callback: sys::napi_value, context: *mut c_void, data: *mut c_void, ) { let mut env = Env::from_raw(raw_env); - let future_promise = Box::from_raw(context as *mut FuturePromise); + let future_promise = Box::from_raw(context as *mut FuturePromise); let value = Box::from_raw(data as *mut Result); let resolver = future_promise.resolver; let deferred = future_promise.deferred; diff --git a/napi/src/threadsafe_function.rs b/napi/src/threadsafe_function.rs index e0d3d929..607ce3fb 100644 --- a/napi/src/threadsafe_function.rs +++ b/napi/src/threadsafe_function.rs @@ -201,7 +201,7 @@ impl ThreadsafeFunction { let initial_thread_count = 1usize; let mut raw_tsfn = ptr::null_mut(); - let ptr = Box::into_raw(Box::new(callback)) as *mut _; + let ptr = Box::into_raw(Box::new(callback)) as *mut c_void; check_status!(unsafe { sys::napi_create_threadsafe_function( env, @@ -389,7 +389,7 @@ unsafe extern "C" fn call_js_cb( ); } Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => { - panic!("{:?}", e); + panic!("{}", e); } Err(e) => { status = sys::napi_call_function( @@ -402,6 +402,9 @@ unsafe extern "C" fn call_js_cb( ); } } + if status == sys::Status::napi_ok { + return; + } if status == sys::Status::napi_pending_exception { let mut error_result = ptr::null_mut(); assert_eq!( @@ -412,7 +415,7 @@ unsafe extern "C" fn call_js_cb( sys::napi_fatal_exception(raw_env, error_result), sys::Status::napi_ok ); - } else if status != sys::Status::napi_ok { + } else { let error_code: Status = status.into(); let error_code_string = format!("{:?}", error_code); let mut error_code_value = ptr::null_mut();