Skip to content

Commit d128664

Browse files
authored
Merge pull request #26744 from weissi/jw-bench-find-string
benchmark: add naive string finding
2 parents c0ec48c + f758d22 commit d128664

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed

benchmark/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ set(SWIFT_BENCH_MODULES
8787
single-source/Exclusivity
8888
single-source/ExistentialPerformance
8989
single-source/Fibonacci
90+
single-source/FindStringNaive
9091
single-source/FlattenList
9192
single-source/FloatingPointParsing
9293
single-source/FloatingPointPrinting
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
//===--- FindStringNaive.swift --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2019 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+
// Mini benchmark implementing a naive String search algorithm that
16+
// at the moment shows a lot of ARC traffic.
17+
let t: [BenchmarkCategory] = [.String, .refcount]
18+
let N = 100
19+
20+
var longStringFoFoFoFox: String?
21+
var longArrayFoFoFoFox: [UInt8]?
22+
23+
public let FindStringNaive = [
24+
BenchmarkInfo(
25+
name: "FindString.Loop1.Substring",
26+
runFunction: runBenchLoop1Substring,
27+
tags: t,
28+
setUpFunction: {
29+
longStringFoFoFoFox = String(repeating: "fo", count: 5_000) + "fox <-- needle"
30+
}),
31+
BenchmarkInfo(
32+
name: "FindString.Rec3.String",
33+
runFunction: runBenchRecursive3String,
34+
tags: t,
35+
setUpFunction: {
36+
longStringFoFoFoFox = String(repeating: "fo", count: 500) + "fox <-- needle"
37+
}),
38+
BenchmarkInfo(
39+
name: "FindString.Rec3.Substring",
40+
runFunction: runBenchRecursive3Substring,
41+
tags: t,
42+
setUpFunction: {
43+
longStringFoFoFoFox = String(repeating: "fo", count: 500) + "fox <-- needle"
44+
}),
45+
BenchmarkInfo(
46+
name: "FindString.Loop1.Array",
47+
runFunction: runBenchLoop1Array,
48+
tags: t,
49+
setUpFunction: {
50+
longArrayFoFoFoFox = []
51+
longArrayFoFoFoFox!.reserveCapacity(1_100_000)
52+
for _ in 0 ..< 500_000 {
53+
longArrayFoFoFoFox!.append(contentsOf: "fo".utf8)
54+
}
55+
longArrayFoFoFoFox!.append(contentsOf: "fox <-- needle".utf8)
56+
}),
57+
BenchmarkInfo(
58+
name: "FindString.Rec3.Array",
59+
runFunction: runBenchRecursive3ArrayOfUTF8,
60+
tags: t,
61+
setUpFunction: {
62+
longArrayFoFoFoFox = []
63+
longArrayFoFoFoFox!.reserveCapacity(11_000)
64+
for _ in 0 ..< 5_000 {
65+
longArrayFoFoFoFox!.append(contentsOf: "fo".utf8)
66+
}
67+
longArrayFoFoFoFox!.append(contentsOf: "fox <-- needle".utf8)
68+
}),
69+
]
70+
71+
func findOne<S: StringProtocol>(
72+
_ string: S,
73+
needle: Character
74+
) -> String.Index? {
75+
var index = string.startIndex
76+
while index < string.endIndex {
77+
let nextIndex = string.index(after: index)
78+
if string[index] == needle {
79+
return index
80+
}
81+
index = nextIndex
82+
}
83+
return nil
84+
}
85+
86+
func findThreeRecursive<S: StringProtocol>(
87+
_ string: S,
88+
needle1: Character,
89+
needle2: Character?,
90+
needle3: Character?
91+
) -> String.Index? {
92+
var index = string.startIndex
93+
while index < string.endIndex {
94+
let nextIndex = string.index(after: index)
95+
if string[index] == needle1 {
96+
// Check subsequent needles recursively (if applicable)
97+
guard let needle2 = needle2 else { return index }
98+
99+
if findThreeRecursive(
100+
string[nextIndex...].prefix(2), needle1: needle2, needle2: needle3, needle3: nil
101+
) == nextIndex {
102+
return index
103+
}
104+
}
105+
index = nextIndex
106+
}
107+
return nil
108+
}
109+
110+
func findOneOnUTF8Collection<Bytes: Collection>(
111+
_ string: Bytes,
112+
needle: UInt8
113+
) -> Bytes.Index? where Bytes.Element == UInt8 {
114+
var index = string.startIndex
115+
while index < string.endIndex {
116+
let nextIndex = string.index(after: index)
117+
if string[index] == needle {
118+
return index
119+
}
120+
index = nextIndex
121+
}
122+
return nil
123+
}
124+
125+
func findThreeOnUTF8Collection<Bytes: Collection>(
126+
_ string: Bytes,
127+
needle1: UInt8,
128+
needle2: UInt8?,
129+
needle3: UInt8?
130+
) -> Bytes.Index? where Bytes.Element == UInt8 {
131+
var index = string.startIndex
132+
while index < string.endIndex {
133+
let nextIndex = string.index(after: index)
134+
if string[index] == needle1 {
135+
// Check subsequent needles recursively (if applicable)
136+
guard let needle2 = needle2 else { return index }
137+
138+
if findThreeOnUTF8Collection(
139+
string[nextIndex...].prefix(2), needle1: needle2, needle2: needle3, needle3: nil
140+
) == nextIndex {
141+
return index
142+
}
143+
}
144+
index = nextIndex
145+
}
146+
return nil
147+
}
148+
149+
@inline(never)
150+
func runBenchLoop1Substring(iterations: Int) {
151+
for _ in 0 ..< iterations {
152+
precondition(findOne(longStringFoFoFoFox![...], needle: "x") != nil)
153+
}
154+
}
155+
156+
@inline(never)
157+
func runBenchLoop1Array(iterations: Int) {
158+
for _ in 0 ..< iterations {
159+
precondition(findOneOnUTF8Collection(longArrayFoFoFoFox!, needle: UInt8(ascii: "x")) != nil)
160+
}
161+
}
162+
163+
@inline(never)
164+
func runBenchRecursive3Substring(iterations: Int) {
165+
for _ in 0 ..< iterations {
166+
precondition(findThreeRecursive(longStringFoFoFoFox![...], needle1: "f", needle2: "o", needle3: "x") != nil)
167+
}
168+
}
169+
170+
@inline(never)
171+
func runBenchRecursive3String(iterations: Int) {
172+
for _ in 0 ..< iterations {
173+
precondition(findThreeRecursive(longStringFoFoFoFox!, needle1: "f", needle2: "o", needle3: "x") != nil)
174+
}
175+
}
176+
177+
@inline(never)
178+
func runBenchRecursive3ArrayOfUTF8(iterations: Int) {
179+
for _ in 0 ..< iterations {
180+
precondition(findThreeOnUTF8Collection(longArrayFoFoFoFox!,
181+
needle1: UInt8(ascii: "f"),
182+
needle2: UInt8(ascii: "o"),
183+
needle3: UInt8(ascii: "x")) != nil)
184+
}
185+
}

benchmark/utils/main.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import ErrorHandling
7575
import Exclusivity
7676
import ExistentialPerformance
7777
import Fibonacci
78+
import FindStringNaive
7879
import FlattenList
7980
import FloatingPointParsing
8081
import FloatingPointPrinting
@@ -253,6 +254,7 @@ registerBenchmark(ErrorHandling)
253254
registerBenchmark(Exclusivity)
254255
registerBenchmark(ExistentialPerformance)
255256
registerBenchmark(Fibonacci)
257+
registerBenchmark(FindStringNaive)
256258
registerBenchmark(FlattenListLoop)
257259
registerBenchmark(FlattenListFlatMap)
258260
registerBenchmark(FloatingPointParsing)

0 commit comments

Comments
 (0)