Are function calls removed if their result is unused?

Does the Swift compiler remove entire function calls if the result is unused? That would be an important point to consider when benchmarking code.

Here is a simple example:

import Foundation

// Some large array:
let a = (0..<10_000_000).map { 100.0 * sin(Double($0)) }

do {
    let start = Date()
    _ = a.reduce(0, +)
    let end = Date()
    print(end.timeIntervalSince(start) * 1000, "ms")
}
do {
    let start = Date()
    let sum = a.reduce(0, +)
    let end = Date()
    print(end.timeIntervalSince(start) * 1000, "ms", sum)
}

Without optimization, both blocks take roughly the same time:

$ swiftc main.swift 
$ ./main
2106.6200733184814 ms
2131.512999534607 ms 153.53436153500783

With optimization, the first block seems to take no time at all:

$ swiftc -O main.swift 
$ ./main
0.0 ms
17.17698574066162 ms 153.53436153500783

I also ran both blocks inside another loop to get rid of “warm-up effects,” but the result was the same.

Therefore I am curious: Does the compiler remove entire function calls (in this case: a.reduce(0, +)) if the result is not used in the further computation (and has no side-effects)?

If the optimizer can see into the code you're calling and guarantee there are no effects, it definitely does remove function calls and other code. The benchmarks used for Swift's development use calls to non-inlinable functions in another module to prevent that optimization. See the functions at the end of this file, which gets compiled into a TestUtils module: swift/TestsUtils.swift at 4694310e51330aadf079cf15497c789ce686e1a1 · apple/swift · GitHub

8 Likes

Thank you for the clarification, much appreciated.