Forked from dabrahams/LazyFilteredCollectionToArrayBenchmark.swift
Last active
March 14, 2017 17:48
Star
You must be signed in to star a gist
Measure the cost of pre-counting lazy filtered collections when converting to Array, WIP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Darwin | |
import CoreFoundation | |
// A variable we can use with exit() to ensure that the optimizer | |
// doesn't remove code we want to time | |
var undead = 0 | |
// A filtered collection that can be more efficiently counted than the stock one. | |
struct LazyFilterBidirectionalCollection2<Base : BidirectionalCollection> : Collection { | |
typealias Impl = LazyFilterBidirectionalCollection<Base> | |
let impl: Impl | |
var startIndex: Impl.Index { return impl.startIndex } | |
var endIndex: Impl.Index { return impl.endIndex } | |
func makeIterator() -> Impl.Iterator { | |
return impl.makeIterator() | |
} | |
subscript(i: Impl.Index) -> Impl.Iterator.Element { | |
return impl[i] | |
} | |
func index(after i: Impl.Index) -> Impl.Index { | |
return impl.index(after: i) | |
} | |
func index(before i: Impl.Index) -> Impl.Index { | |
return impl.index(before: i) | |
} | |
init(_ impl: Impl) { | |
self.impl = impl | |
} | |
typealias IndexDistance = Impl.IndexDistance | |
var count: IndexDistance { | |
var total: IndexDistance = 0 | |
for _ in impl { total += 1 as IndexDistance } | |
return total | |
} | |
} | |
func timeFilteredArrayConversion<C: BidirectionalCollection>( | |
_ c: C, _ label: String, iterations: Int, _ predicate: @escaping (C.Iterator.Element)->Bool | |
) { | |
print("-------\n\(label): iterations: \(iterations)") | |
let sc = c.lazy.filter(predicate) | |
let ss = c.lazy.filter(predicate) as LazyFilterSequence | |
let sc2 = LazyFilterBidirectionalCollection2(sc) | |
let t0 = CFAbsoluteTimeGetCurrent() | |
for _ in 0..<iterations { | |
undead = undead &+ Array(ss).count | |
} | |
let t1 = CFAbsoluteTimeGetCurrent() | |
for _ in 0..<iterations { | |
undead = undead &+ Array(sc2).count | |
} | |
let t2 = CFAbsoluteTimeGetCurrent() | |
for _ in 0..<iterations { | |
undead = undead &+ Array(sc).count | |
} | |
let t3 = CFAbsoluteTimeGetCurrent() | |
print("no preallocation", t1 - t0) | |
print("count by reducing", t2 - t1) | |
print("count by counting", t3 - t2) | |
} | |
func runBenchmark<C: BidirectionalCollection>( | |
_ label: String, | |
smallSize: Int = 10_000_000, | |
_ makeCollection: (CountableRange<Int>) -> C, | |
keepAll: @escaping (C.Iterator.Element) -> Bool, | |
keepHalf: @escaping (C.Iterator.Element) -> Bool, | |
keepNone: @escaping (C.Iterator.Element) -> Bool | |
) { | |
print("=======\n\(label)") | |
let largeSize = smallSize * 10 | |
let small = makeCollection(0..<smallSize) | |
let large = makeCollection(0..<largeSize) | |
timeFilteredArrayConversion(small, "\(smallSize) \(label), keep all", iterations: 10, keepAll) | |
timeFilteredArrayConversion(large, "\(largeSize) \(label), keep all", iterations: 1, keepAll) | |
timeFilteredArrayConversion(small, "\(smallSize) \(label), keep half", iterations: 10, keepHalf) | |
timeFilteredArrayConversion(large, "\(largeSize) \(label), keep half", iterations: 1, keepHalf) | |
timeFilteredArrayConversion(small, "\(smallSize) \(label), keep none", iterations: 10, keepNone) | |
timeFilteredArrayConversion(large, "\(largeSize) \(label), keep none", iterations: 1, keepNone) | |
} | |
runBenchmark("Ints", { $0.map {[$0]} }, | |
keepAll: {$0.first! != -1}, keepHalf: {$0.first! & 1 == 0}, keepNone: {$0.first! == -1}) | |
runBenchmark("[Int]s", { $0.map {[$0]} }, | |
keepAll: {$0.first! != -1}, keepHalf: {$0.first! & 1 == 0}, keepNone: {$0.first! == -1}) | |
runBenchmark("lazy mapped [Int]s", smallSize: 1_000_000, { r -> LazyMapRandomAccessCollection<CountableRange<Int>, [Int]> in r.lazy.map {[$0]} }, | |
keepAll: {$0.first! != -1}, keepHalf: {$0.first! & 1 == 0}, keepNone: {$0.first! == -1}) | |
runBenchmark("[([Int], [Int])]s", { $0.map {([$0], [$0 + 1])} }, | |
keepAll: {$0.0.first! != -1}, keepHalf: {$0.0.first! & 1 == 0}, keepNone: {$0.0.first! == -1}) | |
runBenchmark("lazy mapped [([Int], [Int])]s", smallSize: 1_000_000, { r -> LazyMapRandomAccessCollection<CountableRange<Int>, ([Int], [Int])> in r.lazy.map {([$0], [$0 + 1])} }, | |
keepAll: {$0.0.first! != -1}, keepHalf: {$0.0.first! & 1 == 0}, keepNone: {$0.0.first! == -1}) | |
exit(undead & 1 == 0 ? 0 : 1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Results on my slowpoke computer: