Skip to content

Commit 1e820dc

Browse files
committed
Add benchmarks based on different remove(where:) implementations
1 parent 2d9e429 commit 1e820dc

File tree

4 files changed

+210
-10
lines changed

4 files changed

+210
-10
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ set(SWIFT_BENCH_MODULES
122122
single-source/RangeReplaceableCollectionPlusDefault
123123
single-source/RecursiveOwnedParameter
124124
single-source/ReduceInto
125+
single-source/RemoveWhere
125126
single-source/ReversedCollections
126127
single-source/RomanNumbers
127128
single-source/SetTests
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
//===--- RemoveWhere.swift ------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import TestsUtils
14+
15+
public let RemoveWhere = [
16+
// tests repeated remove(at:) calls in generic code
17+
BenchmarkInfo(name: "RemoveWhereQuadraticStrings", runFunction: run_RemoveWhereQuadraticStrings, tags: [.validation, .api], setUpFunction: buildWorkload),
18+
BenchmarkInfo(name: "RemoveWhereQuadraticInts", runFunction: run_RemoveWhereQuadraticInts, tags: [.validation, .api], setUpFunction: buildWorkload),
19+
// tests performance of RangeReplaceableCollection.filter
20+
BenchmarkInfo(name: "RemoveWhereFilterStrings", runFunction: run_RemoveWhereFilterStrings, tags: [.validation, .api], setUpFunction: buildWorkload),
21+
BenchmarkInfo(name: "RemoveWhereFilterInts", runFunction: run_RemoveWhereFilterInts, tags: [.validation, .api], setUpFunction: buildWorkload),
22+
// these two variants test the impact of reference counting and
23+
// swapping/moving
24+
BenchmarkInfo(name: "RemoveWhereMoveStrings", runFunction: run_RemoveWhereMoveStrings, tags: [.validation, .api], setUpFunction: buildWorkload),
25+
BenchmarkInfo(name: "RemoveWhereMoveInts", runFunction: run_RemoveWhereMoveInts, tags: [.validation, .api], setUpFunction: buildWorkload),
26+
BenchmarkInfo(name: "RemoveWhereSwapStrings", runFunction: run_RemoveWhereSwapStrings, tags: [.validation, .api], setUpFunction: buildWorkload),
27+
BenchmarkInfo(name: "RemoveWhereSwapInts", runFunction: run_RemoveWhereSwapInts, tags: [.validation, .api], setUpFunction: buildWorkload),
28+
// these test performance of filter, character iteration/comparison
29+
BenchmarkInfo(name: "RemoveWhereFilterString", runFunction: run_RemoveWhereFilterString, tags: [.validation, .api], setUpFunction: buildWorkload),
30+
BenchmarkInfo(name: "RemoveWhereQuadraticString", runFunction: run_RemoveWhereQuadraticString, tags: [.validation, .api], setUpFunction: buildWorkload),
31+
]
32+
33+
extension RangeReplaceableCollection {
34+
mutating func removeWhere_quadratic(where match: (Element) throws -> Bool) rethrows {
35+
for i in indices.reversed() {
36+
if try match(self[i]) {
37+
remove(at: i)
38+
}
39+
}
40+
}
41+
42+
mutating func removeWhere_filter(where match: (Element) throws -> Bool) rethrows {
43+
try self = self.filter { try !match($0) }
44+
}
45+
}
46+
47+
extension RangeReplaceableCollection where Self: MutableCollection {
48+
mutating func removeWhere_move(where match: (Element) throws -> Bool) rethrows {
49+
guard var i = try index(where: match) else { return }
50+
51+
var j = index(after: i)
52+
while j != endIndex {
53+
let element = self[j]
54+
if try !match(element) {
55+
self[i] = element
56+
formIndex(after: &i)
57+
}
58+
formIndex(after: &j)
59+
}
60+
61+
removeSubrange(i...)
62+
}
63+
64+
mutating func removeWhere_swap(where match: (Element) throws -> Bool) rethrows {
65+
guard var i = try index(where: match) else { return }
66+
67+
var j = index(after: i)
68+
while j != endIndex {
69+
if try !match(self[i]) {
70+
self.swapAt(i,j)
71+
formIndex(after: &i)
72+
}
73+
formIndex(after: &j)
74+
}
75+
76+
removeSubrange(i...)
77+
}
78+
}
79+
80+
func testQuadratic<C: RangeReplaceableCollection>(workload: inout C) {
81+
var i = 0
82+
workload.removeWhere_quadratic { _ in
83+
i = i &+ 1
84+
return i%8 == 0
85+
}
86+
}
87+
88+
func testFilter<C: RangeReplaceableCollection>(workload: inout C) {
89+
var i = 0
90+
workload.removeWhere_filter { _ in
91+
i = i &+ 1
92+
return i%8 == 0
93+
}
94+
}
95+
96+
func testMove<C: RangeReplaceableCollection & MutableCollection>(workload: inout C) {
97+
var i = 0
98+
workload.removeWhere_move { _ in
99+
i = i &+ 1
100+
return i%8 == 0
101+
}
102+
}
103+
104+
func testSwap<C: RangeReplaceableCollection & MutableCollection>(workload: inout C) {
105+
var i = 0
106+
workload.removeWhere_swap { _ in
107+
i = i &+ 1
108+
return i%8 == 0
109+
}
110+
}
111+
112+
let n = 10_000
113+
let strings = (0..<n).map({ "\($0): A long enough string to defeat the SSO" })
114+
let ints = Array(0..<n)
115+
let str = String(repeating: "A very long ASCII string.", count: n/50)
116+
117+
func buildWorkload() {
118+
_ = strings.count
119+
_ = ints.count
120+
_ = str.count
121+
}
122+
123+
124+
@inline(never)
125+
func run_RemoveWhereQuadraticStrings(_ scale: Int) {
126+
for _ in 0..<scale {
127+
var workload = strings; workload.swapAt(0,1)
128+
testQuadratic(workload: &workload)
129+
}
130+
}
131+
132+
@inline(never)
133+
func run_RemoveWhereQuadraticInts(_ scale: Int) {
134+
for _ in 0..<scale {
135+
var workload = ints; workload.swapAt(0,1)
136+
testQuadratic(workload: &workload)
137+
}
138+
}
139+
140+
@inline(never)
141+
func run_RemoveWhereFilterStrings(_ scale: Int) {
142+
for _ in 0..<scale {
143+
var workload = strings; workload.swapAt(0,1)
144+
testFilter(workload: &workload)
145+
}
146+
}
147+
148+
@inline(never)
149+
func run_RemoveWhereFilterInts(_ scale: Int) {
150+
for _ in 0..<scale {
151+
var workload = ints; workload.swapAt(0,1)
152+
testFilter(workload: &workload)
153+
}
154+
}
155+
156+
@inline(never)
157+
func run_RemoveWhereMoveStrings(_ scale: Int) {
158+
for _ in 0..<scale {
159+
var workload = strings; workload.swapAt(0,1)
160+
testMove(workload: &workload)
161+
}
162+
}
163+
164+
@inline(never)
165+
func run_RemoveWhereMoveInts(_ scale: Int) {
166+
for _ in 0..<scale {
167+
var workload = ints; workload.swapAt(0,1)
168+
testMove(workload: &workload)
169+
}
170+
}
171+
172+
@inline(never)
173+
func run_RemoveWhereSwapStrings(_ scale: Int) {
174+
for _ in 0..<scale {
175+
var workload = strings; workload.swapAt(0,1)
176+
testSwap(workload: &workload)
177+
}
178+
}
179+
180+
@inline(never)
181+
func run_RemoveWhereSwapInts(_ scale: Int) {
182+
for _ in 0..<scale {
183+
var workload = ints; workload.swapAt(0,1)
184+
testSwap(workload: &workload)
185+
}
186+
}
187+
188+
@inline(never)
189+
func run_RemoveWhereFilterString(_ scale: Int) {
190+
for _ in 0..<scale {
191+
var workload = str
192+
workload.append("!")
193+
testFilter(workload: &workload)
194+
}
195+
}
196+
197+
@inline(never)
198+
func run_RemoveWhereQuadraticString(_ scale: Int) {
199+
for _ in 0..<scale {
200+
var workload = str
201+
workload.append("!")
202+
testQuadratic(workload: &workload)
203+
}
204+
}
205+

benchmark/utils/TestsUtils.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,9 @@ public struct BenchmarkInfo {
9090
/// taken.
9191
public var tearDownFunction: (() -> ())?
9292

93-
public init(name: String, runFunction: @escaping (Int) -> (), tags: [BenchmarkCategory]) {
94-
self.name = name
95-
self.runFunction = runFunction
96-
self.tags = tags
97-
self.setUpFunction = nil
98-
self.tearDownFunction = nil
99-
}
100-
10193
public init(name: String, runFunction: @escaping (Int) -> (), tags: [BenchmarkCategory],
102-
setUpFunction: (() -> ())?,
103-
tearDownFunction: (() -> ())?) {
94+
setUpFunction: (() -> ())? = nil,
95+
tearDownFunction: (() -> ())? = nil) {
10496
self.name = name
10597
self.runFunction = runFunction
10698
self.tags = tags

benchmark/utils/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ import RangeIteration
110110
import RangeReplaceableCollectionPlusDefault
111111
import RecursiveOwnedParameter
112112
import ReduceInto
113+
import RemoveWhere
113114
import ReversedCollections
114115
import RomanNumbers
115116
import SetTests
@@ -255,6 +256,7 @@ registerBenchmark(RangeIteration)
255256
registerBenchmark(RangeReplaceableCollectionPlusDefault)
256257
registerBenchmark(RecursiveOwnedParameter)
257258
registerBenchmark(ReduceInto)
259+
registerBenchmark(RemoveWhere)
258260
registerBenchmark(ReversedCollections)
259261
registerBenchmark(RomanNumbers)
260262
registerBenchmark(SetTests)

0 commit comments

Comments
 (0)