Repository navigation
perf: find a moved array element's original index from the offset of its move - #200
Merged
Merged
Conversation
…ement lookup changes
|
Coverage after merging perf/moved-element-offset into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Part of #168.
After a native move, an array draft needs the original index of each moved element that a recipe reads. Since #183, the first eight lookups searched the original array from its end with
lastIndexOf, and later ones built a map of all original indices. V8 runslastIndexOfon frozen, sealed and non-extensible arrays about 15 times as slowly as on other arrays, about 23 ns per element against 1.6 ns, whileindexOftakes 0.21 ns per element on every kind of array. So with several reads of moved rows of a frozen array, the eight searches cost more than the map they deferred: eight edits aftershift()of 10,000 frozen rows took 2,050 µs, against 632 µs before #183.shift(),unshift()andsplice()shift every element they move by the same offset. A lookup now checks first the original index that the offset of the last move gives. When that index holds the element, it searches forward from there withindexOffor a later copy of a repeated element, which finds the same last index aslastIndexOfand the map. Afterreverse(), after moves that the last offset does not explain, or when the check fails, the lookup searches from the end as before. The offset is only a hint: a wrong one costs one comparison and cannot change the result.shift(),unshift()orsplice()is found without a backward search. The three new tests fail onmain.create; ESM with all exports stays under 8.5 kB at 8.48 kB.main. Theshift-and-updatecells were measured again with every library and layered over the batch of perf: rerun the full benchmark on main and refresh the published results #199, so the published figures follow. The default-settings figure becomes 6.7x instead of 6.6x, the matched-settings figure stays at 3.6x, and two cells of the large-array table change; README and website follow.Measurements
The suite, with Mutative alone in processes of its own,
mainat9f93f16and this branch in alternating order, three runs each, µs per update:mainmainshift-and-updatecells. 0.82 of the time ofmainon geometric mean. With patches, emitting the patches of the moved rows takes most of the time.shift-and-updatemoved outside the spread of its processes,search-current-shiftedincluded. Seven freeze-on cells of six upstream scenarios differed by 7–21% in those three rounds. Ten more alternating rounds of the six scenarios measured 0.93–1.03 with overlapping ranges.Recipes that edit several moved rows, measured outside the suite with both production builds, three alternating rounds, µs per update:
mainshift(), then 8 edits near the startshift(), then 8 edits near the startshift(), then 32 edits across the arrayshift(), then 8 edits near the startshift(), then 32 edits across the arrayunshift(), then 1 edit in the middlereverse(), then 1 edit in the middleA prototype of the same lookup ran four recipes in three engines: one edit after
shift(), eight edits aftershift(), one edit aftersplice()and one afterreverse(), on 10,000 frozen and unfrozen rows. These are single runs in one page per recipe and build.mainlastIndexOfhas no slow path for frozen arraysCorrectness
lastIndexOfand of the map in every case.mainexactly with the committed build: results, forward and inverse patches, and both replays. The recipes cover repeated objects, frozen and unfrozen bases, and up to three moves.pnpm test:benchmarks,pnpm test:package,pnpm type-check, lint, format and the website build.Not covered
reverse()and several moves. Afterreverse(), or after several moves of the same element, the lookup still searches from the end. Editing the middle of 10,000 frozen rows afterreverse()takes 310 µs, against 188 µs aftershift(); the summary lists this under its limits.Object.freezeof a 10,000-element array alone takes about 2.4 ms there.Commits
perf: find a moved element's original index from the offset of its movetest: cover moved elements of frozen arrays and lookups from the offset of their movebuild: refresh the size baseline and limits for the offset lookup of moved elementsperf: record measurements of the offset lookup of moved elementsdocs: refresh the README and website figures that the faster moved element lookup changes