You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Accounting for component conversion work in an execution fuel budget #14430
I run Wasmtime components in an embedding where guest instructions and host work share an execution fuel budget. Guest-to-host lifting consumes hostcall_fuel, but that allowance starts over for each lift and does not reduce Store::get_fuel(). I need repeated conversions to count against the execution budget, including work done before a conversion fails.
I can see two possible approaches:
Wasmtime could optionally charge Store fuel as lifting consumes its existing allowance, at a configurable price.
Wasmtime could expose allowance consumption, including partial consumption on errors, and provide reliable points for the embedding to set a limit from remaining fuel before each lift and settle the cost before execution continues. A hook might help, but I am not sure the current hooks cover all those points.
Would you recommend either approach, or a different API for this?
It seems reasonable to me to go ahead and discount the fuel from the store fuel, if enabled, as well on successful lifts/lowers if it doesn't add too much overhead. Perhaps a Drop for the LiftContext might be the best place to put that, but I'm not entirely sure. I agree that adding hooks is probably going to be a bit messy, so just automatically doing this is probably the way to go
Thanks, Alex. Settling in LiftContext::Drop would account for work after completion, but we need to enforce the shared fuel budget before the work occurs. Would you be open to opt-in Store fuel deduction at the existing LiftContext::consume_fuel checks?
I'd also like equivalent accounting for lowering, covering host-side conversion and copying. Would you prefer to include lowering initially or address it separately?
If it's not too bad to add this to consume_fuel that seems ok yeah. For lowering I'm hesitant to have fuel worm its way so so deeply into everywhere in Wasmtime so I'm tempted to leave that up to embedders to do since the hook isn't already there. Is it difficult though on the embedder side to account for the work the host is doing in fuel?
Lowering accounting turned out to be manageable on the embedder side. My strategy was to charge at two points:
Host-function results returned to the guest.wasmtime lowers these after the registered callback returns. I wrapped linker registration so each callback runs the host function, calculates a lowering charge from its result, deducts Store fuel, and then returns the result. If charging fails, lowering never starts. I used bindgen’s wasmtime_crate option to route generated registrations through the same wrapper.
Arguments passed into guest exports. I accumulated their lowering cost during the existing argument-construction traversal, then deducted it before calling Wasmtime.
Leaving lowering to embedders looks workable. Do you see any improvements to this embedder-side approach? Deducting Store fuel directly in lifting’s existing consume_fuel checks should address the remaining gap.
A data point from an embedder: in wasmtime-wasi-http 49, fields.from-list lifts the whole list<tuple<string, list<u8>>> before its 128 KiB field-size check runs, so a call that will be refused can still allocate up to the hostcall-fuel budget first. I use epoch deadlines rather than Store fuel, so for me the per-call budget is the only bound, and I've lowered it to 32 MiB with set_hostcall_fuel. It might be worth a line in the wasmtime-wasi-http docs suggesting embedders do the same.
I've opened #15104 for this, following the approach agreed above: an opt-in per-store setting (Store::set_hostcall_fuel_consumes_store_fuel) that makes LiftContext::consume_fuel also deduct Store fuel before the lift continues, trapping with OutOfFuel when it runs out. Lowering is left to embedders as discussed.
@alexcrichton I listed a few open questions in the PR (naming, Store vs Config, and whether the setter should error without Config::consume_fuel). Happy to adjust.
Activity
alexcrichton commentedon Sep 29, 2026
It seems reasonable to me to go ahead and discount the fuel from the store fuel, if enabled, as well on successful lifts/lowers if it doesn't add too much overhead. Perhaps a
Dropfor theLiftContextmight be the best place to put that, but I'm not entirely sure. I agree that adding hooks is probably going to be a bit messy, so just automatically doing this is probably the way to gowilfreddenton commentedon Sep 29, 2026
Thanks, Alex. Settling in
LiftContext::Dropwould account for work after completion, but we need to enforce the shared fuel budget before the work occurs. Would you be open to opt-in Store fuel deduction at the existingLiftContext::consume_fuelchecks?I'd also like equivalent accounting for lowering, covering host-side conversion and copying. Would you prefer to include lowering initially or address it separately?
alexcrichton commentedon Sep 29, 2026
If it's not too bad to add this to
consume_fuelthat seems ok yeah. For lowering I'm hesitant to have fuel worm its way so so deeply into everywhere in Wasmtime so I'm tempted to leave that up to embedders to do since the hook isn't already there. Is it difficult though on the embedder side to account for the work the host is doing in fuel?wilfreddenton commentedon Sep 30, 2026
Lowering accounting turned out to be manageable on the embedder side. My strategy was to charge at two points:
Host-function results returned to the guest.
wasmtimelowers these after the registered callback returns. I wrapped linker registration so each callback runs the host function, calculates a lowering charge from its result, deducts Store fuel, and then returns the result. If charging fails, lowering never starts. I used bindgen’swasmtime_crateoption to route generated registrations through the same wrapper.Arguments passed into guest exports. I accumulated their lowering cost during the existing argument-construction traversal, then deducted it before calling Wasmtime.
Leaving lowering to embedders looks workable. Do you see any improvements to this embedder-side approach? Deducting Store fuel directly in lifting’s existing
consume_fuelchecks should address the remaining gap.alexcrichton commentedon Sep 30, 2026
That sounds reasonable enough to me yeah
carsonfarmer commentedon Oct 2, 2026
A data point from an embedder: in
wasmtime-wasi-http49,fields.from-listlifts the wholelist<tuple<string, list<u8>>>before its 128 KiB field-size check runs, so a call that will be refused can still allocate up to the hostcall-fuel budget first. I use epoch deadlines rather than Store fuel, so for me the per-call budget is the only bound, and I've lowered it to 32 MiB withset_hostcall_fuel. It might be worth a line in thewasmtime-wasi-httpdocs suggesting embedders do the same.RajaBabu15 commentedon Oct 31, 2026
I've opened #15104 for this, following the approach agreed above: an opt-in per-store setting (
Store::set_hostcall_fuel_consumes_store_fuel) that makesLiftContext::consume_fuelalso deduct Store fuel before the lift continues, trapping withOutOfFuelwhen it runs out. Lowering is left to embedders as discussed.@alexcrichton I listed a few open questions in the PR (naming,
StorevsConfig, and whether the setter should error withoutConfig::consume_fuel). Happy to adjust.