-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Description
A functional-style pipeline a la data.lazy.filter { … }.map { … }.reduce(…)
should in principle be equivalent to the imperative phrasing, e.g. for value in data where … { result += foo(value) }
. The compiler already does a pretty miraculous job of removing all the functional boilerplate and ceremony to turn the former style into the latter. However, the performance is not always comparable.
e.g. for this example benchmark the performance is basically identical on x86-64 (on a Xeon W-2150B) but on arm64 (on an M2) the lazy functional style is four times slower. One obvious difference is that the lazy functional style gets compiled down to a moderately-long sequence of conditional instructions, whereas the imperative form uses plain old branching.
Formal write-up of observations and results.
Some discussion in Swift Forums.
Steps to reproduce
git clone https://github.com/wadetregaskis/Swift-Benchmarks.git
cd Swift-Benchmarks
swift package benchmark --target ArrayProcessing
Expected behavior
Roughly equivalent performance for either approach ("Filter, map, and reduce (lazily)" and "for-in loop").
(ideally the eager functional style, without .lazy
involved, would also perform similarly, but that's probably a different optimiser challenge)
Environment
macOS Sonoma (14.1)
Xcode 15.0.1
Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Test hardware:
- iMac Pro (10-cores) w/ 64 GiB RAM
- M2 MacBook Air w/ 24 GiB RAM