diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2022-07-08 23:19:09 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-08 23:19:09 +0530 |
commit | 20cbd7f0f8f0e0ff4d6656f2fa7e93e01b8805f0 (patch) | |
tree | a26bb845b656d1e28e6f808e7422557e25d4489e | |
parent | b3b30182738981331cb8dcd028f0accafdc227c0 (diff) |
perf(ext/ffi): leverage V8 Fast Calls (#15125)
-rw-r--r-- | core/runtime.rs | 1 | ||||
-rw-r--r-- | ext/ffi/lib.rs | 96 | ||||
-rw-r--r-- | test_ffi/tests/bench.js | 174 |
3 files changed, 209 insertions, 62 deletions
diff --git a/core/runtime.rs b/core/runtime.rs index daefe4121..2026232ef 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -227,6 +227,7 @@ fn v8_init( " --wasm-test-streaming", " --harmony-import-assertions", " --no-validate-asm", + " --turbo_fast_api_calls", ); if predictable { diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 17ebf772b..d998c8138 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -10,6 +10,7 @@ use deno_core::futures::channel::mpsc; use deno_core::futures::Future; use deno_core::include_js_files; use deno_core::op; +use deno_core::v8::fast_api; use std::sync::mpsc::sync_channel; use deno_core::serde_json::json; @@ -604,7 +605,7 @@ where match foreign_fn.non_blocking { // Generate functions for synchronous calls. Some(false) | None => { - let function = make_sync_fn(scope, Box::leak(sym)); + let function = make_sync_fn(scope, sym); obj.set(scope, func_key.into(), function.into()); } // This optimization is not yet supported for non-blocking calls. @@ -623,13 +624,91 @@ where )) } +pub struct FfiFastCallTemplate { + args: Box<[fast_api::Type]>, + ret: fast_api::CType, + symbol_ptr: *const c_void, +} + +impl fast_api::FastFunction for FfiFastCallTemplate { + type Signature = (); + fn function(&self) -> Self::Signature {} + + fn raw(&self) -> *const c_void { + self.symbol_ptr + } + fn args(&self) -> &'static [fast_api::Type] { + Box::leak(self.args.clone()) + } + fn return_type(&self) -> fast_api::CType { + self.ret + } +} + +impl From<&NativeType> for fast_api::Type { + fn from(native_type: &NativeType) -> Self { + match native_type { + NativeType::U8 | NativeType::U16 | NativeType::U32 => { + fast_api::Type::Uint32 + } + NativeType::I8 | NativeType::I16 | NativeType::I32 => { + fast_api::Type::Int32 + } + NativeType::F32 => fast_api::Type::Float32, + NativeType::F64 => fast_api::Type::Float64, + NativeType::Void => fast_api::Type::Void, + NativeType::Function + | NativeType::Pointer + | NativeType::I64 + | NativeType::ISize + | NativeType::U64 + | NativeType::USize => { + panic!("Cannot be fast api") + } + } + } +} + +fn is_fast_api(rv: NativeType) -> bool { + !matches!( + rv, + NativeType::Function + | NativeType::Pointer + | NativeType::I64 + | NativeType::ISize + | NativeType::U64 + | NativeType::USize + ) +} + // Create a JavaScript function for synchronous FFI call to // the given symbol. fn make_sync_fn<'s>( scope: &mut v8::HandleScope<'s>, - sym: *mut Symbol, + sym: Box<Symbol>, ) -> v8::Local<'s, v8::Function> { - let func = v8::Function::builder( + let mut fast_ffi_templ = None; + + if !sym.parameter_types.iter().any(|t| !is_fast_api(*t)) + && is_fast_api(sym.result_type) + { + let mut args = sym + .parameter_types + .iter() + .map(|t| t.into()) + .collect::<Vec<_>>(); + if args.is_empty() { + args.push(fast_api::Type::V8Value); + } + fast_ffi_templ = Some(FfiFastCallTemplate { + args: args.into_boxed_slice(), + ret: (&fast_api::Type::from(&sym.result_type)).into(), + symbol_ptr: sym.ptr.as_ptr() as *const c_void, + }); + } + + let sym = Box::leak(sym); + let builder = v8::FunctionTemplate::builder( |scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, mut rv: v8::ReturnValue| { @@ -650,9 +729,14 @@ fn make_sync_fn<'s>( }; }, ) - .data(v8::External::new(scope, sym as *mut _).into()) - .build(scope) - .unwrap(); + .data(v8::External::new(scope, sym as *mut Symbol as *mut _).into()); + + let func = if let Some(fast_ffi_templ) = fast_ffi_templ { + builder.build_fast(scope, fast_ffi_templ) + } else { + builder.build(scope) + }; + let func = func.get_function(scope).unwrap(); let weak = v8::Weak::with_finalizer( scope, diff --git a/test_ffi/tests/bench.js b/test_ffi/tests/bench.js index 398732cc8..e611d9f3c 100644 --- a/test_ffi/tests/bench.js +++ b/test_ffi/tests/bench.js @@ -11,6 +11,7 @@ const libPath = `${targetDir}/${libPrefix}test_ffi.${libSuffix}`; const dylib = Deno.dlopen(libPath, { "nop": { parameters: [], result: "void" }, + "add_u32": { parameters: ["u32", "u32"], result: "u32" }, "nop_u8": { parameters: ["u8"], result: "void" }, "nop_i8": { parameters: ["i8"], result: "void" }, "nop_u16": { parameters: ["u16"], result: "void" }, @@ -220,228 +221,288 @@ const dylib = Deno.dlopen(libPath, { }, }); +const { nop } = dylib.symbols; Deno.bench("nop()", () => { - dylib.symbols.nop(); + nop(); }); +const { add_u32 } = dylib.symbols; +Deno.bench("add_u32()", () => { + add_u32(1, 2); +}); + +const { nop_u8 } = dylib.symbols; Deno.bench("nop_u8()", () => { - dylib.symbols.nop_u8(100); + nop_u8(100); }); +const { nop_i8 } = dylib.symbols; Deno.bench("nop_i8()", () => { - dylib.symbols.nop_i8(100); + nop_i8(100); }); +const { nop_u16 } = dylib.symbols; Deno.bench("nop_u16()", () => { - dylib.symbols.nop_u16(100); + nop_u16(100); }); +const { nop_i16 } = dylib.symbols; Deno.bench("nop_i16()", () => { - dylib.symbols.nop_i16(100); + nop_i16(100); }); +const { nop_u32 } = dylib.symbols; Deno.bench("nop_u32()", () => { - dylib.symbols.nop_u32(100); + nop_u32(100); }); +const { nop_i32 } = dylib.symbols; Deno.bench("nop_i32()", () => { - dylib.symbols.nop_i32(100); + nop_i32(100); }); +const { nop_u64 } = dylib.symbols; Deno.bench("nop_u64()", () => { - dylib.symbols.nop_u64(100); + nop_u64(100); }); +const { nop_i64 } = dylib.symbols; Deno.bench("nop_i64()", () => { - dylib.symbols.nop_i64(100); + nop_i64(100); }); +const { nop_usize } = dylib.symbols; Deno.bench("nop_usize()", () => { - dylib.symbols.nop_usize(100); + nop_usize(100n); }); +const { nop_isize } = dylib.symbols; Deno.bench("nop_isize()", () => { - dylib.symbols.nop_isize(100); + nop_isize(100n); }); +const { nop_f32 } = dylib.symbols; Deno.bench("nop_f32()", () => { - dylib.symbols.nop_f32(100); + nop_f32(100.1); }); +const { nop_f64 } = dylib.symbols; Deno.bench("nop_f64()", () => { - dylib.symbols.nop_f64(100); + nop_f64(100.1); }); +const { nop_buffer } = dylib.symbols; const buffer = new Uint8Array(8).fill(5); Deno.bench("nop_buffer()", () => { - dylib.symbols.nop_buffer(buffer); + nop_buffer(buffer); }); +const { return_u8 } = dylib.symbols; Deno.bench("return_u8()", () => { - dylib.symbols.return_u8(); + return_u8(); }); +const { return_i8 } = dylib.symbols; Deno.bench("return_i8()", () => { - dylib.symbols.return_i8(); + return_i8(); }); +const { return_u16 } = dylib.symbols; Deno.bench("return_u16()", () => { - dylib.symbols.return_u16(); + return_u16(); }); +const { return_i16 } = dylib.symbols; Deno.bench("return_i16()", () => { - dylib.symbols.return_i16(); + return_i16(); }); +const { return_u32 } = dylib.symbols; Deno.bench("return_u32()", () => { - dylib.symbols.return_u32(); + return_u32(); }); +const { return_i32 } = dylib.symbols; Deno.bench("return_i32()", () => { - dylib.symbols.return_i32(); + return_i32(); }); +const { return_u64 } = dylib.symbols; Deno.bench("return_u64()", () => { - dylib.symbols.return_u64(); + return_u64(); }); +const { return_i64 } = dylib.symbols; Deno.bench("return_i64()", () => { - dylib.symbols.return_i64(); + return_i64(); }); +const { return_usize } = dylib.symbols; Deno.bench("return_usize()", () => { - dylib.symbols.return_usize(); + return_usize(); }); +const { return_isize } = dylib.symbols; Deno.bench("return_isize()", () => { - dylib.symbols.return_isize(); + return_isize(); }); +const { return_f32 } = dylib.symbols; Deno.bench("return_f32()", () => { - dylib.symbols.return_f32(); + return_f32(); }); +const { return_f64 } = dylib.symbols; Deno.bench("return_f64()", () => { - dylib.symbols.return_f64(); + return_f64(); }); +const { return_buffer } = dylib.symbols; Deno.bench("return_buffer()", () => { - dylib.symbols.return_buffer(); + return_buffer(); }); // Nonblocking calls +const { nop_nonblocking } = dylib.symbols; Deno.bench("nop_nonblocking()", async () => { - await dylib.symbols.nop_nonblocking(); + await nop_nonblocking(); }); +const { nop_u8_nonblocking } = dylib.symbols; Deno.bench("nop_u8_nonblocking()", async () => { - await dylib.symbols.nop_u8_nonblocking(100); + await nop_u8_nonblocking(100); }); +const { nop_i8_nonblocking } = dylib.symbols; Deno.bench("nop_i8_nonblocking()", async () => { - await dylib.symbols.nop_i8_nonblocking(100); + await nop_i8_nonblocking(100); }); +const { nop_u16_nonblocking } = dylib.symbols; Deno.bench("nop_u16_nonblocking()", async () => { - await dylib.symbols.nop_u16_nonblocking(100); + await nop_u16_nonblocking(100); }); +const { nop_i16_nonblocking } = dylib.symbols; Deno.bench("nop_i16_nonblocking()", async () => { - await dylib.symbols.nop_i16_nonblocking(100); + await nop_i16_nonblocking(100); }); +const { nop_u32_nonblocking } = dylib.symbols; Deno.bench("nop_u32_nonblocking()", async () => { - await dylib.symbols.nop_u32_nonblocking(100); + await nop_u32_nonblocking(100); }); +const { nop_i32_nonblocking } = dylib.symbols; Deno.bench("nop_i32_nonblocking()", async () => { - await dylib.symbols.nop_i32_nonblocking(100); + await nop_i32_nonblocking(100); }); +const { nop_u64_nonblocking } = dylib.symbols; Deno.bench("nop_u64_nonblocking()", async () => { - await dylib.symbols.nop_u64_nonblocking(100); + await nop_u64_nonblocking(100); }); +const { nop_i64_nonblocking } = dylib.symbols; Deno.bench("nop_i64_nonblocking()", async () => { - await dylib.symbols.nop_i64_nonblocking(100); + await nop_i64_nonblocking(100); }); +const { nop_usize_nonblocking } = dylib.symbols; Deno.bench("nop_usize_nonblocking()", async () => { - await dylib.symbols.nop_usize_nonblocking(100); + await nop_usize_nonblocking(100); }); +const { nop_isize_nonblocking } = dylib.symbols; Deno.bench("nop_isize_nonblocking()", async () => { - await dylib.symbols.nop_isize_nonblocking(100); + await nop_isize_nonblocking(100); }); +const { nop_f32_nonblocking } = dylib.symbols; Deno.bench("nop_f32_nonblocking()", async () => { - await dylib.symbols.nop_f32_nonblocking(100); + await nop_f32_nonblocking(100); }); +const { nop_f64_nonblocking } = dylib.symbols; Deno.bench("nop_f64_nonblocking()", async () => { - await dylib.symbols.nop_f64_nonblocking(100); + await nop_f64_nonblocking(100); }); +const { nop_buffer_nonblocking } = dylib.symbols; Deno.bench("nop_buffer_nonblocking()", async () => { - await dylib.symbols.nop_buffer_nonblocking(buffer); + await nop_buffer_nonblocking(buffer); }); +const { return_u8_nonblocking } = dylib.symbols; Deno.bench("return_u8_nonblocking()", async () => { - await dylib.symbols.return_u8_nonblocking(); + await return_u8_nonblocking(); }); +const { return_i8_nonblocking } = dylib.symbols; Deno.bench("return_i8_nonblocking()", async () => { - await dylib.symbols.return_i8_nonblocking(); + await return_i8_nonblocking(); }); +const { return_u16_nonblocking } = dylib.symbols; Deno.bench("return_u16_nonblocking()", async () => { - await dylib.symbols.return_u16_nonblocking(); + await return_u16_nonblocking(); }); +const { return_i16_nonblocking } = dylib.symbols; Deno.bench("return_i16_nonblocking()", async () => { - await dylib.symbols.return_i16_nonblocking(); + await return_i16_nonblocking(); }); +const { return_u32_nonblocking } = dylib.symbols; Deno.bench("return_u32_nonblocking()", async () => { - await dylib.symbols.return_u32_nonblocking(); + await return_u32_nonblocking(); }); +const { return_i32_nonblocking } = dylib.symbols; Deno.bench("return_i32_nonblocking()", async () => { - await dylib.symbols.return_i32_nonblocking(); + await return_i32_nonblocking(); }); +const { return_u64_nonblocking } = dylib.symbols; Deno.bench("return_u64_nonblocking()", async () => { - await dylib.symbols.return_u64_nonblocking(); + await return_u64_nonblocking(); }); +const { return_i64_nonblocking } = dylib.symbols; Deno.bench("return_i64_nonblocking()", async () => { - await dylib.symbols.return_i64_nonblocking(); + await return_i64_nonblocking(); }); +const { return_usize_nonblocking } = dylib.symbols; Deno.bench("return_usize_nonblocking()", async () => { - await dylib.symbols.return_usize_nonblocking(); + await return_usize_nonblocking(); }); +const { return_isize_nonblocking } = dylib.symbols; Deno.bench("return_isize_nonblocking()", async () => { - await dylib.symbols.return_isize_nonblocking(); + await return_isize_nonblocking(); }); +const { return_f32_nonblocking } = dylib.symbols; Deno.bench("return_f32_nonblocking()", async () => { - await dylib.symbols.return_f32_nonblocking(); + await return_f32_nonblocking(); }); +const { return_f64_nonblocking } = dylib.symbols; Deno.bench("return_f64_nonblocking()", async () => { - await dylib.symbols.return_f64_nonblocking(); + await return_f64_nonblocking(); }); +const { return_buffer_nonblocking } = dylib.symbols; Deno.bench("return_buffer_nonblocking()", async () => { - await dylib.symbols.return_buffer_nonblocking(); + await return_buffer_nonblocking(); }); +const { nop_many_parameters } = dylib.symbols; const buffer2 = new Uint8Array(8).fill(25); Deno.bench("nop_many_parameters()", () => { - dylib.symbols.nop_many_parameters( + nop_many_parameters( 135, 47, 356, @@ -471,8 +532,9 @@ Deno.bench("nop_many_parameters()", () => { ); }); +const { nop_many_parameters_nonblocking } = dylib.symbols; Deno.bench("nop_many_parameters_nonblocking()", () => { - dylib.symbols.nop_many_parameters_nonblocking( + nop_many_parameters_nonblocking( 135, 47, 356, |