Skip to content

Instantly share code, notes, and snippets.

@rosslebeau
Created October 27, 2016 06:44
Show Gist options
  • Save rosslebeau/510b6392d669bbb565bd28c6c605ef47 to your computer and use it in GitHub Desktop.
Save rosslebeau/510b6392d669bbb565bd28c6c605ef47 to your computer and use it in GitHub Desktop.
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