wasm2c: Save memory base locally as a perf optimization - #2804
Open
shravanrn wants to merge 2 commits into
Open
Conversation
shravanrn
force-pushed
the
local-base
branch
4 times, most recently
from
August 3, 2026 22:53
1a4b701 to
dbc0af3
Compare
Member
|
Can you update the PR description now that part 1 has landed? |
Collaborator
Author
|
Did some cleanup to simplify the code and reduce the code-diff and updated the PR description. |
Member
|
In your benchmark results I guess +37.6% means 37% overhead compared to native? i.e. bigger is worse? |
sbc100
reviewed
Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
For Wasm modules with a single unshared wasm-32 memory, and when using the MMap based memory allocation (which guarantees that the base won't move over the Wasm instance lifetime), this change caches the memory base in a local variable and uses that instead of fetching the value each time from the instance pointer.
While this seems like it really shouldn't do much, it unlocks a bunch of optimizations in C compilers, as they don't seem to be able to reason that the base pointer hasn't changed after most function calls with the Wasm code. The end result is some dramatic improvements.
As a reference, this approach is able to claw back a lot of the same performance overheads of segue (i.e., using the segment register) in wasm2c except without segment registers, meaning this will work on all platforms. This makes some intuitive sense, as I expect that segue is more important to classic SFI tools (like LFI) rather than a Wasm like system. (But there are other benchmarks that segue still is a huge win --- on average, segue still remains the best option if supported on the target platform)
Here are the performance numbers from two use cases in Firefox: Expat XML parsing, Graphite font rendering that use Wasm sandboxes. The number is the overhead over native code (so bigger is worse)
Note the "local base" row is the new row below
Expat XML parsing overhead
Baseline: native
Wasm2c (Mmap + guard pages, no segue): +37.6%
Wasm2c (Mmap + guard pages, segue): +13.7%
Wasm2c (Mmap + guard pages, local base): +18.7%
Wasm2c (Mmap + bounds checks, no segue): +50.7%
Wasm2c (Mmap + bounds checks, segue): +37.4%
Wasm2c (Mmap + bounds checks, local base): +32.8%
Graphite font overhead
Baseline: native
Wasm2c (Mmap + guard pages, no segue): +41.2%
Wasm2c (Mmap + guard pages, segue): +18.2%
Wasm2c (Mmap + guard pages, local base): +14.1%
Wasm2c (Mmap + bounds checks, no segue): +60.0%
Wasm2c (Mmap + bounds checks, segue): +43.3%
Wasm2c (Mmap + bounds checks, local base): +41.8%
I intend to enable this optimization in Firefox's builds asap once it is landed here.