Pierre H. 🔥🌸 (@pedantcoder) on X

X (formerly Twitter) ·

4 min read Original article ↗

Post

Post

Pierre H. 🔥🌸 on X: "About objc_direct, a thread. I should have probably anticipated that people would raise eyebrows and spent more time explaining the point in the LLVM commit, so here it is..."

  • user avatar

    About objc_direct, a thread. I should have probably anticipated that people would raise eyebrows and spent more time explaining the point in the LLVM commit, so here it is...

    user avatar

    “The ``objc_direct`` attribute can be used to mark an Objective-C method as being *direct*. A direct method is treated statically like an ordinary method, but dynamically it behaves more like a C function.” Interesting. Seems useful for some hot paths. github.com/llvm/llvm-proj…

  • user avatar

    The Obj-C dynamic dispatch comes with many costs, this is common "knowledge". However the details of it are rarely known. Beside the obvious cost of the h-lookup, it comes with 4 other kinds of costs: - codegen size - optimization barrier - static metadate - runtime metadata

    user avatar

    (1) Codegen size: In addition to `self`, `_cmd` is passed to objc_msgSend to be able to lookup your IMP. A selector is loaded from a GOT-like slot, called a selref, which in arm64 generates assembly akin to: adrp x1, mySelector@PAGE ldr x1, [x1, mySelector@PAGEOFF]

    user avatar

    This is 8 bytes that you pay at every call site. The number of calls to objc_msgSend is large enough that these 8 useless bytes add up. For example, in CloudKit, these 2 instructions represent 10.7% of __text. This is fairly typical of Objective-C heavy code.

    user avatar

    (3) Static metadata An Objective-C method comes with extensive metadata (costs as of today, may change in a future runtime): - a Method that is an entry in your class method list (24 bytes, 3 relocations)

    user avatar

    This is a grand total of typically 150-250 bytes of metadata and 4 relocations to be able to use it. I ignored the cost of the symbol table entry that a pure C call also has. Properties are more costly as they have an entry in the property list, raising the toll even further

    user avatar

    (4) Runtime metadata: To perform a message dispatch, the runtime build IMP caches. This is not a small cost, and adds up quickly. The VAST majority of IMP caches entries aren't called often and are dead weight of dirty memory that is useless to the process.

    user avatar

    So why direct methods/properties? While it has always been possible to use C (and the compiler helps you by giving you visibility into your private ivars if you place the C function within your

    @implementation

    block), it is cumbersome and irregular in syntax.

    user avatar

    It also comes with overhead of having to spell out self-> everywhere to access ivars, and also doesn't respect the create/new/... ARC rules which can lead to surprising performance issues. As such this is rarely used.

    user avatar

    When used on a typical Obj-C framework, it is easy to reduce your binary size by 5% or more. Using LTO will easily make this win even larger. It means in turn that the working-set of the processes is smaller, which gives you more space for things that are actually useful.

    user avatar

    As such, you now star to see that despite the obvious initial reaction which is "holy s**t this is great for hot code", the target audience is even more the long tail of rarely used monomorphic calls that are killing your binary size for very little added value.

    user avatar

    However, not only wasn't it a motivation at all, but on the opposite, it is widely considered as an ABI risk and this risk has been at the center of many discussions. However, in many cases, the performance benefits vastly out-weight the ABI concern.

  • user avatar

    Hey! Great thread! I was interested in expanding this work to leverage either static analysis or user input to (a) automatically "directify" methods without an attribute & (b) "lower" classes if all the methods used on it are direct. Do you think this might be accepted upstream?