Skip to content

Commit 59fbfb6

Browse files
shamanskyhAdlai Holler
authored andcommitted
Measure performance for ASCellNode layout (facebookarchive#2209)
* Measure performance for ASCellNode layout * Address Adlai and Levi's feedback: * Move to ASDisplayNode level * Lock around setting/getting measurement options and results * Record all measurement passes and report times as an array * Only add relevant entries to the performanceMetrics dictionary * Rebase * Store sum and count instead of array * Rename ScopeTimerDataPoint to ScopeTimerSum * Address Levi's feedback * Address Adlai's feedback
1 parent 8c2f3f6 commit 59fbfb6

4 files changed

Lines changed: 100 additions & 3 deletions

File tree

AsyncDisplayKit/ASDisplayNode+Beta.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ void ASPerformBlockOnMainThread(void (^block)());
1818
void ASPerformBlockOnBackgroundThread(void (^block)()); // DISPATCH_QUEUE_PRIORITY_DEFAULT
1919
ASDISPLAYNODE_EXTERN_C_END
2020

21+
/**
22+
* Bitmask to indicate what performance measurements the cell should record.
23+
*/
24+
typedef NS_OPTIONS(NSUInteger, ASDisplayNodePerformanceMeasurementOptions) {
25+
ASDisplayNodePerformanceMeasurementOptionLayoutSpec = 1 << 0,
26+
ASDisplayNodePerformanceMeasurementOptionLayoutGeneration = 1 << 1
27+
};
28+
29+
/**
30+
* Keys to retrieve performance entries from the performance dictionary.
31+
*/
32+
extern NSString *const ASDisplayNodeLayoutSpecTotalTimeKey;
33+
extern NSString *const ASDisplayNodeLayoutSpecNumberOfPassesKey;
34+
extern NSString *const ASDisplayNodeLayoutGenerationTotalTimeKey;
35+
extern NSString *const ASDisplayNodeLayoutGenerationNumberOfPassesKey;
36+
2137
@interface ASDisplayNode (Beta)
2238

2339
/**
@@ -57,6 +73,17 @@ ASDISPLAYNODE_EXTERN_C_END
5773
*/
5874
@property (nonatomic, copy, nullable) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
5975

76+
/**
77+
* @abstract A bitmask representing which actions (layout spec, layout generation) should be measured.
78+
*/
79+
@property (nonatomic, assign) ASDisplayNodePerformanceMeasurementOptions measurementOptions;
80+
81+
/**
82+
* @abstract A dictionary representing performance measurements collected.
83+
* @note see the constants above to retrieve relevant performance measurements
84+
*/
85+
@property (nonatomic, strong, readonly) NSDictionary *performanceMeasurements;
86+
6087
/** @name Layout Transitioning */
6188

6289
/**

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
3939
NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
4040
NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp";
41+
NSString * const ASDisplayNodeLayoutSpecTotalTimeKey = @"ASDisplayNodeLayoutSpecTotalTime";
42+
NSString * const ASDisplayNodeLayoutSpecNumberOfPassesKey = @"ASDisplayNodeLayoutSpecNumberOfPasses";
43+
NSString * const ASDisplayNodeLayoutGenerationTotalTimeKey = @"ASDisplayNodeLayoutGenerationTotalTime";
44+
NSString * const ASDisplayNodeLayoutGenerationNumberOfPassesKey = @"ASDisplayNodeLayoutGenerationNumberOfPasses";
45+
4146

4247
// Forward declare CALayerDelegate protocol as the iOS 10 SDK moves CALayerDelegate from a formal delegate to a protocol.
4348
// We have to forward declare the protocol as this place otherwise it will not compile compiling with an Base SDK < iOS 10
@@ -1156,6 +1161,33 @@ - (void)calculatedLayoutDidChange
11561161
// subclass override
11571162
}
11581163

1164+
- (void)setMeasurementOptions:(ASDisplayNodePerformanceMeasurementOptions)measurementOptions
1165+
{
1166+
ASDN::MutexLocker l(__instanceLock__);
1167+
_measurementOptions = measurementOptions;
1168+
}
1169+
1170+
- (ASDisplayNodePerformanceMeasurementOptions)measurementOptions
1171+
{
1172+
ASDN::MutexLocker l(__instanceLock__);
1173+
return _measurementOptions;
1174+
}
1175+
1176+
- (NSDictionary *)performanceMeasurements
1177+
{
1178+
ASDN::MutexLocker l(__instanceLock__);
1179+
NSMutableDictionary *measurements = [NSMutableDictionary dictionaryWithCapacity:4];
1180+
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
1181+
measurements[ASDisplayNodeLayoutSpecTotalTimeKey] = @(_layoutSpecTotalTime);
1182+
measurements[ASDisplayNodeLayoutSpecNumberOfPassesKey] = @(_layoutSpecNumberOfPasses);
1183+
}
1184+
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutGeneration) {
1185+
measurements[ASDisplayNodeLayoutGenerationTotalTimeKey] = @(_layoutGenerationTotalTime);
1186+
measurements[ASDisplayNodeLayoutGenerationNumberOfPassesKey] = @(_layoutGenerationNumberOfPasses);
1187+
}
1188+
return measurements;
1189+
}
1190+
11591191
#pragma mark - Asynchronous display
11601192

11611193
- (BOOL)displaysAsynchronously
@@ -2343,8 +2375,16 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
23432375

23442376
ASDN::MutexLocker l(__instanceLock__);
23452377
if ((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || _layoutSpecBlock != NULL) {
2346-
ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize];
2347-
2378+
ASLayoutSpec *layoutSpec = nil;
2379+
// optional performance measurement for cell nodes
2380+
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
2381+
ASDN::SumScopeTimer t(_layoutSpecTotalTime);
2382+
_layoutSpecNumberOfPasses++;
2383+
layoutSpec = [self layoutSpecThatFits:constrainedSize];
2384+
} else {
2385+
layoutSpec = [self layoutSpecThatFits:constrainedSize];
2386+
}
2387+
23482388
ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec);
23492389

23502390
layoutSpec.parent = self; // This causes upward propogation of any non-default layoutable values.
@@ -2353,7 +2393,16 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
23532393
ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection);
23542394

23552395
layoutSpec.isMutable = NO;
2356-
ASLayout *layout = [layoutSpec layoutThatFits:constrainedSize];
2396+
ASLayout *layout = nil;
2397+
// optional performance measurement for cell nodes
2398+
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutGeneration) {
2399+
ASDN::SumScopeTimer t(_layoutGenerationTotalTime);
2400+
_layoutGenerationNumberOfPasses++;
2401+
layout = [layoutSpec layoutThatFits:constrainedSize];
2402+
} else {
2403+
layout = [layoutSpec layoutThatFits:constrainedSize];
2404+
}
2405+
23572406
ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec);
23582407

23592408
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.

AsyncDisplayKit/Private/ASDisplayNodeInternal.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#import "ASEnvironment.h"
2323
#import "ASObjectDescriptionHelpers.h"
2424

25+
#import "ASDisplayNode+Beta.h"
26+
2527
@protocol _ASDisplayLayerDelegate;
2628
@class _ASDisplayLayer;
2729
@class _ASPendingState;
@@ -163,6 +165,13 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
163165
CGPoint _accessibilityActivationPoint;
164166
UIBezierPath *_accessibilityPath;
165167

168+
// performance measurement
169+
ASDisplayNodePerformanceMeasurementOptions _measurementOptions;
170+
NSTimeInterval _layoutSpecTotalTime;
171+
NSUInteger _layoutSpecNumberOfPasses;
172+
NSTimeInterval _layoutGenerationTotalTime;
173+
NSUInteger _layoutGenerationNumberOfPasses;
174+
166175
#if TIME_DISPLAYNODE_OPS
167176
@public
168177
NSTimeInterval _debugTimeToCreateView;

AsyncDisplayKit/Private/_ASScopeTimer.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,16 @@ namespace ASDN {
3737
outT = CACurrentMediaTime() - begin;
3838
}
3939
};
40+
41+
// variant where repeated calls are summed
42+
struct SumScopeTimer {
43+
NSTimeInterval begin;
44+
NSTimeInterval &outT;
45+
SumScopeTimer(NSTimeInterval &outRef) : outT(outRef) {
46+
begin = CACurrentMediaTime();
47+
}
48+
~SumScopeTimer() {
49+
outT += CACurrentMediaTime() - begin;
50+
}
51+
};
4052
}

0 commit comments

Comments
 (0)