Performance issues when using a library vs including in code

I apologize for not having an MWE for this, but I'm hoping that someone can point out the error in my process.

I have code that used to be a standalone application (using SPM) that I've made into a library (GitHub - sbromberger/SwiftGraphs: Graphs in Swift). I've created a new application (again, using SPM) that imports the library.

When the application was standalone (GitHub - sbromberger/SwiftGraphs at fea38ecdd3ae623231ec8145e37aeb3ef5eb27db) and built with
swift build -c release -Xswiftc -O -Xswiftc -target -Xswiftc x86_64-apple-macosx10.13

I got the following result:

graph load took 352.701609 ms

When I used the library as an import in another application, I got this:

graph load took 10070.36392 ms

It's almost as if the library is not being built with optimization, as these times are similar to the ones I got when I built the standalone application without optimization.

Can someone please tell me what I'm doing wrong, or where I should check for possible problems?

Could this be a consequence of the cross-module optimizations issues that I've been hearing about? I've heard that there are issues where the compiler doesn't do a great job (or at all?) of doing optimizations that cross the module boundary. I believe that is the whole point of the inlinable and useableFromInline attributes. They basically let the compiler emit SIL within the module so that users of that module can do more aggressive optimizations.


This is exactly it. Moving to 4.2 and judicious use of @inlinable and @usableFromInline annotations on the struct fixed the problem.

Thanks for your help. It's disappointing that good performance from libraries can only be achieved by obscure annotations right now, though. I really hope this gets fixed soon.

Edited to add: without the annotations, there is a huge performance regression in 4.2 - from 10s to 20s.

Cross-reference: [SR-8159] Performance regression from 4.1.2 to 4.2 · Issue #50691 · apple/swift · GitHub

Well that's good to hear. I thought that was how @inlinable and @usableFromInline worked, but then I was told that it didn't work that way.