While doing some refactoring and profiling recently, I noticed that a relatively benign function I call in a tight loop was now showing up in an Instruments trace. The function was recently moved from the main application into a Swift Package.
When a function is moved from the main application source base into a Swift Package, what impact does that have on the compiler's ability to optimize calls to that function? I'm a bit confused as the differences between whole-module-optimization and what I see sometimes referred to as cross-module-optimization.
In Swift 5.5, are there things I can do improve the optimizer's outcome? Is this a case where @inlinable
would be appropriate?
The Swift Package is private and only used internally. While this post only shows one example, the package includes numerous extensions with similar helper functions and properties.
Xcode 13 Beta 5, macOS 12 Beta 21A5506j, Release Build profiled in Instruments.
In MyPackage.swift:
extension Collection {
public var hasElements: Bool {
!isEmpty
}
}
In MyApplication.swift:
// Using this implementation, the call to hasElements
// will show up in the Instruments' trace.
func addItem(...) {
if managedItems.hasElements {
}
}
// Using this implementation, no noticeable call is
// showing up in Instruments.
func addItem(...) {
if managedItems.count > 0 {
}
}
The Instruments' Trace:
And yes, I recognize that in this trace we're "only" talking about 92ms of profiled time. I'm not that interested in whether this should be of concern or not, but more interested in the semantics as to why this shows up at all and if it can be avoided...