Conversation
dimokol
force-pushed
the
embind-value-array-writes
branch
from
September 19, 2026 09:59
62cef9e to
805b45d
Compare
Under DYNAMIC_EXECUTION, build one write function per value_object and value_array so each element's wasm setter gets its own call site. The shared per-element closure made that site megamorphic in modules with many value types, and V8 then boxed every non-integer double on the way in. Number types with an identity conversion are passed straight through.
dimokol
force-pushed
the
embind-value-array-writes
branch
from
September 19, 2026 10:00
805b45d to
87a63ef
Compare
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.
Follow-up to #27610. On box3d.js (about forty value types, node v24, -O3) every value_object or value_array argument with non-integer float fields still cost 16 B of garbage per element:
b3Body_SetTransformwith real floats measured 80 B/call,ApplyForce96, while the same calls with integer values measured 0. The element writes went through one closure literal shared by every value type, so its setter call site was megamorphic and V8 stopped inlining the JS-to-wasm call, boxing each double.With DYNAMIC_EXECUTION the element write step is now generated per value type, one call per element, each site monomorphic, and in non-ASSERTIONS builds number types pass straight through without the identity toWireType call. Without DYNAMIC_EXECUTION the path is unchanged. value_object writes keep the field table's enumeration order and last-registration-wins for a repeated name, with a test for both paths. In the same measurement the float setter rows above go to 0 B/call. Timings also dropped in that run (SetTransform 904 ns to 589 ns, GetPosition 378 ns to 182 ns), taken on a loaded machine, so treat them as indicative. The two
identityToWireTypeflags add 10 bytes to the embind codesize baselines.No allocation regression test is added: whether the boxing happens depends on V8 tiering and on how many value types a module registers, so it isn't stable in a unit test. The numbers come from forced-GC heapUsed deltas over 20k-call rounds after a 300k-call warmup, with GC events inside the windows checked; happy to share the probe. On the preceding revision, I ran 105 test_other and 92 core0/core2 embind tests; the only failures also reproduced on main (test_embind_resource_management needs node 25, and the *_emscripten exception variants need -femscripten-exceptions, which my installed LLVM lacks). On this revision, all 13 targeted checks passed, including the new field-order regression in both modes, Closure, AOT and code-size checks.