The Swift runtime executes a protocol conformance check when you cast a type to a protocol, such as with as? or as!. This operation is surprisingly slow, as detailed in my previous post. In this article we’ll look at an easy way to speed this up by ~20%, without making any changes to your source code. First, a brief review of protocol conformance checks.
Records of every conformance you write in source code get stored in the TEXT/const section of the binary in a form similar to this:
A typical app can have tens of thousands of these. Many are conformances to common protocols such as Equatable, Hashable, Decodable or Encodable. When the Swift runtime encounters something like myVar as? MyProtocol (may not be directly in your code, common functions like String(describing:) internally do an as?) it loops over every ProtocolConformanceDescriptor in the binary plus any dynamically linked binaries. This operation is O(n). In the worst case if you need to lookup a protocol conformance record for every type that would be O(n^2).
iOS 16 greatly improves on this. As I explained in a previous post, iOS 16 precomputes protocol conformances in the dyld closure, and the Swift runtime consults dyld before running the O(n) lookup. At the time of the previous blog post Apple had not released the iOS 16 dyld source code, but now that they have, we can see the actual implementation in the function _dyld_find_protocol_conformance_on_disk. This function is conceptually the same as the zconform library which speeds up these checks using a hash table that maps types to a list of protocols that they conform to.