{"id":29432,"date":"2020-07-06T16:38:04","date_gmt":"2020-07-06T20:38:04","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=29432"},"modified":"2020-07-07T07:49:44","modified_gmt":"2020-07-07T11:49:44","slug":"optimizing-the-objective-c-runtime-in-big-sur","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2020\/07\/06\/optimizing-the-objective-c-runtime-in-big-sur\/","title":{"rendered":"Optimizing the Objective-C Runtime in Big Sur"},"content":{"rendered":"<p><a href=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2020\/10163\/\">WWDC 2020<\/a>:<\/p>\n<blockquote cite=\"https:\/\/developer.apple.com\/videos\/play\/wwdc2020\/10163\/\"><p>Dive into the microscopic world of low-level bits and bytes that underlie every Objective-C and Swift class. Find out how recent changes to internal data structures, method lists, and tagged pointers provide better performance and lower memory usage. We&rsquo;ll demonstrate how to recognize and fix crashes in code that depend on internal details, and show you how to keep your code unaffected by changes to the runtime.<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/pedantcoder\/status\/1276581660041986048\">Pierre Habouzit<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/pedantcoder\/status\/1276581660041986048\">\n<p>Also the tagged pointer change allows the piece of assembly I&rsquo;m the most insanely proud of: the tagged pointer decoding is now much faster in msgSend.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/pedantcoder\/status\/1277296159803863040\">Pierre Habouzit<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/pedantcoder\/status\/1277296159803863040\">\n<p>This structure holds Writeable runtime metadata for the classes to work at runtime. But only half of that 8-word structure was used commonly.<\/p>\n<p>So we split it, and only allocate the extended part when needed (which is rare) and as Ben mentions, we saved dozens of MBs (given that we save 32B a-piece, yes it means there are several hundreds of thousands of classes initialized system wide)<\/p>\n<p>[&#8230;]<\/p>\n<p>We found that it&rsquo;s quite common in certain UI code (but not only) to repeatedly autorelease the same object over and over again. We have implemented a small LRU that is consulted each time an object is autoreleased.<\/p>\n<p>[&#8230;]<\/p>\n<p>Also, because the runtime caches negative [IMP cache] entries, the speed of a lookup miss is not very relevant, so we can tolerate denser tables.<\/p>\n<p>We added 2-entries hashes. tables up to 8 entries are filled up to 100% and and others up to ~90% (7\/8th).<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/pedantcoder\/status\/1276933610025545733\">Pierre Habouzit<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/pedantcoder\/status\/1276933610025545733\">\n<p>[The] motivation for us is that a single method made direct saves you typically 30bytes (that&rsquo;s what the average cost of an IMP entry used to be).<\/p>\n<p>A monomorphic IMP cached in 100 processes gives you 3k, save 1000 such IMPs you save 3M system wide.<\/p>\n<p>It also saves a lot of binary size.<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/Catfish_Man\/status\/1277102609833197568\">David Smith<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/Catfish_Man\/status\/1277102609833197568\">\n<p>The idea that saving 30 bytes per process per method is worth doing significant work is not intuitive until you internalize just how many processes are on a typical iOS device and how valuable memory freed up for the frontmost app is[&#8230;]<\/p>\n<\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/pedantcoder\/status\/1277702402490040320\">Pierre Habouzit<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/pedantcoder\/status\/1277702402490040320\"><p>[We] have pre-optimized some IMP Caches at build time. How do you think we did that...<\/p><\/blockquote>\n\n<p><a href=\"https:\/\/twitter.com\/pedantcoder\/status\/1277795696855277568\">Pierre Habouzit<\/a>:<\/p>\n<blockquote cite=\"https:\/\/twitter.com\/pedantcoder\/status\/1277795696855277568\"><p>To beat a hash-table with linear probing, there&rsquo;s only one thing you can do: a perfect hash table. The problem is, perfect hash tables that exist today in the literature are large, use complex hash functions (the one Obj-C uses is just a mask).<\/p><p>So that was quite the conundrum.<\/p><p>Now there are two ways to get a perfect hash table: either you have a perfect hash function&#8230;. or you cheat and make sure that all your keys hash perfectly. Keys for us, are selectors. They live in the shared cache.<\/p><p>Do you see it coming?<\/p><p>[&#8230;]<\/p><p>Memory savings are &#8230; substantial. There&rsquo;s also a huge speed win during startup because&#8230; you don&rsquo;t have to build those caches anymore and the contention on the runtime locks is reduced.<\/p><\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2020\/06\/26\/reverse-engineering-macos-11-0\/\">Reverse Engineering macOS 11.0<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2019\/11\/19\/direct-objective-c-properties\/\">Direct Objective-C Properties<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2017\/07\/25\/dissecting-objc_msgsend-on-arm64\/\">Dissecting objc_msgSend on ARM64<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2014\/09\/24\/objective-c-drops-vtable-optimization\/\">Objective-C Drops vtable Optimization<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2014\/02\/12\/an-illustrated-history-of-objc_msgsend\/\">An Illustrated History of objc_msgSend<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2011\/07\/21\/objective-c-tagged-pointers-in-lion\/\">Objective-C Tagged Pointers in Lion<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2009\/12\/18\/objc_msgsend-tour\/\">objc_msgSend() Tour<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>WWDC 2020: Dive into the microscopic world of low-level bits and bytes that underlie every Objective-C and Swift class. Find out how recent changes to internal data structures, method lists, and tagged pointers provide better performance and lower memory usage. We&rsquo;ll demonstrate how to recognize and fix crashes in code that depend on internal details, [&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":"2020-07-06T20:38:09Z","apple_news_api_id":"3f5803f6-b0f4-4900-a65c-590f0b6e5e0a","apple_news_api_modified_at":"2020-07-07T11:49:48Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/AP1gD9rD0SQCmXFkPC25eCg","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":[770,205,31,1837,30,1666,571,966,54,760,138,71],"class_list":["post-29432","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-assembly-language","tag-dyld","tag-ios","tag-ios-14","tag-mac","tag-macos-10-15","tag-memory-management","tag-message-passing","tag-objective-c","tag-objective-c-runtime","tag-optimization","tag-programming"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/29432","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=29432"}],"version-history":[{"count":2,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/29432\/revisions"}],"predecessor-version":[{"id":29449,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/29432\/revisions\/29449"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=29432"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=29432"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=29432"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}