forked from ra1028/DifferenceKit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BenchmarkTools.swift
132 lines (107 loc) 路 4.12 KB
/
BenchmarkTools.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import DifferenceKit
import Differentiator
import IGListKit
import DeepDiff
extension UUID: Differentiable {}
extension UUID: IdentifiableType {
public var identity: UUID {
return self
}
}
extension UUID: DiffAware {
public var diffId: Int {
return hashValue
}
public static func compareContent(_ a: UUID, _ b: UUID) -> Bool {
return a == b
}
}
extension NSUUID: ListDiffable {
public func diffIdentifier() -> NSObjectProtocol {
return self
}
public func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
guard let other = object as? NSUUID else {
return false
}
return self == other
}
}
struct BenchmarkData {
var source: [UUID]
var target: [UUID]
var deleteRange: CountableRange<Int>
var insertRange: CountableRange<Int>
var shuffleRange: CountableRange<Int>
init(count: Int, deleteRange: CountableRange<Int>, insertRange: CountableRange<Int>, shuffleRange: CountableRange<Int>) {
source = (0..<count).map { _ in UUID() }
target = source
self.deleteRange = deleteRange
self.insertRange = insertRange
self.shuffleRange = shuffleRange
target.removeSubrange(deleteRange)
target.insert(contentsOf: insertRange.map { _ in UUID() }, at: insertRange.lowerBound)
target[shuffleRange].shuffle()
}
}
struct Benchmark {
var name: String
var prepare: (BenchmarkData) -> () -> Void
func measure(with data: BenchmarkData) -> CFAbsoluteTime {
let action = prepare(data)
let start = CFAbsoluteTimeGetCurrent()
action()
let end = CFAbsoluteTimeGetCurrent()
return end - start
}
}
struct BenchmarkRunner {
var benchmarks: [Benchmark]
init(_ benchmarks: Benchmark...) {
self.benchmarks = benchmarks
}
func run(with data: BenchmarkData) {
let benchmarks = self.benchmarks
let sourceCount = String.localizedStringWithFormat("%d", data.source.count)
let deleteCount = String.localizedStringWithFormat("%d", data.deleteRange.count)
let insertCount = String.localizedStringWithFormat("%d", data.insertRange.count)
let shuffleCount = String.localizedStringWithFormat("%d", data.shuffleRange.count)
let maxLength = benchmarks.lazy
.map { $0.name.count }
.max() ?? 0
let empty = String(repeating: " ", count: maxLength)
let timeTitle = "Time(sec)".padding(toLength: maxLength, withPad: " ", startingAt: 0)
let leftAlignSpacer = ":" + String(repeating: "-", count: maxLength - 1)
let rightAlignSpacer = String(repeating: "-", count: maxLength - 1) + ":"
print("#### - From \(sourceCount) elements to \(deleteCount) deleted, \(insertCount) inserted and \(shuffleCount) shuffled")
print()
print("""
|\(empty)|\(timeTitle)|
|\(leftAlignSpacer)|\(rightAlignSpacer)|
""")
var results = ContiguousArray<CFAbsoluteTime?>(repeating: nil, count: benchmarks.count)
let group = DispatchGroup()
let queue = DispatchQueue(label: "Measure benchmark queue", attributes: .concurrent)
for (offset, benchmark) in benchmarks.enumerated() {
group.enter()
queue.async(group: group) {
let first = benchmark.measure(with: data)
let second = benchmark.measure(with: data)
let third = benchmark.measure(with: data)
results[offset] = min(first, second, third)
group.leave()
}
}
group.wait()
for (offset, benchmark) in benchmarks.enumerated() {
guard let result = results[offset] else {
fatalError("Measuring was not works correctly.")
}
let paddingName = benchmark.name.padding(toLength: maxLength, withPad: " ", startingAt: 0)
let paddingTime = String(format: "`%.4f`", result).padding(toLength: maxLength, withPad: " ", startingAt: 0)
print("|\(paddingName)|", terminator: "")
print("\(paddingTime)|")
}
print()
}
}