summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2023-05-02 20:17:11 +0530
committerGitHub <noreply@github.com>2023-05-02 20:17:11 +0530
commit022aae9854bed6219d75eeb82fcf46652c21050d (patch)
tree1f0cfa03c8510d5dcca825ebbb3e539be7aef58f
parent97147faf891dce8d77bc829cbd0ebc1afebb2575 (diff)
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>
-rw-r--r--Cargo.lock11
-rw-r--r--core/Cargo.toml3
-rw-r--r--core/runtime.rs64
3 files changed, 78 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 77b6c0cbb..f4647350b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -875,6 +875,7 @@ dependencies = [
"serde_v8",
"smallvec",
"sourcemap",
+ "tikv-jemalloc-sys",
"tokio",
"url",
"v8",
@@ -5131,6 +5132,16 @@ dependencies = [
]
[[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]]
name = "time"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
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<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).
@@ -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),