Skip to content

Commit 66fa6c5

Browse files
Add package-benchmark benchmark suite
1 parent 5f21d5d commit 66fa6c5

File tree

6 files changed

+348
-7
lines changed

6 files changed

+348
-7
lines changed
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import Benchmark
2+
import WasmKit
3+
import SystemPackage
4+
import Foundation
5+
import WasmKitWASI
6+
7+
let benchmarks = {
8+
let macrosDir = FilePath(#filePath)
9+
.removingLastComponent()
10+
.removingLastComponent()
11+
.removingLastComponent()
12+
.removingLastComponent()
13+
.pushing("Vendor/swift-stringify-macro.wasm/Sources")
14+
15+
let handshakeMessage = """
16+
{
17+
"getCapability":{
18+
"capability":{
19+
"protocolVersion":7
20+
}
21+
}
22+
}
23+
"""
24+
let expandMessages = [
25+
"StringifyMacros.wasm": """
26+
{
27+
"expandFreestandingMacro":{
28+
"discriminator":"$s7Example0015mainswift_tzEGbfMX2_6_33_B384B672EB89465DCC67528E23350CF9Ll9stringifyfMf_",
29+
"lexicalContext":[
30+
31+
],
32+
"macro":{
33+
"moduleName":"StringifyMacros",
34+
"name":"stringify",
35+
"typeName":"StringifyMacro"
36+
},
37+
"macroRole":"expression",
38+
"syntax":{
39+
"kind":"expression",
40+
"location":{
41+
"column":7,
42+
"fileID":"Example/main.swift",
43+
"fileName":"",
44+
"line":3,
45+
"offset":24
46+
},
47+
"source":"#stringify(1 + 1)"
48+
}
49+
}
50+
}
51+
""",
52+
"FoundationMacros.wasm": """
53+
{
54+
"expandFreestandingMacro":{
55+
"discriminator":"$s7Example0015mainswift_tzEGbfMX2_6_33_B384B672EB89465DCC67528E23350CF9Ll9stringifyfMf_",
56+
"lexicalContext":[
57+
58+
],
59+
"macro":{
60+
"moduleName":"FoundationMacros",
61+
"name":"Expression",
62+
"typeName":"ExpressionMacro"
63+
},
64+
"macroRole":"expression",
65+
"syntax":{
66+
"kind":"expression",
67+
"location":{
68+
"column":7,
69+
"fileID":"Example/main.swift",
70+
"fileName":"",
71+
"line":3,
72+
"offset":24
73+
},
74+
"source":"#Expression<Int, Int> { $0 + 1 }"
75+
}
76+
}
77+
}
78+
""",
79+
"TestingMacros.wasm": """
80+
{
81+
"expandFreestandingMacro":{
82+
"discriminator":"$s7Example0015mainswift_tzEGbfMX2_6_33_B384B672EB89465DCC67528E23350CF9Ll9stringifyfMf_",
83+
"lexicalContext":[
84+
85+
],
86+
"macro":{
87+
"moduleName":"TestingMacros",
88+
"name":"expect",
89+
"typeName":"ExpectMacro"
90+
},
91+
"macroRole":"expression",
92+
"syntax":{
93+
"kind":"expression",
94+
"location":{
95+
"column":7,
96+
"fileID":"Example/main.swift",
97+
"fileName":"",
98+
"line":3,
99+
"offset":24
100+
},
101+
"source":"#expect(1 == 2)"
102+
}
103+
}
104+
}
105+
"""
106+
]
107+
108+
for file in try! FileManager.default.contentsOfDirectory(
109+
atPath: macrosDir.string
110+
) {
111+
guard file.hasSuffix(".wasm") else { continue }
112+
113+
struct Setup {
114+
let hostToPlugin: FileDescriptor
115+
let pluginToHost: FileDescriptor
116+
let pump: Function
117+
let expandMessage: String
118+
119+
init(filePath: FilePath, expandMessage: String) throws {
120+
let engine = Engine()
121+
let store = Store(engine: engine)
122+
let module = try parseWasm(filePath: filePath)
123+
124+
let hostToPluginPipes = try FileDescriptor.pipe()
125+
let pluginToHostPipes = try FileDescriptor.pipe()
126+
let bridge = try WASIBridgeToHost(
127+
stdin: hostToPluginPipes.readEnd,
128+
stdout: pluginToHostPipes.writeEnd,
129+
stderr: .standardError
130+
)
131+
var imports = Imports()
132+
bridge.link(to: &imports, store: store)
133+
let instance = try module.instantiate(store: store, imports: imports)
134+
try instance.exports[function: "_start"]!()
135+
let pump = instance.exports[function: "swift_wasm_macro_v1_pump"]!
136+
137+
self.hostToPlugin = hostToPluginPipes.writeEnd
138+
self.pluginToHost = pluginToHostPipes.readEnd
139+
self.pump = pump
140+
self.expandMessage = expandMessage
141+
}
142+
143+
func writeMessage(_ message: String) throws {
144+
let bytes = message.utf8
145+
try withUnsafeBytes(of: UInt64(bytes.count).littleEndian) {
146+
_ = try hostToPlugin.writeAll($0)
147+
}
148+
try hostToPlugin.writeAll(bytes)
149+
}
150+
func readMessage() throws -> [UInt8] {
151+
let lengthRaw = try withUnsafeTemporaryAllocation(of: UInt8.self, capacity: 8) { buffer in
152+
let lengthCount = try pluginToHost.read(into: UnsafeMutableRawBufferPointer(buffer))
153+
guard lengthCount == 8 else { fatalError() }
154+
return buffer.withMemoryRebound(to: UInt64.self, \.baseAddress!.pointee)
155+
}
156+
let length = Int(UInt64(littleEndian: lengthRaw))
157+
return try [UInt8](unsafeUninitializedCapacity: length) { buffer, size in
158+
let received = try pluginToHost.read(into: UnsafeMutableRawBufferPointer(buffer))
159+
guard received == length else {
160+
fatalError()
161+
}
162+
size = received
163+
}
164+
}
165+
166+
func tick() throws {
167+
try writeMessage(expandMessage)
168+
try pump()
169+
_ = try readMessage()
170+
}
171+
}
172+
173+
guard let expandMessage = expandMessages[file] else {
174+
fatalError("Expand message definition not found for \(file)")
175+
}
176+
177+
Benchmark("Startup \(file)") { benchmark in
178+
let setup = try Setup(filePath: macrosDir.appending(file), expandMessage: expandMessage)
179+
try setup.writeMessage(handshakeMessage)
180+
try setup.tick()
181+
}
182+
183+
Benchmark("Expand \(file)") { benchmark, setup in
184+
try setup.tick()
185+
} setup: { () -> Setup in
186+
let setup = try Setup(
187+
filePath: macrosDir.appending(file),
188+
expandMessage: expandMessage
189+
)
190+
try setup.writeMessage(handshakeMessage)
191+
192+
// Warmup
193+
try setup.tick()
194+
195+
return setup
196+
}
197+
}
198+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Benchmark
2+
import WasmKit
3+
import WAT
4+
5+
let benchmarks = {
6+
Benchmark("empty instantiation") { benchmark in
7+
for _ in benchmark.scaledIterations {
8+
let engine = Engine()
9+
let store = Store(engine: engine)
10+
let module = try parseWasm(bytes: wat2wasm("""
11+
(module
12+
(func (export "_start"))
13+
)
14+
"""))
15+
_ = try module.instantiate(store: store)
16+
}
17+
}
18+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import Benchmark
2+
import WasmKit
3+
import WasmKitWASI
4+
import Foundation
5+
import SystemPackage
6+
7+
let benchmarks = {
8+
let wishYouWereFast = URL(fileURLWithPath: #filePath)
9+
.deletingLastPathComponent()
10+
.deletingLastPathComponent()
11+
.deletingLastPathComponent()
12+
.deletingLastPathComponent()
13+
.appendingPathComponent("Vendor")
14+
.appendingPathComponent("wish-you-were-fast")
15+
.appendingPathComponent("wasm")
16+
.appendingPathComponent("suites")
17+
.appendingPathComponent("libsodium")
18+
19+
let devNull = try! FileDescriptor.open("/dev/null", .readWrite)
20+
21+
for file in try! FileManager.default.contentsOfDirectory(
22+
atPath: wishYouWereFast.path
23+
) {
24+
guard file.hasSuffix(".wasm") else { continue }
25+
Benchmark("\(file)") { benchmark in
26+
let engine = Engine()
27+
let store = Store(engine: engine)
28+
let module = try parseWasm(
29+
filePath: FilePath(wishYouWereFast.appendingPathComponent(file).path)
30+
)
31+
let wasi = try WASIBridgeToHost(stdout: devNull, stderr: devNull)
32+
var imports = Imports()
33+
wasi.link(to: &imports, store: store)
34+
let instance = try module.instantiate(store: store, imports: imports)
35+
_ = try wasi.start(instance)
36+
}
37+
}
38+
}

Benchmarks/Package.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// swift-tools-version: 5.8
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "Benchmarks",
7+
platforms: [.macOS(.v13)],
8+
dependencies: [
9+
.package(path: "../"),
10+
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")),
11+
]
12+
)
13+
14+
// Benchmark of WishYouWereFast
15+
package.targets += [
16+
.executableTarget(
17+
name: "WishYouWereFast",
18+
dependencies: [
19+
.product(name: "WasmKit", package: "WasmKit"),
20+
.product(name: "WasmKitWASI", package: "WasmKit"),
21+
.product(name: "Benchmark", package: "package-benchmark"),
22+
],
23+
path: "Benchmarks/WishYouWereFast",
24+
plugins: [
25+
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
26+
]
27+
),
28+
]
29+
30+
// Benchmark of MicroBench
31+
package.targets += [
32+
.executableTarget(
33+
name: "MicroBench",
34+
dependencies: [
35+
.product(name: "WAT", package: "WasmKit"),
36+
.product(name: "WasmKit", package: "WasmKit"),
37+
.product(name: "WasmKitWASI", package: "WasmKit"),
38+
.product(name: "Benchmark", package: "package-benchmark"),
39+
],
40+
path: "Benchmarks/MicroBench",
41+
plugins: [
42+
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
43+
]
44+
),
45+
]
46+
47+
// Benchmark of MacroPlugin
48+
package.targets += [
49+
.executableTarget(
50+
name: "MacroPlugin",
51+
dependencies: [
52+
.product(name: "WasmKit", package: "WasmKit"),
53+
.product(name: "WasmKitWASI", package: "WasmKit"),
54+
.product(name: "Benchmark", package: "package-benchmark"),
55+
],
56+
path: "Benchmarks/MacroPlugin",
57+
plugins: [
58+
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
59+
]
60+
),
61+
]

Vendor/checkout-dependency

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,16 @@ class CheckoutDependency:
3434
print(f"Checking out '{dependency_name}' to {dependency['revision']}")
3535
subprocess.run(["git", "-C", dependency_path, "checkout", dependency["revision"]], check=True)
3636

37+
def checkout_category(self, category):
38+
for dependency_name, dependency in self.dependencies.items():
39+
if category in dependency.get("categories", []):
40+
self.checkout(dependency_name)
41+
3742
def checkout_all(self):
3843
for dependency_name in self.dependencies.keys():
3944
self.checkout(dependency_name)
4045

46+
4147
def main():
4248
import argparse
4349
dependencies_file = os.path.join(os.path.dirname(__file__), "dependencies.json")
@@ -46,13 +52,22 @@ def main():
4652
parser = argparse.ArgumentParser(description="Checkout dependency repositories")
4753
available_dependencies = ", ".join(checkout_dependency.dependencies.keys())
4854
parser.add_argument("names", nargs="*", help=f"Available dependencies: {available_dependencies}")
55+
parser.add_argument("--all", action="store_true", help="Checkout all dependencies")
56+
parser.add_argument("--category", action="append", dest="categories",
57+
default=["default"],
58+
help="Checkout dependencies by category")
59+
4960
args = parser.parse_args()
5061

5162
if args.names:
5263
for dependency_name in args.names:
5364
checkout_dependency.checkout(dependency_name)
54-
else:
65+
elif args.all:
5566
checkout_dependency.checkout_all()
67+
elif args.categories:
68+
for category in args.categories:
69+
checkout_dependency.checkout_category(category)
70+
5671

5772
if __name__ == "__main__":
5873
main()

0 commit comments

Comments
 (0)