I'm new to Swift and wanted to start off by implementing Advent of Code 2024 Day 1. I was a little bit concerned by the resulting runtime performance of my Swift implementation. As a reference point, I also implemented the first day in Rust.
My Swift implementation can be found here: swifty_advent_2024
Running the Rust implementation on my laptop 1,000 times takes on average 100 µs. Swift on the other hand takes between 5 and 6 ms → more than 50 times slower.
UPDATE:
Thank's to the suggestions from @taylorswift and @bjhomer, the runtime improved from 5 to 1.7 ms on average.
I know Rust is notoriously famous for being "blazingly" fast, but I don't believe Rust is that much faster. Does my Swift implementation contain some severe performance bottlenecks?
I compile the code with:
$ swift build --configuration release -Xswiftc -Ounchecked
OS: Linux Mint 20.3.
The Swift code currently looks like this (just the day1-related code):
import Foundation
// parsing the puzzle with regex was 20 % faster than the first approach of parsing the numbers without regex. However, the variant utilizing "lazy" is now significantly faster than the regex approach.
// This function is not used anymore - this is dead code!
func parseNumbersRegex(_ input: String) -> ([Int],[Int]) {
let xsRegex = try! Regex(#"\d+"#)
var xs: [Int] = []
var ys: [Int] = []
xs.reserveCapacity(1000)
ys.reserveCapacity(1000)
for (i, d) in input.matches(of: xsRegex)
.compactMap({ match in
Int(input[match.range])
}).enumerated() {
if i % 2 == 0 {
xs.append(d)
} else {
ys.append(d)
}
}
return (xs, ys)
}
// parsing the puzzle: after lazy-optimizations now faster than the regex variant
func parseNumbers(_ input: String) -> ([Int],[Int]) {
var xs = [Int]();
var ys = [Int]();
// let lines = input.components(separatedBy: "\n");
// optimized integer parsing, thanks to taylorswift and bjhomer:
let lines = input.lazy.split(separator: "\n")
for line in lines {
// let nums = line.components(separatedBy: .whitespaces).compactMap{ Int($0) }
// optimized integer parsing, thanks to taylorswift and bjhomer:
let nums = line.split(separator: " ").compactMap{ Int($0)}
xs.append(nums[0])
ys.append(nums[1])
}
return (xs, ys)
}
func countOccurences(_ xs: [Int]) -> [Int:Int] {
return xs.reduce(into: [:]) { acc, x in
acc[x, default: 0] += 1
}
}
func day1() -> (Int, Int){
var (xs, ys) = parseNumbersRegex(puzzle_1)
xs.sort()
ys.sort()
let dist0 = zip(xs, ys).reduce(0) { acc, pair in
let (x, y) = pair
return acc + abs(x - y)
}
let yOcc = countOccurences(ys)
let dist1 = xs.reduce(0) {acc, x in
return acc + x * yOcc[x, default: 0]
}
return (dist0, dist1)
}
let puzzle_1 = """
38665 13337
84587 21418
93374 50722
68298 57474
54771 18244
...
"""