Skip to content

Commit

Permalink
perf(core): use jemalloc for V8 array buffer allocator (#18875)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
2 people authored and levex committed May 4, 2023
1 parent 8f46dc2 commit f924066
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ sourcemap = "6.1"
url.workspace = true
v8.workspace = true

[target.'cfg(not(target_env = "msvc"))'.dependencies]
tikv-jemalloc-sys = "0.5"

[[example]]
name = "http_bench_json_ops"
path = "examples/http_bench_json_ops/main.rs"
Expand Down
64 changes: 64 additions & 0 deletions core/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,48 @@ struct IsolateAllocations {
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
/// Worker" concept in the DOM. A JsRuntime is a Future that can be used with
/// an event loop (Tokio, async_std).
Expand Down Expand Up @@ -393,6 +435,20 @@ impl JsRuntime {
}
(isolate, snapshot_options)
} 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
.create_params
.take()
Expand All @@ -404,6 +460,14 @@ impl JsRuntime {
})
.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 {
params = match snapshot {
Snapshot::Static(data) => params.snapshot_blob(data),
Expand Down

0 comments on commit f924066

Please sign in to comment.