mirror of
https://github.com/denoland/deno.git
synced 2025-01-03 04:48:52 -05:00
perf(core): use jemalloc for V8 array buffer allocator (#18875)
This commits changes "deno_core" to use jemalloc allocator as an allocator for V8 array buffers. This greatly improves our GC characteristics as we are using a lot of short lived array buffers. They no longer go through the expensive malloc/free cycle using the default Rust allocator, but instead use jemallocator's memory pool. As a result the flamegraphs for WS/HTTP server flamegraphs no longer show stacks for malloc/free around ops that use ZeroCopyBuf and &[u8]. --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
97147faf89
commit
022aae9854
3 changed files with 78 additions and 0 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -875,6 +875,7 @@ dependencies = [
|
||||||
"serde_v8",
|
"serde_v8",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sourcemap",
|
"sourcemap",
|
||||||
|
"tikv-jemalloc-sys",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
"v8",
|
"v8",
|
||||||
|
@ -5130,6 +5131,16 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tikv-jemalloc-sys"
|
||||||
|
version = "0.5.3+5.3.0-patched"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.20"
|
version = "0.3.20"
|
||||||
|
|
|
@ -39,6 +39,9 @@ sourcemap = "6.1"
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
v8.workspace = true
|
v8.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||||
|
tikv-jemalloc-sys = "0.5"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "http_bench_json_ops"
|
name = "http_bench_json_ops"
|
||||||
path = "examples/http_bench_json_ops/main.rs"
|
path = "examples/http_bench_json_ops/main.rs"
|
||||||
|
|
|
@ -72,6 +72,48 @@ struct IsolateAllocations {
|
||||||
Option<(Box<RefCell<dyn Any>>, v8::NearHeapLimitCallback)>,
|
Option<(Box<RefCell<dyn Any>>, v8::NearHeapLimitCallback)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A custom allocator for array buffers for V8. It uses `jemalloc` so it's
|
||||||
|
/// not available on Windows.
|
||||||
|
#[cfg(not(target_env = "msvc"))]
|
||||||
|
mod custom_allocator {
|
||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
pub struct RustAllocator;
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn allocate(
|
||||||
|
_alloc: &RustAllocator,
|
||||||
|
n: usize,
|
||||||
|
) -> *mut c_void {
|
||||||
|
tikv_jemalloc_sys::calloc(1, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn allocate_uninitialized(
|
||||||
|
_alloc: &RustAllocator,
|
||||||
|
n: usize,
|
||||||
|
) -> *mut c_void {
|
||||||
|
tikv_jemalloc_sys::malloc(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn free(
|
||||||
|
_alloc: &RustAllocator,
|
||||||
|
data: *mut c_void,
|
||||||
|
_n: usize,
|
||||||
|
) {
|
||||||
|
tikv_jemalloc_sys::free(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn reallocate(
|
||||||
|
_alloc: &RustAllocator,
|
||||||
|
prev: *mut c_void,
|
||||||
|
_oldlen: usize,
|
||||||
|
newlen: usize,
|
||||||
|
) -> *mut c_void {
|
||||||
|
tikv_jemalloc_sys::realloc(prev, newlen)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn drop(_alloc: *const RustAllocator) {}
|
||||||
|
}
|
||||||
|
|
||||||
/// A single execution context of JavaScript. Corresponds roughly to the "Web
|
/// A single execution context of JavaScript. Corresponds roughly to the "Web
|
||||||
/// Worker" concept in the DOM. A JsRuntime is a Future that can be used with
|
/// Worker" concept in the DOM. A JsRuntime is a Future that can be used with
|
||||||
/// an event loop (Tokio, async_std).
|
/// an event loop (Tokio, async_std).
|
||||||
|
@ -393,6 +435,20 @@ impl JsRuntime {
|
||||||
}
|
}
|
||||||
(isolate, snapshot_options)
|
(isolate, snapshot_options)
|
||||||
} else {
|
} else {
|
||||||
|
#[cfg(not(target_env = "msvc"))]
|
||||||
|
let vtable: &'static v8::RustAllocatorVtable<
|
||||||
|
custom_allocator::RustAllocator,
|
||||||
|
> = &v8::RustAllocatorVtable {
|
||||||
|
allocate: custom_allocator::allocate,
|
||||||
|
allocate_uninitialized: custom_allocator::allocate_uninitialized,
|
||||||
|
free: custom_allocator::free,
|
||||||
|
reallocate: custom_allocator::reallocate,
|
||||||
|
drop: custom_allocator::drop,
|
||||||
|
};
|
||||||
|
#[cfg(not(target_env = "msvc"))]
|
||||||
|
let allocator = Arc::new(custom_allocator::RustAllocator);
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
let mut params = options
|
let mut params = options
|
||||||
.create_params
|
.create_params
|
||||||
.take()
|
.take()
|
||||||
|
@ -404,6 +460,14 @@ impl JsRuntime {
|
||||||
})
|
})
|
||||||
.external_references(&**refs);
|
.external_references(&**refs);
|
||||||
|
|
||||||
|
#[cfg(not(target_env = "msvc"))]
|
||||||
|
// SAFETY: We are leaking the created `allocator` variable so we're sure
|
||||||
|
// it will outlive the created isolate. We also made sure that the vtable
|
||||||
|
// is correct.
|
||||||
|
let mut params = params.array_buffer_allocator(unsafe {
|
||||||
|
v8::new_rust_allocator(Arc::into_raw(allocator), vtable)
|
||||||
|
});
|
||||||
|
|
||||||
if let Some(snapshot) = options.startup_snapshot {
|
if let Some(snapshot) = options.startup_snapshot {
|
||||||
params = match snapshot {
|
params = match snapshot {
|
||||||
Snapshot::Static(data) => params.snapshot_blob(data),
|
Snapshot::Static(data) => params.snapshot_blob(data),
|
||||||
|
|
Loading…
Reference in a new issue