Skip to content

JMH Benchmark and passing java String to JExtracted Swift #203

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 4 commits into from
Dec 17, 2024
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
2 changes: 2 additions & 0 deletions Samples/SwiftKitSampleApp/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ let package = Package(
.target(
name: "MySwiftLibrary",
dependencies: [
.product(name: "JavaKit", package: "swift-java"),
.product(name: "JavaRuntime", package: "swift-java"),
.product(name: "SwiftKitSwift", package: "swift-java"),
],
exclude: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public func globalTakeInt(i: Int) {
p("i:\(i)")
}

public func globalMakeInt() -> Int {
return 42
}

public func globalWriteString(string: String) -> Int {
return string.count
}

public func globalTakeIntInt(i: Int, j: Int) {
p("i:\(i), j:\(j)")
}
Expand Down Expand Up @@ -79,6 +87,11 @@ public class MySwiftClass {
return 12
}

public func writeString(string: String) -> Int {
p("echo -> \(string)")
return string.count
}

public func makeRandomIntMethod() -> Int {
return Int.random(in: 1..<256)
}
Expand All @@ -87,8 +100,8 @@ public class MySwiftClass {
// ==== Internal helpers

private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
print("[swift][\(file):\(line)](\(function)) \(msg)")
fflush(stdout)
// print("[swift][\(file):\(line)](\(function)) \(msg)")
// fflush(stdout)
}

#if os(Linux)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import JavaKit
import JavaRuntime

@JavaClass("com.example.swift.HelloJava2Swift")
open class HelloJava2Swift: JavaObject {
}

extension JavaClass<HelloJava2Swift> {
}

/// Describes the Java `native` methods for ``HelloJava2Swift``.
///
/// To implement all of the `native` methods for HelloSwift in Swift,
/// extend HelloSwift to conform to this protocol and mark each
/// implementation of the protocol requirement with `@JavaMethod`.
protocol HelloJava2SwiftNativeMethods {
func jniWriteString(_ message: String) -> Int32
func jniGetInt() -> Int32
}

@JavaImplementation("com.example.swift.HelloJava2Swift")
extension HelloJava2Swift: HelloJava2SwiftNativeMethods {
@JavaMethod
func jniWriteString(_ message: String) -> Int32 {
return Int32(message.count)
}

@JavaMethod
func jniGetInt() -> Int32 {
return 12
}
}
11 changes: 11 additions & 0 deletions Samples/SwiftKitSampleApp/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,21 @@ application {
]
}

String jmhIncludes = findProperty("jmhIncludes")

jmh {
if (jmhIncludes != null) {
includes = [jmhIncludes]
}

jvmArgsAppend = [
"--enable-native-access=ALL-UNNAMED",

"-Djava.library.path=" +
(BuildUtils.javaLibraryPaths(rootDir) +
BuildUtils.javaLibraryPaths(project.projectDir)).join(":"),

// Enable tracing downcalls (to Swift)
"-Djextract.trace.downcalls=false"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,51 @@

package org.swift.swiftkit;

import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;
import com.example.swift.HelloJava2Swift;
import com.example.swift.MySwiftLibrary;
import org.openjdk.jmh.annotations.*;

import com.example.swift.MySwiftClass;

@SuppressWarnings("unused")
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" })
public class JavaToSwiftBenchmark {

@State(Scope.Benchmark)
public static class BenchmarkState {
ClosableSwiftArena arena;
MySwiftClass obj;

@Setup(Level.Trial)
public void beforeALl() {
System.loadLibrary("swiftCore");
System.loadLibrary("ExampleSwiftLibrary");

// Tune down debug statements so they don't fill up stdout
System.setProperty("jextract.trace.downcalls", "false");
public void beforeAll() {
arena = SwiftArena.ofConfined();
obj = new MySwiftClass(arena, 1, 2);
}

obj = new MySwiftClass(1, 2);
@TearDown(Level.Trial)
public void afterAll() {
arena.close();
}
}

@Benchmark @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)
public void simpleSwiftApiCall(BenchmarkState state, Blackhole blackhole) {
blackhole.consume(state.obj.makeRandomIntMethod());
@Benchmark
public long jextract_getInt_ffm(BenchmarkState state) {
return MySwiftLibrary.globalMakeInt();
}

@Benchmark
public long getInt_global_jni(BenchmarkState state) {
return HelloJava2Swift.jniGetInt();
}

@Benchmark
public long getInt_member_ffi(BenchmarkState state) {
return state.obj.makeIntMethod();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

package org.swift.swiftkit;

import com.example.swift.HelloJava2Swift;
import com.example.swift.MySwiftClass;
import com.example.swift.MySwiftLibrary;
import org.openjdk.jmh.annotations.*;

import java.lang.foreign.Arena;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(value = 2, jvmArgsAppend = {"--enable-native-access=ALL-UNNAMED"})
public class StringPassingBenchmark {

@Param({
"5",
"10",
"100",
"200"
})
public int stringLen;
public String string;

ClosableSwiftArena arena;
MySwiftClass obj;

@Setup(Level.Trial)
public void beforeAll() {
arena = SwiftArena.ofConfined();
obj = new MySwiftClass(arena, 1, 2);
string = makeString(stringLen);
}

@TearDown(Level.Trial)
public void afterAll() {
arena.close();
}

@Benchmark
public long writeString_global_fmm() {
return MySwiftLibrary.globalWriteString(string);
}

@Benchmark
public long writeString_global_jni() {
return HelloJava2Swift.jniWriteString(string);
}

@Benchmark
public long writeString_baseline() {
return string.length();
}

static String makeString(int size) {
var text =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut in augue ullamcorper, mattis lacus tincidunt, " +
"accumsan massa. Morbi gravida purus ut porttitor iaculis. Vestibulum lacinia, mi in tincidunt hendrerit," +
"lectus est placerat magna, vitae vestibulum nulla ligula at massa. Pellentesque nibh quam, pulvinar eu " +
"nunc congue, molestie molestie augue. Nam convallis consectetur velit, at dictum risus ullamcorper iaculis. " +
"Vestibulum lacinia nisi in elit consectetur vulputate. Praesent id odio tristique, tincidunt arcu et, convallis velit. " +
"Sed vitae pulvinar arcu. Curabitur euismod mattis dui in suscipit. Morbi aliquet facilisis vulputate. Phasellus " +
"non lectus dapibus, semper magna eu, aliquet magna. Suspendisse vel enim at augue luctus gravida. Suspendisse " +
"venenatis justo non accumsan sollicitudin. Suspendisse vitae ornare odio, id blandit nibh. Nulla facilisi. " +
"Nulla nulla orci, finibus nec luctus et, faucibus et ligula.";
return text.substring(0, size);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@ static void examples() {

System.out.println("DONE.");
}

public static native long jniWriteString(String str);
public static native long jniGetInt();

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package com.example.swift;

import com.example.swift.MySwiftLibrary;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
Expand All @@ -27,6 +28,10 @@

public class MySwiftLibraryTest {

static {
System.loadLibrary(MySwiftLibrary.LIB_NAME);
}

@Test
void call_helloWorld() {
MySwiftLibrary.helloWorld();
Expand All @@ -41,6 +46,22 @@ void call_globalTakeInt() {
assertNotNull(MySwiftLibrary.globalTakeInt$address());
}

@Test
void call_writeString_jextract() {
var string = "Hello Swift!";
long reply = MySwiftLibrary.globalWriteString(string);

assertEquals(string.length(), reply);
}

@Test
void call_writeString_jni() {
var string = "Hello Swift!";
long reply = HelloJava2Swift.jniWriteString(string);

assertEquals(string.length(), reply);
}

@Test
@Disabled("Upcalls not yet implemented in new scheme")
@SuppressWarnings({"Convert2Lambda", "Convert2MethodRef"})
Expand Down
12 changes: 12 additions & 0 deletions Sources/JExtractSwift/ImportedDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public struct ImportedParam {
var effectiveName: String? {
firstName ?? secondName
}

var effectiveValueName: String {
secondName ?? firstName ?? "_"
}

// The Swift type as-is from the swift interface
var swiftType: String {
Expand All @@ -113,6 +117,14 @@ extension ImportedParam {
}
}

public enum ParameterVariant {
/// Used when declaring the "Swift thunks" we call through into Swift.
///
/// Some types need to be represented as raw pointers and recovered into
/// Swift types inside the thunks when we do this.
case cDeclThunk
}

// TODO: this is used in different contexts and needs a cleanup
// Perhaps this is "which parameter passing style"?
public enum SelfParameterVariant {
Expand Down
5 changes: 5 additions & 0 deletions Sources/JExtractSwift/JavaTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ extension JavaType {
.class(package: "java.lang.foreign", name: "MemorySegment")
}

/// The description of the type java.lang.String.
static var javaLangString: JavaType {
.class(package: "java.lang", name: "String")
}

/// The description of the type java.lang.Runnable.
static var javaLangRunnable: JavaType {
.class(package: "java.lang", name: "Runnable")
Expand Down
Loading
Loading