- 8.2.1 (8C1002)
- Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1)
- Code base: Swift 3.0
- El Capitan 10.11.6
- Sierra 10.12.3
- MacBook Pro (Retina, 15-inch, Late 2013)
4-cores 2.6 GHz Intel Core i7
16 GB 1600 MHz DDR3
- Mac Pro (Late 2013)
2.7 GHz 12-Core Intel Xeon E5
32 GB 1866 MHz DDR3 ECC
- Mac mini (Late 2014)
2-cores 3 GHz Intel Core i7
16 GB 1600 MHz DDR3
Xcode/Swift versions 8.2.1 (8C1002) Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1) Code base: Swift 3.0 OS versions tried: El Capitan 10.11.6 Sierra 10.12.3 Hardware used: MacBook Pro (Retina, 15-inch, Late 2013) 4-cores 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 Mac Pro (Late 2013) 2.7 GHz 12-Core Intel Xeon E5 32 GB 1866 MHz DDR3 ECC Mac mini (Late 2014) 2-cores 3 GHz Intel Core i7 16 GB 1600 MHz DDR3 iMac (Retina 5K, 27-inch, late 2015) 4-cores 4 GHz Intel Core i7 32 GB 1867 MHz DDR3
- iMac (Retina 5K, 27-inch, late 2015)
4-cores 4 GHz Intel Core i7
32 GB 1867 MHz DDR3
We noticed a concerning issue where building our primarily Swift projects would take significantly longer on hardware with more cores available. Building a project on a 4-core MacBookPro would be about 33% faster than building the same project on a powerful 12-core MacPro tower.
Further investigation showed that, counterintuitively, reducing the number of concurrent jobs for xcodebuild on the MacPro machine improved the build times. For the 12-core machine, going from the default 24 jobs setting down to only 5 threads would improve compilation time by 23%.
For most of the hardware we tested with, 4-6 seems to be the optimal number of concurrent threads for the build speed, and allowing more to be used actually hurts the build times. The number of jobs was controlled using both IDEBuildOperationMaxNumberOfConcurrentCompileTasks setting, and -jobs command line option for xcodebuild.
We've observed this pattern both for a sizable project (5K Swift files, containing 540K lines of code plus 2.5K ObjC files, containing 320K lines of code), as well on a smaller one (500 Swift files, containing 36K lines of code and 1,5K ObjC files, containing 150K lines of code).
Initially we thought this could be related to some SSD/filesystem inefficiencies, so we tried building with a ram disk (no difference), and also tried APFS on macSierra with the same results. The issue has been observed both with Whole Module Optimization on and off (for both debug and release builds).
See the chart attached which compares build times for 4 different types of hardware used vs. number of concurrent jobs requested. Notice how for all machines the fastest builds are around 4-6 jobs, after which point build times regress - most prominently for the MacPro. Even a two-core MacMini builds faster than the 12-core MacPro!
Another thing we've observed was that during the builds, the CPUs were heavily under-utilized. On average we've seen the cores being roughly 40% idle, even when building the 0.5M lines of code project.
See the text file attached with build times elapsed showing time consumed by system vs. user for MacPro. Numbers going from 3 to 24 indicate the number of concurrent jobs requested. Notice the times going from 32 minutes (for 3 jobs) up to almost an hour (24 jobs).
Please advise as to why we're seeing such a concerning behavior. Last year (around Swift 1.2-2.0) we performed a similar hardware analysis, and at that time for our projects 12-core MacPro machines would build 2-3x times faster than MacBookPros, and 4-5x faster than the MacMinis - which was expected based on the number of cores used for the builds.