{"id":34589,"date":"2021-12-31T15:39:59","date_gmt":"2021-12-31T20:39:59","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=34589"},"modified":"2023-02-16T16:11:43","modified_gmt":"2023-02-16T21:11:43","slug":"the-surprising-cost-of-protocol-conformances-in-swift","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2021\/12\/31\/the-surprising-cost-of-protocol-conformances-in-swift\/","title":{"rendered":"The Surprising Cost of Checking Protocol Conformances in Swift"},"content":{"rendered":"<p><a href=\"https:\/\/www.emergetools.com\/blog\/posts\/SwiftProtocolConformance\">Noah Martin<\/a> (<a href=\"https:\/\/news.ycombinator.com\/item?id=29419349\">Hacker News<\/a>):<\/p>\n<blockquote cite=\"https:\/\/www.emergetools.com\/blog\/posts\/SwiftProtocolConformance\">\n<p>The entry point to our investigation is <a href=\"https:\/\/github.com\/apple\/swift\/pull\/33487\">Mike Ash&rsquo;s PR<\/a> which implements a 13x faster cache that was released in Swift 5.4.<\/p>\n<p>[&#8230;]<\/p>\n<p>We now see that the speed of protocol conformance lookups is dependent on the number of conformances in your app. This will be influenced by how many Swift libraries you link to, and how many conformances you include in your own code. <code>otool -l Helix.app\/Helix | | grep _swift5_proto -A 4<\/code> tells us Uber&rsquo;s app has a 411200 byte protocol conformance section. Each 4 bytes is a relative pointer so 411200 \/ 4 = 102,800 conformances.<\/p>\n<p>[&#8230;]<\/p>\n<p>One source of low hanging fruit that might be in your app is removing protocols that are used only for providing stub implementations in unit tests. These can be compiled out of release builds of the app to avoid them being included in runtime metadata.<\/p>\n<p>[&#8230;]<\/p>\n<p>Profiling your app using tools like Instruments or the Emerge startup time visualization can help you identify where conformance checks are most often used in your app. Then you can refactor code to avoid them entirely.<\/p>\n<p>[&#8230;]<\/p>\n<p>The concept behind <a href=\"https:\/\/github.com\/EmergeTools\/zconform\">zconform<\/a> is to eagerly load all possible protocol conformances and store them in a map keyed by the protocol&rsquo;s address in memory.<\/p>\n<\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2021\/06\/30\/faster-app-launching-in-ios-15-and-monterey\/\">Faster App Launching in iOS 15 and Monterey<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2021\/05\/31\/how-uber-deals-with-large-ios-app-size\/\">How Uber Deals With Large iOS App Size<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2021\/04\/27\/swift-5-4-released\/\">Swift 5.4 Released<\/a><\/li>\n<\/ul>\n\n<p id=\"the-surprising-cost-of-protocol-conformances-in-swift-update-2022-01-03\">Update (2022-01-03): Checking protocol conformances can also be a bottleneck <a href=\"https:\/\/twitter.com\/_saagarjha\/status\/1476739462197506048\">in Objective-C<\/a>.<\/p>\n\n<p id=\"the-surprising-cost-of-protocol-conformances-in-swift-update-2022-02-08\">Update (2022-02-08): <a href=\"https:\/\/twitter.com\/_saagarjha\/status\/1490594720719855616\">Saagar Jha<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/_saagarjha\/status\/1490594720719855616\">\n<p>This appears to have been fixed in Xcode Version 13.3 beta (13E5086k). There&rsquo;s a new <code>DVTCachedConformsToProtocol<\/code> method that is now used extensively throughout the app for <code>conformsToProtocol:<\/code> checks, including for the specific block I originally identified as being problematic.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/gparker\/status\/1490673817475174402\">Greg Parker<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/gparker\/status\/1490673817475174402\">\n<p>Protocol conformance is one of those things that is never quite high enough priority to optimize in the OS. But some apps really do suffer, so they&rsquo;re forced to work around it. Then the presence of those workarounds makes it less important to improve it in the OS.<\/p>\n<\/blockquote>\n\n<p id=\"the-surprising-cost-of-protocol-conformances-in-swift-update-2023-02-16\">Update (2023-02-16): <a href=\"https:\/\/www.emergetools.com\/blog\/posts\/iOS16LaunchTime\">Noah Martin<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.emergetools.com\/blog\/posts\/iOS16LaunchTime\"><p>The big change [in iOS 16] comes in the &ldquo;dyld closure&rdquo;, which is a per-app cache used to accelerate various dyld operations during app launch. The <strong>closure now contains pre-computed conformances<\/strong>, allowing each lookup to be much faster. Note that the dyld closure is not always used, e.g. because it&rsquo;s out-of-date or because it&rsquo;s being launched from Xcode, which complicates things.<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/www.emergetools.com\/blog\/posts\/improve-popular-iOS-app-startup-times\">Michael Eisel<\/a>:<\/p>\n<blockquote cite=\"https:\/\/www.emergetools.com\/blog\/posts\/improve-popular-iOS-app-startup-times\">\n<p>Beyond the issues mentioned above, the Salesforce Service Cloud SDK spends 67ms running <code>class_conformsToProtocol<\/code>\nand <code>objc_copyClassList<\/code> (perhaps iterating over all classes to determine which ones conform to some protocol) in\nnon-initializer setup. All of this setup can likely be moved out of startup.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/www.emergetools.com\/blog\/posts\/how-order-files-speed-up-protocols\">Noah Martin<\/a>:<\/p><blockquote cite=\"https:\/\/www.emergetools.com\/blog\/posts\/how-order-files-speed-up-protocols\"><p>Although this improvement is in iOS 16, it&rsquo;s difficult to measure in practice because this dyld behavior is disabled when running the app from Xcode or Instruments.\nEmerge has a <a href=\"https:\/\/docs.emergetools.com\/docs\/performance-debugging\">local performance debugging tool<\/a> that works around this and can be used to profile apps that do have access to the dyld closure.<\/p><\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Noah Martin (Hacker News): The entry point to our investigation is Mike Ash&rsquo;s PR which implements a 13x faster cache that was released in Swift 5.4. [&#8230;] We now see that the speed of protocol conformance lookups is dependent on the number of conformances in your app. This will be influenced by how many Swift [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2021-12-31T20:40:03Z","apple_news_api_id":"325aac49-9d31-4fcc-b2fb-6f1528a7c7ad","apple_news_api_modified_at":"2023-02-16T21:11:46Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAABQ==","apple_news_api_share_url":"https:\/\/apple.news\/AMlqsSZ0xT8yy-28VKKfHrQ","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[31,2078,30,2077,54,138,71,901],"class_list":["post-34589","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-ios","tag-ios-15","tag-mac","tag-macos-12","tag-objective-c","tag-optimization","tag-programming","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/34589","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=34589"}],"version-history":[{"count":7,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/34589\/revisions"}],"predecessor-version":[{"id":38493,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/34589\/revisions\/38493"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=34589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=34589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=34589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}