Calling class_getImageName on a Swift class exposed to Objective-C is significantly slower than calling it on an Objective-C-only class, by orders of magnitude. Empirically, we've seen a difference as large as 100x—on a particular device, class_getImageName on a Swift class takes ~2ms whereas it takes ~20µs on an Obj-C-only class.
When the Swift runtime hooks class_getImageName with its own version, it calls dladdr, which takes longer because it populates the Dl_info struct with other information that gets thrown away. (The other class metadata accessor calls are fast, as they merely return pointers or do pointer tests.) <
By my own rough benchmarking, the CoreFoundation approach is still slower than calling dyld_image_path_containing_address directly, but only by a factor of about 4x in an optimized build, so it would still be a large improvement, and perhaps there are other ways to improve it further.
Are there drawbacks to doing either of these replacements, or anything I haven't considered?
The text was updated successfully, but these errors were encountered:
Thanks for the report! I suspect that we're using dladdr just because it's been that way, and this hasn't been a performance bottleneck. I think we should switch it to dyld_image_path_containing_address. This would be far from the only internal call used by the Swift runtime, and it's safe to do so since Swift is bundled in the OS.
If you feel like making a PR with this fix, I'd be happy to review it and see if we can get it in. Otherwise I'll see if I can find a moment to do it on our end.
Environment
Swift 5.4
Observed on iOS 14.x, macOS 11.x
Additional Detail from JIRA
md5: dfc2da22081fcc746b00aebf8c99e241
Issue Description:
Calling
class_getImageName
on a Swift class exposed to Objective-C is significantly slower than calling it on an Objective-C-only class, by orders of magnitude. Empirically, we've seen a difference as large as 100x—on a particular device,class_getImageName
on a Swift class takes ~2ms whereas it takes ~20µs on an Obj-C-only class.We believe this to be caused by different dyld functions called to find the image name. In the Objective-C runtime,
class_getImageName
callsdyld_image_path_containing_address
, a private dyld function optimized to return only the image name for an address. <https://github.com/apple-opensource/objc4/blob/a367941bce42b1515aeb2cc17020c65e3a57fa20/runtime/objc-runtime.mm#L626>When the Swift runtime hooks
class_getImageName
with its own version, it callsdladdr
, which takes longer because it populates theDl_info
struct with other information that gets thrown away. (The other class metadata accessor calls are fast, as they merely return pointers or do pointer tests.) <swift/stdlib/public/runtime/ObjCRuntimeGetImageNameFromClass.mm
Lines 72 to 76 in ca5e721
I believe the performance for Swift classes could be brought much closer to that of Objective-C classes if the Swift runtime did one of the following:
Declare
dyld_image_path_containing_address
and call it instead ofdladdr
.If relying on private dyld details is a concern, the approach used by
CFBundle_binary.c
to manually iterate over the images in the process and find the one containing the address would still be more performant. <https://github.com/apple/swift-corelibs-foundation/blob/f8dc3985367ffdc7f3aef5b8c431980df136c72b/CoreFoundation/PlugIn.subproj/CFBundle_Binary.c#L166-L192>By my own rough benchmarking, the CoreFoundation approach is still slower than calling
dyld_image_path_containing_address
directly, but only by a factor of about 4x in an optimized build, so it would still be a large improvement, and perhaps there are other ways to improve it further.Are there drawbacks to doing either of these replacements, or anything I haven't considered?
The text was updated successfully, but these errors were encountered: