Impact of Type Inference on Swift Compile Time: A Comparative Story
In this story, I want to talk about the Swift compiler and find out if it affects compile time to use Type Inferred and Type Inference.
Recently, it has become popular to use .init
instead of specifying an object name for initializing an object. I was thinking about does it has any impact on compile time or not. I did a couple of tests and I want to explain them here and in the end, we will find the answer.
First of all, I used hyperfine
to measure the compile time. you can check the repo and install it on your machine. Hyperfine
Type Inference in Swift:
Type inference, a cornerstone of Swift, allows the compiler to deduce types based on the context of the code. When a type is inferred, Swift intelligently determines the appropriate type for a variable or expression without explicit type annotations. This enhances code readability and reduces verbosity.
Inferred Types:
When the type is inferred, Swift automatically deduces the type of a variable or constant from its assigned value. For instance:
let number = 42 // Type of 'number' is inferred as 'Int'
Here, Swift infers that number
is of type Int
because its value is an integer literal.
Type Inference Not Required:
In contrast, sometimes explicit type annotations are necessary, especially when Swift cannot deduce the type from the context. In situations where type inference is not required, developers explicitly specify the type of a variable or expression using type annotations.
let pi: Double = 3.14159 // Explicitly specifying 'pi' as 'Double'
Here, the type annotation : Double
is provided to clarify that pi
is of type Double
, even though it's initialized with a floating-point literal.
A more realistic example:
Imagine there is a User
object, and you aim to create a new object of type User
.
struct User {
let name: String
}
let user = User(name: "Saeid")
Let’s measure compile time for this scenario; I’ve multiplied it by 1000 to obtain a more discernible signal and also tested different kinds of type checks.
#!/usr/bin/env python3
filenames = ["userA", "userB", "userC"]
code = [
'let a{} = User(name: "Saeid")',
'let b{}: User = .init(name: "Saeid")',
'let c{}: User = User(name: "Saeid")'
]
for (i, filename) in enumerate(filenames):
with open(filename + ".swift", "w") as f:
s = ""
for j in range(1000):
s += (code[i] + '\n').format(j)
f.write(s)
Now, I have three files with three distinct scenarios. Let’s use the swiftc
command to check the compile time for each of them.
userA
, the type is Inferred.
hyperfine 'swiftc -typecheck userA.swift'
Here is the response
userB
, type inference is not required.
userC
, type inference is not required.
As seen, let b: User = .init(name: "Saeid")
is significantly faster than the other options. I also tried to change the User to a class, but the result was the same.
However, for the String
type, type inference is notably faster than the alternatives.
But currently, hardly anyone uses .init
for the String
type, so it should not pose an issue here.
Conclusion
In exploring the impact of type inference on Swift compile time, the experiment demonstrated intriguing differences in compilation speeds between inferred types and explicitly annotated types. Utilizing .init
for object initialization notably showcased faster compile times compared to inferred types or explicit object creation.
The findings highlighted that leveraging .init
for object initialization in Swift, especially for structured types, notably improved compilation efficiency. While this difference was evident for structured types, the performance variance was negligible or even reversed for simpler types like String
.
In essence, the choice between inferred types and explicit type annotations could influence compile time, especially for structured types. For developers aiming to optimize compilation speed, utilizing .init
could be a beneficial approach, particularly in scenarios with complex or structured objects. However, for simpler types, the difference may not be substantial enough to drive a significant change in coding practices.
Understanding these nuances can aid in making informed decisions when balancing between code readability and compile-time efficiency in Swift development.