Skip to content

Commit cefa961

Browse files
committed
Add a benchmark executable
1 parent 8843065 commit cefa961

File tree

6 files changed

+165
-1
lines changed

6 files changed

+165
-1
lines changed

Package.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ let package = Package(
2020
products: [
2121
.library(name: "RediStack", targets: ["RediStack"]),
2222
.library(name: "RediStackTestUtils", targets: ["RediStackTestUtils"]),
23-
.library(name: "RedisTypes", targets: ["RedisTypes"])
23+
.library(name: "RedisTypes", targets: ["RedisTypes"]),
24+
.executable(name: "RediStackPerformanceTester", targets: ["RediStackPerformanceTester"]),
2425
],
2526
dependencies: [
2627
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.1.0"),
@@ -44,6 +45,13 @@ let package = Package(
4445
]
4546
),
4647
.target(name: "RedisTypes", dependencies: ["RediStack"]),
48+
.executableTarget(
49+
name: "RediStackPerformanceTester",
50+
dependencies: [
51+
"RediStack",
52+
"RESP3",
53+
]
54+
),
4755
.target(
4856
name: "RediStackTestUtils",
4957
dependencies: [
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import NIOCore
2+
import RESP3
3+
4+
func benchmarkRESP3Parsing() throws {
5+
let valueBuffer = ByteBuffer(string: "*2\r\n$3\r\nGET\r\n$7\r\nwelcome\r\n")
6+
let values: [RESP3Token.Value] = [
7+
.blobString(ByteBuffer(string: "GET")),
8+
.blobString(ByteBuffer(string: "welcome")),
9+
]
10+
11+
try benchmark {
12+
var valueBuffer = valueBuffer
13+
14+
guard
15+
let token = try RESP3Token(consuming: &valueBuffer),
16+
case .array(let array) = token.value,
17+
array.map(\.value) == values
18+
else {
19+
fatalError("\(#function) Test failed: Invalid test result")
20+
}
21+
}
22+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import RediStack
2+
import RESP3
3+
import Foundation
4+
import NIOCore
5+
import NIOEmbedded
6+
7+
func benchmarkRESP3Protocol() throws {
8+
let channel = EmbeddedChannel()
9+
10+
// Precalculate the server response
11+
try channel.connect(to: .init(unixDomainSocketPath: "/fakeserver")).wait()
12+
let serverReply = "Hello, world"
13+
let redisReplyBuffer = ByteBuffer(string: "$\(serverReply.count)\r\n\(serverReply)\r\n")
14+
15+
try benchmark {
16+
// Client sends a command
17+
// GET welcome
18+
// TODO: Replace when we get RESP3 serialization
19+
try channel.writeOutbound(ByteBuffer(string: "*2\r\n$3\r\nGET\r\n$7\r\nwelcome\r\n"))
20+
21+
// Server reads the command
22+
_ = try channel.readOutbound(as: ByteBuffer.self)
23+
// Server replies
24+
try channel.writeInbound(redisReplyBuffer)
25+
26+
// Client reads the reply
27+
guard var serverReplyBuffer = try channel.readInbound(as: ByteBuffer.self) else {
28+
fatalError("Missing reply")
29+
}
30+
31+
guard case .blobString(var blobString) = try RESP3Token(consuming: &serverReplyBuffer)?.value else {
32+
fatalError("Invalid reply")
33+
}
34+
35+
guard blobString.readString(length: blobString.readableBytes) == serverReply else {
36+
fatalError("Invalid test result")
37+
}
38+
}
39+
40+
guard case .clean = try channel.finish() else {
41+
fatalError("Test didn't exit cleanly")
42+
}
43+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import NIOCore
2+
import RediStack
3+
4+
func benchmarkRESPParsing() throws {
5+
let valueBuffer = ByteBuffer(string: "*2\r\n$3\r\nGET\r\n$7\r\nwelcome\r\n")
6+
let result: [RESPValue] = [
7+
.bulkString(ByteBuffer(string: "GET")),
8+
.bulkString(ByteBuffer(string: "welcome")),
9+
]
10+
let translator = RESPTranslator()
11+
try benchmark {
12+
var valueBuffer = valueBuffer
13+
let value = try translator.parseBytes(from: &valueBuffer)
14+
guard case .array(result) = value else {
15+
fatalError("\(#function) Test failed: Invalid test result")
16+
}
17+
}
18+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import RediStack
2+
import RESP3
3+
import Foundation
4+
import NIOCore
5+
import NIOEmbedded
6+
7+
func benchmarkRESPProtocol() throws {
8+
let channel = EmbeddedChannel()
9+
try channel.pipeline.addBaseRedisHandlers().wait()
10+
11+
// Precalculate the server response
12+
try channel.connect(to: .init(unixDomainSocketPath: "/fakeserver")).wait()
13+
var redisReplyBuffer = ByteBuffer()
14+
let serverValue = "Hello, world"
15+
let replyValue = RESPValue.simpleString(ByteBuffer(string: serverValue))
16+
RESPTranslator().write(replyValue, into: &redisReplyBuffer)
17+
18+
try benchmark {
19+
let promise = channel.eventLoop.makePromise(of: RESPValue.self)
20+
21+
// Client sends a command
22+
try channel.writeOutbound(RedisCommand(
23+
message: .array([
24+
.bulkString(ByteBuffer(string: "GET")),
25+
.bulkString(ByteBuffer(string: "welcome")),
26+
]),
27+
responsePromise: promise
28+
))
29+
30+
// Server reads the command
31+
_ = try channel.readOutbound(as: ByteBuffer.self)
32+
// Server replies
33+
try channel.writeInbound(redisReplyBuffer)
34+
35+
// Client reads the reply
36+
let serverReply = try promise.futureResult.wait()
37+
guard serverReply.string == serverValue else {
38+
fatalError("Invalid test result")
39+
}
40+
}
41+
42+
guard case .clean = try channel.finish() else {
43+
fatalError("Test didn't exit cleanly")
44+
}
45+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import RediStack
2+
import Foundation
3+
import NIOCore
4+
import NIOEmbedded
5+
import RESP3
6+
7+
func benchmark(label: String = #function, _ n: Int = 100_000, run: () throws -> Void) throws {
8+
let start = Date()
9+
for _ in 0..<n {
10+
try run()
11+
}
12+
let end = Date()
13+
let mode: String
14+
15+
#if DEBUG
16+
mode = "DEBUG"
17+
#else
18+
mode = "RELEASE"
19+
#endif
20+
21+
print("\(label): Test took \(end.timeIntervalSince(start)) seconds on \(mode)")
22+
}
23+
24+
try benchmarkRESPProtocol()
25+
try benchmarkRESP3Protocol()
26+
27+
try benchmarkRESPParsing()
28+
try benchmarkRESP3Parsing()

0 commit comments

Comments
 (0)