Skip to content

Commit

Permalink
fix(ios): optimize performance of getting components from viewRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
wwwcg authored and hippy-actions[bot] committed Dec 27, 2023
1 parent 5440642 commit 5863fe7
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 19 deletions.
31 changes: 24 additions & 7 deletions renderer/native/ios/renderer/HippyComponentMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ typedef NS_ENUM(NSUInteger, HippyComponentReferenceType) {
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

#pragma mark - Root Component

- (void)addRootComponent:(id<HippyComponent>)component
rootNode:(std::weak_ptr<hippy::RootNode>)rootNode
forTag:(NSNumber *)tag;
Expand All @@ -70,18 +72,33 @@ typedef NS_ENUM(NSUInteger, HippyComponentReferenceType) {

- (std::weak_ptr<hippy::RootNode>)rootNodeForTag:(NSNumber *)tag;

- (void)addComponent:(__kindof id<HippyComponent>)component
forRootTag:(NSNumber *)tag;

- (void)removeComponent:(__kindof id<HippyComponent>)component
forRootTag:(NSNumber *)tag;
#pragma mark -

/// Add a component to ComponentMap
- (void)addComponent:(__kindof id<HippyComponent>)component forRootTag:(NSNumber *)tag;

- (void)removeComponentByComponentTag:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag;
/// Remove one component from ComponentMap
- (void)removeComponent:(__kindof id<HippyComponent>)component forRootTag:(NSNumber *)tag;

- (NSDictionary<NSNumber *, __kindof id<HippyComponent>> *)componentsForRootTag:(NSNumber *)tag;

- (__kindof id<HippyComponent>)componentForTag:(NSNumber *)componentTag
onRootTag:(NSNumber *)tag;
- (__kindof id<HippyComponent>)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

Expand Down
49 changes: 38 additions & 11 deletions renderer/native/ios/renderer/HippyComponentMap.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ @interface HippyComponentMap () {
NSMapTable<NSNumber *, id<HippyComponent>> *_rootComponentsMap;
NSMutableDictionary<NSNumber *, id> *_componentsMap;
std::unordered_map<int32_t, std::weak_ptr<RootNode>> _rootNodesMap;

BOOL _enableWeakComponentsTempCache;
NSDictionary *_cacheDictionaryForWeakComponents;
}

@end
Expand Down Expand Up @@ -105,6 +108,10 @@ - (void)addComponent:(__kindof id<HippyComponent>)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;
}
}
}

Expand All @@ -115,16 +122,10 @@ - (void)removeComponent:(__kindof id<HippyComponent>)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;
}
}
}

Expand All @@ -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;
Expand Down Expand Up @@ -171,4 +186,16 @@ - (NSString *)description {
return [description copy];
}


#pragma mark -

- (void)generateTempCacheBeforeAcquireAllStoredWeakComponents {
_enableWeakComponentsTempCache = YES;
}

- (void)clearTempCacheAfterAcquireAllStoredWeakComponents {
_enableWeakComponentsTempCache = NO;
_cacheDictionaryForWeakComponents = nil;
}

@end
10 changes: 9 additions & 1 deletion renderer/native/ios/renderer/HippyUIManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -575,12 +575,14 @@ - (UIView *)createViewRecursiveFromRenderObjectWithNOLock:(HippyShadowView *)sha
NSMutableSet<NativeRenderApplierBlock> *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<NSNumber *, UIView *> *viewRegistry = [self.viewRegistry componentsForRootTag:shadowView.rootTag];
block(viewRegistry, view);
}
[self.viewRegistry clearTempCacheAfterAcquireAllStoredWeakComponents];
}
}
return view;
Expand Down Expand Up @@ -728,6 +730,7 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr<RootNode>)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
Expand All @@ -738,6 +741,7 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr<RootNode>)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);
}
});
Expand Down Expand Up @@ -802,7 +806,6 @@ - (void)createRenderNodes:(std::vector<std::shared_ptr<DomNode>> &&)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.
Expand All @@ -816,6 +819,11 @@ - (void)createRenderNodes:(std::vector<std::shared_ptr<DomNode>> &&)nodes
}];
}
}
[self addUIBlock:^(HippyUIManager *uiManager, NSDictionary<NSNumber *,__kindof UIView *> *viewRegistry) {
for (UIView *view in tempCreatedViews) {
[uiManager.viewRegistry addComponent:view forRootTag:rootNodeTag];
}
}];
[manager enumerateViewsHierarchy:^(int32_t tag, const std::vector<int32_t> &subviewTags, const std::vector<int32_t> &subviewIndices) {
auto subViewTags_ = subviewTags;
auto subViewIndices_ = subviewIndices;
Expand Down

0 comments on commit 5863fe7

Please sign in to comment.