It’s currently the case that all Instance
s of a Store
share the same VMRuntimeLimits
object, which is stored in the Store
(more concretely, the StoreOpaque
). Each VMContext
then contains a pointer to this shared object.
I was wondering if there is a particular reason for this design?
For example, this design means that every access to the current stack limit now requires an additional pointer indirection, as the stack limit is saved in the VMRuntimeLimits
.
One place I see where the current design seems to be helpful is in functions like Store::gc
: The latter requires access to the VMRuntimeLimits
. As far as I can tell, a Store
has no notion of what the most recently executing Instance
is, meaning that if the VMRuntimeLimits
were stored per-Instance
rather than per-Store
, Store::gc
wouldn’t actually be able to determine which VMRuntimeLimits
corresponds to the most recent execution of wasm.
In summary, my questions are:
VMRuntimeLimits
object in the Store
, instead of saving them per Instance
? Or is this just some historical coincidence?Store
itself has no notion of the most recently executing Instance
?I'm not suggesting to change the current design, I'm just asking this because I'm working on related code and trying to understand the current design.
Hm, I'm now realizing that VMRuntimeLimits
contains some fields that are probably supposed to be global to the Store
, such as fuel_consumed
. I was mostly looking at the last_*
values and stack_limit
in VMRuntimeLimits
, which seem to be properties of a particular Instance
rather than the whole Store
when wondering about this.
Is there a particular reason for sharing a single VMRuntimeLimits object in the Store, instead of saving them per Instance? Or is this just some historical coincidence?
The reason for this is that the Store<T>
is intended as shared state amongst a group of instances and semantically configuring options at the Store<T>
level is defined as affecting all instances. In that sense it's part of the API contract that all instances refer to the same runtime limits, so not a historical coincidence but required today.
One major reason for this is that it enables linking instances together. For example one instance's import can be provided by another instance's export. That can be configured in host code and there's no need for special trampolines when crossing instances.
All that being said we have not heavily scritinized or optimized our stack checks, so if the double indirection is something you're seeing hot in profiles I think it'd be good to try to explore alternative solutions!
Is it true that a Store itself has no notion of the most recently executing Instance?
Correct. This ties into the above a bit where a Store
is sort of a soup of instances/functions/etc and so instance identity isn't really a thing and instead it's all about the contents of the Store
also I believe there are things in the runtime limits that get mutated, and those mutations need to be seen by all instances in the store. iirc fuel and/or epochs is like this
Thanks for both of your answers! My originally impression was that the stack_limit
and last_wasm_*
fields in VMRuntimeLimits
were conceptually describing aspects of wasm execution inside a specific Instance
, and that it was just a happy coincidence that you can share (this part of) the VMRuntimeLimits
between the different Instances
, even when execution alternates between different instances inside the same Store
. But thanks to Alex' explanation, I understand now that my conceptual view on this just wasn't accurate.
I have no reason to believe that the pointer indirection makes any meaningful performance difference (and haven't measured it). My train of thought was more like "oh, doesn't some of the data in here conceptually belong somewhere else?" followed by "oh, the current design also means that you have this additional indirection at every stack_limit
check". That just got me curious about the reasons for the current design.
Frank Emrich has marked this topic as resolved.
Last updated: Nov 22 2024 at 16:03 UTC