diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 9a4051402e..08ba807a80 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -38,16 +38,15 @@ ASDISPLAYNODE_EXTERN_C_END */ typedef NS_OPTIONS(NSUInteger, ASDisplayNodePerformanceMeasurementOptions) { ASDisplayNodePerformanceMeasurementOptionLayoutSpec = 1 << 0, - ASDisplayNodePerformanceMeasurementOptionLayoutGeneration = 1 << 1 + ASDisplayNodePerformanceMeasurementOptionLayoutComputation = 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; +typedef struct { + CFTimeInterval layoutSpecTotalTime; + NSInteger layoutSpecNumberOfPasses; + CFTimeInterval layoutComputationTotalTime; + NSInteger layoutComputationNumberOfPasses; +} ASDisplayNodePerformanceMeasurements; @interface ASDisplayNode (Beta) @@ -94,10 +93,9 @@ extern NSString *const ASDisplayNodeLayoutGenerationNumberOfPassesKey; @property (nonatomic, assign) ASDisplayNodePerformanceMeasurementOptions measurementOptions; /** - * @abstract A dictionary representing performance measurements collected. - * @note see the constants above to retrieve relevant performance measurements + * @abstract A simple struct representing performance measurements collected. */ -@property (nonatomic, strong, readonly) NSDictionary *performanceMeasurements; +@property (nonatomic, assign, readonly) ASDisplayNodePerformanceMeasurements performanceMeasurements; /** @name Layout Transitioning */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 7517ef5c35..ec85c72c67 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -38,11 +38,6 @@ 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 @@ -1227,17 +1222,17 @@ ASLayoutableSizeHelperForwarding return _measurementOptions; } -- (NSDictionary *)performanceMeasurements +- (ASDisplayNodePerformanceMeasurements)performanceMeasurements { ASDN::MutexLocker l(__instanceLock__); - NSMutableDictionary *measurements = [NSMutableDictionary dictionaryWithCapacity:4]; + ASDisplayNodePerformanceMeasurements measurements = { .layoutSpecNumberOfPasses = -1, .layoutSpecTotalTime = NAN, .layoutComputationNumberOfPasses = -1, .layoutComputationTotalTime = NAN }; if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) { - measurements[ASDisplayNodeLayoutSpecTotalTimeKey] = @(_layoutSpecTotalTime); - measurements[ASDisplayNodeLayoutSpecNumberOfPassesKey] = @(_layoutSpecNumberOfPasses); + measurements.layoutSpecNumberOfPasses = _layoutSpecNumberOfPasses; + measurements.layoutSpecTotalTime = _layoutSpecTotalTime; } - if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutGeneration) { - measurements[ASDisplayNodeLayoutGenerationTotalTimeKey] = @(_layoutGenerationTotalTime); - measurements[ASDisplayNodeLayoutGenerationNumberOfPassesKey] = @(_layoutGenerationNumberOfPasses); + if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation) { + measurements.layoutComputationNumberOfPasses = _layoutComputationNumberOfPasses; + measurements.layoutComputationTotalTime = _layoutComputationTotalTime; } return measurements; } @@ -2433,34 +2428,37 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) ASDN::MutexLocker l(__instanceLock__); if ((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || _layoutSpecBlock != NULL) { - ASLayoutSpec *layoutSpec = nil; - // optional performance measurement for cell nodes - if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) { - ASDN::SumScopeTimer t(_layoutSpecTotalTime); + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + if (measureLayoutSpec) { _layoutSpecNumberOfPasses++; - layoutSpec = [self layoutSpecThatFits:constrainedSize]; - } else { - layoutSpec = [self layoutSpecThatFits:constrainedSize]; } + ASLayoutSpec *layoutSpec = ({ + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + [self layoutSpecThatFits:constrainedSize]; + }); + ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec); layoutSpec.parent = self; // This causes upward propogation of any non-default layoutable values. // manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection - ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection); + { + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection); + } layoutSpec.isMutable = NO; - 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]; + BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation; + if (measureLayoutComputation) { + _layoutComputationNumberOfPasses++; } + ASLayout *layout = ({ + ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation); + [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. diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index b5bda3b879..6755cd007e 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -171,9 +171,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo // performance measurement ASDisplayNodePerformanceMeasurementOptions _measurementOptions; NSTimeInterval _layoutSpecTotalTime; - NSUInteger _layoutSpecNumberOfPasses; - NSTimeInterval _layoutGenerationTotalTime; - NSUInteger _layoutGenerationNumberOfPasses; + NSInteger _layoutSpecNumberOfPasses; + NSTimeInterval _layoutComputationTotalTime; + NSInteger _layoutComputationNumberOfPasses; #if TIME_DISPLAYNODE_OPS @public diff --git a/AsyncDisplayKit/Private/_ASScopeTimer.h b/AsyncDisplayKit/Private/_ASScopeTimer.h index 456b2aefb6..5d9aa9cec6 100644 --- a/AsyncDisplayKit/Private/_ASScopeTimer.h +++ b/AsyncDisplayKit/Private/_ASScopeTimer.h @@ -42,11 +42,16 @@ namespace ASDN { struct SumScopeTimer { NSTimeInterval begin; NSTimeInterval &outT; - SumScopeTimer(NSTimeInterval &outRef) : outT(outRef) { - begin = CACurrentMediaTime(); + BOOL enable; + SumScopeTimer(NSTimeInterval &outRef, BOOL enable = YES) : outT(outRef), enable(enable) { + if (enable) { + begin = CACurrentMediaTime(); + } } ~SumScopeTimer() { - outT += CACurrentMediaTime() - begin; + if (enable) { + outT += CACurrentMediaTime() - begin; + } } }; }