Compile times for large mixed codebase

We have a large codebase that is about 60/40 split with 60% Swift and 40% Objective-C. Surprisingly the compilation time for ObjectiveC code as observed under the CompileC step is about 760 seconds while Swift was only 128 seconds. We used the time trace feature in Clang (SN Systems - Clang Time Trace Feature) to see where most time was being spent and it was the swift autogenerated header file that was
~14,000 lines. Yes, we are exporting a lot of code back to ObjectiveC unfortunately.

To test this hypotheses we wrote a script that concatenated all ObjC .m files that were importing the Swift autogenerated header file (Target-Swift.h) and noticed the build step for CompileC dropped down to 168 seconds from 760 seconds before.

Given the massive performance improvements we have seen, I am wondering if there is any existing support in Xcode for precompiling the Swift autogenerated header, or are there other solutions out there that we can try short of moving more code to Swift, or cutting down the size of the autogenerated header.

2 Likes

You're right that we precompile the Objective-C bridging header that is imported into Swift, but as far as I know, not the other direction -- the Swift generated header that swiftc emits for exporting Swift declarations back into Objective-C. Maybe with some module.map magic it can be made to work though. Perhaps @beccadax or @Xi_Ge have some insights.

1 Like

Thank you! The module map worked great. We imported the autogenerated header in an empty .h file because the module map wasn’t able to directly reference the autogenerated file. Now all ObjC code imports the module and things are fast!

I want to note that this is not necessarily a "correct" transformation in that it'll indirectly put every header in your bridging header into the module you've defined. (Objective-C #import is transitive, and headers that are not claimed by other modules are assumed to be part of the module that did the import.) I can't think of something inherently "wrong" with that, but if you start seeing weird behavior with your app's headers, that's why: they're being held to a higher standard now.

Thanks for the heads up. When you say it is indirectly imported, is it referring to the fact that the autogenerated header imports the bridging header?

Yep, since the Swift classes might reference something from the bridging header. (It’s a bit hard for the compiler to figure out whether it’s actually needed, but that’s okay—it makes the behavior more consistent.)