Possibility of reducing class metadata?

There are 3 levels of metadata overhead:

  • no objc-interop (roughly 156 bytes, 16 bytes per method)
  • with objc-interop, but the classes are not marked @objc implicitly or explicitly (extra of 192 bytes plus 32 bytes per ivar, plus cstring for ivar names)
  • with objc-interop, and classes are marked @objc implicitly or explicitly (overhead for methods/properties with @objc, 24 bytes per method with @objc)

Note that we are paying metadata overhead for each ivar of the Swift class as long as we enable objc-interop. For method and properties, there is overhead when the method or property is marked with @objc.

Can we reduce the metadata overhead for case 2 so it is closer to case 1? My guess is that the extra metadata in case 2 is for construction/destruction of objects.

CC @Mike_Ash @Joe_Groff

4 Likes

The ivar metadata serves two purposes:

  1. Reflection using key/value coding or ObjC runtime calls.
  2. Allows for recomputing ivar offsets and the class instance size when the superclass turns out to be bigger at runtime than it was at build time. (Solving the fragile base class problem.)

#1 is not useful for stored properties of types that can't be represented in ObjC (and nobody knows about bridging here, so even e.g. String wouldn't work).

#1 is potentially useful for non-@objc properties that happen to store ObjC-compatible types, but there's a pretty obvious argument that we have no obligation to make this work, and it's probably better to forbid it. You can mark your properties as @objc if you need ObjC reflection.

#2 is only necessary when the size of the class hierarchy isn't fixed at build time. Basically, classes that subclass classes from the frameworks. If your class hierarchy is all internal, then the size is known at build time and this is already fixed. In fact, we already take advantage of this by emitting the ivar offsets into read-only memory in this case. We can also extend this slightly to say that the superclass size is fixed when inheriting from NSObject, since that's always a single word. I'm not sure if we already emit read-only ivar offsets in that case or not.

For classes that do have a superclass of unknown size, this could still be improved upon by emitting a single "ivar" record that encompasses all stored properties of the class. For example:

class MyView: UIView {
  var x: Int
  var y: String
  var z: UIButton
}

Instead of emitting separate ivar records for x, y, and z, we could emit a single "ivar" of size 32, and emit code to access y and z at the appropriate offset to that "ivar". This would be equivalent to emitting the stored properties as an anonymous struct

As far as methods go, it's a requirement to emit a record for any method that's callable with objc_msgSend, since it's an inherent part of how the message send machinery works. For what it's worth, that 24 bytes will go down to 12 bytes when you target macOS 11/iOS 14, as there's a new format available there that uses 4-byte relative pointers.

5 Likes

Thanks for all the details! I will take a look and try to come up with some experimental patches.

The other extra metadata between case 2 and case 1 is _DATA and METACLASS_DATA, which are similar to class_ro_t, also metaclass with suffix of "Mm". I assume we can't reduce these since they are the basic data structure for an ObjC class?

Manman

1 Like

Right, those are all obligatory.

2 Likes

For classes that aren’t @objc and so don’t need to be linkable by ObjC code, then if we were willing to save some code size by trading more initialization time and runtime memory allocation, we could also defer allocating some bits of ObjC-specific metadata by generating them at class instantiation time. Things like the metaclass and ivar offsets seem like they could be derived straightforwardly from existing Swift metadata. With a more cooperative ObjC runtime, it’d be nice if allocating the ObjC paraphernalia could be put off until a class actually needed to be realized as an ObjC class, so that pure Swift uses of the class could avoid the cost altogether.

5 Likes