peterhuene opened PR #2842 from engine-sig-registry
to main
:
This PR contains a number of internal refactorings that help make
Store::register_module
do as little work as possible (registration is now only an insertion into aBTreeMap
).This will improve module instantiation performance for modules containing lots of functions and instructions.
The changes are:
- Move the shared signature registry out of
Store
and intoEngine
. Modules now register their signatures with the registry upon creation and store the signature index -> shared index mappings internally.- Engines now register the signatures of any host functions on
Config
upon engine creation as well as store the anyfunc representations of the host functions (previously kept inStore
).- Removed
StoreFrameInfo
andStackMapRegistry
in favor of a unifiedModuleRegistry
per-store. The module registry can be queried based on program counter values to quickly locate the owning module.- With the removal of
StackMapRegistry
, the runtime is now given a trait object to call back on for finding the stack map for a program counter value during GC. The lookup uses the existing data inCompiledModule
which is already optimized for the search.This PR supersedes #2820.
peterhuene requested alexcrichton for a review on PR #2842.
peterhuene requested alexcrichton and fitzgen for a review on PR #2842.
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
FWIW I think originally I made this function (or something like it) to prevent access to all the internals of
FunctionInfo
(or w/e it's called in this module) to ensure that the caller only got access to the items it needed, but if we're returning 3-element tuples we can probably go ahead and just return a reference to the struct itself
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
Having only read this far in the diff, this seems like something that should happen automatically and shouldn't need a destructor. Should this logic be behind a
debug_assertions
cfg
?
alexcrichton created PR Review Comment:
Since this returns
*const StackMap
this should be anunsafe
trait I think?That being said though it'd be great if this could return
&StackMap
, it feels sort of unnecessarily unsafe to return a*const
pointer here. Since this is a purely internal trait and it's just for our own purposes, could we go ahead and update this to returning aRef<StackMap>
so we can persist the borrow that's happening within wasmtime?
alexcrichton created PR Review Comment:
Could this be preceded with a debug assert that the entry is
None
?
alexcrichton created PR Review Comment:
This could use
get_mut
I think instead ofwrite()
perhaps?
alexcrichton created PR Review Comment:
Instead of hashing
WasmFuncType
this could lookupindex
within thesigs
map just created I think?
alexcrichton created PR Review Comment:
I think the original intention was to avoid allocations here, but since
lookup_func_type
clones the signature this can probably just callself.ty().params().len()
perhaps?(afaik the speed of this function isn't paramount)
alexcrichton created PR Review Comment:
I'm a bit wary having an unregister function like this mostly because it's related to memory safety in that it's comparing function signatures at the wasm level. This feels like manual memory management which can be error prone and difficult to get right.
Would it be possible, though, to have the two registration functions above return an owned handled which has a destructor? We could store that owned handle within
Func
/HostFunc
/Module
/etc ideally and have the destructor automatically take care of the cleanup.(not sure if that requires bending over backwards too much)
alexcrichton created PR Review Comment:
Could this return
&
instead of*const
and it's coerced to something unsafe by the caller?
alexcrichton created PR Review Comment:
Since this is a one-liner now could this method get removed in favor of putting the
borrow_mut
on the caller?
alexcrichton created PR Review Comment:
Could this
debug_assert
that the start is the same?
alexcrichton created PR Review Comment:
This is one of the things I was a little worried about with the signatures and ownership of the returned shared indices. In the middle of this code it's not clear to me (immediately at least) how this is valid for the lifetime of a
RegisteredModule
. I figured that the lifetime of indexes for a signature would be tied to aModule
itself but theModule
isn't persisted here and we just retain a signature map.This might just be that I don't have the signature management all in my head though. I don't think this is a great place for a comment per se, but I'm curious what you think about this. Also, how does this ensure that the module's signatures' registrations outlive this
RegisteredModule
?
alexcrichton created PR Review Comment:
FWIW this seems like it could be prohibitively slow or a source of accidental n^2 things. I'm basically worried that this linear search could take a surprisingly long time in situations where a store has a number of modules or something like that (especially with module linking and whatnot).
I think this is only called from
Func::from_wasmtime_function
so the impact is at least somewhat isolated, but even then that's a pretty core piece of functionality that I'm not entirely sure how would be refactored.Do you have thoughts on how this might be possible to remove the loop?
alexcrichton created PR Review Comment:
I'm a little wary of this too, there's no need for the global registration to hang on to signatures, right?
alexcrichton created PR Review Comment:
(I think the way we access info has improved over time so "limiting what you can access" is much less important than I originally thought it might be)
alexcrichton created PR Review Comment:
If this function is only used for deregistration, could this perhaps return an iterator over the signature index and the reference count? That way the deregistration wouldn't have to do lookup multiple times in a row
alexcrichton created PR Review Comment:
As module construction gets a little more complicated, could this be outlined in a new wasmtime-only constructor on
Module
? I'm imagining that ideally this function andModule::from_binary
would end up in the same place which si the only place that actually literally constructs aModule
alexcrichton created PR Review Comment:
(and also have a comment as to why it's an early-return here for double-registration of a module.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
I'm fine with changing
RegisteredModule
to storeModule
and just rely on that to get at the compiled module and signatures.I think the original intention was to just keep alive the bare minimum for the registry's operation, as with module linking implemented, there's a bunch of stuff stored in
Module
geared towards instantiation that could be dropped while the module is still in use by aStore
(i.e. no longer instantiatable, but an instance still exists for the module). But if it's confusing ownerships and lifetimes, perhaps it's not worth it.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Re: the question about how this ensures signatures outlive the
RegisteredModule
: only that the module's shared signatures are only unregistered on drop ofModuleSharedSignatures
and theArc
references the only owner of that. So for it to drop: the referencingModuleInner
needs to drop and all stores where the module is registered need to drop; at that point the signatures are no longer needed.
peterhuene edited PR Review Comment.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
If the shared signature space were guaranteed contiguous for a module, we could easily keep a range map around for at least O(log(n)) lookup, but the free list maintained by the signature registry precludes that.
Let me think on this more.
fitzgen submitted PR Review.
fitzgen submitted PR Review.
fitzgen created PR Review Comment:
Do these
VMCallerCheckedAnyfunc
s need to be boxed? The only reason I would assume so is if their addresses are taken and assumed stable. If that is the case, then I would expect some comments here warning readers about that.
fitzgen created PR Review Comment:
Having read a bit more, I guess the
SharedSignatures
structure sort of is this RAII type, but for a whole collection of signatures.I do think that this function should still be marked
unsafe
though, and have a comment laying out why, as discussed up above.
fitzgen created PR Review Comment:
Also, because memory safety relies on these indices being unique for a given signature's lifetime, and unregistering allows reusing the index, then this function should be marked
unsafe
(and then the RAII type that Alex describes would be the thing that encapsulates this unsafety and internally has anunafe
block to make this call).
fitzgen created PR Review Comment:
One final thing: since nothing can guarantee that we aren't currently "borrowing" an index out of the
SharedSignatures
by the time we drop it and unregister its indices (because the indices aren't references and/or types with a lifetime and they are used by JIT code and such) I think that actually creating aSharedSignatures
should beunsafe
and have the contract that "by the time you drop this, you promise you aren't 'borrowing' any indices from within it anymore".
fitzgen submitted PR Review.
fitzgen created PR Review Comment:
(And if their addresses aren't being taken and assumed stable, then can we remove the boxing here?)
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Indeed, that is the intention as
Func
's ABI representation is*mut VMCallerCheckedAnyfunc
. Normally that's stored with the export inFunc
, butHostFunc
is shared across engines and thus the engine needs to be responsible for (stably) storing the anyfunc with the appropriate shared signature index for the engine.I'll update this with a comment.
alexcrichton created PR Review Comment:
Nah I think it's a good idea to not store
Module
here so we can split it up as necessary.I think I just got confused a bit as we have so many module wrappers and module bits and pieces I couldn't keep them all straight in my head. I realize now though that the lifetime here is obvious in that this is the point a store retains a module's signatures, and the drop of these signatures is what deregisters.
I think that this is probably fine to leave as-is, I just needed to sit and think and understand more about what was going on
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
Since this is only called from
from_wasmtime_function
could we perhaps use the entry point of the wasm function provided there for lookup? If this took aVMCallerCheckedAnyfunc
instead of aVMSharedSignatureIndex
we could do a pc lookup to find which module has that pc, then we look up in that module's trampoline map directly?
alexcrichton submitted PR Review.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
OooOoo excellent idea :) I'll make that change.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Is there a good way to do this given
Ref::filter_map
is unstable?
peterhuene edited PR Review Comment.
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Ok I've reworked this such that
Engine
,Module
, andStore
all internally use aSignatureCollection
that has a reference on the signature registry. This is an RAII type that will unregister the signatures on drop.This cleans up the code quite a bit and makes it less error prone. I'll push up the change soon.
peterhuene edited PR Review Comment.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
alexcrichton submitted PR Review.
alexcrichton submitted PR Review.
alexcrichton created PR Review Comment:
The leading
&*
can be removed here I think
alexcrichton created PR Review Comment:
This seems to compile for me:
trait A: 'static {} fn foo(a: Option<*const dyn A>) {} fn bar(a: &dyn A) { foo(Some(a)); }
so is this
transmute
required? (same for the one ininstance.rs
)
alexcrichton created PR Review Comment:
The leading
&*
I think can be removed here
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Adding the
'static
constraint to the trait did the trick, so I'll remove the transmute
peterhuene submitted PR Review.
peterhuene created PR Review Comment:
Thanks, forgot to update as I changed
stack_map_lookup
's return type.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
peterhuene requested alexcrichton for a review on PR #2842.
peterhuene updated PR #2842 from engine-sig-registry
to main
.
alexcrichton submitted PR Review.
peterhuene merged PR #2842.
Last updated: Dec 23 2024 at 13:07 UTC