From 022aae9854bed6219d75eeb82fcf46652c21050d Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Tue, 2 May 2023 20:17:11 +0530 Subject: perf(core): use jemalloc for V8 array buffer allocator (#18875) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- core/Cargo.toml | 3 +++ core/runtime.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) (limited to 'core') diff --git a/core/Cargo.toml b/core/Cargo.toml index 141583710..0e0b1d2c7 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -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" diff --git a/core/runtime.rs b/core/runtime.rs index 46256b8d8..5470a89b9 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -72,6 +72,48 @@ struct IsolateAllocations { Option<(Box>, 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). @@ -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() @@ -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), -- cgit v1.2.3