A test of a grouping function showing how copy-on-write can slow your code down if you aren't vigilant.
import Foundation | |
class SharedArray<T> { | |
var storage: [T] = [] | |
func append(_ value: T) { | |
storage.append(value) | |
} | |
} | |
public extension Sequence { | |
/** | |
Groups the elements in a `Sequence` by the provided `condition` | |
*/ | |
public func grouped<T: Hashable>(by condition: (Iterator.Element) -> T) -> [T: [Iterator.Element]] { | |
var result: [T: [Iterator.Element]] = [:] | |
for item in self { | |
let key = condition(item) | |
var items = result[key] ?? [] | |
items.append(item) | |
result[key] = items | |
} | |
return result | |
} | |
public func groupedNoCopy<T: Hashable>(by condition: (Iterator.Element) -> T) -> [T: [Iterator.Element]] { | |
var result: [T: [Iterator.Element]] = [:] | |
for item in self { | |
let key = condition(item) | |
var items = result.removeValue(forKey: key) ?? [] | |
items.append(item) | |
result[key] = items | |
} | |
return result | |
} | |
public func groupedWithWrapper<T: Hashable>(by condition: (Iterator.Element) -> T) -> [T: [Iterator.Element]] { | |
var groups: [T: SharedArray<Iterator.Element>] = [:] | |
for item in self { | |
let key = condition(item) | |
if let items = groups[key] { | |
items.append(item) | |
} else { | |
let items = SharedArray<Iterator.Element>() | |
items.append(item) | |
groups[key] = items | |
} | |
} | |
var result: [T: [Iterator.Element]] = [:] | |
for group in groups { | |
result[group.key] = group.value.storage | |
} | |
return result | |
} | |
} | |
let manyGroupsTest = {(item: Int) -> Int in | |
return item % 12000 | |
} | |
let fewGroupsTest = {(item: Int) -> Int in | |
return item % 2 | |
} | |
let copyManyGroupsStart = Date() | |
let copyManyGroups = (0 ..< 100_000).grouped(by: manyGroupsTest) | |
let copyManyGroupsEnd = Date() | |
print("Grouping with copy - many groups: \(copyManyGroupsEnd.timeIntervalSince(copyManyGroupsStart)) seconds") | |
let copyFewGroupsStart = Date() | |
let copyFewGroups = (0 ..< 100_000).grouped(by: fewGroupsTest) | |
let copyFewGroupsEnd = Date() | |
print("Grouping with copy - few groups: \(copyFewGroupsEnd.timeIntervalSince(copyFewGroupsStart)) seconds") | |
let noCopyManyGroupsStart = Date() | |
let noCopyManyGroups = (0 ..< 100_000).groupedNoCopy(by: manyGroupsTest) | |
let noCopyManyGroupsEnd = Date() | |
print("Grouping without copy - many groups: \(noCopyManyGroupsEnd.timeIntervalSince(noCopyManyGroupsStart)) seconds") | |
let noCopyFewGroupsStart = Date() | |
let noCopyFewGroups = (0 ..< 100_000).groupedNoCopy(by: fewGroupsTest) | |
let noCopyFewGroupsEnd = Date() | |
print("Grouping without copy - few groups: \(noCopyFewGroupsEnd.timeIntervalSince(noCopyFewGroupsStart)) seconds") | |
let wrappedManyGroupsStart = Date() | |
let wrappedManyGroups = (0 ..< 100_000).groupedWithWrapper(by: manyGroupsTest) | |
let wrappedManyGroupsEnd = Date() | |
print("Grouping with wrapper - many groups: \(wrappedManyGroupsEnd.timeIntervalSince(wrappedManyGroupsStart)) seconds") | |
let wrappedFewGroupsStart = Date() | |
let wrappedFewGroups = (0 ..< 100_000).groupedWithWrapper(by: fewGroupsTest) | |
let wrappedFewGroupsEnd = Date() | |
print("Grouping with wrapper - few groups: \(wrappedFewGroupsEnd.timeIntervalSince(wrappedFewGroupsStart)) seconds") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment