I have added fuel mechanism in my embedder using the following example: https://github.com/bytecodealliance/wasmtime/blob/main/examples/fuel.rs
I have a module which works fine without the fuel changes but fails suspiciously while using fuel. Is there any module side changes I should have before incorporating fuel changes ?
the module shouldn't have to change at all: fuel operates underneath the semantic level of Wasm execution. Could you say more about "fails suspiciously"?
note also that that example uses fuel as a hard limit, whereas if you're building a server-side execution environment where fuel helps to meter usage you probably want to be operating in async mode and use fuel as a yield point instead (and refill the fuel when exhausted)
Much thanks @Chris Fallin for the reply! after reading through it I set the fuel limit just for debugging to be u64::MAX and everything works fine so I guess I should indeed have a yield point and callback in place to refill. Thanks again @Chris Fallin
@Chris Fallin is this the API you talking about ?
fuel_async_yield_interval
yep
Last thing which I don't understand is where will the callback (refill logic) be placed, it just takes interval, the doc says that it will yield the control to the running thread which I don't quite understand
the "yield" mechanism is in the context of the Rust async model: the pollable future returned by the call_async
method on a Func
will yield (pause execution and return a Poll::Pending
) when fuel runs out
but I have async functions all the way uptill the async APIs of the server, I don't really have access to running thread where I can refill store and runtime can continue execution of the module function
oh, the store will automatically refill, IIRC; total fuel is distinct from the yield interval
this doc says that wasm will trap if fuel goes out: https://docs.rs/wasmtime/latest/wasmtime/struct.Store.html#method.fuel_async_yield_interval
That's right; so you don't let fuel run out. Set total fuel to u64::MAX
. The yield interval is distinct from total fuel.
So for example if you set total fuel to u64::MAX
and yield interval to 10_000
, your future will yield every 10 thousand ops all the way down to zero at the end of a very long (u64::MAX) execution
ahh! ohh okay, makes sense. But then what's the use case of yield ? if fuel is set to max then I can let the module run and compute the consumed fuel at the end ?
ohh is it just for yield control back to the scheduler to avoid stalling the thread for other futures to be executed
?
The purpose of yielding is to allow periodic returns to the async event loop. Usually in an async server you don't want any particular computation to run for too long -- it blocks the worker thread entirely, and latency can spike as other events pile up. So you slice long-running computations into pieces and run the pieces as individual event-loop steps. That happens automatically with a Rust async executor like Tokio if you use the periodic yielding feature of async calls
yep exactly
ohh this is so cool ! I always wondered how one would do that thing with WASM, it's amazing that it's possible with this fuel mechanism! Much thanks @Chris Fallin for the help, I highly appreciate it :)
sure thing!
Last updated: Jan 24 2025 at 00:11 UTC