Skip to content

Build benchmarks with PackageToJS #343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .github/workflows/perf.yml

This file was deleted.

20 changes: 20 additions & 0 deletions Benchmarks/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// swift-tools-version: 6.0

import PackageDescription

let package = Package(
name: "Benchmarks",
dependencies: [
.package(path: "../")
],
targets: [
.executableTarget(
name: "Benchmarks",
dependencies: ["JavaScriptKit"],
exclude: ["Generated/JavaScript", "bridge.d.ts"],
swiftSettings: [
.enableExperimentalFeature("Extern")
]
)
]
)
30 changes: 30 additions & 0 deletions Benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# JavaScriptKit Benchmarks

This directory contains performance benchmarks for JavaScriptKit.

## Building Benchmarks

Before running the benchmarks, you need to build the test suite:

```bash
JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1 swift package --swift-sdk $SWIFT_SDK_ID js -c release
```

## Running Benchmarks

```bash
# Run with default settings
node run.js

# Save results to a JSON file
node run.js --output=results.json

# Specify number of iterations
node run.js --runs=20

# Run in adaptive mode until results stabilize
node run.js --adaptive --output=stable-results.json

# Run benchmarks and compare with previous results
node run.js --baseline=previous-results.json
```
78 changes: 78 additions & 0 deletions Benchmarks/Sources/Benchmarks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import JavaScriptKit

class Benchmark {
init(_ title: String) {
self.title = title
}

let title: String

func testSuite(_ name: String, _ body: @escaping () -> Void) {
let jsBody = JSClosure { arguments -> JSValue in
body()
return .undefined
}
benchmarkRunner("\(title)/\(name)", jsBody)
}
}

@JS func run() {

let call = Benchmark("Call")

call.testSuite("JavaScript function call through Wasm import") {
for _ in 0..<20_000_000 {
benchmarkHelperNoop()
}
}

call.testSuite("JavaScript function call through Wasm import with int") {
for _ in 0..<10_000_000 {
benchmarkHelperNoopWithNumber(42)
}
}

let propertyAccess = Benchmark("Property access")

do {
let swiftInt: Double = 42
let object = JSObject()
object.jsNumber = JSValue.number(swiftInt)
propertyAccess.testSuite("Write Number") {
for _ in 0..<1_000_000 {
object.jsNumber = JSValue.number(swiftInt)
}
}
}

do {
let object = JSObject()
object.jsNumber = JSValue.number(42)
propertyAccess.testSuite("Read Number") {
for _ in 0..<1_000_000 {
_ = object.jsNumber.number
}
}
}

do {
let swiftString = "Hello, world"
let object = JSObject()
object.jsString = swiftString.jsValue
propertyAccess.testSuite("Write String") {
for _ in 0..<1_000_000 {
object.jsString = swiftString.jsValue
}
}
}

do {
let object = JSObject()
object.jsString = JSValue.string("Hello, world")
propertyAccess.testSuite("Read String") {
for _ in 0..<1_000_000 {
_ = object.jsString.string
}
}
}
}
15 changes: 15 additions & 0 deletions Benchmarks/Sources/Generated/ExportSwift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.
@_extern(wasm, module: "bjs", name: "return_string")
private func _return_string(_ ptr: UnsafePointer<UInt8>?, _ len: Int32)
@_extern(wasm, module: "bjs", name: "init_memory")
private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer<UInt8>?)

@_expose(wasm, "bjs_main")
@_cdecl("bjs_main")
public func _bjs_main() -> Void {
main()
}
38 changes: 38 additions & 0 deletions Benchmarks/Sources/Generated/ImportTS.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

@_spi(JSObject_id) import JavaScriptKit

@_extern(wasm, module: "bjs", name: "make_jsstring")
private func _make_jsstring(_ ptr: UnsafePointer<UInt8>?, _ len: Int32) -> Int32

@_extern(wasm, module: "bjs", name: "init_memory_with_result")
private func _init_memory_with_result(_ ptr: UnsafePointer<UInt8>?, _ len: Int32)

@_extern(wasm, module: "bjs", name: "free_jsobject")
private func _free_jsobject(_ ptr: Int32) -> Void

func benchmarkHelperNoop() -> Void {
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoop")
func bjs_benchmarkHelperNoop() -> Void
bjs_benchmarkHelperNoop()
}

func benchmarkHelperNoopWithNumber(_ n: Double) -> Void {
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkHelperNoopWithNumber")
func bjs_benchmarkHelperNoopWithNumber(_ n: Float64) -> Void
bjs_benchmarkHelperNoopWithNumber(n)
}

func benchmarkRunner(_ name: String, _ body: JSObject) -> Void {
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkRunner")
func bjs_benchmarkRunner(_ name: Int32, _ body: Int32) -> Void
var name = name
let nameId = name.withUTF8 { b in
_make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
}
bjs_benchmarkRunner(nameId, Int32(bitPattern: body.id))
}
19 changes: 19 additions & 0 deletions Benchmarks/Sources/Generated/JavaScript/ExportSwift.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"classes" : [

],
"functions" : [
{
"abiName" : "bjs_main",
"name" : "main",
"parameters" : [

],
"returnType" : {
"void" : {

}
}
}
]
}
67 changes: 67 additions & 0 deletions Benchmarks/Sources/Generated/JavaScript/ImportTS.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"children" : [
{
"functions" : [
{
"name" : "benchmarkHelperNoop",
"parameters" : [

],
"returnType" : {
"void" : {

}
}
},
{
"name" : "benchmarkHelperNoopWithNumber",
"parameters" : [
{
"name" : "n",
"type" : {
"double" : {

}
}
}
],
"returnType" : {
"void" : {

}
}
},
{
"name" : "benchmarkRunner",
"parameters" : [
{
"name" : "name",
"type" : {
"string" : {

}
}
},
{
"name" : "body",
"type" : {
"jsObject" : {

}
}
}
],
"returnType" : {
"void" : {

}
}
}
],
"types" : [

]
}
],
"moduleName" : "Benchmarks"
}
3 changes: 3 additions & 0 deletions Benchmarks/Sources/bridge.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare function benchmarkHelperNoop(): void;
declare function benchmarkHelperNoopWithNumber(n: number): void;
declare function benchmarkRunner(name: string, body: (n: number) => void): void;
1 change: 1 addition & 0 deletions Benchmarks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "module" }
Loading