Remove branching for optimized differ QE

Summary: Changelog: [Internal]

Reviewed By: JoshuaGross

Differential Revision: D21556312

fbshipit-source-id: 0d6d275de2d691cb42e5e70e5bf19bcc983cae12
This commit is contained in:
Samuel Susla
2020-05-14 05:30:48 -07:00
committed by Facebook GitHub Bot
parent 0b8a82a6ee
commit 16d15209e5
11 changed files with 29 additions and 263 deletions
@@ -26,8 +26,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak) id<RCTMountingManagerDelegate> delegate;
@property (nonatomic, strong) RCTComponentViewRegistry *componentViewRegistry;
@property (atomic, assign) BOOL useModernDifferentiatorMode;
/**
* Schedule a mounting transaction to be performed on the main thread.
* Can be called from any thread.
+1 -4
View File
@@ -266,10 +266,7 @@ static void RNPerformMountInstructions(
SystraceSection s("-[RCTMountingManager performTransaction:]");
RCTAssertMainQueue();
auto differentiatorMode =
self.useModernDifferentiatorMode ? DifferentiatorMode::OptimizedMoves : DifferentiatorMode::Classic;
auto transaction = mountingCoordinator->pullTransaction(differentiatorMode);
auto transaction = mountingCoordinator->pullTransaction();
if (!transaction.has_value()) {
return;
}
-5
View File
@@ -323,11 +323,6 @@ static inline LayoutContext RCTGetLayoutContext()
RCTScheduler *scheduler = [[RCTScheduler alloc] initWithToolbox:toolbox];
scheduler.delegate = self;
if (reactNativeConfig) {
_mountingManager.useModernDifferentiatorMode =
reactNativeConfig->getBool("react_fabric:enabled_optimized_moves_differ_ios");
}
return scheduler;
}
@@ -271,9 +271,6 @@ void Binding::installFabricUIManager(
disablePreallocateViews_ = reactNativeConfig_->getBool(
"react_fabric:disabled_view_preallocation_android");
enableOptimizedMovesDiffer_ = reactNativeConfig_->getBool(
"react_fabric:enabled_optimized_moves_differ_android");
auto toolbox = SchedulerToolbox{};
toolbox.contextContainer = contextContainer;
toolbox.componentRegistryFactory = componentsRegistry->buildRegistryFunction;
@@ -583,9 +580,7 @@ void Binding::schedulerDidFinishTransaction(
return;
}
auto mountingTransaction = mountingCoordinator->pullTransaction(
enableOptimizedMovesDiffer_ ? DifferentiatorMode::OptimizedMoves
: DifferentiatorMode::Classic);
auto mountingTransaction = mountingCoordinator->pullTransaction();
if (!mountingTransaction.has_value()) {
return;
@@ -112,7 +112,6 @@ class Binding : public jni::HybridClass<Binding>, public SchedulerDelegate {
bool collapseDeleteCreateMountingInstructions_{false};
bool disablePreallocateViews_{false};
bool disableVirtualNodePreallocation_{false};
bool enableOptimizedMovesDiffer_{false};
};
} // namespace react
+13 -200
View File
@@ -254,185 +254,7 @@ static_assert(
std::is_move_assignable<ShadowViewNodePair::List>::value,
"`ShadowViewNodePair::List` must be `move assignable`.");
static void calculateShadowViewMutationsClassic(
ShadowViewMutation::List &mutations,
ShadowView const &parentShadowView,
ShadowViewNodePair::List &&oldChildPairs,
ShadowViewNodePair::List &&newChildPairs) {
// This version of the algorithm is optimized for simplicity,
// not for performance or optimal result.
if (oldChildPairs.size() == 0 && newChildPairs.size() == 0) {
return;
}
// Sorting pairs based on `orderIndex` if needed.
reorderInPlaceIfNeeded(oldChildPairs);
reorderInPlaceIfNeeded(newChildPairs);
auto index = int{0};
// Maps inserted node tags to pointers to them in `newChildPairs`.
auto insertedPairs = TinyMap<Tag, ShadowViewNodePair const *>{};
// Lists of mutations
auto createMutations = ShadowViewMutation::List{};
auto deleteMutations = ShadowViewMutation::List{};
auto insertMutations = ShadowViewMutation::List{};
auto removeMutations = ShadowViewMutation::List{};
auto updateMutations = ShadowViewMutation::List{};
auto downwardMutations = ShadowViewMutation::List{};
auto destructiveDownwardMutations = ShadowViewMutation::List{};
// Stage 1: Collecting `Update` mutations
for (index = 0; index < oldChildPairs.size() && index < newChildPairs.size();
index++) {
auto const &oldChildPair = oldChildPairs[index];
auto const &newChildPair = newChildPairs[index];
if (oldChildPair.shadowView.tag != newChildPair.shadowView.tag) {
// Totally different nodes, updating is impossible.
break;
}
if (oldChildPair.shadowView != newChildPair.shadowView) {
updateMutations.push_back(ShadowViewMutation::UpdateMutation(
parentShadowView,
oldChildPair.shadowView,
newChildPair.shadowView,
index));
}
auto oldGrandChildPairs =
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
auto newGrandChildPairs =
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
calculateShadowViewMutationsClassic(
*(newGrandChildPairs.size() ? &downwardMutations
: &destructiveDownwardMutations),
oldChildPair.shadowView,
std::move(oldGrandChildPairs),
std::move(newGrandChildPairs));
}
int lastIndexAfterFirstStage = index;
// Stage 2: Collecting `Insert` mutations
for (; index < newChildPairs.size(); index++) {
auto const &newChildPair = newChildPairs[index];
insertMutations.push_back(ShadowViewMutation::InsertMutation(
parentShadowView, newChildPair.shadowView, index));
insertedPairs.insert({newChildPair.shadowView.tag, &newChildPair});
}
// Stage 3: Collecting `Delete` and `Remove` mutations
for (index = lastIndexAfterFirstStage; index < oldChildPairs.size();
index++) {
auto const &oldChildPair = oldChildPairs[index];
// Even if the old view was (re)inserted, we have to generate `remove`
// mutation.
removeMutations.push_back(ShadowViewMutation::RemoveMutation(
parentShadowView, oldChildPair.shadowView, index));
auto const it = insertedPairs.find(oldChildPair.shadowView.tag);
if (it == insertedPairs.end()) {
// The old view was *not* (re)inserted.
// We have to generate `delete` mutation and apply the algorithm
// recursively.
deleteMutations.push_back(
ShadowViewMutation::DeleteMutation(oldChildPair.shadowView));
// We also have to call the algorithm recursively to clean up the entire
// subtree starting from the removed view.
calculateShadowViewMutationsClassic(
destructiveDownwardMutations,
oldChildPair.shadowView,
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
{});
} else {
// The old view *was* (re)inserted.
// We have to call the algorithm recursively if the inserted view
// is *not* the same as removed one.
auto const &newChildPair = *it->second;
if (newChildPair != oldChildPair) {
auto oldGrandChildPairs =
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
auto newGrandChildPairs =
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
calculateShadowViewMutationsClassic(
*(newGrandChildPairs.size() ? &downwardMutations
: &destructiveDownwardMutations),
newChildPair.shadowView,
std::move(oldGrandChildPairs),
std::move(newGrandChildPairs));
}
// In any case we have to remove the view from `insertedPairs` as
// indication that the view was actually removed (which means that
// the view existed before), hence we don't have to generate
// `create` mutation.
insertedPairs.erase(it);
}
}
// Stage 4: Collecting `Create` mutations
for (index = lastIndexAfterFirstStage; index < newChildPairs.size();
index++) {
auto const &newChildPair = newChildPairs[index];
if (insertedPairs.find(newChildPair.shadowView.tag) ==
insertedPairs.end()) {
// The new view was (re)inserted, so there is no need to create it.
continue;
}
createMutations.push_back(
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
calculateShadowViewMutationsClassic(
downwardMutations,
newChildPair.shadowView,
{},
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode));
}
// All mutations in an optimal order:
std::move(
destructiveDownwardMutations.begin(),
destructiveDownwardMutations.end(),
std::back_inserter(mutations));
std::move(
updateMutations.begin(),
updateMutations.end(),
std::back_inserter(mutations));
std::move(
removeMutations.rbegin(),
removeMutations.rend(),
std::back_inserter(mutations));
std::move(
deleteMutations.begin(),
deleteMutations.end(),
std::back_inserter(mutations));
std::move(
createMutations.begin(),
createMutations.end(),
std::back_inserter(mutations));
std::move(
downwardMutations.begin(),
downwardMutations.end(),
std::back_inserter(mutations));
std::move(
insertMutations.begin(),
insertMutations.end(),
std::back_inserter(mutations));
}
static void calculateShadowViewMutationsOptimizedMoves(
static void calculateShadowViewMutations(
ShadowViewMutation::List &mutations,
ShadowView const &parentShadowView,
ShadowViewNodePair::List &&oldChildPairs,
@@ -479,7 +301,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
auto newGrandChildPairs =
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
*(newGrandChildPairs.size() ? &downwardMutations
: &destructiveDownwardMutations),
oldChildPair.shadowView,
@@ -502,7 +324,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
// We also have to call the algorithm recursively to clean up the entire
// subtree starting from the removed view.
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
destructiveDownwardMutations,
oldChildPair.shadowView,
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
@@ -519,7 +341,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
createMutations.push_back(
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
downwardMutations,
newChildPair.shadowView,
{},
@@ -576,7 +398,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
auto newGrandChildPairs =
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
*(newGrandChildPairs.size() ? &downwardMutations
: &destructiveDownwardMutations),
oldChildPair.shadowView,
@@ -617,7 +439,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode);
auto newGrandChildPairs =
sliceChildShadowNodeViewPairs(*newChildPair.shadowNode);
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
*(newGrandChildPairs.size() ? &downwardMutations
: &destructiveDownwardMutations),
oldChildPair.shadowView,
@@ -641,7 +463,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
// We also have to call the algorithm recursively to clean up the
// entire subtree starting from the removed view.
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
destructiveDownwardMutations,
oldChildPair.shadowView,
sliceChildShadowNodeViewPairs(*oldChildPair.shadowNode),
@@ -677,7 +499,7 @@ static void calculateShadowViewMutationsOptimizedMoves(
createMutations.push_back(
ShadowViewMutation::CreateMutation(newChildPair.shadowView));
calculateShadowViewMutationsOptimizedMoves(
calculateShadowViewMutations(
downwardMutations,
newChildPair.shadowView,
{},
@@ -717,7 +539,6 @@ static void calculateShadowViewMutationsOptimizedMoves(
}
ShadowViewMutation::List calculateShadowViewMutations(
DifferentiatorMode differentiatorMode,
ShadowNode const &oldRootShadowNode,
ShadowNode const &newRootShadowNode) {
SystraceSection s("calculateShadowViewMutations");
@@ -736,19 +557,11 @@ ShadowViewMutation::List calculateShadowViewMutations(
ShadowView(), oldRootShadowView, newRootShadowView, -1));
}
if (differentiatorMode == DifferentiatorMode::Classic) {
calculateShadowViewMutationsClassic(
mutations,
ShadowView(oldRootShadowNode),
sliceChildShadowNodeViewPairs(oldRootShadowNode),
sliceChildShadowNodeViewPairs(newRootShadowNode));
} else {
calculateShadowViewMutationsOptimizedMoves(
mutations,
ShadowView(oldRootShadowNode),
sliceChildShadowNodeViewPairs(oldRootShadowNode),
sliceChildShadowNodeViewPairs(newRootShadowNode));
}
calculateShadowViewMutations(
mutations,
ShadowView(oldRootShadowNode),
sliceChildShadowNodeViewPairs(oldRootShadowNode),
sliceChildShadowNodeViewPairs(newRootShadowNode));
return mutations;
}
@@ -21,7 +21,6 @@ enum class DifferentiatorMode { Classic, OptimizedMoves };
* The list of mutations might be and might not be optimal.
*/
ShadowViewMutationList calculateShadowViewMutations(
DifferentiatorMode differentiatorMode,
ShadowNode const &oldRootShadowNode,
ShadowNode const &newRootShadowNode);
@@ -66,8 +66,8 @@ bool MountingCoordinator::waitForTransaction(
lock, timeout, [this]() { return lastRevision_.has_value(); });
}
better::optional<MountingTransaction> MountingCoordinator::pullTransaction(
DifferentiatorMode differentiatorMode) const {
better::optional<MountingTransaction> MountingCoordinator::pullTransaction()
const {
std::lock_guard<std::mutex> lock(mutex_);
if (!lastRevision_.has_value()) {
@@ -80,9 +80,7 @@ better::optional<MountingTransaction> MountingCoordinator::pullTransaction(
telemetry.willDiff();
auto mutations = calculateShadowViewMutations(
differentiatorMode,
baseRevision_.getRootShadowNode(),
lastRevision_->getRootShadowNode());
baseRevision_.getRootShadowNode(), lastRevision_->getRootShadowNode());
telemetry.didDiff();
@@ -52,8 +52,7 @@ class MountingCoordinator final {
* However, a consumer should always call it on the same thread (e.g. on the
* main thread) or ensure sequentiality of mount transactions separately.
*/
better::optional<MountingTransaction> pullTransaction(
DifferentiatorMode differentiatorMode) const;
better::optional<MountingTransaction> pullTransaction() const;
/*
* Blocks the current thread until a new mounting transaction is available or
@@ -227,8 +227,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) {
}*/
// Calculating mutations.
auto mutations1 = calculateShadowViewMutations(
DifferentiatorMode::OptimizedMoves, *rootNodeV1, *rootNodeV2);
auto mutations1 = calculateShadowViewMutations(*rootNodeV1, *rootNodeV2);
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -244,8 +243,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) {
assert(mutations1[1].index == 0);
// Calculating mutations.
auto mutations2 = calculateShadowViewMutations(
DifferentiatorMode::OptimizedMoves, *rootNodeV2, *rootNodeV3);
auto mutations2 = calculateShadowViewMutations(*rootNodeV2, *rootNodeV3);
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -261,8 +259,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) {
assert(mutations2[1].oldChildShadowView.tag == 100);
// Calculating mutations.
auto mutations3 = calculateShadowViewMutations(
DifferentiatorMode::OptimizedMoves, *rootNodeV3, *rootNodeV4);
auto mutations3 = calculateShadowViewMutations(*rootNodeV3, *rootNodeV4);
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -283,8 +280,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) {
assert(mutations3[3].index == 2);
// Calculating mutations.
auto mutations4 = calculateShadowViewMutations(
DifferentiatorMode::OptimizedMoves, *rootNodeV4, *rootNodeV5);
auto mutations4 = calculateShadowViewMutations(*rootNodeV4, *rootNodeV5);
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -309,8 +305,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) {
assert(mutations4[5].newChildShadowView.tag == 102);
assert(mutations4[5].index == 3);
auto mutations5 = calculateShadowViewMutations(
DifferentiatorMode::OptimizedMoves, *rootNodeV5, *rootNodeV6);
auto mutations5 = calculateShadowViewMutations(*rootNodeV5, *rootNodeV6);
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -329,8 +324,7 @@ TEST(MountingTest, testMinimalInstructionGeneration) {
assert(mutations5[3].newChildShadowView.tag == 105);
assert(mutations5[3].index == 3);
auto mutations6 = calculateShadowViewMutations(
DifferentiatorMode::OptimizedMoves, *rootNodeV6, *rootNodeV7);
auto mutations6 = calculateShadowViewMutations(*rootNodeV6, *rootNodeV7);
// The order and exact mutation instructions here may change at any time.
// This test just ensures that any changes are intentional.
@@ -20,7 +20,6 @@ namespace facebook {
namespace react {
static void testShadowNodeTreeLifeCycle(
DifferentiatorMode differentiatorMode,
uint_fast32_t seed,
int treeSize,
int repeats,
@@ -72,8 +71,8 @@ static void testShadowNodeTreeLifeCycle(
// Building an initial view hierarchy.
auto viewTree = stubViewTreeFromShadowNode(*emptyRootNode);
viewTree.mutate(calculateShadowViewMutations(
differentiatorMode, *emptyRootNode, *currentRootNode));
viewTree.mutate(
calculateShadowViewMutations(*emptyRootNode, *currentRootNode));
for (int j = 0; j < stages; j++) {
auto nextRootNode = currentRootNode;
@@ -99,8 +98,8 @@ static void testShadowNodeTreeLifeCycle(
allNodes.push_back(nextRootNode);
// Calculating mutations.
auto mutations = calculateShadowViewMutations(
differentiatorMode, *currentRootNode, *nextRootNode);
auto mutations =
calculateShadowViewMutations(*currentRootNode, *nextRootNode);
// Mutating the view tree.
viewTree.mutate(mutations);
@@ -148,27 +147,8 @@ static void testShadowNodeTreeLifeCycle(
using namespace facebook::react;
TEST(MountingTest, stableBiggerTreeFewerIterationsClassic) {
testShadowNodeTreeLifeCycle(
DifferentiatorMode::Classic,
/* seed */ 1,
/* size */ 512,
/* repeats */ 32,
/* stages */ 32);
}
TEST(MountingTest, stableSmallerTreeMoreIterationsClassic) {
testShadowNodeTreeLifeCycle(
DifferentiatorMode::Classic,
/* seed */ 1,
/* size */ 16,
/* repeats */ 512,
/* stages */ 32);
}
TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMoves) {
testShadowNodeTreeLifeCycle(
DifferentiatorMode::OptimizedMoves,
/* seed */ 0,
/* size */ 512,
/* repeats */ 32,
@@ -177,7 +157,6 @@ TEST(MountingTest, stableBiggerTreeFewerIterationsOptimizedMoves) {
TEST(MountingTest, stableSmallerTreeMoreIterationsOptimizedMoves) {
testShadowNodeTreeLifeCycle(
DifferentiatorMode::OptimizedMoves,
/* seed */ 0,
/* size */ 16,
/* repeats */ 512,