Skip to content

Reduce contention on the global module rwlock#4041

Merged
alexcrichton merged 2 commits into
bytecodealliance:mainfrom
alexcrichton:less-rwlock
Apr 19, 2022
Merged

Reduce contention on the global module rwlock#4041
alexcrichton merged 2 commits into
bytecodealliance:mainfrom
alexcrichton:less-rwlock

Conversation

@alexcrichton

Copy link
Copy Markdown
Member

This commit intendes to close #4025 by reducing contention on the global
rwlock Wasmtime has for module information during instantiation and
dropping a store. Currently registration of a module into this global
map happens during instantiation, but this can be a hot path as
embeddings may want to, in parallel, instantiate modules.

Instead this switches to a strategy of inserting into the global module
map when a Module is created and then removing it from the map when
the Module is dropped. Registration in a Store now preserves the
entire Module within the store as opposed to trying to only save it
piecemeal. In reality the only piece that wasn't saved within a store
was the TypeTables which was pretty inconsequential for core wasm
modules anyway.

This means that instantiation should now clone a singluar Arc into a
Store per Module (previously it cloned two) with zero managemnt on
the global rwlock as that happened at Module creation time.
Additionally dropping a Store again involves zero rwlock management
and only a single Arc drop per-instantiated module (previously it was
two).

In the process of doing this I also went ahead and removed the
Module::new_with_name API. This has been difficult to support
historically with various variations on the internals of ModuleInner
because it involves mutating a Module after it's been created. My hope
is that this API is pretty rarely used and/or isn't super important, so
it's ok to remove.

Finally this change removes some internal Arc layerings that are no
longer necessary, attempting to use either T or &T where possible
without dealing with the overhead of an Arc.

Closes #4025

types: TypeTables,
) -> Result<Self> {
let module = CompiledModule::from_artifacts(
let module = Arc::new(CompiledModule::from_artifacts(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I ideally wanted to remove this Arc as well but the GlobalModuleRegistry needed to hang on to something and the Module isn't constructed yet (and Module needs the dtor to unregister), so I went ahead and left this in an Arc but at this Module layer instead of the previous layer.

@github-actions github-actions Bot added wasmtime:api Related to the API of the `wasmtime` crate itself wasmtime:ref-types Issues related to reference types and GC in Wasmtime labels Apr 15, 2022
@github-actions

Copy link
Copy Markdown

Subscribe to Label Action

cc @fitzgen, @peterhuene

Details This issue or pull request has been labeled: "wasmtime:api", "wasmtime:ref-types"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: wasmtime:ref-types
  • peterhuene: wasmtime:api

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

This commit intendes to close bytecodealliance#4025 by reducing contention on the global
rwlock Wasmtime has for module information during instantiation and
dropping a store. Currently registration of a module into this global
map happens during instantiation, but this can be a hot path as
embeddings may want to, in parallel, instantiate modules.

Instead this switches to a strategy of inserting into the global module
map when a `Module` is created and then removing it from the map when
the `Module` is dropped. Registration in a `Store` now preserves the
entire `Module` within the store as opposed to trying to only save it
piecemeal. In reality the only piece that wasn't saved within a store
was the `TypeTables` which was pretty inconsequential for core wasm
modules anyway.

This means that instantiation should now clone a singluar `Arc` into a
`Store` per `Module` (previously it cloned two) with zero managemnt on
the global rwlock as that happened at `Module` creation time.
Additionally dropping a `Store` again involves zero rwlock management
and only a single `Arc` drop per-instantiated module (previously it was
two).

In the process of doing this I also went ahead and removed the
`Module::new_with_name` API. This has been difficult to support
historically with various variations on the internals of `ModuleInner`
because it involves mutating a `Module` after it's been created. My hope
is that this API is pretty rarely used and/or isn't super important, so
it's ok to remove.

Finally this change removes some internal `Arc` layerings that are no
longer necessary, attempting to use either `T` or `&T` where possible
without dealing with the overhead of an `Arc`.

Closes bytecodealliance#4025
Comment thread crates/wasmtime/src/module/registry.rs Outdated
Comment on lines +24 to +29
modules_with_code: BTreeMap<usize, Arc<RegisteredModule>>,
modules_without_code: Vec<Arc<CompiledModule>>,
// Sorted by module start address in memory, used for looking up based on a
// pc of a wasm function.
modules_with_code: Vec<Module>,

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.

Why switch modules_with_code from a BTreeMap to a sorted Vec? This is introducing O(n) runtime to registering new modules. Is the binary search in the sorted Vec really that much faster that we are willing to take the hit on O(n) module registration?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This was a drive-by cleanup I figured I'd do while I was changing things. When thinking again about this I figured that most stores would only ever have a small handful of modules so the overhead of BTreeMap probably wasn't worth it. If you'd prefer though I can add the map back in

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.

Does the BTreeMap actually have overhead for a small number of modules? If they all fit in a single node, then I think there wouldn't be any overhead at all?

Without profiling evidence to the contrary, I'd lean towards keeping the BTreeMap and avoiding turning registration from O(log n) to O(n). Especially since, with the component model, we will expect smaller but more modules to become the norm.

@fitzgen fitzgen 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.

r=me with either the BTreeMap reinstated or with profiling evidence that it isn't worth it / binary searching a sorted vec is faster in practice.

@alexcrichton

Copy link
Copy Markdown
Member Author

Sounds good to me, I haven't profiled this so I'll switch back to BTreeMap

@alexcrichton
alexcrichton merged commit 90791a0 into bytecodealliance:main Apr 19, 2022
@alexcrichton
alexcrichton deleted the less-rwlock branch April 19, 2022 20:13
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 wasmtime:ref-types Issues related to reference types and GC in Wasmtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove rwlock around instantiating a module within a store

2 participants