Skip to content

Commit e5cff55

Browse files
authored
(134536126) Benchmarks for Swift URL (#907)
1 parent 3cf61a2 commit e5cff55

File tree

2 files changed

+243
-0
lines changed

2 files changed

+243
-0
lines changed
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 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 Benchmark
14+
import func Benchmark.blackHole
15+
16+
#if os(macOS) && USE_PACKAGE
17+
import FoundationEssentials
18+
#else
19+
import Foundation
20+
#endif
21+
22+
let benchmarks = {
23+
24+
Benchmark.defaultConfiguration.maxIterations = 1_000_000_000
25+
Benchmark.defaultConfiguration.maxDuration = .seconds(3)
26+
Benchmark.defaultConfiguration.scalingFactor = .kilo
27+
Benchmark.defaultConfiguration.metrics = [.cpuTotal, .throughput]
28+
29+
let validURLString = "scheme://username:[email protected]:80/pathwithoutspaces/morepath?queryname=queryvalue#fragmentwithoutspaces"
30+
let invalidURLString = "scheme://username:[email protected]:invalidport/path?query#fragment"
31+
let encodableURLString = "scheme://user name:pass word@😂😂😂.example.com:80/path with spaces/more path?query name=query value#fragment with spaces"
32+
33+
// MARK: - String Parsing
34+
35+
Benchmark("URL-ParseValidASCII") { benchmark in
36+
for _ in benchmark.scaledIterations {
37+
blackHole(URL(string: validURLString))
38+
}
39+
}
40+
41+
Benchmark("URLComponents-ParseValidASCII") { benchmark in
42+
for _ in benchmark.scaledIterations {
43+
blackHole(URLComponents(string: validURLString))
44+
}
45+
}
46+
47+
Benchmark("URL-ParseInvalid") { benchmark in
48+
for _ in benchmark.scaledIterations {
49+
blackHole(URL(string: invalidURLString))
50+
}
51+
}
52+
53+
Benchmark("URLComponents-ParseInvalid") { benchmark in
54+
for _ in benchmark.scaledIterations {
55+
blackHole(URLComponents(string: invalidURLString))
56+
}
57+
}
58+
59+
#if os(macOS) || compiler(>=6)
60+
Benchmark("URL-ParseAndEncode") { benchmark in
61+
for _ in benchmark.scaledIterations {
62+
blackHole(URL(string: encodableURLString))
63+
}
64+
}
65+
66+
Benchmark("URLComponents-ParseAndEncode") { benchmark in
67+
for _ in benchmark.scaledIterations {
68+
blackHole(URLComponents(string: encodableURLString))
69+
}
70+
}
71+
#endif
72+
73+
// MARK: - Get URL Components
74+
75+
// Old swift-corelibs-foundation implementation fails to parse an
76+
// encodable string but allows encodable components to be set
77+
var encodedComp = URLComponents()
78+
encodedComp.scheme = "scheme"
79+
encodedComp.user = "user name"
80+
encodedComp.password = "pass word"
81+
encodedComp.host = "😂😂😂.example.com"
82+
encodedComp.port = 80
83+
encodedComp.path = "/path with spaces/more path"
84+
encodedComp.query = "query name=query value"
85+
encodedComp.fragment = "fragment with spaces"
86+
let encodedURL = encodedComp.url!
87+
88+
#if os(macOS) || compiler(>=6)
89+
// Component functions, e.g. path(), are available in macOS 13 and Swift 6
90+
Benchmark("URL-GetEncodedComponents") { benchmark in
91+
for _ in benchmark.scaledIterations {
92+
blackHole(encodedURL.scheme)
93+
blackHole(encodedURL.user())
94+
blackHole(encodedURL.password())
95+
blackHole(encodedURL.host())
96+
blackHole(encodedURL.path())
97+
blackHole(encodedURL.query())
98+
blackHole(encodedURL.fragment())
99+
}
100+
}
101+
#endif
102+
103+
Benchmark("URLComponents-GetEncodedComponents") { benchmark in
104+
for _ in benchmark.scaledIterations {
105+
blackHole(encodedComp.scheme)
106+
blackHole(encodedComp.percentEncodedUser)
107+
blackHole(encodedComp.percentEncodedPassword)
108+
#if os(macOS) || compiler(>=6)
109+
blackHole(encodedComp.encodedHost)
110+
#else
111+
blackHole(encodedComp.percentEncodedHost)
112+
#endif
113+
blackHole(encodedComp.percentEncodedPath)
114+
blackHole(encodedComp.percentEncodedQuery)
115+
blackHole(encodedComp.percentEncodedFragment)
116+
}
117+
}
118+
119+
Benchmark("URL-GetDecodedComponents") { benchmark in
120+
for _ in benchmark.scaledIterations {
121+
blackHole(encodedURL.scheme)
122+
blackHole(encodedURL.user)
123+
blackHole(encodedURL.password)
124+
blackHole(encodedURL.host)
125+
blackHole(encodedURL.path)
126+
blackHole(encodedURL.query)
127+
blackHole(encodedURL.fragment)
128+
}
129+
}
130+
131+
Benchmark("URLComponents-GetDecodedComponents") { benchmark in
132+
for _ in benchmark.scaledIterations {
133+
blackHole(encodedComp.scheme)
134+
blackHole(encodedComp.user)
135+
blackHole(encodedComp.password)
136+
blackHole(encodedComp.host)
137+
blackHole(encodedComp.path)
138+
blackHole(encodedComp.query)
139+
blackHole(encodedComp.fragment)
140+
}
141+
}
142+
143+
let validComp = URLComponents(string: validURLString)!
144+
Benchmark("URLComponents-GetComponentRanges") { benchmark in
145+
for _ in benchmark.scaledIterations {
146+
blackHole(validComp.rangeOfScheme)
147+
blackHole(validComp.rangeOfUser)
148+
blackHole(validComp.rangeOfPassword)
149+
blackHole(validComp.rangeOfHost)
150+
blackHole(validComp.rangeOfPort)
151+
blackHole(validComp.rangeOfPath)
152+
blackHole(validComp.rangeOfQuery)
153+
blackHole(validComp.rangeOfFragment)
154+
}
155+
}
156+
157+
// MARK: - Set URL Components
158+
159+
Benchmark("URLComponents-SetComponents") { benchmark in
160+
for _ in benchmark.scaledIterations {
161+
var comp = URLComponents()
162+
comp.scheme = "scheme"
163+
comp.user = "username"
164+
comp.password = "password"
165+
comp.host = "app.example.com"
166+
comp.port = 80
167+
comp.path = "/pathwithoutspaces/morepath"
168+
comp.query = "queryname=queryvalue"
169+
comp.fragment = "fragmentwithoutspaces"
170+
blackHole(comp.string)
171+
}
172+
}
173+
174+
Benchmark("URLComponents-SetEncodableComponents") { benchmark in
175+
for _ in benchmark.scaledIterations {
176+
var comp = URLComponents()
177+
comp.scheme = "scheme"
178+
comp.user = "user name"
179+
comp.password = "pass word"
180+
comp.host = "😂😂😂.example.com"
181+
comp.port = 80
182+
comp.path = "/path with spaces/more path"
183+
comp.query = "query name=query value"
184+
comp.fragment = "fragment with spaces"
185+
blackHole(comp.string)
186+
}
187+
}
188+
189+
// MARK: - Query Items
190+
191+
let validQueryItems = [
192+
URLQueryItem(name: "querywithoutspace", value: "valuewithoutspace"),
193+
URLQueryItem(name: "myfavoriteletters", value: "abcdabcdabcdabcd"),
194+
URLQueryItem(name: "namewithnovalueorspace", value: nil)
195+
]
196+
197+
let encodableQueryItems = [
198+
URLQueryItem(name: "query with space", value: "value with space"),
199+
URLQueryItem(name: "my favorite emojis", value: "😂😂😂"),
200+
URLQueryItem(name: "name with no value", value: nil)
201+
]
202+
203+
Benchmark("URLComponents-SetQueryItems") { benchmark in
204+
for _ in benchmark.scaledIterations {
205+
var comp = URLComponents()
206+
comp.queryItems = validQueryItems
207+
blackHole(comp)
208+
}
209+
}
210+
211+
Benchmark("URLComponents-SetEncodableQueryItems") { benchmark in
212+
for _ in benchmark.scaledIterations {
213+
var comp = URLComponents()
214+
comp.queryItems = encodableQueryItems
215+
blackHole(comp)
216+
}
217+
}
218+
219+
var queryComp = URLComponents()
220+
queryComp.queryItems = encodableQueryItems
221+
222+
Benchmark("URLComponents-GetEncodedQueryItems") { benchmark in
223+
for _ in benchmark.scaledIterations {
224+
blackHole(queryComp.percentEncodedQueryItems)
225+
}
226+
}
227+
228+
Benchmark("URLComponents-GetDecodedQueryItems") { benchmark in
229+
for _ in benchmark.scaledIterations {
230+
blackHole(queryComp.queryItems)
231+
}
232+
}
233+
234+
}

Benchmarks/Package.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ let package = Package(
155155
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
156156
]
157157
),
158+
.executableTarget(
159+
name: "URLBenchmarks",
160+
dependencies: targetDependency,
161+
path: "Benchmarks/URL",
162+
swiftSettings: swiftSettings,
163+
plugins: [
164+
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
165+
]
166+
),
158167
]
159168
)
160169

0 commit comments

Comments
 (0)