From 5863fe7baf7c1422be3c937b55313caec7579e44 Mon Sep 17 00:00:00 2001 From: wwwcg Date: Wed, 27 Dec 2023 20:24:37 +0800 Subject: [PATCH] fix(ios): optimize performance of getting components from viewRegistry --- .../native/ios/renderer/HippyComponentMap.h | 31 +++++++++--- .../native/ios/renderer/HippyComponentMap.mm | 49 ++++++++++++++----- .../native/ios/renderer/HippyUIManager.mm | 10 +++- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/renderer/native/ios/renderer/HippyComponentMap.h b/renderer/native/ios/renderer/HippyComponentMap.h index 2b8ac6224ee..3d6d2cbc98c 100644 --- a/renderer/native/ios/renderer/HippyComponentMap.h +++ b/renderer/native/ios/renderer/HippyComponentMap.h @@ -56,6 +56,8 @@ typedef NS_ENUM(NSUInteger, HippyComponentReferenceType) { - (instancetype)init NS_UNAVAILABLE; + (instancetype)new NS_UNAVAILABLE; +#pragma mark - Root Component + - (void)addRootComponent:(id)component rootNode:(std::weak_ptr)rootNode forTag:(NSNumber *)tag; @@ -70,18 +72,33 @@ typedef NS_ENUM(NSUInteger, HippyComponentReferenceType) { - (std::weak_ptr)rootNodeForTag:(NSNumber *)tag; -- (void)addComponent:(__kindof id)component - forRootTag:(NSNumber *)tag; -- (void)removeComponent:(__kindof id)component - forRootTag:(NSNumber *)tag; +#pragma mark - + +/// Add a component to ComponentMap +- (void)addComponent:(__kindof id)component forRootTag:(NSNumber *)tag; -- (void)removeComponentByComponentTag:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag; +/// Remove one component from ComponentMap +- (void)removeComponent:(__kindof id)component forRootTag:(NSNumber *)tag; - (NSDictionary> *)componentsForRootTag:(NSNumber *)tag; -- (__kindof id)componentForTag:(NSNumber *)componentTag - onRootTag:(NSNumber *)tag; +- (__kindof id)componentForTag:(NSNumber *)componentTag onRootTag:(NSNumber *)tag; + + +#pragma mark - Performance optimization + +/// Generate a dictionary cache for all the weak components. +/// +/// Calling componentsForRootTag methods is time-consuming, +/// and in particular, outside may call this in the loop, +/// so we optimize this with a temporary cache. +/// +/// The cache must be actively cleared after acquiring components +- (void)generateTempCacheBeforeAcquireAllStoredWeakComponents; + +/// Clear the temp dictionary cache for weak components. +- (void)clearTempCacheAfterAcquireAllStoredWeakComponents; @end diff --git a/renderer/native/ios/renderer/HippyComponentMap.mm b/renderer/native/ios/renderer/HippyComponentMap.mm index 6ef58deb851..db2e8257a6b 100644 --- a/renderer/native/ios/renderer/HippyComponentMap.mm +++ b/renderer/native/ios/renderer/HippyComponentMap.mm @@ -30,6 +30,9 @@ @interface HippyComponentMap () { NSMapTable> *_rootComponentsMap; NSMutableDictionary *_componentsMap; std::unordered_map> _rootNodesMap; + + BOOL _enableWeakComponentsTempCache; + NSDictionary *_cacheDictionaryForWeakComponents; } @end @@ -105,6 +108,10 @@ - (void)addComponent:(__kindof id)component forRootTag:(NSNumber if (component && tag) { id map = [_componentsMap objectForKey:tag]; [map setObject:component forKey:[component hippyTag]]; + if (!_isStrongHoldAllComponents && _enableWeakComponentsTempCache && _cacheDictionaryForWeakComponents) { + // see `generateTempCacheBeforeAcquireAllStoredWeakComponents` + _cacheDictionaryForWeakComponents = nil; + } } } @@ -115,16 +122,10 @@ - (void)removeComponent:(__kindof id)component forRootTag:(NSNum if (component && tag) { id map = [_componentsMap objectForKey:tag]; [map removeObjectForKey:[component hippyTag]]; - } -} - -- (void)removeComponentByComponentTag:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag { - NSAssert(componentTag, @"component and tag must not be null in method %@", NSStringFromSelector(_cmd)); - NSAssert(rootTag, @"component's tag must not be null in %@", NSStringFromSelector(_cmd)); - NSAssert([self threadCheck], @"%@ method needs run in main thread", NSStringFromSelector(_cmd)); - if (componentTag && rootTag) { - id map = [_componentsMap objectForKey:rootTag]; - [map removeObjectForKey:componentTag]; + if (!_isStrongHoldAllComponents && _enableWeakComponentsTempCache && _cacheDictionaryForWeakComponents) { + // see `generateTempCacheBeforeAcquireAllStoredWeakComponents` + _cacheDictionaryForWeakComponents = nil; + } } } @@ -136,7 +137,21 @@ - (void)removeComponentByComponentTag:(NSNumber *)componentTag onRootTag:(NSNumb if (_isStrongHoldAllComponents) { return map; } else { - return ((NSMapTable *)map).dictionaryRepresentation; + // Note: Performance optimization: + // Calling dictionaryRepresentation methods is time-consuming, + // and in particular, outside may call this in the loop, + // so we optimize this with a temporary cache. + // Remember: + // 1. The cache is automatically removed when a new component is inserted. + // 2. The cache must exist only temporarily, otherwise it will affect the lifecycle of the component. + if (_enableWeakComponentsTempCache) { + if (!_cacheDictionaryForWeakComponents) { + _cacheDictionaryForWeakComponents = ((NSMapTable *)map).dictionaryRepresentation; + } + return _cacheDictionaryForWeakComponents; + } else { + return ((NSMapTable *)map).dictionaryRepresentation; + } } } return nil; @@ -171,4 +186,16 @@ - (NSString *)description { return [description copy]; } + +#pragma mark - + +- (void)generateTempCacheBeforeAcquireAllStoredWeakComponents { + _enableWeakComponentsTempCache = YES; +} + +- (void)clearTempCacheAfterAcquireAllStoredWeakComponents { + _enableWeakComponentsTempCache = NO; + _cacheDictionaryForWeakComponents = nil; +} + @end diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index 1934390a705..b7824696acd 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -575,12 +575,14 @@ - (UIView *)createViewRecursiveFromRenderObjectWithNOLock:(HippyShadowView *)sha NSMutableSet *applierBlocks = [NSMutableSet set]; [shadowView amendLayoutBeforeMount:applierBlocks]; if (applierBlocks.count) { + [self.viewRegistry generateTempCacheBeforeAcquireAllStoredWeakComponents]; for (NativeRenderApplierBlock block in applierBlocks) { // Note: viewRegistry may be modified in the block, and it may be stored internally as NSMapTable // so to ensure that it is up-to-date, it can only be retrieved each time. NSDictionary *viewRegistry = [self.viewRegistry componentsForRootTag:shadowView.rootTag]; block(viewRegistry, view); } + [self.viewRegistry clearTempCacheAfterAcquireAllStoredWeakComponents]; } } return view; @@ -728,6 +730,7 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr)rootNode { __strong __typeof(weakSelf)strongSelf = weakSelf; if (strongSelf) { TDF_PERF_LOG("flushUIBlocksOnRootNode on main thread(random id:%u)",rand); + [strongSelf.viewRegistry generateTempCacheBeforeAcquireAllStoredWeakComponents]; for (HippyViewManagerUIBlock block in previousPendingUIBlocks) { @try { // Note: viewRegistry may be modified in the block, and it may be stored internally as NSMapTable @@ -738,6 +741,7 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr)rootNode { HippyLogError(@"Exception thrown while executing UI block: %@", exception); } } + [strongSelf.viewRegistry clearTempCacheAfterAcquireAllStoredWeakComponents]; TDF_PERF_LOG("flushUIBlocksOnRootNode done, block count:%d(random id:%u)", previousPendingUIBlocks.count, rand); } }); @@ -802,7 +806,6 @@ - (void)createRenderNodes:(std::vector> &&)nodes if ([view respondsToSelector:@selector(hippyBridgeDidFinishTransaction)]) { [uiManager->_componentTransactionListeners addObject:view]; } - [uiManager.viewRegistry addComponent:view forRootTag:shadowView.rootTag]; [tempCreatedViews addObject:view]; // TODO: hippy3 events binding handling, performance needs to be improved here. @@ -816,6 +819,11 @@ - (void)createRenderNodes:(std::vector> &&)nodes }]; } } + [self addUIBlock:^(HippyUIManager *uiManager, NSDictionary *viewRegistry) { + for (UIView *view in tempCreatedViews) { + [uiManager.viewRegistry addComponent:view forRootTag:rootNodeTag]; + } + }]; [manager enumerateViewsHierarchy:^(int32_t tag, const std::vector &subviewTags, const std::vector &subviewIndices) { auto subViewTags_ = subviewTags; auto subViewIndices_ = subviewIndices;