Stream: wasmtime

Topic: ✔ Why are the VMRuntimeLimits saved in the Store?


view this post on Zulip Frank Emrich (Feb 04 2024 at 15:08):

It’s currently the case that all Instances 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:

  1. 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?
  2. Is it true that a 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.

view this post on Zulip Frank Emrich (Feb 04 2024 at 15:14):

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.

view this post on Zulip Alex Crichton (Feb 05 2024 at 14:52):

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

view this post on Zulip fitzgen (he/him) (Feb 05 2024 at 22:22):

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

view this post on Zulip Frank Emrich (Feb 05 2024 at 23:35):

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.

view this post on Zulip Notification Bot (Feb 05 2024 at 23:36):

Frank Emrich has marked this topic as resolved.


Last updated: Oct 23 2024 at 20:03 UTC