Add epoch-based interruption for cooperative async timeslicing.
This PR introduces a new way of performing cooperative timeslicing that is intended to replace the "fuel" mechanism. The tradeoff is that this mechanism interrupts with less precision: not at deterministic points where fuel runs out, but rather when the Engine enters a new epoch. The generated code instrumentation is substantially faster, however, because it does not need to do as much work as when tracking fuel; it only loads the global "epoch counter" and does a compare-and-branch at backedges and function prologues. This change has been measured as ~twice as fast as fuel-based timeslicing for some workloads, especially control-flow-intensive workloads such as the SpiderMonkey JS interpreter on Wasm/WASI. The intended interface is that the embedder of the `Engine` performs an `engine.increment_epoch()` call periodically, e.g. once per millisecond. An async invocation of a Wasm guest on a `Store` can specify a number of epoch-ticks that are allowed before an async yield back to the executor's event loop. (The initial amount and automatic "refills" are configured on the `Store`, just as for fuel.) This call does only signal-safe work (it increments an `AtomicU64`) so could be invoked from a periodic signal, or from a thread that wakes up once per period.
This commit is contained in:
@@ -73,6 +73,7 @@ pub struct VMOffsets<P> {
|
||||
|
||||
// precalculated offsets of various member fields
|
||||
interrupts: u32,
|
||||
epoch_ptr: u32,
|
||||
externref_activations_table: u32,
|
||||
store: u32,
|
||||
signature_ids: u32,
|
||||
@@ -174,6 +175,7 @@ impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
|
||||
num_defined_memories: fields.num_defined_memories,
|
||||
num_defined_globals: fields.num_defined_globals,
|
||||
interrupts: 0,
|
||||
epoch_ptr: 0,
|
||||
externref_activations_table: 0,
|
||||
store: 0,
|
||||
signature_ids: 0,
|
||||
@@ -190,10 +192,14 @@ impl<P: PtrSize> From<VMOffsetsFields<P>> for VMOffsets<P> {
|
||||
};
|
||||
|
||||
ret.interrupts = 0;
|
||||
ret.externref_activations_table = ret
|
||||
ret.epoch_ptr = ret
|
||||
.interrupts
|
||||
.checked_add(u32::from(ret.ptr.size()))
|
||||
.unwrap();
|
||||
ret.externref_activations_table = ret
|
||||
.epoch_ptr
|
||||
.checked_add(u32::from(ret.ptr.size()))
|
||||
.unwrap();
|
||||
ret.store = ret
|
||||
.externref_activations_table
|
||||
.checked_add(u32::from(ret.ptr.size()))
|
||||
@@ -469,6 +475,12 @@ impl<P: PtrSize> VMOffsets<P> {
|
||||
pub fn vminterrupts_fuel_consumed(&self) -> u8 {
|
||||
self.pointer_size()
|
||||
}
|
||||
|
||||
/// Return the offset of the `epoch_deadline` field of `VMInterrupts`
|
||||
#[inline]
|
||||
pub fn vminterupts_epoch_deadline(&self) -> u8 {
|
||||
self.pointer_size() + 8 // `stack_limit` is a pointer; `fuel_consumed` is an `i64`
|
||||
}
|
||||
}
|
||||
|
||||
/// Offsets for `VMCallerCheckedAnyfunc`.
|
||||
@@ -508,6 +520,13 @@ impl<P: PtrSize> VMOffsets<P> {
|
||||
self.interrupts
|
||||
}
|
||||
|
||||
/// Return the offset to the `*const AtomicU64` epoch-counter
|
||||
/// pointer.
|
||||
#[inline]
|
||||
pub fn vmctx_epoch_ptr(&self) -> u32 {
|
||||
self.epoch_ptr
|
||||
}
|
||||
|
||||
/// The offset of the `*mut VMExternRefActivationsTable` member.
|
||||
#[inline]
|
||||
pub fn vmctx_externref_activations_table(&self) -> u32 {
|
||||
|
||||
Reference in New Issue
Block a user