Merge pull request #621 from napi-rs/optimize-execute-tokio
perf(napi): make FuturePromise static dispatch
This commit is contained in:
commit
b4158e5e76
10 changed files with 55 additions and 42 deletions
|
@ -9,3 +9,6 @@ members = [
|
||||||
"./bench",
|
"./bench",
|
||||||
"./memory-testing",
|
"./memory-testing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
|
|
@ -13,10 +13,7 @@ napi-derive = {path = "../napi-derive"}
|
||||||
serde = "1"
|
serde = "1"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64"), not(target_arch = "arm")))'.dependencies]
|
[target.'cfg(all(target_arch = "x86_64", not(target_env = "musl")))'.dependencies]
|
||||||
jemallocator = {version = "0.3", features = ["disable_initial_exec_tls"]}
|
|
||||||
|
|
||||||
[target.'cfg(all(windows, not(target_arch = "aarch64")))'.dependencies]
|
|
||||||
mimalloc = {version = "0.1"}
|
mimalloc = {version = "0.1"}
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { cpus } from 'os'
|
||||||
|
|
||||||
import b from 'benny'
|
import b from 'benny'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -8,25 +10,32 @@ const {
|
||||||
|
|
||||||
const buffer = Buffer.from('hello 🚀 rust!')
|
const buffer = Buffer.from('hello 🚀 rust!')
|
||||||
|
|
||||||
|
const ALL_THREADS = Array.from({ length: cpus().length })
|
||||||
|
|
||||||
export const benchAsync = () =>
|
export const benchAsync = () =>
|
||||||
b.suite(
|
b.suite(
|
||||||
'Async task',
|
'Async task',
|
||||||
b.add('spawn task', async () => {
|
b.add('spawn task', async () => {
|
||||||
await benchAsyncTask(buffer)
|
await Promise.all(ALL_THREADS.map(() => benchAsyncTask(buffer)))
|
||||||
}),
|
}),
|
||||||
b.add('ThreadSafeFunction', async () => {
|
b.add('ThreadSafeFunction', async () => {
|
||||||
await new Promise<number | undefined>((resolve, reject) => {
|
await Promise.all(
|
||||||
benchThreadsafeFunction(buffer, (err?: Error, value?: number) => {
|
ALL_THREADS.map(
|
||||||
if (err) {
|
() =>
|
||||||
reject(err)
|
new Promise<number | undefined>((resolve, reject) => {
|
||||||
} else {
|
benchThreadsafeFunction(buffer, (err?: Error, value?: number) => {
|
||||||
resolve(value)
|
if (err) {
|
||||||
}
|
reject(err)
|
||||||
})
|
} else {
|
||||||
})
|
resolve(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
b.add('Tokio future to Promise', async () => {
|
b.add('Tokio future to Promise', async () => {
|
||||||
await benchTokioFuture(buffer)
|
await Promise.all(ALL_THREADS.map(() => benchTokioFuture(buffer)))
|
||||||
}),
|
}),
|
||||||
b.cycle(),
|
b.cycle(),
|
||||||
b.complete(),
|
b.complete(),
|
||||||
|
|
|
@ -3,7 +3,7 @@ import b from 'benny'
|
||||||
const { benchCreateBuffer } = require('./index.node')
|
const { benchCreateBuffer } = require('./index.node')
|
||||||
|
|
||||||
function createBuffer() {
|
function createBuffer() {
|
||||||
const buf = Buffer.alloc(100000)
|
const buf = Buffer.allocUnsafe(1024)
|
||||||
buf[0] = 1
|
buf[0] = 1
|
||||||
buf[1] = 2
|
buf[1] = 2
|
||||||
return buf
|
return buf
|
||||||
|
|
|
@ -7,10 +7,10 @@ const e = engine('model A {}')
|
||||||
export const benchQuery = () =>
|
export const benchQuery = () =>
|
||||||
b.suite(
|
b.suite(
|
||||||
'Query',
|
'Query',
|
||||||
b.add('napi-rs', async () => {
|
b.add('query * 100', async () => {
|
||||||
await Promise.all(Array.from({ length: 100 }).map(() => query(e)))
|
await Promise.all(Array.from({ length: 100 }).map(() => query(e)))
|
||||||
}),
|
}),
|
||||||
b.add('neon', async () => {
|
b.add('query * 1', async () => {
|
||||||
await query(e)
|
await query(e)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use napi::{ContextlessResult, Env, JsBuffer, JsObject, Result};
|
||||||
|
|
||||||
#[contextless_function]
|
#[contextless_function]
|
||||||
pub fn bench_create_buffer(env: Env) -> ContextlessResult<JsBuffer> {
|
pub fn bench_create_buffer(env: Env) -> ContextlessResult<JsBuffer> {
|
||||||
let mut output = Vec::with_capacity(100000);
|
let mut output = Vec::with_capacity(1024);
|
||||||
output.push(1);
|
output.push(1);
|
||||||
output.push(2);
|
output.push(2);
|
||||||
env
|
env
|
||||||
|
|
|
@ -3,11 +3,11 @@ extern crate napi_derive;
|
||||||
|
|
||||||
use napi::{Env, JsObject, Result};
|
use napi::{Env, JsObject, Result};
|
||||||
|
|
||||||
#[cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64")))]
|
#[cfg(all(
|
||||||
#[global_allocator]
|
target_arch = "x86_64",
|
||||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
not(target_env = "musl"),
|
||||||
|
not(debug_assertions)
|
||||||
#[cfg(all(windows, not(target_arch = "aarch64")))]
|
))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
|
|
|
@ -1132,8 +1132,7 @@ impl Env {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let raw_env = self.0;
|
let raw_env = self.0;
|
||||||
let future_promise =
|
let future_promise = promise::FuturePromise::create(raw_env, raw_deferred, resolver)?;
|
||||||
promise::FuturePromise::create(raw_env, raw_deferred, Box::from(resolver))?;
|
|
||||||
let future_to_resolve = promise::resolve_from_future(future_promise.start()?, fut);
|
let future_to_resolve = promise::resolve_from_future(future_promise.start()?, fut);
|
||||||
handle.spawn(future_to_resolve);
|
handle.spawn(future_to_resolve);
|
||||||
Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_promise) })
|
Ok(unsafe { JsObject::from_raw_unchecked(self.0, raw_promise) })
|
||||||
|
@ -1286,6 +1285,7 @@ impl Env {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(feature = "serde-json")]
|
#[cfg(feature = "serde-json")]
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_js_value<T>(&self, node: &T) -> Result<JsUnknown>
|
pub fn to_js_value<T>(&self, node: &T) -> Result<JsUnknown>
|
||||||
where
|
where
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use crate::{check_status, sys, Env, JsError, NapiRaw, Result};
|
use crate::{check_status, sys, Env, JsError, NapiRaw, Result};
|
||||||
|
|
||||||
pub struct FuturePromise<T, V: NapiRaw> {
|
pub struct FuturePromise<T, V: NapiRaw, F: FnOnce(&mut Env, T) -> Result<V>> {
|
||||||
deferred: sys::napi_deferred,
|
deferred: sys::napi_deferred,
|
||||||
env: sys::napi_env,
|
env: sys::napi_env,
|
||||||
tsfn: sys::napi_threadsafe_function,
|
tsfn: sys::napi_threadsafe_function,
|
||||||
async_resource_name: sys::napi_value,
|
async_resource_name: sys::napi_value,
|
||||||
resolver: Box<dyn FnOnce(&mut Env, T) -> Result<V>>,
|
resolver: F,
|
||||||
|
_data: PhantomData<T>,
|
||||||
|
_value: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T, V: NapiRaw> Send for FuturePromise<T, V> {}
|
unsafe impl<T, V: NapiRaw, F: FnOnce(&mut Env, T) -> Result<V>> Send for FuturePromise<T, V, F> {}
|
||||||
|
|
||||||
impl<T, V: NapiRaw> FuturePromise<T, V> {
|
impl<T, V: NapiRaw, F: FnOnce(&mut Env, T) -> Result<V>> FuturePromise<T, V, F> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create(
|
pub fn create(env: sys::napi_env, raw_deferred: sys::napi_deferred, resolver: F) -> Result<Self> {
|
||||||
env: sys::napi_env,
|
|
||||||
raw_deferred: sys::napi_deferred,
|
|
||||||
resolver: Box<dyn FnOnce(&mut Env, T) -> Result<V>>,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let mut async_resource_name = ptr::null_mut();
|
let mut async_resource_name = ptr::null_mut();
|
||||||
let s = "napi_resolve_promise_from_future";
|
let s = "napi_resolve_promise_from_future";
|
||||||
check_status!(unsafe {
|
check_status!(unsafe {
|
||||||
|
@ -38,6 +37,8 @@ impl<T, V: NapiRaw> FuturePromise<T, V> {
|
||||||
env,
|
env,
|
||||||
tsfn: ptr::null_mut(),
|
tsfn: ptr::null_mut(),
|
||||||
async_resource_name,
|
async_resource_name,
|
||||||
|
_data: PhantomData,
|
||||||
|
_value: PhantomData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +58,8 @@ impl<T, V: NapiRaw> FuturePromise<T, V> {
|
||||||
1,
|
1,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
None,
|
None,
|
||||||
self_ref as *mut FuturePromise<T, V> as *mut c_void,
|
self_ref as *mut FuturePromise<T, V, F> as *mut c_void,
|
||||||
Some(call_js_cb::<T, V>),
|
Some(call_js_cb::<T, V, F>),
|
||||||
&mut tsfn_value,
|
&mut tsfn_value,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
@ -94,14 +95,14 @@ pub(crate) async fn resolve_from_future<T: Send, F: Future<Output = Result<T>>>(
|
||||||
.expect("Failed to release thread safe function");
|
.expect("Failed to release thread safe function");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn call_js_cb<T, V: NapiRaw>(
|
unsafe extern "C" fn call_js_cb<T, V: NapiRaw, F: FnOnce(&mut Env, T) -> Result<V>>(
|
||||||
raw_env: sys::napi_env,
|
raw_env: sys::napi_env,
|
||||||
_js_callback: sys::napi_value,
|
_js_callback: sys::napi_value,
|
||||||
context: *mut c_void,
|
context: *mut c_void,
|
||||||
data: *mut c_void,
|
data: *mut c_void,
|
||||||
) {
|
) {
|
||||||
let mut env = Env::from_raw(raw_env);
|
let mut env = Env::from_raw(raw_env);
|
||||||
let future_promise = Box::from_raw(context as *mut FuturePromise<T, V>);
|
let future_promise = Box::from_raw(context as *mut FuturePromise<T, V, F>);
|
||||||
let value = Box::from_raw(data as *mut Result<T>);
|
let value = Box::from_raw(data as *mut Result<T>);
|
||||||
let resolver = future_promise.resolver;
|
let resolver = future_promise.resolver;
|
||||||
let deferred = future_promise.deferred;
|
let deferred = future_promise.deferred;
|
||||||
|
|
|
@ -201,7 +201,7 @@ impl<T: 'static, ES: ErrorStrategy::T> ThreadsafeFunction<T, ES> {
|
||||||
|
|
||||||
let initial_thread_count = 1usize;
|
let initial_thread_count = 1usize;
|
||||||
let mut raw_tsfn = ptr::null_mut();
|
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 {
|
check_status!(unsafe {
|
||||||
sys::napi_create_threadsafe_function(
|
sys::napi_create_threadsafe_function(
|
||||||
env,
|
env,
|
||||||
|
@ -389,7 +389,7 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => {
|
Err(e) if ES::VALUE == ErrorStrategy::Fatal::VALUE => {
|
||||||
panic!("{:?}", e);
|
panic!("{}", e);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
status = sys::napi_call_function(
|
status = sys::napi_call_function(
|
||||||
|
@ -402,6 +402,9 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if status == sys::Status::napi_ok {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if status == sys::Status::napi_pending_exception {
|
if status == sys::Status::napi_pending_exception {
|
||||||
let mut error_result = ptr::null_mut();
|
let mut error_result = ptr::null_mut();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -412,7 +415,7 @@ unsafe extern "C" fn call_js_cb<T: 'static, V: NapiRaw, R, ES>(
|
||||||
sys::napi_fatal_exception(raw_env, error_result),
|
sys::napi_fatal_exception(raw_env, error_result),
|
||||||
sys::Status::napi_ok
|
sys::Status::napi_ok
|
||||||
);
|
);
|
||||||
} else if status != sys::Status::napi_ok {
|
} else {
|
||||||
let error_code: Status = status.into();
|
let error_code: Status = status.into();
|
||||||
let error_code_string = format!("{:?}", error_code);
|
let error_code_string = format!("{:?}", error_code);
|
||||||
let mut error_code_value = ptr::null_mut();
|
let mut error_code_value = ptr::null_mut();
|
||||||
|
|
Loading…
Reference in a new issue