From 0332cc815e5300c5e34728119b63f0d4cc22b41f Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Mon, 2 Dec 2024 10:36:09 -0500 Subject: [PATCH] Fix tests and benchmarks in GraalVM Native Images --- .github/workflows/kt-bench.yml | 36 +++++++++- .github/workflows/kt.yml | 31 ++++---- kt/README.md | 22 ++++++ kt/graalvm/build.gradle.kts | 72 ++++++++++++++----- .../graalvm/native-image.properties | 1 + .../graalvm/native-image.properties | 1 + kt/gradle.properties | 2 +- 7 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 kt/graalvm/src/benchmark/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties create mode 100644 kt/graalvm/src/test/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties diff --git a/.github/workflows/kt-bench.yml b/.github/workflows/kt-bench.yml index ffb9a266..37e4dfbc 100644 --- a/.github/workflows/kt-bench.yml +++ b/.github/workflows/kt-bench.yml @@ -74,8 +74,34 @@ jobs: name: ${{ matrix.target }}-benchmarks path: kt/aoc2024-exe/build/reports/benchmarks + graalvm: + needs: [ assemble, get-inputs ] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: inputs + path: inputs + - uses: graalvm/setup-graalvm@v1 + with: + java-version: 21 + github-token: ${{ secrets.GITHUB_TOKEN }} + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + - run: ./gradlew --no-configuration-cache -Pagent nativeBenchmarkRun + working-directory: kt + env: + AOC2024_DATADIR: ${{ github.workspace }}/inputs + - uses: actions/upload-artifact@v4 + with: + name: graalvm-benchmarks + path: kt/graalvm/build/reports/benchmarks + docs: - needs: [ jmh-visualizer, build ] + needs: [ jmh-visualizer, build, graalvm ] runs-on: ubuntu-latest steps: @@ -97,13 +123,17 @@ jobs: with: name: linuxX64-benchmarks path: benchmarks + - uses: actions/download-artifact@v4 + with: + name: graalvm-benchmarks + path: benchmarks - run: rm -rf jmh-visualizer - run: unzip -d jmh-visualizer jmh-visualizer.zip - name: Create provided.js run: | - shopt -s failglob + shopt -s failglob globstar names=() jsonargs=() - for file in benchmarks/main/*/*.json; do + for file in benchmarks/**/*.json; do name=${file##*/} name=${name%.*} name=${name%Bench} diff --git a/.github/workflows/kt.yml b/.github/workflows/kt.yml index b3a76842..9e493c2b 100644 --- a/.github/workflows/kt.yml +++ b/.github/workflows/kt.yml @@ -25,23 +25,13 @@ jobs: with: distribution: temurin java-version: 21 - - uses: graalvm/setup-graalvm@v1 - with: - java-version: 21 - github-token: ${{ secrets.GITHUB_TOKEN }} - set-java-home: false - components: native-image - uses: gradle/actions/setup-gradle@v4 - - run: ./gradlew build nativeCompile distZip + - run: ./gradlew build distZip working-directory: kt - uses: actions/upload-artifact@v4 with: name: aoc2024-exe path: kt/aoc2024-exe/build/distributions/*.zip - - uses: actions/upload-artifact@v4 - with: - name: aoc2024-native - path: kt/graalvm/build/native/nativeCompile/* - uses: actions/upload-artifact@v4 with: name: aoc2024-kexe @@ -57,6 +47,23 @@ jobs: name: aoc2024-js path: kt/build/js/packages/aoc2024-aoc2024-exe/kotlin/* + graalvm: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: graalvm/setup-graalvm@v1 + with: + java-version: 21 + github-token: ${{ secrets.GITHUB_TOKEN }} + - uses: gradle/actions/setup-gradle@v4 + - run: ./gradlew --no-configuration-cache nativeTest nativeCompile + working-directory: kt + - uses: actions/upload-artifact@v4 + with: + name: aoc2024-native + path: kt/graalvm/build/native/nativeCompile/* + run-jvm: needs: [ get-inputs, build ] runs-on: ubuntu-latest @@ -79,7 +86,7 @@ jobs: AOC2024_DATADIR: inputs run-graalvm: - needs: [ get-inputs, build ] + needs: [ get-inputs, graalvm ] runs-on: ubuntu-latest steps: diff --git a/kt/README.md b/kt/README.md index cf8e9eac..1d45887f 100644 --- a/kt/README.md +++ b/kt/README.md @@ -26,3 +26,25 @@ Run all checks, including [Detekt](https://detekt.github.io/) static code analys ```sh ./gradlew check ``` + +## [GraalVM](https://www.graalvm.org/) + +Run the test suite as a GraalVM Native Image: + +```sh +export GRAALVM_HOME=... +./gradlew --no-configuration-cache :graalvm:nativeTest +``` + +Run [JMH](https://openjdk.java.net/projects/code-tools/jmh/) benchmarks as a GraalVM Native Image: + +```sh +export GRAALVM_HOME=... +./gradlew --no-configuration-cache -Pagent :graalvm:nativeBenchmarkRun +``` + +Print solutions for the inputs provided in local data files as a GraalVM Native Image: + +```sh +./gradlew --no-configuration-cache :graalvm:nativeRun +``` diff --git a/kt/graalvm/build.gradle.kts b/kt/graalvm/build.gradle.kts index f0e99b66..fbe64871 100644 --- a/kt/graalvm/build.gradle.kts +++ b/kt/graalvm/build.gradle.kts @@ -1,3 +1,8 @@ +import org.graalvm.buildtools.gradle.internal.agent.AgentConfigurationFactory +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.function.Predicate + plugins { application alias(libs.plugins.native.image) @@ -7,7 +12,53 @@ application { mainClass.set("com.github.ephemient.aoc2024.exe.Main") } +val benchmarkDir = layout.buildDirectory + .dir( + "reports/benchmarks/main/" + + LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME).replace(':', '.') + ) + val benchmark by sourceSets.creating +val benchmarkRun by tasks.registering(JavaExec::class) { + System.getenv("GRAALVM_HOME")?.ifEmpty { null }?.let { setExecutable("$it/bin/java") } + mainClass = "org.openjdk.jmh.Main" + classpath(benchmark.output, benchmark.runtimeClasspath) + args("-f", 0, "-wi", 1, "-w", "0s", "-r", "0s", "-bm", "avgt", "-tu", "us", "-i", 1) + args("-rf", "json", "-rff", "/dev/null") + outputs.dir(AgentConfigurationFactory.getAgentOutputDirectoryForTask(layout, name)) +} +val syncBenchmarkRunMetadata by tasks.registering(Sync::class) { + into(layout.buildDirectory.dir("generated/resources/benchmark")) + from(benchmarkRun) { + into("META-INF/native-image/com.github.ephemient.aoc2024/benchmark") + } +} +graalvmNative { + binaries { + getByName("main") { + imageName = "aoc2024-native" + } + create("benchmark") { + imageName = "aoc2024-native-benchmark" + mainClass = "org.openjdk.jmh.Main" + classpath(syncBenchmarkRunMetadata, benchmark.output, benchmark.runtimeClasspath) + runtimeArgs("-f", 0, "-wi", 1, "-w", "1s", "-r", "1s", "-bm", "avgt", "-tu", "us") + val benchmarkFile = benchmarkDir.get().file("graalvmBench.json") + runtimeArgs("-rf", "json", "-rff", benchmarkFile.asFile) + findProperty("benchmarkInclude")?.let { runtimeArgs("-e", it) } + findProperty("benchmarkExclude")?.let { runtimeArgs(it) } + } + } + agent { + tasksToInstrumentPredicate.set(Predicate { it.name == "benchmarkRun" }) + } +} +tasks.named("nativeBenchmarkRun") { + val benchmarkDir = benchmarkDir.get() + outputs.file(benchmarkDir.file("graalvmBench.json")) + doFirst { benchmarkDir.asFile.mkdirs() } +} + val externalTestClasses = configurations.dependencyScope("externalTestClasses") val externalTestClasspath = configurations.resolvable("externalTestClasspath") { extendsFrom(externalTestClasses.get()) @@ -22,6 +73,10 @@ val externalTestClasspath = configurations.resolvable("externalTestClasspath") { configurations.testImplementation { extendsFrom(externalTestClasses.get()) } +tasks.named("test") { + testClassesDirs = files(testClassesDirs, externalTestClasspath.get()) + useJUnitPlatform() +} dependencies { implementation(projects.aoc2024Exe) @@ -37,20 +92,3 @@ dependencies { } } } - -graalvmNative { - binaries { - getByName("main") { - imageName = "aoc2024-native" - } - create("benchmark") { - mainClass = "org.openjdk.jmh.Main" - classpath(benchmark.runtimeClasspath) - } - } -} - -tasks.withType().configureEach { - testClassesDirs = files(testClassesDirs, externalTestClasspath.get()) - useJUnitPlatform() -} diff --git a/kt/graalvm/src/benchmark/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties b/kt/graalvm/src/benchmark/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties new file mode 100644 index 00000000..5a7339c4 --- /dev/null +++ b/kt/graalvm/src/benchmark/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=org.openjdk.jmh.infra,org.openjdk.jmh.util.Utils,org.openjdk.jmh.runner.InfraControl,org.openjdk.jmh.runner.InfraControlL0,org.openjdk.jmh.runner.InfraControlL1,org.openjdk.jmh.runner.InfraControlL2,org.openjdk.jmh.runner.InfraControlL3,org.openjdk.jmh.runner.InfraControlL4 diff --git a/kt/graalvm/src/test/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties b/kt/graalvm/src/test/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties new file mode 100644 index 00000000..443d8738 --- /dev/null +++ b/kt/graalvm/src/test/resources/META-INF/native-image/com.github.ephemient.aoc22024/graalvm/native-image.properties @@ -0,0 +1 @@ +Args = --initialize-at-build-time=kotlin.annotation.AnnotationRetention,kotlin.annotation.AnnotationTarget diff --git a/kt/gradle.properties b/kt/gradle.properties index be68e385..123fca04 100644 --- a/kt/gradle.properties +++ b/kt/gradle.properties @@ -1,5 +1,5 @@ kotlin.code.style=official org.gradle.caching=true -#org.gradle.configuration-cache=true +org.gradle.configuration-cache=true org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.parallel=true