Skip to content

Commit 4db1842

Browse files
sammy-SCfacebook-github-bot
authored andcommitted
Replace TinyMap with std::unordered_map in Differentiator (facebook#55680)
Summary: Replace TinyMap<Tag, X> with std::unordered_map<Tag, X>. There is no detectable regression in performance and code is simpler, let's remove TinyMap. ``` ┌────────────────────────────┬──────────────────┬───────────────────────────────────┬───────────────┐ │ Benchmark │ TinyMap (before) │ unordered_map + ctor size (after) │ Change │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 100 uncollapsable views │ 1,499,917 ns │ 1,343,333 ns │ -10.4% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 1000 uncollapsable views │ 52,148,021 ns │ 44,131,792 ns │ -15.4% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 100 views large props │ 5,131,292 ns │ 4,529,937 ns │ -11.7% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 1000 views large props │ 82,706,813 ns │ 84,334,167 ns │ +2.0% (noise) │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ 1500 views large props │ 61,470,542 ns │ 62,976,646 ns │ +2.5% (noise) │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ deep (depth=5, breadth=4) │ 32,672,583 ns │ 30,700,542 ns │ -6.0% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ deep (depth=7, breadth=3) │ 78,523,542 ns │ 74,121,729 ns │ -5.6% │ ├────────────────────────────┼──────────────────┼───────────────────────────────────┼───────────────┤ │ deep (depth=10, breadth=2) │ 48,328,562 ns │ 45,702,188 ns │ -5.4% │ └────────────────────────────┴──────────────────┴───────────────────────────────────┴───────────────┘ ``` Reviewed By: NickGerleman Differential Revision: D90346356
1 parent ac1aa75 commit 4db1842

5 files changed

Lines changed: 81 additions & 189 deletions

File tree

packages/react-native/Libraries/Components/View/__tests__/View-benchmark-itest.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@ import {View} from 'react-native';
1717
let root;
1818
let testViews: React.MixedElement;
1919

20+
function createDeepViewHierarchy(depth: number, breadth: number): React.Node {
21+
if (depth === 0) {
22+
return (
23+
<View
24+
collapsable={false}
25+
style={{width: 10, height: 10}}
26+
nativeID="leaf"
27+
/>
28+
);
29+
}
30+
const children = [];
31+
for (let i = 0; i < breadth; i++) {
32+
children.push(
33+
<View
34+
key={i}
35+
collapsable={false}
36+
nativeID={`d${depth.toString()}-${i.toString()}`}
37+
style={{width: depth + 1, height: depth + 1}}>
38+
{createDeepViewHierarchy(depth - 1, breadth)}
39+
</View>,
40+
);
41+
}
42+
return <View collapsable={false}>{children}</View>;
43+
}
44+
2045
function createViewsWithLargeAmountOfPropsAndStyles(count: number): React.Node {
2146
let views: React.Node = null;
2247
for (let i = 0; i < count; i++) {
@@ -124,4 +149,28 @@ Fantom.unstable_benchmark
124149
root.destroy();
125150
},
126151
}),
152+
)
153+
.test.each(
154+
[
155+
[5, 4],
156+
[7, 3],
157+
[10, 2],
158+
],
159+
([depth, breadth]) =>
160+
`render deep view hierarchy (depth=${depth.toString()}, breadth=${breadth.toString()})`,
161+
() => {
162+
Fantom.runTask(() => root.render(testViews));
163+
},
164+
([depth, breadth]) => ({
165+
beforeAll: () => {
166+
// $FlowExpectedError[incompatible-type]
167+
testViews = createDeepViewHierarchy(depth, breadth);
168+
},
169+
beforeEach: () => {
170+
root = Fantom.createRoot();
171+
},
172+
afterEach: () => {
173+
root.destroy();
174+
},
175+
}),
127176
);

packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,7 @@ LayoutAnimationKeyFrameManager::pullTransaction(
256256
// Catch delete+create (reparenting) (this should be optimized away at
257257
// the diffing level eventually?)
258258
// TODO: to prevent this step we could tag Remove/Insert mutations as
259-
// being moves on the Differ level, since we know that there? We could use
260-
// TinyMap here, but it's not exposed by Differentiator (yet).
259+
// being moves on the Differ level, since we know that there?
261260
std::unordered_set<Tag> insertedTags;
262261
std::unordered_set<Tag> deletedTags;
263262
std::unordered_set<Tag>

packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
#include <react/debug/react_native_assert.h>
1212
#include <react/featureflags/ReactNativeFeatureFlags.h>
1313
#include <algorithm>
14+
#include <unordered_map>
1415
#include "internal/CullingContext.h"
1516
#include "internal/ShadowViewNodePair.h"
16-
#include "internal/TinyMap.h"
1717
#include "internal/sliceChildShadowNodeViewPairs.h"
1818

1919
#include "ShadowView.h"
@@ -58,7 +58,9 @@ static std::ostream& operator<<(
5858

5959
#ifdef DEBUG_LOGS_DIFFER
6060
template <typename KeyT, typename ValueT>
61-
static std::ostream& operator<<(std::ostream& out, TinyMap<KeyT, ValueT>& map) {
61+
static std::ostream& operator<<(
62+
std::ostream& out,
63+
std::unordered_map<KeyT, ValueT>& map) {
6264
auto it = map.begin();
6365
if (it != map.end()) {
6466
out << *it->second;
@@ -143,7 +145,7 @@ struct OrderedMutationInstructionContainer {
143145
static void updateMatchedPairSubtrees(
144146
ViewNodePairScope& scope,
145147
OrderedMutationInstructionContainer& mutationContainer,
146-
TinyMap<Tag, ShadowViewNodePair*>& newRemainingPairs,
148+
std::unordered_map<Tag, ShadowViewNodePair*>& newRemainingPairs,
147149
std::vector<ShadowViewNodePair*>& oldChildPairs,
148150
Tag parentTag,
149151
const ShadowViewNodePair& oldPair,
@@ -164,11 +166,11 @@ static void calculateShadowViewMutationsFlattener(
164166
ReparentMode reparentMode,
165167
OrderedMutationInstructionContainer& mutationContainer,
166168
Tag parentTag,
167-
TinyMap<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
169+
std::unordered_map<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
168170
const ShadowViewNodePair& node,
169171
Tag parentTagForUpdate,
170-
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
171-
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes,
172+
std::unordered_map<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
173+
std::unordered_map<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes,
172174
const CullingContext& cullingContextForUnvisitedOtherNodes,
173175
const CullingContext& cullingContext);
174176

@@ -183,7 +185,7 @@ static void calculateShadowViewMutationsFlattener(
183185
static void updateMatchedPairSubtrees(
184186
ViewNodePairScope& scope,
185187
OrderedMutationInstructionContainer& mutationContainer,
186-
TinyMap<Tag, ShadowViewNodePair*>& newRemainingPairs,
188+
std::unordered_map<Tag, ShadowViewNodePair*>& newRemainingPairs,
187189
std::vector<ShadowViewNodePair*>& oldChildPairs,
188190
Tag parentTag,
189191
const ShadowViewNodePair& oldPair,
@@ -232,14 +234,16 @@ static void updateMatchedPairSubtrees(
232234
// Unflattening
233235
else {
234236
// Construct unvisited nodes map
235-
auto unvisitedOldChildPairs = TinyMap<Tag, ShadowViewNodePair*>{};
236237
// We don't know where all the children of oldChildPair are
237238
// within oldChildPairs, but we know that they're in the same
238239
// relative order. The reason for this is because of flattening
239240
// + zIndex: the children could be listed before the parent,
240241
// interwoven with children from other nodes, etc.
241242
auto oldFlattenedNodes = sliceChildShadowNodeViewPairsFromViewNodePair(
242243
oldPair, scope, true, oldCullingContextCopy);
244+
auto unvisitedOldChildPairs =
245+
std::unordered_map<Tag, ShadowViewNodePair*>(
246+
oldFlattenedNodes.size());
243247
for (size_t i = 0, j = 0;
244248
i < oldChildPairs.size() && j < oldFlattenedNodes.size();
245249
i++) {
@@ -422,11 +426,11 @@ static void calculateShadowViewMutationsFlattener(
422426
ReparentMode reparentMode,
423427
OrderedMutationInstructionContainer& mutationContainer,
424428
Tag parentTag,
425-
TinyMap<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
429+
std::unordered_map<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
426430
const ShadowViewNodePair& node,
427431
Tag parentTagForUpdate,
428-
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
429-
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes,
432+
std::unordered_map<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
433+
std::unordered_map<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes,
430434
const CullingContext& cullingContextForUnvisitedOtherNodes,
431435
const CullingContext& cullingContext) {
432436
// Step 1: iterate through entire tree
@@ -445,8 +449,8 @@ static void calculateShadowViewMutationsFlattener(
445449

446450
// Views in other tree that are visited by sub-flattening or
447451
// sub-unflattening
448-
TinyMap<Tag, ShadowViewNodePair*> subVisitedOtherNewNodes{};
449-
TinyMap<Tag, ShadowViewNodePair*> subVisitedOtherOldNodes{};
452+
std::unordered_map<Tag, ShadowViewNodePair*> subVisitedOtherNewNodes{};
453+
std::unordered_map<Tag, ShadowViewNodePair*> subVisitedOtherOldNodes{};
450454
auto subVisitedNewMap =
451455
(parentSubVisitedOtherNewNodes != nullptr ? parentSubVisitedOtherNewNodes
452456
: &subVisitedOtherNewNodes);
@@ -456,7 +460,7 @@ static void calculateShadowViewMutationsFlattener(
456460

457461
// Candidates for full tree creation or deletion at the end of this function
458462
auto deletionCreationCandidatePairs =
459-
TinyMap<Tag, const ShadowViewNodePair*>{};
463+
std::unordered_map<Tag, const ShadowViewNodePair*>(treeChildren.size());
460464

461465
for (size_t index = 0;
462466
index < treeChildren.size() && index < treeChildren.size();
@@ -471,7 +475,7 @@ static void calculateShadowViewMutationsFlattener(
471475
: subVisitedNewMap->end());
472476
auto subVisitedOtherOldIt =
473477
(unvisitedIt == unvisitedOtherNodes.end() &&
474-
(subVisitedNewMap->end() != nullptr)
478+
subVisitedOtherNewIt == subVisitedNewMap->end()
475479
? subVisitedOldMap->find(treeChildPair.shadowView.tag)
476480
: subVisitedOldMap->end());
477481

@@ -697,7 +701,8 @@ static void calculateShadowViewMutationsFlattener(
697701
: adjustedOldCullingContext);
698702
// Construct unvisited nodes map
699703
auto unvisitedRecursiveChildPairs =
700-
TinyMap<Tag, ShadowViewNodePair*>{};
704+
std::unordered_map<Tag, ShadowViewNodePair*>(
705+
flattenedNodes.size());
701706
for (auto& flattenedNode : flattenedNodes) {
702707
auto& newChild = *flattenedNode;
703708

@@ -756,9 +761,6 @@ static void calculateShadowViewMutationsFlattener(
756761
// loop of this function.
757762
for (auto& unvisitedRecursiveChildPair :
758763
unvisitedRecursiveChildPairs) {
759-
if (unvisitedRecursiveChildPair.first == 0) {
760-
continue;
761-
}
762764
auto& oldFlattenedNode = *unvisitedRecursiveChildPair.second;
763765

764766
// Node unvisited - mark the entire subtree for deletion
@@ -820,9 +822,6 @@ static void calculateShadowViewMutationsFlattener(
820822
// subtrees if they were never visited during the execution of the above
821823
// loop and recursions.
822824
for (auto& deletionCreationCandidatePair : deletionCreationCandidatePairs) {
823-
if (deletionCreationCandidatePair.first == 0) {
824-
continue;
825-
}
826825
auto& treeChildPair = *deletionCreationCandidatePair.second;
827826

828827
// If node was visited during a flattening/unflattening recursion,
@@ -1042,9 +1041,13 @@ static void calculateShadowViewMutations(
10421041
}
10431042
} else {
10441043
// Collect map of tags in the new list
1045-
auto newRemainingPairs = TinyMap<Tag, ShadowViewNodePair*>{};
1046-
auto newInsertedPairs = TinyMap<Tag, ShadowViewNodePair*>{};
1047-
auto deletionCandidatePairs = TinyMap<Tag, const ShadowViewNodePair*>{};
1044+
auto remainingCount = newChildPairs.size() - index;
1045+
auto newRemainingPairs =
1046+
std::unordered_map<Tag, ShadowViewNodePair*>(remainingCount);
1047+
auto newInsertedPairs =
1048+
std::unordered_map<Tag, ShadowViewNodePair*>(remainingCount);
1049+
auto deletionCandidatePairs =
1050+
std::unordered_map<Tag, const ShadowViewNodePair*>{};
10481051
for (; index < newChildPairs.size(); index++) {
10491052
auto& newChildPair = *newChildPairs[index];
10501053
newRemainingPairs.insert({newChildPair.shadowView.tag, &newChildPair});
@@ -1243,10 +1246,6 @@ static void calculateShadowViewMutations(
12431246
// list to make sure that a node was not reparented into an unflattened
12441247
// node that occurs *after* it in the hierarchy, due to zIndex ordering.
12451248
for (auto& deletionCandidatePair : deletionCandidatePairs) {
1246-
if (deletionCandidatePair.first == 0) {
1247-
continue;
1248-
}
1249-
12501249
const auto& oldChildPair = *deletionCandidatePair.second;
12511250

12521251
DEBUG_LOGS({
@@ -1285,14 +1284,6 @@ static void calculateShadowViewMutations(
12851284
// Final step: generate Create instructions for entirely new
12861285
// subtrees/nodes that are not the result of flattening or unflattening.
12871286
for (auto& newInsertedPair : newInsertedPairs) {
1288-
// Erased elements of a TinyMap will have a Tag/key of 0 - skip those
1289-
// These *should* be removed by the map; there are currently no KNOWN
1290-
// cases where TinyMap will do the wrong thing, but there are not yet
1291-
// any unit tests explicitly for TinyMap, so this is safer for now.
1292-
if (newInsertedPair.first == 0) {
1293-
continue;
1294-
}
1295-
12961287
const auto& newChildPair = *newInsertedPair.second;
12971288

12981289
DEBUG_LOGS({

0 commit comments

Comments
 (0)