Shrink the size of the anyfunc table in VMContext (#3850)

* Shrink the size of the anyfunc table in `VMContext`

This commit shrinks the size of the `VMCallerCheckedAnyfunc` table
allocated into a `VMContext` to be the size of the number of "escaped"
functions in a module rather than the number of functions in a module.
Escaped functions include exports, table elements, etc, and are
typically an order of magnitude smaller than the number of functions in
general. This should greatly shrink the `VMContext` for some modules
which while we aren't necessarily having any problems with that today
shouldn't cause any problems in the future.

The original motivation for this was that this came up during the recent
lazy-table-initialization work and while it no longer has a direct
performance benefit since tables aren't initialized at all on
instantiation it should still improve long-running instances
theoretically with smaller `VMContext` allocations as well as better
locality between anyfuncs.

* Fix some tests

* Remove redundant hash set

* Use a helper for pushing function type information

* Use a more descriptive `is_escaping` method

* Clarify a comment

* Fix condition
This commit is contained in:
Alex Crichton
2022-02-28 10:11:04 -06:00
committed by GitHub
parent b57dc5e334
commit 2a6969d2bd
8 changed files with 126 additions and 58 deletions

View File

@@ -1045,6 +1045,7 @@ mod tests {
num_defined_tables: 0,
num_defined_memories: 0,
num_defined_globals: 0,
num_escaped_funcs: 0,
});
assert_eq!(
offsets.vm_extern_data_ref_count(),
@@ -1071,6 +1072,7 @@ mod tests {
num_defined_tables: 0,
num_defined_memories: 0,
num_defined_globals: 0,
num_escaped_funcs: 0,
});
assert_eq!(
offsets.vm_extern_ref_activation_table_next() as usize,
@@ -1097,6 +1099,7 @@ mod tests {
num_defined_tables: 0,
num_defined_memories: 0,
num_defined_globals: 0,
num_escaped_funcs: 0,
});
assert_eq!(
offsets.vm_extern_ref_activation_table_end() as usize,

View File

@@ -30,9 +30,9 @@ use std::{mem, ptr, slice};
use wasmtime_environ::{
packed_option::ReservedValue, DataIndex, DefinedGlobalIndex, DefinedMemoryIndex,
DefinedTableIndex, ElemIndex, EntityIndex, EntityRef, EntitySet, FuncIndex, GlobalIndex,
HostPtr, MemoryIndex, Module, PrimaryMap, TableIndex, TrapCode, VMOffsets, WasmType,
GlobalInit, HostPtr, MemoryIndex, Module, PrimaryMap, SignatureIndex, TableIndex,
TableInitialization, TrapCode, VMOffsets, WasmType,
};
use wasmtime_environ::{GlobalInit, TableInitialization};
mod allocator;
@@ -483,8 +483,12 @@ impl Instance {
/// than tracking state related to whether it's been initialized
/// before, because resetting that state on (re)instantiation is
/// very expensive if there are many anyfuncs.
fn construct_anyfunc(&mut self, index: FuncIndex, into: *mut VMCallerCheckedAnyfunc) {
let sig = self.module().functions[index];
fn construct_anyfunc(
&mut self,
index: FuncIndex,
sig: SignatureIndex,
into: *mut VMCallerCheckedAnyfunc,
) {
let type_index = self.runtime_info.signature(sig);
let (func_ptr, vmctx) = if let Some(def_index) = self.module().defined_func_index(index) {
@@ -551,9 +555,13 @@ impl Instance {
// expensive, so it's better for instantiation performance
// if we don't have to track "is-initialized" state at
// all!
let anyfunc: *mut VMCallerCheckedAnyfunc =
self.vmctx_plus_offset::<VMCallerCheckedAnyfunc>(self.offsets.vmctx_anyfunc(index));
self.construct_anyfunc(index, anyfunc);
let func = &self.module().functions[index];
let sig = func.signature;
let anyfunc: *mut VMCallerCheckedAnyfunc = self
.vmctx_plus_offset::<VMCallerCheckedAnyfunc>(
self.offsets.vmctx_anyfunc(func.anyfunc),
);
self.construct_anyfunc(index, sig, anyfunc);
Some(anyfunc)
}