Skip to content

Commit 70fb530

Browse files
authored
Merge pull request #134 from helmetcrest/add-placeholder-benchmarks
2 parents d1e4b8f + 80f91a0 commit 70fb530

File tree

11 files changed

+240
-68
lines changed

11 files changed

+240
-68
lines changed

.github/workflows/pull_request.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
steps:
3030
- uses: actions/checkout@v4
3131
- name: Install System Dependencies
32-
run: apt-get -qq update && apt-get -qq install -y make curl wget
32+
run: apt-get -qq update && apt-get -qq install -y make curl wget libjemalloc2 libjemalloc-dev
3333
- name: Cache JDK
3434
id: cache-jdk
3535
uses: actions/cache@v4
@@ -69,6 +69,9 @@ jobs:
6969
run: |
7070
./gradlew build -x test --no-daemon # just build
7171
./gradlew build --info --no-daemon
72+
- name: Gradle build (benchmarks)
73+
run: |
74+
./gradlew compileJmh --info --no-daemon
7275
7376
test-swift:
7477
name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }})
@@ -86,7 +89,7 @@ jobs:
8689
steps:
8790
- uses: actions/checkout@v4
8891
- name: Install System Dependencies
89-
run: apt-get -qq update && apt-get -qq install -y make curl wget
92+
run: apt-get -qq update && apt-get -qq install -y make curl wget libjemalloc2 libjemalloc-dev
9093
- name: Cache JDK
9194
id: cache-jdk
9295
uses: actions/cache@v4
@@ -130,3 +133,6 @@ jobs:
130133
- name: Build (Swift) Sample Apps
131134
run: |
132135
find Samples/ -name Package.swift -maxdepth 2 -exec swift build --package-path $(dirname {}) \;;
136+
# TODO: Benchmark compile crashes in CI, enable when nightly toolchains in better shape.
137+
# - name: Build (Swift) Benchmarks
138+
# run: "swift package --package-path Benchmarks/ benchmark list"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Benchmark
16+
import Foundation
17+
import JavaKit
18+
import JavaKitNetwork
19+
20+
@MainActor let benchmarks = {
21+
var jvm: JavaVirtualMachine {
22+
get throws {
23+
try .shared()
24+
}
25+
}
26+
Benchmark("Simple call to Java library") { benchmark in
27+
for _ in benchmark.scaledIterations {
28+
let environment = try jvm.environment()
29+
30+
let urlConnectionClass = try JavaClass<URLConnection>(environment: environment)
31+
blackHole(urlConnectionClass.getDefaultAllowUserInteraction())
32+
}
33+
}
34+
}

Benchmarks/Package.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// swift-tools-version: 6.0
2+
3+
import PackageDescription
4+
5+
import class Foundation.FileManager
6+
import class Foundation.ProcessInfo
7+
8+
// Note: the JAVA_HOME environment variable must be set to point to where
9+
// Java is installed, e.g.,
10+
// Library/Java/JavaVirtualMachines/openjdk-21.jdk/Contents/Home.
11+
func findJavaHome() -> String {
12+
if let home = ProcessInfo.processInfo.environment["JAVA_HOME"] {
13+
return home
14+
}
15+
16+
// This is a workaround for envs (some IDEs) which have trouble with
17+
// picking up env variables during the build process
18+
let path = "\(FileManager.default.homeDirectoryForCurrentUser.path()).java_home"
19+
if let home = try? String(contentsOfFile: path, encoding: .utf8) {
20+
if let lastChar = home.last, lastChar.isNewline {
21+
return String(home.dropLast())
22+
}
23+
24+
return home
25+
}
26+
27+
fatalError("Please set the JAVA_HOME environment variable to point to where Java is installed.")
28+
}
29+
let javaHome = findJavaHome()
30+
31+
let javaIncludePath = "\(javaHome)/include"
32+
#if os(Linux)
33+
let javaPlatformIncludePath = "\(javaIncludePath)/linux"
34+
#elseif os(macOS)
35+
let javaPlatformIncludePath = "\(javaIncludePath)/darwin"
36+
#else
37+
// TODO: Handle windows as well
38+
#error("Currently only macOS and Linux platforms are supported, this may change in the future.")
39+
#endif
40+
41+
let package = Package(
42+
name: "benchmarks",
43+
platforms: [
44+
.macOS("15.03")
45+
],
46+
dependencies: [
47+
.package(path: "../"),
48+
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")),
49+
],
50+
targets: [
51+
.executableTarget(
52+
name: "JavaApiCallBenchmarks",
53+
dependencies: [
54+
.product(name: "JavaRuntime", package: "swift-java"),
55+
.product(name: "JavaKit", package: "swift-java"),
56+
.product(name: "JavaKitNetwork", package: "swift-java"),
57+
.product(name: "Benchmark", package: "package-benchmark"),
58+
],
59+
path: "Benchmarks/JavaApiCallBenchmarks",
60+
swiftSettings: [
61+
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"]),
62+
.swiftLanguageMode(.v5),
63+
],
64+
plugins: [
65+
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
66+
]
67+
)
68+
]
69+
)

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,12 @@ jextract-generate: jextract-swift generate-JExtract-interface-files
133133
swift run jextract-swift \
134134
--package-name com.example.swift.generated \
135135
--swift-module ExampleSwiftLibrary \
136-
--output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/src/generated/java \
136+
--output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/build/generated/sources/jextract/main \
137137
$(BUILD_DIR)/jextract/ExampleSwiftLibrary/MySwiftLibrary.swiftinterface; \
138138
swift run jextract-swift \
139139
--package-name org.swift.swiftkit.generated \
140140
--swift-module SwiftKitSwift \
141-
--output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/src/generated/java \
141+
--output-directory ${SAMPLES_DIR}/SwiftKitSampleApp/build/generated/sources/jextract/main \
142142
$(BUILD_DIR)/jextract/SwiftKitSwift/SwiftKit.swiftinterface
143143

144144

Package.swift

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,18 @@ let package = Package(
9494

9595
// ==== Plugin for building Java code
9696
.plugin(
97-
name: "JavaCompilerPlugin",
98-
targets: [
99-
"JavaCompilerPlugin"
100-
]
97+
name: "JavaCompilerPlugin",
98+
targets: [
99+
"JavaCompilerPlugin"
100+
]
101101
),
102102

103103
// ==== Plugin for wrapping Java classes in Swift
104104
.plugin(
105-
name: "Java2SwiftPlugin",
106-
targets: [
107-
"Java2SwiftPlugin"
108-
]
105+
name: "Java2SwiftPlugin",
106+
targets: [
107+
"Java2SwiftPlugin"
108+
]
109109
),
110110

111111
// ==== jextract-swift (extract Java accessors from Swift interface files)
@@ -140,6 +140,7 @@ let package = Package(
140140
.package(url: "https://github.com/swiftlang/swift-syntax.git", branch: "main"),
141141
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"),
142142
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(from: "1.1.0")),
143+
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")),
143144
],
144145
targets: [
145146
.macro(
@@ -225,16 +226,16 @@ let package = Package(
225226
]
226227
),
227228
.plugin(
228-
name: "JavaCompilerPlugin",
229-
capability: .buildTool()
229+
name: "JavaCompilerPlugin",
230+
capability: .buildTool()
230231
),
231232

232233
.plugin(
233-
name: "Java2SwiftPlugin",
234-
capability: .buildTool(),
235-
dependencies: [
236-
"Java2Swift"
237-
]
234+
name: "Java2SwiftPlugin",
235+
capability: .buildTool(),
236+
dependencies: [
237+
"Java2Swift"
238+
]
238239
),
239240

240241
.target(
@@ -373,6 +374,6 @@ let package = Package(
373374
.swiftLanguageMode(.v5),
374375
.unsafeFlags(["-I\(javaIncludePath)", "-I\(javaPlatformIncludePath)"])
375376
]
376-
),
377+
)
377378
]
378379
)

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,28 @@ To run a simple example app showcasing the jextract (Java calling Swift) approac
111111
This will also generate the necessary sources (by invoking jextract, extracting the `Sources/ExampleSwiftLibrary`)
112112
and generating Java sources in `src/generated/java`.
113113

114+
## Benchmarks
115+
116+
You can run Swift [ordo-one/package-benchmark](https://github.com/ordo-one/package-benchmark) and OpenJDK [JMH](https://github.com/openjdk/jmh) benchmarks in this project.
117+
118+
Swift benchmarks are located under `Benchmarks/` and JMH benchmarks are currently part of the SwiftKit sample project: `Samples/SwiftKitSampleApp/src/jmh` because they depend on generated sources from the sample.
119+
120+
To run **Swift benchmarks** you can:
121+
122+
```bash
123+
cd Benchmarks
124+
swift package benchmark
125+
```
126+
127+
In order to run JMH benchmarks you can:
128+
129+
```bash
130+
cd Samples/SwiftKitSampleApp
131+
gradle jmh
132+
```
133+
134+
Please read documentation of both performance testing tools and understand that results must be interpreted and not just taken at face value. Benchmarking is tricky and environment sensitive task, so please be careful when constructing and reading benchmarks and their results. If in doubt, please reach out on the forums.
135+
114136
## User Guide
115137

116138
More details about the project and how it can be used are available in [USER_GUIDE.md](USER_GUIDE.md)

Samples/SwiftKitSampleApp/build.gradle

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.swift.swiftkit.gradle.BuildUtils
1616

1717
plugins {
1818
id("build-logic.java-application-conventions")
19+
id("me.champeau.jmh") version "0.7.2"
1920
}
2021

2122
group = "org.swift.swiftkit"
@@ -31,43 +32,38 @@ java {
3132
}
3233
}
3334

34-
sourceSets {
35-
generated {
36-
java.srcDir "${buildDir}/generated/src/java/"
37-
}
3835

39-
// Make the 'main' and 'test' source sets depend on the generated sources
40-
main {
41-
compileClasspath += sourceSets.generated.output
42-
runtimeClasspath += sourceSets.generated.output
43-
}
44-
test {
45-
compileClasspath += sourceSets.main.output
46-
runtimeClasspath += sourceSets.main.output
36+
def jextract = tasks.register("jextract", Exec) {
37+
description = "Extracts Java accessor sources using jextract"
38+
39+
outputs.dir(layout.buildDirectory.dir("generated/sources/jextract/main"))
40+
inputs.dir("$rootDir/Sources/ExampleSwiftLibrary") // monitored library
41+
42+
// any changes in the source generator sources also mean the resulting output might change
43+
inputs.dir("$rootDir/Sources/JExtractSwift")
44+
inputs.dir("$rootDir/Sources/JExtractSwiftTool")
4745

48-
compileClasspath += sourceSets.generated.output
49-
runtimeClasspath += sourceSets.generated.output
46+
workingDir = rootDir
47+
commandLine "make"
48+
args "jextract-generate"
49+
}
50+
51+
sourceSets {
52+
main {
53+
java {
54+
srcDir(jextract)
55+
}
5056
}
5157
}
5258

5359
dependencies {
5460
implementation(project(':SwiftKit'))
55-
generatedImplementation(project(':SwiftKit'))
5661

5762
testImplementation(platform("org.junit:junit-bom:5.10.0"))
5863
testImplementation("org.junit.jupiter:junit-jupiter")
5964
}
6065

61-
configurations {
62-
generatedImplementation.extendsFrom(mainImplementation)
63-
generatedRuntimeOnly.extendsFrom(mainRuntimeOnly)
64-
}
65-
66-
tasks.named("compileJava").configure {
67-
dependsOn("jextract")
68-
}
69-
70-
tasks.test {
66+
tasks.named('test', Test) {
7167
useJUnitPlatform()
7268
}
7369

@@ -84,27 +80,15 @@ application {
8480
"--enable-native-access=ALL-UNNAMED",
8581

8682
// Include the library paths where our dylibs are that we want to load and call
87-
"-Djava.library.path=" + BuildUtils.javaLibraryPaths().join(":"),
83+
"-Djava.library.path=" + BuildUtils.javaLibraryPaths(rootDir).join(":"),
8884

8985
// Enable tracing downcalls (to Swift)
9086
"-Djextract.trace.downcalls=true"
9187
]
9288
}
9389

94-
task jextract(type: Exec) {
95-
description = "Extracts Java accessor sources using jextract"
96-
outputs.dir(layout.buildDirectory.dir("generated"))
97-
inputs.dir("$rootDir/Sources/ExampleSwiftLibrary") // monitored library
98-
99-
// any changes in the source generator sources also mean the resulting output might change
100-
inputs.dir("$rootDir/Sources/JExtractSwift")
101-
inputs.dir("$rootDir/Sources/JExtractSwiftTool")
102-
103-
workingDir = rootDir
104-
commandLine "make"
105-
args "jextract-generate"
106-
}
107-
108-
tasks.named("compileGeneratedJava").configure {
109-
dependsOn jextract
90+
jmh {
91+
jvmArgsAppend = [
92+
"-Djava.library.path=" + BuildUtils.javaLibraryPaths(rootDir).join(":"),
93+
]
11094
}

0 commit comments

Comments
 (0)