Skip to content

Commit 595ffdb

Browse files
authored
Clean up benchmarks and re-enable them (#550)
Limit them to metrics that don't change drastically in CI, remove any concurrency, remove HTTP
1 parent c24569e commit 595ffdb

File tree

4 files changed

+110
-161
lines changed

4 files changed

+110
-161
lines changed

Benchmarks/Benchmarks/HTTP1/HTTP1ChannelBenchmarks.swift

-114
This file was deleted.

Benchmarks/Benchmarks/Router/Benchmarks.swift

+13-1
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,26 @@ import Benchmark
1616
import Hummingbird
1717

1818
let benchmarks = {
19+
#if CI
20+
Benchmark.defaultConfiguration = .init(
21+
metrics: [
22+
.instructions,
23+
.mallocCountTotal,
24+
],
25+
warmupIterations: 10
26+
)
27+
trieRouterBenchmarks()
28+
routerBenchmarks()
29+
#else
1930
Benchmark.defaultConfiguration = .init(
2031
metrics: [
2132
.cpuTotal,
22-
.throughput,
33+
.instructions,
2334
.mallocCountTotal,
2435
],
2536
warmupIterations: 10
2637
)
2738
trieRouterBenchmarks()
2839
routerBenchmarks()
40+
#endif
2941
}

Benchmarks/Benchmarks/Router/RouterBenchmarks.swift

+96-34
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import Benchmark
1616
import HTTPTypes
1717
import Hummingbird
1818
import HummingbirdCore
19+
import HummingbirdRouter
1920
import Logging
2021
import NIOCore
2122
import NIOEmbedded
@@ -34,95 +35,141 @@ struct BasicBenchmarkContext: RequestContext {
3435
}
3536

3637
public struct BenchmarkRequestContextSource: RequestContextSource {
37-
public let allocator = ByteBufferAllocator()
3838
public let logger = Logger(label: "Benchmark")
3939
}
4040

4141
/// Writes ByteBuffers to AsyncChannel outbound writer
4242
struct BenchmarkBodyWriter: Sendable, ResponseBodyWriter {
43+
func finish(_ trailingHeaders: HTTPFields?) async throws {}
4344
func write(_: ByteBuffer) async throws {}
4445
}
4546

47+
/// Implementation of a basic request context that supports everything the Hummingbird library needs
48+
struct BasicRouterBenchmarkContext: RouterRequestContext {
49+
typealias Source = BenchmarkRequestContextSource
50+
51+
var coreContext: CoreRequestContextStorage
52+
var routerContext: RouterBuilderContext
53+
54+
init(source: Source) {
55+
self.coreContext = CoreRequestContextStorage(source: source)
56+
self.routerContext = .init()
57+
}
58+
}
59+
4660
typealias ByteBufferWriter = (ByteBuffer) async throws -> Void
4761
extension Benchmark {
4862
@discardableResult
49-
convenience init?<Context: RequestContext>(
50-
name: String,
51-
context: Context.Type = BasicBenchmarkContext.self,
63+
convenience init?<ResponderBuilder: HTTPResponderBuilder>(
64+
_ name: String,
5265
configuration: Benchmark.Configuration = Benchmark.defaultConfiguration,
5366
request: HTTPRequest,
54-
writeBody: @escaping @Sendable (ByteBufferWriter) async throws -> Void = { _ in },
55-
setupRouter: @escaping @Sendable (Router<Context>) async throws -> Void
56-
) where Context.Source == BenchmarkRequestContextSource {
57-
let router = Router(context: Context.self)
67+
writeBody: (@Sendable (ByteBufferWriter) async throws -> Void)? = nil,
68+
createRouter: @escaping @Sendable () async throws -> ResponderBuilder
69+
) where ResponderBuilder.Responder.Context: RequestContext, ResponderBuilder.Responder.Context.Source == BenchmarkRequestContextSource {
5870
self.init(name, configuration: configuration) { benchmark in
59-
let responder = router.buildResponder()
60-
benchmark.startMeasurement()
71+
let responder = try await createRouter().buildResponder()
72+
73+
if let writeBody {
74+
let context = ResponderBuilder.Responder.Context(source: BenchmarkRequestContextSource())
6175

62-
for _ in benchmark.scaledIterations {
63-
for _ in 0..<50 {
64-
try await withThrowingTaskGroup(of: Void.self) { group in
65-
let context = Context(source: BenchmarkRequestContextSource())
76+
benchmark.startMeasurement()
77+
78+
for _ in benchmark.scaledIterations {
79+
for _ in 0..<50 {
6680
let (requestBody, source) = RequestBody.makeStream()
6781
let Request = Request(head: request, body: requestBody)
68-
group.addTask {
69-
let response = try await responder.respond(to: Request, context: context)
70-
_ = try await response.body.write(BenchmarkBodyWriter())
71-
}
7282
try await writeBody(source.yield)
7383
source.finish()
84+
let response = try await responder.respond(to: Request, context: context)
85+
_ = try await response.body.write(BenchmarkBodyWriter())
86+
}
87+
}
88+
} else {
89+
let context = ResponderBuilder.Responder.Context(source: BenchmarkRequestContextSource())
90+
let (requestBody, source) = RequestBody.makeStream()
91+
let Request = Request(head: request, body: requestBody)
92+
source.finish()
93+
94+
benchmark.startMeasurement()
95+
96+
for _ in benchmark.scaledIterations {
97+
for _ in 0..<50 {
98+
let response = try await responder.respond(to: Request, context: context)
99+
_ = try await response.body.write(BenchmarkBodyWriter())
74100
}
75101
}
76102
}
77-
} setup: {
78-
try await setupRouter(router)
79-
Self.blackHole(MultiThreadedEventLoopGroup.singleton.any())
80103
}
81104
}
82105
}
83106

107+
struct EmptyMiddleware<Context>: RouterMiddleware {
108+
func handle(_ request: Request, context: Context, next: (Request, Context) async throws -> Response) async throws -> Response {
109+
return try await next(request, context)
110+
}
111+
}
112+
113+
84114
extension HTTPField.Name {
85115
static let test = Self("Test")!
86116
}
87117

88118
func routerBenchmarks() {
89119
let buffer = ByteBufferAllocator().buffer(repeating: 0xFF, count: 10000)
90120
Benchmark(
91-
name: "Router:GET",
121+
"Router:GET",
92122
configuration: .init(warmupIterations: 10),
93123
request: .init(method: .get, scheme: "http", authority: "localhost", path: "/")
94-
) { router in
124+
) {
125+
let router = Router(context: BasicBenchmarkContext.self)
95126
router.get { _, _ in
96127
buffer
97128
}
129+
return router
98130
}
99131

100132
Benchmark(
101-
name: "Router:PUT",
133+
"Router:Parameters",
134+
configuration: .init(warmupIterations: 10),
135+
request: .init(method: .get, scheme: "http", authority: "localhost", path: "/testthis")
136+
) {
137+
let router = Router(context: BasicBenchmarkContext.self)
138+
router.get("{test}") { _, context in
139+
try context.parameters.require("test")
140+
}
141+
return router
142+
}
143+
144+
Benchmark(
145+
"Router:PUT",
102146
configuration: .init(warmupIterations: 10),
103147
request: .init(method: .put, scheme: "http", authority: "localhost", path: "/")
104148
) { write in
105149
try await write(buffer)
106150
try await write(buffer)
107151
try await write(buffer)
108152
try await write(buffer)
109-
} setupRouter: { router in
153+
} createRouter: {
154+
let router = Router(context: BasicBenchmarkContext.self)
110155
router.put { request, _ in
111156
let body = try await request.body.collect(upTo: .max)
112157
return body.readableBytes.description
113158
}
159+
return router
114160
}
115161

116162
Benchmark(
117-
name: "Router:Echo",
163+
"Router:Echo",
118164
configuration: .init(warmupIterations: 10),
119165
request: .init(method: .post, scheme: "http", authority: "localhost", path: "/")
120166
) { write in
121167
try await write(buffer)
122168
try await write(buffer)
123169
try await write(buffer)
124170
try await write(buffer)
125-
} setupRouter: { router in
171+
} createRouter: {
172+
let router = Router(context: BasicBenchmarkContext.self)
126173
router.post { request, _ in
127174
Response(status: .ok, headers: [:], body: .init { writer in
128175
for try await buffer in request.body {
@@ -131,24 +178,39 @@ func routerBenchmarks() {
131178
try await writer.finish(nil)
132179
})
133180
}
181+
return router
134182
}
135183

136184
Benchmark(
137-
name: "Middleware Chain",
185+
"Router:Middleware",
138186
configuration: .init(warmupIterations: 10),
139187
request: .init(method: .get, scheme: "http", authority: "localhost", path: "/")
140-
) { router in
141-
struct EmptyMiddleware<Context>: RouterMiddleware {
142-
func handle(_ request: Request, context: Context, next: (Request, Context) async throws -> Response) async throws -> Response {
143-
return try await next(request, context)
144-
}
145-
}
188+
) {
189+
let router = Router(context: BasicBenchmarkContext.self)
146190
router.middlewares.add(EmptyMiddleware())
147191
router.middlewares.add(EmptyMiddleware())
148192
router.middlewares.add(EmptyMiddleware())
149193
router.middlewares.add(EmptyMiddleware())
150194
router.get { _, _ in
151195
HTTPResponse.Status.ok
152196
}
197+
return router
198+
}
199+
200+
Benchmark(
201+
"RouterBuilder:Middleware",
202+
configuration: .init(warmupIterations: 10),
203+
request: .init(method: .get, scheme: "http", authority: "localhost", path: "/")
204+
) {
205+
let router = RouterBuilder(context: BasicRouterBenchmarkContext.self) {
206+
EmptyMiddleware()
207+
EmptyMiddleware()
208+
EmptyMiddleware()
209+
EmptyMiddleware()
210+
Get { _,_ -> HTTPResponse.Status in
211+
.ok
212+
}
213+
}
214+
return router
153215
}
154216
}

Benchmarks/Package.swift

+1-12
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,13 @@ let package = Package(
1111
.package(path: "../../hummingbird"),
1212
],
1313
targets: [
14-
// HTTP1 benchmarks
15-
.executableTarget(
16-
name: "HTTP1",
17-
dependencies: [
18-
.product(name: "Benchmark", package: "package-benchmark"),
19-
.product(name: "Hummingbird", package: "hummingbird"),
20-
],
21-
path: "Benchmarks/HTTP1",
22-
plugins: [
23-
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
24-
]
25-
),
2614
// Router benchmarks
2715
.executableTarget(
2816
name: "Router",
2917
dependencies: [
3018
.product(name: "Benchmark", package: "package-benchmark"),
3119
.product(name: "Hummingbird", package: "hummingbird"),
20+
.product(name: "HummingbirdRouter", package: "hummingbird"),
3221
],
3322
path: "Benchmarks/Router",
3423
plugins: [

0 commit comments

Comments
 (0)