Skip to content

Commit

Permalink
RFC: Diff in the background
Browse files Browse the repository at this point in the history
Summary:
Experimenting with a new change. We have observed instances of very large (3k+) lists stalling, even on modern devices. This is especially noticeable if the models being diffed are:

- Immutable
- `performUpdates:` often and models are alloc/init'd each time
- The `isEqualToDiffableObject:` is sort of expensive (many `-[NSString isEqualToString:]` across thousands of models or something)

Instead of just rolling this out, I plan on experimenting with results and seeing how much of a performance and stability boost we gain w/ this. Things to measure:

- Scroll performance
- CPU stalls
- WatchDog kills on older devices
Closes Instagram#841

Reviewed By: amonshiz

Differential Revision: D5364127

Pulled By: rnystrom

fbshipit-source-id: 31d50d2e4b3c7c73584d6ec521a9047efd83f315
  • Loading branch information
Ryan Nystrom authored and facebook-github-bot committed Jul 6, 2017
1 parent 9f5bf3f commit 1d773aa
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 24 deletions.
2 changes: 2 additions & 0 deletions Source/Common/IGListExperiments.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ NS_SWIFT_NAME(ListExperiment)
typedef NS_OPTIONS (NSInteger, IGListExperiment) {
/// Specifies no experiments.
IGListExperimentNone = 1 << 1,
/// Test updater diffing performed on a background queue.
IGListExperimentBackgroundDiffing = 1 << 2,
};

/**
Expand Down
74 changes: 50 additions & 24 deletions Source/IGListAdapterUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,17 @@ - (void)performBatchUpdatesWithCollectionView:(UICollectionView *)collectionView
return;
}

IGListIndexSetResult *result = IGListDiffExperiment(fromObjects, toObjects, IGListDiffEquality, self.experiments);
// disables multiple performBatchUpdates: from happening at the same time
[self beginPerformBatchUpdatesToObjects:toObjects];

const IGListExperiment experiments = self.experiments;

void (^updateBlock)() = ^{
IGListIndexSetResult *(^performDiff)() = ^{
return IGListDiffExperiment(fromObjects, toObjects, IGListDiffEquality, experiments);
};

// block executed in the first param block of -[UICollectionView performBatchUpdates:completion:]
void (^batchUpdatesBlock)(IGListIndexSetResult *result) = ^(IGListIndexSetResult *result){
executeUpdateBlocks();

self.applyingUpdateData = [self flushCollectionView:collectionView
Expand All @@ -190,7 +198,8 @@ - (void)performBatchUpdatesWithCollectionView:(UICollectionView *)collectionView
[self performBatchUpdatesItemBlockApplied];
};

void (^completionBlock)(BOOL) = ^(BOOL finished) {
// block used as the second param of -[UICollectionView performBatchUpdates:completion:]
void (^batchUpdatesCompletionBlock)(BOOL) = ^(BOOL finished) {
executeCompletionBlocks(finished);

[delegate listAdapterUpdater:self didPerformBatchUpdates:(id)self.applyingUpdateData collectionView:collectionView];
Expand All @@ -200,28 +209,45 @@ - (void)performBatchUpdatesWithCollectionView:(UICollectionView *)collectionView
[self queueUpdateWithCollectionView:collectionView];
};

// disables multiple performBatchUpdates: from happening at the same time
[self beginPerformBatchUpdatesToObjects:toObjects];

@try {
[delegate listAdapterUpdater:self willPerformBatchUpdatesWithCollectionView:collectionView];
if (animated) {
[collectionView performBatchUpdates:updateBlock completion:completionBlock];
} else {
[CATransaction begin];
[CATransaction setDisableActions:YES];
[collectionView performBatchUpdates:updateBlock completion:^(BOOL finished) {
completionBlock(finished);
[CATransaction commit];
}];
// block that executes the batch update and exception handling
void (^performUpdate)(IGListIndexSetResult *) = ^(IGListIndexSetResult *result){
@try {
[delegate listAdapterUpdater:self willPerformBatchUpdatesWithCollectionView:collectionView];
if (animated) {
[collectionView performBatchUpdates:^{
batchUpdatesBlock(result);
} completion:batchUpdatesCompletionBlock];
} else {
[CATransaction begin];
[CATransaction setDisableActions:YES];
[collectionView performBatchUpdates:^{
batchUpdatesBlock(result);
} completion:^(BOOL finished) {
batchUpdatesCompletionBlock(finished);
[CATransaction commit];
}];
}
} @catch (NSException *exception) {
[delegate listAdapterUpdater:self
willCrashWithException:exception
fromObjects:fromObjects
toObjects:toObjects
updates:(id)self.applyingUpdateData];
@throw exception;
}
} @catch (NSException *exception) {
[delegate listAdapterUpdater:self
willCrashWithException:exception
fromObjects:fromObjects
toObjects:toObjects
updates:(id)self.applyingUpdateData];
@throw exception;
};

// temporary test to try out background diffing
if (IGListExperimentEnabled(experiments, IGListExperimentBackgroundDiffing)) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
IGListIndexSetResult *result = performDiff();
dispatch_async(dispatch_get_main_queue(), ^{
performUpdate(result);
});
});
} else {
IGListIndexSetResult *result = performDiff();
performUpdate(result);
}
}

Expand Down

0 comments on commit 1d773aa

Please sign in to comment.