add IGListAdapterUpdaterDelegate calls to measure diffing performance

Summary: To prepare testing `Foundation` vs `IGListKit` diffing, lets measure how long it takes.

Reviewed By: Haud

Differential Revision: D22295546

fbshipit-source-id: 8023717f66ea68cbc24981272e8c134dd893274a
This commit is contained in:
Maxime Ollivier 2020-07-02 13:04:00 -07:00 committed by Facebook GitHub Bot
parent aa5b229ba2
commit d5c7076063
3 changed files with 89 additions and 18 deletions

View file

@ -17,6 +17,8 @@
#import "IGListReloadIndexPath.h"
#import "UICollectionView+IGListBatchUpdateData.h"
typedef void (^IGListAdapterUpdaterDiffResultBlock)(IGListIndexSetResult *);
@implementation IGListAdapterUpdater
- (instancetype)init {
@ -112,6 +114,7 @@
void (^objectTransitionBlock)(NSArray *) = [self.objectTransitionBlock copy];
const BOOL animated = self.queuedUpdateIsAnimated;
const BOOL allowsReloadingOnTooManyUpdates = self.allowsReloadingOnTooManyUpdates;
const IGListExperiment experiments = self.experiments;
IGListBatchUpdates *batchUpdates = self.batchUpdates;
// clean up all state so that new updates can be coalesced while the current update is in flight
@ -197,12 +200,6 @@
return;
}
const IGListExperiment experiments = self.experiments;
IGListIndexSetResult *(^performDiff)(void) = ^{
return IGListDiffExperiment(fromObjects, toObjects, IGListDiffEquality, experiments);
};
// block executed in the first param block of -[UICollectionView performBatchUpdates:completion:]
void (^batchUpdatesBlock)(IGListIndexSetResult *result) = ^(IGListIndexSetResult *result){
executeUpdateBlocks();
@ -230,7 +227,7 @@
experiments,
self.movesAsDeletesInserts,
self.preferItemReloadsForSectionReloads);
}
}
[self _cleanStateAfterUpdates];
[self _performBatchUpdatesItemBlockApplied];
@ -306,17 +303,12 @@ willPerformBatchUpdatesWithCollectionView:collectionView
}
};
if (IGListExperimentEnabled(experiments, IGListExperimentBackgroundDiffing)) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
IGListIndexSetResult *result = performDiff();
dispatch_async(dispatch_get_main_queue(), ^{
tryToPerformUpdate(result);
});
});
} else {
IGListIndexSetResult *result = performDiff();
const BOOL onBackgroundThread = IGListExperimentEnabled(experiments, IGListExperimentBackgroundDiffing);
[delegate listAdapterUpdater:self willDiffFromObjects:fromObjects toObjects:toObjects];
IGListAdapterUpdaterPerformDiffing(fromObjects, toObjects, IGListDiffEquality, onBackgroundThread, experiments, ^(IGListIndexSetResult *result){
[delegate listAdapterUpdater:self didDiffWithResults:result onBackgroundThread:onBackgroundThread];
tryToPerformUpdate(result);
}
});
}
- (void)_beginPerformBatchUpdatesToObjects:(NSArray *)toObjects {
@ -374,7 +366,6 @@ willPerformBatchUpdatesWithCollectionView:collectionView
});
}
#pragma mark - IGListUpdatingDelegate
static BOOL IGListIsEqual(const void *a, const void *b, NSUInteger (*size)(const void *item)) {
@ -575,5 +566,25 @@ static NSUInteger IGListIdentifierHash(const void *item, NSUInteger (*size)(cons
}
}
#pragma mark - Diffing
static void IGListAdapterUpdaterPerformDiffing(NSArray<id<IGListDiffable>> *_Nullable oldArray,
NSArray<id<IGListDiffable>> *_Nullable newArray,
IGListDiffOption option,
IGListExperiment experiments,
BOOL onBackgroundThread,
IGListAdapterUpdaterDiffResultBlock completion) {
if (onBackgroundThread) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
IGListIndexSetResult *result = IGListDiffExperiment(oldArray, newArray, option, experiments);
dispatch_async(dispatch_get_main_queue(), ^{
completion(result);
});
});
} else {
IGListIndexSetResult *result = IGListDiffExperiment(oldArray, newArray, option, experiments);
completion(result);
}
}
@end

View file

@ -21,6 +21,28 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(ListAdapterUpdaterDelegate)
@protocol IGListAdapterUpdaterDelegate <NSObject>
/**
Notifies the delegate that the updater is about to beging diffing.
@param listAdapterUpdater The adapter updater owning the transition.
@param fromObjects The items transitioned from in the batch updates, if any.
@param toObjects The items transitioned to in the batch updates, if any.
*/
- (void)listAdapterUpdater:(IGListAdapterUpdater *)listAdapterUpdater
willDiffFromObjects:(nullable NSArray <id<IGListDiffable>> *)fromObjects
toObjects:(nullable NSArray <id<IGListDiffable>> *)toObjects;
/**
Notifies the delegate that the updater finished diffing.
@param listAdapterUpdater The adapter updater owning the transition.
@param listIndexSetResults The diffing result of indices to be inserted/removed/updated/moved/etc.
@param onBackgroundThread Was the diffing performed on a background thread
*/
- (void)listAdapterUpdater:(IGListAdapterUpdater *)listAdapterUpdater
didDiffWithResults:(nullable IGListIndexSetResult *)listIndexSetResults
onBackgroundThread:(BOOL)onBackgroundThread;
/**
Notifies the delegate that the updater will call `-[UICollectionView performBatchUpdates:completion:]`.

View file

@ -814,6 +814,44 @@
[self waitForExpectationsWithTimeout:30 handler:nil];
}
- (void)test_whenPerformingUpdate_thatCallsDiffingDelegate {
self.updater.experiments |= IGListExperimentBackgroundDiffing;
NSArray *from = @[
[IGSectionObject sectionWithObjects:@[] identifier:@"0"]
];
NSArray *to = @[
[IGSectionObject sectionWithObjects:@[] identifier:@"0"],
[IGSectionObject sectionWithObjects:@[] identifier:@"1"]
];
IGListToObjectBlock toBlock = ^NSArray *{
return to;
};
self.dataSource.sections = from;
[self.updater performReloadDataWithCollectionViewBlock:[self collectionViewBlock]];
id mockDelegate = [OCMockObject niceMockForProtocol:@protocol(IGListAdapterUpdaterDelegate)];
self.updater.delegate = mockDelegate;
[mockDelegate setExpectationOrderMatters:YES];
[[mockDelegate expect] listAdapterUpdater:self.updater willDiffFromObjects:from toObjects:to];
[[mockDelegate expect] listAdapterUpdater:self.updater didDiffWithResults:[OCMArg checkWithBlock:^BOOL(IGListIndexSetResult *result) {
return [result.inserts isEqualToIndexSet:[NSIndexSet indexSetWithIndex:1]]
&& result.deletes.count == 0
&& result.updates.count == 0
&& result.moves.count == 0;
}] onBackgroundThread:YES];
XCTestExpectation *expectation = genExpectation;
[self.updater performUpdateWithCollectionViewBlock:[self collectionViewBlock] fromObjects:from toObjectsBlock:toBlock animated:NO objectTransitionBlock:self.updateBlock completion:^(BOOL finished) {
[expectation fulfill];
}];
waitExpectation;
[mockDelegate verify];
}
# pragma mark - preferItemReloadsFroSectionReloads
- (void)test_whenReloadIsCalledWithSameItemCount_andPreferItemReload_updateIndexPathsHappen {