For the sake of sharing experience, I recently used @inline(never) with success, in order to prevent release builds from consuming more memory than debug builds.
The problem was that a heavy static global was loaded in memory, in release builds only, even though this global is actually never used at all, defeating the expectation that Swift globals are lazily loaded.
We start from "actual pseudo code":
struct SomeHeavyType {
static var `default`= { ... }()
}
some loop {
use SomeHeavyType.default through some function
}
Then we apply inlining optimization:
some loop {
use SomeHeavyType.default
}
And then (now this is a guess), the optimizer extracts the loop invariant in a temp local before the loop starts:
doubts
I'm not sure that the optimization which pre-loads the global is really the extraction of a loop invariant, because I'm not sure the optimizer is allowed to assume the global is constant during the loop execution, due to its public setter (some other thread may mutate it concurrently, after all). But very likely I just don't understand well the exclusivity laws of Swift.
let local = SomeHeavyType.default
some loop {
use local
}
The last optimization, whatever is really is, has the global loaded even if the loop is empty.
In the end, this creates a measurable difference between the release and debug builds, when two conditions are met:
- The global uses a lot of memory
- The loop is always empty (because the user doesn't use a feature, for example).
The solution I chose was to make sure that no use of this heavy global is never ever inlined, with @inline(never) (Don't load association inflections unless necessary by groue · Pull Request #757 · groue/GRDB.swift · GitHub).