New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Poor Buffer (and possibly bigint?) performance #1973
Comments
Also creating/maintaining the Arc seems to be adding a bit of overhead. |
@vekexasia can you try the code on branch #1975 |
hey @Brooooooklyn thanks for the reply. I see there is no branch associated to that pr anymore. Any easy way for me to try what's been merged? or do i have to git clone , build and specify the path in cargo.toml sorry for the dumb question. Quite a bit new to rust. |
It was merged, so you can try the main branch code now. https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories |
Ok i think i managed to test. here results:with:
with
custom implementation of buffer
here is my custom impl of Buffer called Buffer2 impl napi::bindgen_prelude::FromNapiValue for Buffer2 {
unsafe fn from_napi_value(
env: napi::sys::napi_env,
napi_val: napi::sys::napi_value,
) -> napi::Result<Self> {
let mut buf = ptr::null_mut();
let mut len = 0;
// napi::check_status!(
// unsafe { sys::napi_create_reference(env, napi_val, 1, &mut ref_) },
// "Failed to create reference from Buffer"
// )?;
napi::check_status!(
unsafe { napi::sys::napi_get_buffer_info(env, napi_val, &mut buf, &mut len as *mut usize) },
"Failed to get Buffer pointer and length"
)?;
// From the docs of `napi_get_buffer_info`:
// > [out] data: The underlying data buffer of the node::Buffer. If length is 0, this may be
// > NULL or any other pointer value.
//
// In order to guarantee that `slice::from_raw_parts` is sound, the pointer must be non-null, so
// let's make sure it always is, even in the case of `napi_get_buffer_info` returning a null
// ptr.
let buf = NonNull::new(buf as *mut u8);
let inner = match buf {
Some(buf) if len != 0 => buf,
_ => NonNull::dangling(),
};
Ok(Self {
inner,
len,
capacity: len,
})
}
}
impl AsRef<[u8]> for Buffer2 {
fn as_ref(&self) -> &[u8] {
// SAFETY: the pointer is guaranteed to be non-null, and guaranteed to be valid if `len` is not 0.
unsafe { std::slice::from_raw_parts(self.inner.as_ptr(), self.len) }
}
}
impl AsMut<[u8]> for Buffer2 {
fn as_mut(&mut self) -> &mut [u8] {
// SAFETY: This is literally undefined behavior. `Buffer::clone` allows you to create shared
// access to the underlying data, but `as_mut` and `deref_mut` allow unsynchronized mutation of
// that data (not to speak of the JS side having write access as well, at the same time).
unsafe { std::slice::from_raw_parts_mut(self.inner.as_ptr(), self.len) }
}
}
impl std::ops::Deref for Buffer2 {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl std::ops::DerefMut for Buffer2 {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
} |
It looks like passing buffer using napi-rs introduces a lot of computing overhead.
I discovered this while working on a new library hoping to replace the current native C binding. I wanted to benchmark the new code and to my surprise the rust version was a lot slower. The benchmark is basically a for loop to 40000000 in nodejs calling every time the associated fn.
The library I was working on should deal with both BigInts and Buffers so i created a simple .rs lib containing several empty functions whose signature can be seen in the results below:
I then created a .c file, compiled with node-gyp and here the results:
noop is self-explanatory and is on-par with
nothing()
in rust.bufferOnly_c
and bufferOnlyWithRef_c are basically the same with the exception that "WithRef" also handles napi_create_reference and napi_delete_reference which i saw is being used in napi-rs in the FromNapiValue trait implementation for Buffer.anyway. here the code:
There is obviously some other overhead in rust implementation that might justify this gap:
I'm out of ideas on how to optimize this and to be honest i'm not even sure why we want to use napi_create/delete_reference.
Any other insight is more than welcome. If needed i can provide a github repo with some messy code i was playing with.
The text was updated successfully, but these errors were encountered: