Hi folks,
This is my first time on Zulip, so sorry in advance if I'm doing it wrong. :)
I have a bunch of questions about fuel and async execution in Wasmtime.
Is async execution via Config::async_support the only way to interrupt execution of Wasm code and then later resume it? I'm aware of Store::out_of_fuel_trap but if I'm reading it right it is not suitable for preemptive concurrency because it aborts the computation rather than suspending it. If async execution is not the only way, is it at least the recommended way?
Is async execution the only way to have concurrently executing Wasm using the same Store
? If not, is it at least the recommended way?
How does fuel consumption actually work? Is it based on some processor feature that allows efficiently interrupting based on some counter that the CPU manages itself, or is it completely synthesized by decrement instructions peppered throughout the compiled code by Wasmtime? What things actually consume fuel — i.e. is it wall time, CPU time, instructions executed, or something else? If the Wasm code sleeps for a long time, does that consume much fuel? What happens with host functions — are they assumed by default to be effectively "instant"?
I've been tinkering with Wasmtime and have just gotten to the point where I really need to start thinking about the overall architecture of my program. So I'd like to know what I'm getting myself with sync vs async execution modes.
Many thanks in advance for any guidance! And let me know if you think the answers to some of these questions would be worth rolling into the docs and I can try my hand at a PR for that.
:sparkling_heart:
Fuel was implemented in https://github.com/bytecodealliance/wasmtime/pull/2611. You may want to read that PR.
or is it completely synthesized by decrement instructions peppered throughout the compiled code by Wasmtime?
This is the way it is implemented.
What things actually consume fuel — i.e. is it wall time, CPU time, instructions executed, or something else?
Except for a couple of special instructions each wasm instruction consumes one unit of fuel. That is the only deterministic quantity across multiple executions on the same or different cpus and between different versions of wasmtime.
If the Wasm code sleeps for a long time, does that consume much fuel? What happens with host functions — are they assumed by default to be effectively "instant"?
AFAIK host functions need to manually consume fuel, or else they will only consume a single unit of fuel from the call wasm instruction.
@Jeff Parsons you're correct in that async_support
is the only way in Wasmtime to interrupt execution and resume it. The current APIs and implementation don't actually allow concurrent execution within the same Store
because the Store
is borrowed for the entire duration of the call. Concurrenly executing wasm is quite tricky and typically needs guest cooperation as well so supporting this natively in Wasmtime is unlikely to happen in the near future (unless something major changes I guess?)
For fuel consumption it's implemented as roughly each instruction consuming one unit of fuel. Loop and function headers check if there is no more fuel and take an action on the lack of fuel. This is all implemented with code instrumentation in the generated Cranelift code. If a host function blocks indefinitely then that will block the wasm indefinitely, it's up to the embedder to make sure host functions "behave well" in the sense of not exceeding the budget of the guest.
Many thanks to both of you, @bjorn3 and @Alex Crichton. I think I understand how all of this fits together a lot better now. (And that PR was a lot easier to read than I expected, for something that I was imagining to be very esoteric!)
Jeff Parsons has marked this topic as resolved.
Last updated: Nov 22 2024 at 17:03 UTC