# Generics, Swift Local Packages & Whole Module Optimization

**URL:** <https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687>\
**Category:** Using Swift\
**Tags:** packagemanager\
**Created:** [April 11, 2022, 4:01pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687 "2022-04-11T16:01:56Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![michaell](https://avatars.discourse-cdn.com/v4/letter/m/8e8cbc/32.png) [@michaell](https://forums.swift.org/u/michaell)\
**Post date:** [April 11, 2022, 4:01pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/1 "2022-04-11T16:01:56Z")

</div>

I'm a heavy user of both Generics and Swift Local Packages.

Here's my question:

- How can I be sure that my app is using specialized versions of the generic functions that are defined in different modules — i.e., different local packages?

I compile my Release builds with whole module optimization. But my current understanding is that whole module optimization is compile-time-only and module-only. That makes me think that when I compile and link my overall app I may not be getting cross-module specialization of my generic funcs.

If that's correct, should I be making generous use of `@inlinable` to ensure cross-module optimization?

(I've tried finding a reliable answer to this, but so far no luck. I know that the SPM [Usage](https://github.com/apple/swift-package-manager/blob/main/Documentation/Usage.md#release) page talks about whole module optimization, but only at the module level.)

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [April 11, 2022, 4:18pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/2 "2022-04-11T16:18:40Z")

</div>

> [@michaell](#):
>
> If that's correct, should I be making generous use of `@inlinable` to ensure cross-module optimization?

_you_ as a library consumer won’t get any benefit from using `@inlinable` in your own module. but the library author ought to be using `@inlinable` on his/her end to enable cross-module inlining.

if you have multiple modules in your project, `@inlinable` _might_ help. but keep in mind that it’s not necessarily a win if all it does is push generic abstraction "down a level" in the call stack, since the `@usableFromInline` calls aren’t inlined.

---

<div class="post-metadata">

**Author:** ![michaell](https://avatars.discourse-cdn.com/v4/letter/m/8e8cbc/32.png) [@michaell](https://forums.swift.org/u/michaell)\
**Post date:** [April 11, 2022, 4:57pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/3 "2022-04-11T16:57:31Z")

</div>

Sorry, I wasn’t as clear as I could have been: I use Swift _local_ packages. In other words, I’ve used SPM to modularize my app’s own code.

So what I meant was: should I liberally add @inlinable declarations to the generic funcs inside those packages so that call sites in the app and other modules are potentially specialized?

---

<div class="post-metadata">

**Author:** ![michaell](https://avatars.discourse-cdn.com/v4/letter/m/8e8cbc/32.png) [@michaell](https://forums.swift.org/u/michaell)\
**Post date:** [April 11, 2022, 5:02pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/4 "2022-04-11T17:02:49Z")

</div>

Sorry; I read your reply in email, and for whatever reason it didn’t include your second paragraph. I think I understand the caveat.

---

<div class="post-metadata">

**Author:** ![lukasa](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lukasa/32/1232_2.png) [@lukasa](https://forums.swift.org/u/lukasa)\
**Post date:** [April 11, 2022, 6:21pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/5 "2022-04-11T18:21:33Z")

</div>

In your instance you may also want to add `-cross-module-optimization` to your Swift flags in Xcode, which should help matters.

---

<div class="post-metadata">

**Author:** ![michaell](https://avatars.discourse-cdn.com/v4/letter/m/8e8cbc/32.png) [@michaell](https://forums.swift.org/u/michaell)\
**Post date:** [April 11, 2022, 7:42pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/6 "2022-04-11T19:42:09Z")

</div>

Thank you, I didn't know about that.

For those interested in learning more, the pull request for the (still experimental?) setting is [here](https://github.com/apple/swift/pull/28407):

> A new SIL module pass "annotates" functions and types with @inlinable and @usableFromInline.

A comment [here](https://forums.swift.org/t/what-are-the-tradeoffs-of-cross-module-optimization/45585/8) (by the author of the pull request) says that it can also be enabled using the undocumented Xcode build setting `SWIFT_CROSS_MODULE_OPTIMIZATION`.

I'm not sure about the impact on runtime performance yet, but it definitely has a big impact on buildtime performance. :-)

---

<div class="post-metadata">

**Author:** ![SDGGiesbrecht](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/sdggiesbrecht/32/4768_2.png) [@SDGGiesbrecht](https://forums.swift.org/u/SDGGiesbrecht)\
**Post date:** [April 11, 2022, 10:27pm UTC](https://forums.swift.org/t/generics-swift-local-packages-whole-module-optimization/56687/7 "2022-04-11T22:27:08Z")

</div>

The answer is that `@inlinable` is about defining ABI boundaries on libraries vended as binaries. You are not supposed to need the attribute at all for libraries vended as source, because in that context it is a meaningless distinction. However, as long as `-cross-module-optimization` remains experimental and not the default, `@inlinable`’s _side effect_ of punching a hole in the module boundary can help let the optimizer through too (which is help the optimizer ought not to need).

What I consider best practice at the moment for a source‐vended library is to ignore it until you end up with a piece of code that demonstrably is not fast enough. At that point, try `-cross-module-optimization` to see if the module barrier is part of the problem. If it turns out to be the case, then you will be able to get the same improvement by applying `@inlinable` to the related code, and doing so will be an infinitely better experience for your clients than telling them to use an experimental flag. You can remove them again at a future date when the standard optimizer has been improved.

For most code, applying `@inlinable` everywhere manually\* is too much effort for the little benefit it brings. But the only real danger from doing so is that if you ever decide to vend your library as a binary (or someone forks your work to do so), there will be a lot of cleaning up to do.

\*It just occurred to me that it might now be feasible to automate with a plug‐in. If anyone else tries this before I get around to it, let me know how if works out.
