The final part of the Billion Row Challenge series replaces Swift's Dictionary with a hand-rolled flat hash table using open addressing and linear probing. We profile with Instruments to find that dictionary get/set accounts for over half the runtime, then build a non-copyable struct backed by UnsafeMutablePointer, mask hashes with a power-of-two capacity instead of using modulo, and mutate slots in place to cut the runtime from roughly 5 seconds to about 2.5 seconds.
Links UnsafeMutablePointer UnsafeBufferPointer Dictionary Int.init(truncatingIfNeeded:) precondition(::file:line:) Noncopyable types (The Swift Programming Language) SE-0390: Noncopyable structs and enums Open addressing (Wikipedia) Linear probing (Wikipedia) The One Billion Row Challenge Code The slot and the table. The table is ~Copyable because it owns raw memory and frees it in deinit — an implicit copy would give us a double free. struct Slot { var keyPointer: UnsafePointer<UInt8>? var keyLength: Int var hash: UInt64 var entry: Entry } struct StationTable: ~Copyable { static let capacity = 1 << 14 // 16384, a power of two static let mask = capacity - 1 private(set) var count = 0 private var slots: UnsafeMutablePointer<Slot> init() { slots = UnsafeMutablePointer<Slot>.allocate(capacity: Self.capacity) slots.initialize( repeating: Slot(keyPointer: nil, keyLength: 0, hash: 0, entry: Entry()), count: Self.capacity ) } deinit { slots.deallocate() } } Insertion with linear probing. Masking with capacity - 1 replaces an expensive modulo, and the hash and length checks short-circuit before the memcmp. mutating func add( temperature: Int, cityPointer: UnsafePointer<UInt8>, length: Int, hash: UInt64 ) { var index = Int(truncatingIfNeeded: hash) & Self.mask while true { let slot = slots + index if slot.pointee.keyPointer == nil { slot.pointee.keyPointer = cityPointer slot.pointee.keyLength = length slot.pointee.hash = hash slot.pointee.entry = Entry(first: temperature) count += 1 precondition(count < Self.capacity, "station table is full") return } if slot.pointee.hash == hash, slot.pointee.keyLength == length, memcmp(slot.pointee.keyPointer!, cityPointer, length) == 0 { slot.pointee.entry.update(temperature: temperature) return } index = (index + 1) & Self.mask } } The aggregate entry, mutated in place rather than read-modify-written through a dictionary subscript. struct Entry { var min: Int var max: Int var sum: Int64 var count: Int init(first temperature: Int) { min = temperature max = temperature sum = Int64(temperature) count = 1 } mutating func update(temperature: Int) { min = Swift.min(min, temperature) max = Swift.max(max, temperature) sum += Int64(temperature) count += 1 } }