Skip to content

Component hostcall optimizations#13876

Open
tschneidereit wants to merge 8 commits into
bytecodealliance:mainfrom
tschneidereit:hostcall-opts
Open

Component hostcall optimizations#13876
tschneidereit wants to merge 8 commits into
bytecodealliance:mainfrom
tschneidereit:hostcall-opts

Conversation

@tschneidereit

Copy link
Copy Markdown
Member

This set of commits applies a bunch of independent optimizations to calling host imports from components. In combination, they reduce overhead of sync calls by about 84%, from 83ns to 13ns, and that of immediately-ready async host imports by about 62%, from 142ns to 53ns. All numbers measured in a Linux VM on an M5 Max MBP, but I don't think any of this is particularly architecture-specific.

A few of notes on the results:

  1. I have a change to wit-bindgen pending that reduces guest-side overhead of async calls, bringing the 53ns down to something like 35ns.
  2. The fast paths introduced here only work for calls that don't involve borrows. I have changes pending that expand coverage and improve performance for calls involving borrows, but those need more vetting.
  3. Calls to async functions whose results aren't immediately ready are substantially slower, IIRC the last number I measured was about 700ns. There's more to be done there, but I didn't want to expand scope here too much.

All commits can be reviewed independently and come with improvement numbers relative to the previous commit.

Splitting current_thread into an #[inline] fast path plus #[cold]
outlined deferred-promotion slow path reduces call overhead by
about 20% (measured in a Linux VM on an M5 Max MBP): sync calls go
from about 83ns to about 65ns, immediately ready async calls from
142ns to 117ns.
Precompute `TypeFunc::contains_borrow` at compile time and use it in
the hostcall entrypoint to skip `CallContext` scope push/pop and
`validate_scope_exit` when lending is statically impossible.

Relative to the previous commit, this reduces call overhead by
about 29% for sync calls, and about 9% for async calls (both measured
in a Linux VM on an M5 Max MBP): sync calls go from about 65ns to about
46ns, immediately ready async calls from 117ns to 106ns.

I'm not entirely sure why the win is so much less for async calls, but
there are more wins in the next commits.
Such calls now run as the caller's guest thread without creating a
pending future at all: `poll_and_block` polls the host future once before
materializing anything and returns ready results directly.
`host_task_reenter_caller` becomes a no-op when no task was created.
Async-lowered calls keep the eager task for subtask status/cancellation),
and borrow-ful calls keep it as scope identity.

Relative to the previous commit, this reduces call overhead by
almost another 60% (46ns -> 19ns) for sync calls, with async calls,
expectedly, stay unchanged.
Specifically, if no borrows need to be tracked, no scope needs to be resolved.

Plus, inline a bunch of single-instantiation generics.

Relative to the previous commit, this reduces call overhead by another
31% for sync calls (19ns to 13ns), with async calls unchanged (both measured
in a Linux VM on an M5 Max MBP).
A ready async-lowered call returns Status::Returned with no waitable,
so the guest cannot observe the task's absence. first_poll now polls
the raw future before creating anything, only pending futures get
in `JoinHandle::run` and materialize a task.
This only works for borrow-free calls, because for borrows the task is
still needed for scope tracking.

Relative to the previous commit, sync calls stay unchanged, but async
calls are improved by about 45%: 109ns to 60ns. (measured in a Linux VM
on an M5 Max MBP)
Polls the result future once in-place and skips creating a `HostTask`,
etc if it's ready immediately.

Reduces costs of immediately-ready async calls by another 12% (60.5ns to 53ns)
@tschneidereit
tschneidereit requested a review from a team as a code owner July 14, 2026 12:00
@tschneidereit
tschneidereit requested review from dicej and removed request for a team July 14, 2026 12:00
@github-actions github-actions Bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Jul 14, 2026
@tschneidereit tschneidereit added performance wasm-proposal:component-model Issues related to the WebAssembly Component Model proposal labels Jul 14, 2026
@alexcrichton

Copy link
Copy Markdown
Member

Thanks! Could this be split up to land each commit independently? Some of these I'm more sure about than others and might take more time to review

@alexcrichton
alexcrichton requested review from alexcrichton and removed request for dicej July 14, 2026 14:29
@tschneidereit

Copy link
Copy Markdown
Member Author

Happy to do that, yes! I might in that case also restructure some of them a little bit to bring in some borrows-related fixes which change some of the new code again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance wasm-proposal:component-model Issues related to the WebAssembly Component Model proposal wasmtime:api Related to the API of the `wasmtime` crate itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants