Repository navigation
perf: emplace buffered JSI array values - #2037
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
This branch was previously 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.
Construct heap-buffered JSI values with
reserve()andemplace_back()instead of default-initializing and then assigning every slot. Keep the stack path for up to eight elements and one bulk push into Hermes.The shared helper covers C++ vectors, Swift arrays, and JNI primitive/object arrays. Tuples already construct their fixed staging values directly; their direct-insertion alternative is measured below.
Measured heap-backed numeric arrays take about 19–23% less time. Direct indexed insertion is slower for multi-element arrays and tuples, so bulk insertion stays here.
Reproducible benchmark source, raw samples and build commands: https://gist.github.com/mrousavy/039d0c2ee0624790396f84deda84dd80
Benchmark method and environment
Measurements are ns per produced array/tuple. Candidate cells show median time, median paired ratio to the current implementation, and the paired ratio's P10–P90 range; lower is faster. Each case has 35 samples across five fresh processes, seven rounds per process. All three methods used the same calibrated iteration count per case; option order and case order were shuffled. Each option warmed up before timing; a full Hermes GC ran before each batch, outside the timer. Every produced array escaped through a compiler barrier; result lengths, values and reference identity were checked outside timers. Allocation, conversion, stores, push and result destruction are inside the measured loop.
Apple M2 Pro (arm64), macOS 27.2 (26B123), Apple clang 21.0.0 (clang-2100.4.1.1), benchmark
-std=c++20 -O3 -DNDEBUG. Hermes pinned commit26fbb82fad39d9d06901adb1d6d630dacc84c877, rebuilt from its clean checkout using the repository's cached Release configuration:-O3 -DNDEBUG, Hades GC,HEAP_HV_64, Unicode-lite, debugger/Intl disabled. The benchmark is C++ → JSI → Hermes, with no JNI, Swift or React Native bridge. Object/string-reference cases clone preexisting rooted JS values; fresh strings useString::createFromUtf8on 22–26-byte ASCII inputs. One process ran at a time during an exclusive no-build window.Full benchmark cases
Validation: the caller and template implementations pass ASan+UBSan checks for empty/small/large arrays, heterogeneous values, reference identity, conversion order, conversion exceptions and forced GC. Hermes itself is an uninstrumented Release build. All 127 generated Android C++ translation units pass NDK syntax checks. Prototype-setter behavior matches the current implementation. Benchmark fixtures remain outside the repository.
Draft direct-insertion follow-up: #2038.
Small-array follow-up: sizes 1–8
Fresh five-process run on the same Release Hermes/M2 Pro setup, with seven paired randomized rounds per case (35 samples per option). Times are ns per numeric array; ratio ranges are paired P10–P90. Baseline means the original resize/assignment implementation, reserve/emplace is #2037, and direct insertion is #2038.
Direct insertion is effectively tied at one element and becomes slower at two. All four measured payload kinds show a regression from two elements. The small bulk paths use stack storage, so this range has no temporary heap-buffer allocation to eliminate.
Source, raw samples, build commands and full payload table: https://gist.github.com/mrousavy/baf71995556d27d5cf3f61921f8db4d3