@@ -16,6 +16,7 @@ import Benchmark
16
16
import HTTPTypes
17
17
import Hummingbird
18
18
import HummingbirdCore
19
+ import HummingbirdRouter
19
20
import Logging
20
21
import NIOCore
21
22
import NIOEmbedded
@@ -34,95 +35,141 @@ struct BasicBenchmarkContext: RequestContext {
34
35
}
35
36
36
37
public struct BenchmarkRequestContextSource : RequestContextSource {
37
- public let allocator = ByteBufferAllocator ( )
38
38
public let logger = Logger ( label: " Benchmark " )
39
39
}
40
40
41
41
/// Writes ByteBuffers to AsyncChannel outbound writer
42
42
struct BenchmarkBodyWriter : Sendable , ResponseBodyWriter {
43
+ func finish( _ trailingHeaders: HTTPFields ? ) async throws { }
43
44
func write( _: ByteBuffer ) async throws { }
44
45
}
45
46
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
+
46
60
typealias ByteBufferWriter = ( ByteBuffer ) async throws -> Void
47
61
extension Benchmark {
48
62
@discardableResult
49
- convenience init ? < Context: RequestContext > (
50
- name: String ,
51
- context: Context . Type = BasicBenchmarkContext . self,
63
+ convenience init ? < ResponderBuilder: HTTPResponderBuilder > (
64
+ _ name: String ,
52
65
configuration: Benchmark . Configuration = Benchmark . defaultConfiguration,
53
66
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 {
58
70
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 ( ) )
61
75
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 {
66
80
let ( requestBody, source) = RequestBody . makeStream ( )
67
81
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
- }
72
82
try await writeBody ( source. yield)
73
83
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 ( ) )
74
100
}
75
101
}
76
102
}
77
- } setup: {
78
- try await setupRouter ( router)
79
- Self . blackHole ( MultiThreadedEventLoopGroup . singleton. any ( ) )
80
103
}
81
104
}
82
105
}
83
106
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
+
84
114
extension HTTPField . Name {
85
115
static let test = Self ( " Test " ) !
86
116
}
87
117
88
118
func routerBenchmarks( ) {
89
119
let buffer = ByteBufferAllocator ( ) . buffer ( repeating: 0xFF , count: 10000 )
90
120
Benchmark (
91
- name : " Router:GET " ,
121
+ " Router:GET " ,
92
122
configuration: . init( warmupIterations: 10 ) ,
93
123
request: . init( method: . get, scheme: " http " , authority: " localhost " , path: " / " )
94
- ) { router in
124
+ ) {
125
+ let router = Router ( context: BasicBenchmarkContext . self)
95
126
router. get { _, _ in
96
127
buffer
97
128
}
129
+ return router
98
130
}
99
131
100
132
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 " ,
102
146
configuration: . init( warmupIterations: 10 ) ,
103
147
request: . init( method: . put, scheme: " http " , authority: " localhost " , path: " / " )
104
148
) { write in
105
149
try await write ( buffer)
106
150
try await write ( buffer)
107
151
try await write ( buffer)
108
152
try await write ( buffer)
109
- } setupRouter: { router in
153
+ } createRouter: {
154
+ let router = Router ( context: BasicBenchmarkContext . self)
110
155
router. put { request, _ in
111
156
let body = try await request. body. collect ( upTo: . max)
112
157
return body. readableBytes. description
113
158
}
159
+ return router
114
160
}
115
161
116
162
Benchmark (
117
- name : " Router:Echo " ,
163
+ " Router:Echo " ,
118
164
configuration: . init( warmupIterations: 10 ) ,
119
165
request: . init( method: . post, scheme: " http " , authority: " localhost " , path: " / " )
120
166
) { write in
121
167
try await write ( buffer)
122
168
try await write ( buffer)
123
169
try await write ( buffer)
124
170
try await write ( buffer)
125
- } setupRouter: { router in
171
+ } createRouter: {
172
+ let router = Router ( context: BasicBenchmarkContext . self)
126
173
router. post { request, _ in
127
174
Response ( status: . ok, headers: [ : ] , body: . init { writer in
128
175
for try await buffer in request. body {
@@ -131,24 +178,39 @@ func routerBenchmarks() {
131
178
try await writer. finish ( nil )
132
179
} )
133
180
}
181
+ return router
134
182
}
135
183
136
184
Benchmark (
137
- name : " Middleware Chain " ,
185
+ " Router: Middleware" ,
138
186
configuration: . init( warmupIterations: 10 ) ,
139
187
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)
146
190
router. middlewares. add ( EmptyMiddleware ( ) )
147
191
router. middlewares. add ( EmptyMiddleware ( ) )
148
192
router. middlewares. add ( EmptyMiddleware ( ) )
149
193
router. middlewares. add ( EmptyMiddleware ( ) )
150
194
router. get { _, _ in
151
195
HTTPResponse . Status. ok
152
196
}
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
153
215
}
154
216
}
0 commit comments