Measure performance for ASCellNode layout (#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
This commit is contained in:
Harry Shamansky
2016-09-09 10:38:13 -07:00
committed by Adlai Holler
parent 8c2f3f666f
commit 59fbfb682d
4 changed files with 100 additions and 3 deletions

View File

@@ -18,6 +18,22 @@ void ASPerformBlockOnMainThread(void (^block)());
void ASPerformBlockOnBackgroundThread(void (^block)()); // DISPATCH_QUEUE_PRIORITY_DEFAULT
ASDISPLAYNODE_EXTERN_C_END
/**
* Bitmask to indicate what performance measurements the cell should record.
*/
typedef NS_OPTIONS(NSUInteger, ASDisplayNodePerformanceMeasurementOptions) {
ASDisplayNodePerformanceMeasurementOptionLayoutSpec = 1 << 0,
ASDisplayNodePerformanceMeasurementOptionLayoutGeneration = 1 << 1
};
/**
* Keys to retrieve performance entries from the performance dictionary.
*/
extern NSString *const ASDisplayNodeLayoutSpecTotalTimeKey;
extern NSString *const ASDisplayNodeLayoutSpecNumberOfPassesKey;
extern NSString *const ASDisplayNodeLayoutGenerationTotalTimeKey;
extern NSString *const ASDisplayNodeLayoutGenerationNumberOfPassesKey;
@interface ASDisplayNode (Beta)
/**
@@ -57,6 +73,17 @@ ASDISPLAYNODE_EXTERN_C_END
*/
@property (nonatomic, copy, nullable) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
/**
* @abstract A bitmask representing which actions (layout spec, layout generation) should be measured.
*/
@property (nonatomic, assign) ASDisplayNodePerformanceMeasurementOptions measurementOptions;
/**
* @abstract A dictionary representing performance measurements collected.
* @note see the constants above to retrieve relevant performance measurements
*/
@property (nonatomic, strong, readonly) NSDictionary *performanceMeasurements;
/** @name Layout Transitioning */
/**

View File

@@ -38,6 +38,11 @@
NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp";
NSString * const ASDisplayNodeLayoutSpecTotalTimeKey = @"ASDisplayNodeLayoutSpecTotalTime";
NSString * const ASDisplayNodeLayoutSpecNumberOfPassesKey = @"ASDisplayNodeLayoutSpecNumberOfPasses";
NSString * const ASDisplayNodeLayoutGenerationTotalTimeKey = @"ASDisplayNodeLayoutGenerationTotalTime";
NSString * const ASDisplayNodeLayoutGenerationNumberOfPassesKey = @"ASDisplayNodeLayoutGenerationNumberOfPasses";
// Forward declare CALayerDelegate protocol as the iOS 10 SDK moves CALayerDelegate from a formal delegate to a protocol.
// 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 @@ ASLayoutableSizeHelperForwarding
// subclass override
}
- (void)setMeasurementOptions:(ASDisplayNodePerformanceMeasurementOptions)measurementOptions
{
ASDN::MutexLocker l(__instanceLock__);
_measurementOptions = measurementOptions;
}
- (ASDisplayNodePerformanceMeasurementOptions)measurementOptions
{
ASDN::MutexLocker l(__instanceLock__);
return _measurementOptions;
}
- (NSDictionary *)performanceMeasurements
{
ASDN::MutexLocker l(__instanceLock__);
NSMutableDictionary *measurements = [NSMutableDictionary dictionaryWithCapacity:4];
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
measurements[ASDisplayNodeLayoutSpecTotalTimeKey] = @(_layoutSpecTotalTime);
measurements[ASDisplayNodeLayoutSpecNumberOfPassesKey] = @(_layoutSpecNumberOfPasses);
}
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutGeneration) {
measurements[ASDisplayNodeLayoutGenerationTotalTimeKey] = @(_layoutGenerationTotalTime);
measurements[ASDisplayNodeLayoutGenerationNumberOfPassesKey] = @(_layoutGenerationNumberOfPasses);
}
return measurements;
}
#pragma mark - Asynchronous display
- (BOOL)displaysAsynchronously
@@ -2343,7 +2375,15 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
ASDN::MutexLocker l(__instanceLock__);
if ((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || _layoutSpecBlock != NULL) {
ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize];
ASLayoutSpec *layoutSpec = nil;
// optional performance measurement for cell nodes
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
ASDN::SumScopeTimer t(_layoutSpecTotalTime);
_layoutSpecNumberOfPasses++;
layoutSpec = [self layoutSpecThatFits:constrainedSize];
} else {
layoutSpec = [self layoutSpecThatFits:constrainedSize];
}
ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec);
@@ -2353,7 +2393,16 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection);
layoutSpec.isMutable = NO;
ASLayout *layout = [layoutSpec layoutThatFits:constrainedSize];
ASLayout *layout = nil;
// optional performance measurement for cell nodes
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutGeneration) {
ASDN::SumScopeTimer t(_layoutGenerationTotalTime);
_layoutGenerationNumberOfPasses++;
layout = [layoutSpec layoutThatFits:constrainedSize];
} else {
layout = [layoutSpec layoutThatFits:constrainedSize];
}
ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec);
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.

View File

@@ -22,6 +22,8 @@
#import "ASEnvironment.h"
#import "ASObjectDescriptionHelpers.h"
#import "ASDisplayNode+Beta.h"
@protocol _ASDisplayLayerDelegate;
@class _ASDisplayLayer;
@class _ASPendingState;
@@ -163,6 +165,13 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
CGPoint _accessibilityActivationPoint;
UIBezierPath *_accessibilityPath;
// performance measurement
ASDisplayNodePerformanceMeasurementOptions _measurementOptions;
NSTimeInterval _layoutSpecTotalTime;
NSUInteger _layoutSpecNumberOfPasses;
NSTimeInterval _layoutGenerationTotalTime;
NSUInteger _layoutGenerationNumberOfPasses;
#if TIME_DISPLAYNODE_OPS
@public
NSTimeInterval _debugTimeToCreateView;

View File

@@ -37,4 +37,16 @@ namespace ASDN {
outT = CACurrentMediaTime() - begin;
}
};
// variant where repeated calls are summed
struct SumScopeTimer {
NSTimeInterval begin;
NSTimeInterval &outT;
SumScopeTimer(NSTimeInterval &outRef) : outT(outRef) {
begin = CACurrentMediaTime();
}
~SumScopeTimer() {
outT += CACurrentMediaTime() - begin;
}
};
}