Skip to content

Commit 3c331b1

Browse files
Centrilcoolreader18
authored andcommitted
v8, v2: reuse reducer arguments DataView across calls
1 parent 36d4566 commit 3c331b1

3 files changed

Lines changed: 66 additions & 15 deletions

File tree

crates/core/src/host/v8/mod.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ use tokio::sync::oneshot;
5252
use tracing::Instrument;
5353
use v8::script_compiler::{compile_module, Source};
5454
use v8::{
55-
scope_with_context, Context, Function, Isolate, Local, MapFnTo, OwnedIsolate, PinScope, ResolveModuleCallback,
56-
ScriptOrigin, Value,
55+
scope_with_context, ArrayBuffer, Context, Function, Isolate, Local, MapFnTo, OwnedIsolate, PinScope,
56+
ResolveModuleCallback, ScriptOrigin, Value,
5757
};
5858

5959
mod budget;
@@ -615,10 +615,17 @@ async fn spawn_instance_worker(
615615
let instance_env = InstanceEnv::new(replica_ctx.clone(), scheduler);
616616
scope.set_slot(JsInstanceEnv::new(instance_env));
617617

618+
// Create a zero-initialized buffer for holding reducer args.
619+
// Arguments needing more space will not use this.
620+
const REDUCER_ARGS_BUFFER_SIZE: usize = 4_096; // 1 page.
621+
let buf = ArrayBuffer::new(scope, REDUCER_ARGS_BUFFER_SIZE);
622+
let reducer_args_data_view = v8::DataView::new(scope, buf, 0, buf.byte_length());
623+
618624
let mut inst = V8Instance {
619625
scope,
620626
replica_ctx,
621627
hooks: &hooks,
628+
reducer_args_data_view,
622629
};
623630

624631
// Process requests to the worker.
@@ -807,7 +814,8 @@ fn call_free_fun<'scope>(
807814
struct V8Instance<'a, 'scope, 'isolate> {
808815
scope: &'a mut PinScope<'scope, 'isolate>,
809816
replica_ctx: &'a Arc<ReplicaContext>,
810-
hooks: &'a HookFunctions<'a>,
817+
hooks: &'a HookFunctions<'scope>,
818+
reducer_args_data_view: Local<'scope, v8::DataView>,
811819
}
812820

813821
impl WasmInstance for V8Instance<'_, '_, '_> {
@@ -825,7 +833,7 @@ impl WasmInstance for V8Instance<'_, '_, '_> {
825833

826834
fn call_reducer(&mut self, op: ReducerOp<'_>, budget: FunctionBudget) -> ReducerExecuteResult {
827835
common_call(self.scope, budget, op, |scope, op| {
828-
Ok(call_call_reducer(scope, self.hooks, op)?)
836+
Ok(call_call_reducer(scope, self.hooks, op, self.reducer_args_data_view)?)
829837
})
830838
.map_result(|call_result| call_result.and_then(|res| res.map_err(ExecutionError::User)))
831839
}
@@ -975,7 +983,9 @@ mod test {
975983
timestamp: Timestamp::from_micros_since_unix_epoch(24),
976984
args: &ArgsTuple::nullary(),
977985
};
978-
Ok(call_call_reducer(scope, &hooks, op)?)
986+
let buffer = v8::ArrayBuffer::new(scope, 4096);
987+
let data_view = v8::DataView::new(scope, buffer, 0, 4096);
988+
Ok(call_call_reducer(scope, &hooks, op, data_view)?)
979989
})
980990
};
981991

crates/core/src/host/v8/syscall/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@ fn resolve_sys_module_inner<'scope>(
7474
/// Calls the registered `__call_reducer__` function hook.
7575
///
7676
/// This handles any (future) ABI version differences.
77-
pub(super) fn call_call_reducer(
78-
scope: &mut PinScope<'_, '_>,
79-
hooks: &HookFunctions<'_>,
77+
pub(super) fn call_call_reducer<'scope>(
78+
scope: &mut PinScope<'scope, '_>,
79+
hooks: &HookFunctions<'scope>,
8080
op: ReducerOp<'_>,
81+
reducer_args_data_view: Local<'scope, v8::DataView>,
8182
) -> ExcResult<ReducerResult> {
8283
match hooks.abi {
8384
AbiVersion::V1 => v1::call_call_reducer(scope, hooks, op),
84-
AbiVersion::V2 => v2::call_call_reducer(scope, hooks, op),
85+
AbiVersion::V2 => v2::call_call_reducer(scope, hooks, op, reducer_args_data_view),
8586
}
8687
}
8788

crates/core/src/host/v8/syscall/v2.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ use crate::host::wasm_common::module_host_actor::{
2222
AnonymousViewOp, ProcedureOp, ReducerOp, ReducerResult, ViewOp, ViewReturnData,
2323
};
2424
use crate::host::wasm_common::{err_to_errno_and_log, RowIterIdx, TimingSpan, TimingSpanIdx};
25-
use crate::host::AbiCall;
25+
use crate::host::{AbiCall, ArgsTuple};
2626
use anyhow::Context;
2727
use bytes::Bytes;
28+
use core::ptr::NonNull;
2829
use spacetimedb_lib::{bsatn, ConnectionId, Identity, RawModuleDef, Timestamp};
2930
use spacetimedb_primitives::{errno, ColId, IndexId, ProcedureId, ReducerId, TableId, ViewFnPtr};
3031
use spacetimedb_sats::u256;
3132
use v8::{
3233
callback_scope, ConstructorBehavior, Function, FunctionCallbackArguments, Isolate, Local, Module, Object,
33-
PinCallbackScope, PinScope,
34+
PinCallbackScope, PinScope, Value,
3435
};
3536

3637
macro_rules! create_synthetic_module {
@@ -384,10 +385,11 @@ fn register_hooks_v2_0<'scope>(scope: &mut PinScope<'scope, '_>, args: FunctionC
384385
}
385386

386387
/// Calls the `__call_reducer__` function `fun`.
387-
pub(super) fn call_call_reducer(
388-
scope: &mut PinScope<'_, '_>,
389-
hooks: &HookFunctions<'_>,
388+
pub(super) fn call_call_reducer<'scope>(
389+
scope: &mut PinScope<'scope, '_>,
390+
hooks: &HookFunctions<'scope>,
390391
op: ReducerOp<'_>,
392+
reducer_args_data_view: Local<'scope, v8::DataView>,
391393
) -> ExcResult<ReducerResult> {
392394
let ReducerOp {
393395
id: ReducerId(reducer_id),
@@ -402,7 +404,8 @@ pub(super) fn call_call_reducer(
402404
let sender = serialize_to_js(scope, &sender.to_u256())?;
403405
let conn_id: v8::Local<'_, v8::Value> = serialize_to_js(scope, &conn_id.to_u128())?;
404406
let timestamp = serialize_to_js(scope, &timestamp.to_micros_since_unix_epoch())?;
405-
let reducer_args = make_dataview(scope, <Box<[u8]>>::from(&**reducer_args.get_bsatn())).into();
407+
let reducer_args = reducer_args_to_value(scope, reducer_args, reducer_args_data_view);
408+
406409
let args = &[reducer_id, sender, conn_id, timestamp, reducer_args];
407410

408411
// Call the function.
@@ -418,6 +421,43 @@ pub(super) fn call_call_reducer(
418421
Ok(user_res)
419422
}
420423

424+
/// Converts `args` into a `Value`.
425+
fn reducer_args_to_value<'scope>(
426+
scope: &mut PinScope<'scope, '_>,
427+
args: &ArgsTuple,
428+
buffer: Local<'scope, v8::DataView>,
429+
) -> Local<'scope, Value> {
430+
let reducer_args = &**args.get_bsatn();
431+
432+
let len = reducer_args.len();
433+
let dv = if len <= buffer.byte_length() {
434+
// We can use the buffer.
435+
let dst_ptr = NonNull::new(buffer.data())
436+
.expect("backing store byte length should be > 0")
437+
.cast::<u8>();
438+
439+
// Copy `reducer_args` into `buffer`.
440+
let mut dst_ptr = NonNull::slice_from_raw_parts(dst_ptr, len);
441+
// SAFETY: We know `dst_ptr` to be:
442+
// - trivially properly aligned due to `u8`s alignment being 1.
443+
// - non-null as it was derived from `NonNull`.
444+
// - the range is within the allocation of `buffer`
445+
// as `len <= buffer.byte_length()`,
446+
// so `buffer` is dereferenceable.
447+
// - `dst` will point to a valid `[u8]` as it was zero-initialized.
448+
// - nothing is aliasing the pointer.
449+
let dst = unsafe { dst_ptr.as_mut() };
450+
dst.copy_from_slice(reducer_args);
451+
452+
buffer
453+
} else {
454+
// Fall back to allocating new buffers.
455+
make_dataview(scope, <Box<[u8]>>::from(reducer_args))
456+
};
457+
458+
dv.into()
459+
}
460+
421461
/// Calls the `__call_view__` function `fun`.
422462
pub(super) fn call_call_view(
423463
scope: &mut PinScope<'_, '_>,

0 commit comments

Comments
 (0)