Difference between WMO and LTO

Hi there,

I was curious about the differences between Swift's Whole Module Optimisation and LLVM Link Time Optimisation.

Am I correct in believing that, from a reductionist viewpoint, that WMO is an inter-module optimisation and LTO is enables optimisations for the entire project cross-modules?

Following on from that; from an app development point of view, does Swift support LLVM LTO for projects when enabled under via Xcode build settings?

Thank you! :slight_smile:

I have started reading up on LTO recently; have you got any lead here?
May be I would post if I can conclude after couple of weeks

I know you’re resurrecting an old thread, but there was a talk at the recent LLVM meeting about this: https://llvm.org/devmtg/2019-10/talk-abstracts.html#tech7

The videos have just started to be uploaded to LLVM‘s YouTube channel. This one isn’t up yet, so look out for it in the coming days.

Thanks @Karl The video is already up :tada:
I did checked out the talk and it has added more details to what LTO means but to answer OP's question, I would collect some more data.

Thanks for the heads up!

@shaun AFAIK LTO works for C family of lang (C, C++, Objc) and WMO is for Swift. CMIW

That characterization is not really fully correct. Yes, LTO was designed originally with the C languages in mind and WMO (in Swift) was designed with Swift in mind.

More broadly, WMO is whole module optimization, intraprocedural optimizations (IPO) that the Swift compiler performs at the SIL level. This allows the Swift compiler to perform Swift specific optimizations across multiple source files within the same module. It is more easily able to do this as the compilation actually occurs across the entire module rather than in the C model with a translation unit (source file) at a time.

LTO or Link Time Optimizations occur, as the name implies, at link time. This is also known as LTCG in Microsoft's parlance (link time code generation), and gcc had a previous approach known WHOPR. Here, the linker will receive not an object file but code which the compiler (or more specifically in our case, the compiler backend - LLVM) can consume. In our case, this code is LLVM bitcode. This enables the linker to call back into compiler to perform additional IPO (although not limited to just IPO).

When you enable WMO, the Swift compiler optimizes a single Swift module as one unit (blog post). This allows for better optimization opportunities across different files in a single module. However, this doesn't affect optimization across different Swift modules, or in cross-language optimization.

LLVM LTO optimizes all the LLVM modules as a single unit. This allows for whole program optimization (potentially crossing language boundaries and Swift module boundaries). For example, there is now support for cross-language LTO between Rust and C/C++, which is being used by Firefox. I don't think the Swift compiler supports LTO right now.

There is a new form of LTO called ThinLTO, which is like (plain) LTO in terms of optimizations but parallelizes much better (at a minute to negligible runtime performance cost). It might make sense to support ThinLTO directly instead of plain LTO, or even enable it by default for WMO builds depending on how it impacts compile times. [Perhaps this is a good opportunity to submit a patch? :smile: ]

1 Like

ThinLTO and LTO are implemented the same way, with ThinLTO using SCCs as a boundary to improve parallelization and reduce memory overheads. This is done entirely in the LLVM side, so this would occur post IRGen in Swift. This already can be done today, maybe not in the most ergonomic manner, but is possible with Swift code, though I don't know what benefits it currently would yield since the pass manager would not necessarily be optimal for Swift.

Wasn’t expecting this thread to kick off post hoc. Thanks for the informative and detailed replies!

Thanks for informative discussion folks. I was exploring LTO for Swift + Objc + C++ code project primarily to reduce binary size. We use Swift static libs but the -ObjC flag ends up adding a load of ObjC symbols. I had tried LTO/ThinLTO settings using Xcode without any gain. I can co-relate to explanation from @typesanitizer and @compnerd why I am not getting the desired gains.