Affect of @inline(__always) and @inline(never) on App size reduction and performance
ChatGPT helped me write this blog
Get Bogamythrai’s stories in your inbox
Join Medium for free to get updates from this writer.
Recently, I was working on reducing the iOS app binary size, which was a key priority of our team. While optimizing, I noticed that certain function calls affected performance and code size. This led me to explore Swift’s function inlining attributes: @inline(__always) and @inline(never). By strategically using these, I was able to strike the right balance between speed and efficiency.
The Challenge: Balancing Performance and App Size
Our app had grown significantly, and we were hitting size limitations that affected both download speed and startup performance. One of the culprits was the excessive inlining of functions, which led to code bloat. At the same time, certain performance-critical functions were not being inlined when they should have been, adding unnecessary function call overhead.
What is Function Inlining?
Function inlining is an optimization technique where the compiler replaces a function call with its actual body. While this eliminates function call overhead, excessive inlining can lead to a larger binary. Swift’s compiler makes these decisions automatically, but using @inline(__always) and @inline(never), I could fine-tune this behavior.
How I Used @inline(__always)
The @inline(__always) attribute forces the compiler to inline a function’s body at every call site. I used this for small, frequently called utility functions that were adding unnecessary function call overhead.
Example in My Optimization
@inline(__always)
func fastMultiply(_ a: Int, _ b: Int) -> Int {
return a * b
}
func test() {
let result = fastMultiply(4, 5) // This will be replaced by `let result = 4 * 5`
}In our performance tests, using @inline(__always) for simple mathematical operations inside tight loops reduced execution time without significantly increasing binary size.
Lessons Learned
- Best for small functions — Works well when the function is simple and frequently used.
- Avoid overuse — Excessive inlining led to binary growth, which negated the size savings.
How I Used @inline(never)
On the other hand, the @inline(never) attribute prevents the compiler from inlining a function, ensuring that every call remains a distinct function call. I applied this to large functions that were needlessly getting inlined, bloating the binary.
Example in My Optimization
@inline(never)
func complexComputation(_ x: Int) -> Int {
return (x * x) + (x / 2) - 3
}
func test() {
let result = complexComputation(10) // This function call will NOT be inlined.
}By marking large functions as @inline(never), I saw a noticeable reduction in app size, as the compiler stopped replicating function bodies across different call sites.
Lessons Learned
- Best for large functions — Avoids unnecessary code duplication.
- Improves debugging — Helps retain clear function call stacks for profiling.
The Result: A Leaner, Faster App
By carefully applying @inline(__always) and @inline(never), I managed to achieve:
- A smaller binary size without sacrificing performance.
- Faster execution times by inlining critical functions.
- Easier debugging and profiling by preventing unnecessary inlining.
Conclusion
When to Use @inline(__always)
- For small, frequently used functions where function call overhead is a concern.
- When optimizing tight loops to reduce execution time.
- When function size is minimal and inlining won’t cause code bloat.
When to Use @inline(never)
- For large functions that should not be duplicated across multiple call sites.
- When reducing binary size is a priority.
- When preserving function call stacks for debugging and profiling.
Final Thoughts
This experience reinforced that optimization isn’t just about making code faster — it’s about making the right trade-offs. Function inlining is a powerful tool, but using it wisely requires a good understanding of your app’s performance characteristics. By profiling and testing different inlining strategies, I was able to make meaningful improvements without unnecessary bloat.
If you’re facing similar challenges with app size and performance, give these attributes a try — but always measure the impact before making changes!









