perf: borrow the executing function from its module instance - #74
Open
matthargett wants to merge 2 commits into
Open
matthargett wants to merge 2 commits into
matthargett wants to merge 2 commits into
Conversation
Entering a function reserved and zero-filled all three value-stack lanes even when the function never touches the 64- or 128-bit lane, which is the common case. Skip a lane whose locals and operand-stack depth are both zero. A single-result return now moves the result down and truncates once, instead of popping, truncating and pushing it back.
Every call and return between two functions cloned the callee's `Shared<WasmFunction>` and dropped the previous one, two refcount updates each way, and looked the function up in the store. A module instance now keeps its own functions, which the store allocates contiguously. The interpreter holds the instance for the whole run, so the executor borrows the executing function and its module from it: a call or return within the instance switches a reference, and a direct call to one of the module's own functions skips the address table, the host check and the owner check. Execution that continues in another instance's function (a call through an import, table or reference, a return, or an exception unwinding into it) ends the run, and `InterpreterRuntime` resumes that frame with an executor for its instance. Fuel and time budgets carry over, so a run suspends at the same points as before.
This branch has not been deployed
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.
Every call or return between two functions cloned the callee's
Shared<WasmFunction>and dropped the previous one (two atomic refcount updates each way) and looked the function up in the store.A module instance now keeps its own functions, which the store allocates contiguously (helping with locality for cache and prefetch).
InterpreterRuntimeholds the instance for the whole run and the executor borrows the executing function from it, so a call or return inside the instance switches a reference (I think this is what makes cache evict more often than I would expect). A direct call to one of the module's own functions also skips the address table, the host check and the owner check. Execution that moves into another instance's function (through an import, a table, a function reference, a return or an unwinding exception) ends the run and resumes with an executor for that instance. Fuel and time budgets carry over, so budgeted runs should suspend at the same points as before.Entering a function also skips the value-stack lanes it never uses (most functions only touch the 32-bit lane), and a single-result return moves its result down once instead of popping and pushing it.
tests/cross_instance_calls.rscovers calls, tail calls, table calls, callbacks and exceptions across an instance boundary in both directions, and fuel- and time-budgeted runs that cross it.Microbenchmark explains the uplift in the larger integrated benchmarks: a loop calling a one-line function drops from 393 to 311 instructions per iteration. Change in cycles per call against
next(d1165c2), on the efficiency cores of an iPhone 12 (A14), iPhone XS Max (A12) and iPhone SE (A13), median of five interleaved launches per build: