Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-14759] class_getImageName is much slower for Swift classes than Obj-C classes #57109

Closed
allevato opened this issue Jun 11, 2021 · 4 comments
Closed
Assignees

Comments

@allevato
Copy link
Collaborator

@allevato allevato commented Jun 11, 2021

Previous ID SR-14759
Radar rdar://problem/79198156
Original Reporter @allevato
Type Bug
Status Resolved
Resolution Done
Environment

Swift 5.4

Observed on iOS 14.x, macOS 11.x

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug, Performance
Assignee @tbkka
Priority Medium

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 calls dyld_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 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.) <

Dl_info imageInfo = {};
if (!dladdr(descriptor, &imageInfo))
return NO;
*outImageName = imageInfo.dli_fname;
return imageInfo.dli_fname != nullptr;
>

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:

  1. Declare dyld_image_path_containing_address and call it instead of dladdr.

  2. 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?

@weissi
Copy link
Member

@weissi weissi commented Jun 11, 2021

@swift-ci create

@mikeash
Copy link
Contributor

@mikeash mikeash commented Jun 14, 2021

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.

@tbkka
Copy link
Contributor

@tbkka tbkka commented Jun 15, 2021

@tbkka
Copy link
Contributor

@tbkka tbkka commented Jun 16, 2021

PR merged

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants