Skip to content

Skip {enter,exit}-sync-call for "thread-transparent" adapters - #14270

Open
fitzgen wants to merge 1 commit into
bytecodealliance:mainfrom
fitzgen:sync-adapter-optimizations
Open

Skip {enter,exit}-sync-call for "thread-transparent" adapters#14270
fitzgen wants to merge 1 commit into
bytecodealliance:mainfrom
fitzgen:sync-adapter-optimizations

Conversation

@fitzgen

@fitzgen fitzgen commented Sep 2, 2026

Copy link
Copy Markdown
Member

Today, every sync adapter calls enter-sync-call, then does its lifting and lowering of arguments and reesults, and then calls exit-sync-call afterwards. The {enter,exit}-sync-call helpers save and restore the old thread's TLS context and create the new thread's TLS context. For sync-to-sync calls, we inline these helpers and do their work lazily via the VMDeferredThread machinery. But even so, creating a lazy VMDeferredThread can be pretty expensive if the adapter's callee is just doing like a single load or store or has been boiled away into returning a constant value.

Therefore, this commit introduces an analysis to find "thread-transparent" components. These are components that do not canon lower any component model intrinsic to access the thread state, and therefore cannot read or write that state. When we are compiling adapters whose callee is thread-transparent, we don't even need to {enter,exit}-sync-call at all because the callee will not read/write its thread state, so we don't need to save and restore the current thread state, we can just leave it in place.

@fitzgen
fitzgen requested review from a team as code owners September 2, 2026 19:54
@fitzgen
fitzgen requested review from cfallin and removed request for a team September 2, 2026 19:54
Today, every sync adapter calls `enter-sync-call`, then does its lifting and
lowering of arguments and reesults, and then calls `exit-sync-call`
afterwards. The `{enter,exit}-sync-call` helpers save and restore the old
thread's TLS context and create the new thread's TLS context. For sync-to-sync
calls, we inline these helpers and do their work lazily via the
`VMDeferredThread` machinery. But even so, creating a lazy `VMDeferredThread`
can be pretty expensive if the adapter's callee is just doing like a single load
or store or has been boiled away into returning a constant value.

Therefore, this commit introduces an analysis to find "thread-transparent"
components. These are components that do not `canon lower` any component model
intrinsic to access the thread state, and therefore *cannot* read or write that
state. When we are compiling adapters whose callee is thread-transparent, we
don't even need to `{enter,exit}-sync-call` at all because the callee will not
read/write its thread state, so we don't need to save and restore the current
thread state, we can just leave it in place.
@github-actions github-actions Bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Sep 2, 2026
@fitzgen
fitzgen force-pushed the sync-adapter-optimizations branch from cb770a2 to 3a0c4f0 Compare September 2, 2026 22:32

@cfallin cfallin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this optimization! Some thoughts below.

Overall I will rate my review status as "seems plausible" but I haven't been in this code deeply enough to confidently sign off -- probably @alexcrichton should take a look as well?

/// Returns whether `adapter` can omit its `{enter,exit}-sync-call` window.
pub fn adapter_is_transparent(&self, types: &ComponentTypesBuilder, adapter: &Adapter) -> bool {
Self::signature_is_transparent(types, adapter)
&& self.instances.contains(adapter.lift_options.instance)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We query the set here and we both insert and remove from it above -- could we add notes to the API doc-comments specifying the order in which actions must occur for correctness (and a correctness/convergence argument in general)? Something like:

  • We initially assume every instance is transparent (that's the push_instance as we first walk the instance graph).
  • Then we scan initializers and remove from the transparent set as we discover the canon lifts that could observe thread state.
  • Only after that scan is complete, we can call adapter_is_transparent to query the result during codegen.

Basically, I want a "stratification" of the API according to its expected usage pattern.

fn func_def_is_transparent(&self, def: &ComponentFuncDef<'_>) -> bool {
match def {
// Goes through an adapter, which saves/restores its own state if
// needed, but doesn't affect this adapter.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a key bit to the correctness argument too I think. It's implicit in the name ("transparent") but I think I want to see an explicit statement on the "transitively reached function" problem: naively a sync call to a component that doesn't use the thread-state-observing intrinsics might be fine except that that component calls another component that does. The observation is that the nested call itself will do the state-save if needed. So "transparent" really means that we adopt the thread/task identity of our caller (unobservably) and so doesn't have a transitive nature. Can we write that up somewhere?

/// Whether this type contains any handle (`own`, `borrow`, `future`,
/// `stream`, or `error-context`) anywhere within it. Note that this is a
/// superset of `has_borrow`.
has_handle: bool,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Little nit but these two bools now define an ad-hoc enum with three valid states, not four, because of the implies-relation that you note. Maybe make it an enum with accessors?

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

Labels

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