Skip to content

Commit

Permalink
Add support for PGO instrumentation (#471)
Browse files Browse the repository at this point in the history
* Add support for PGO instrumentation

This commit adds support for PGO instrumentation. This should be
enabled by adding the `--pgo-instrument` option to the Gradle
command line. When this is done, then the generated binary will
be compiled with PGO instrumentation enabled, and the binary
name will be suffixed with `-instrumented`.

It is possible to run the instrumented binary directly too,
in which case the profile files will be written in the same
directory as the binary.

* Add support for a PGO profiles directory

By convention, the directory is set to `src/pgo-profiles/<binary>`.
For example, for the `main` binary, the directory where to put
PGO profiles would be `src/pgo-profiles/main`. If that directory
is present _and that we're not instrumenting_, then the profile
will be used when compiling with native image.

It is possible to provide multiple profiles in a single directory.

* Remove GraalVM version from workflows

* Add documentation about PGO support

See #457

* Fix JUnit native test

* Make checkstyle happy

* Fix tests

* Temporarily(?) disable testing with config cache

As we're not compatible. Test `org.graalvm.buildtools.gradle.OfficialMetadataRepoFunctionalTest`
throws an incomprehensible error message, in all versions of Gradle I've tested:

```
Configuration cache state could not be cached: field `spec` of `org.gradle.api.internal.tasks.execution.SelfDescribingSpec` bean found in task `:compileJava` of type `org.gradle.api.tasks.compile.JavaCompile`: error writing value of type 'org.gradle.api.internal.tasks.compile.CompilerForkUtils$$Lambda$1235/0x00000008015b1c38'
> Unable to make field private final java.lang.Object[] java.lang.invoke.SerializedLambda.capturedArgs accessible: module java.base does not "opens java.lang.invoke" to unnamed module @3cc98b0c
```

This PR also rewrote some code which fixed other configuration
cache issues which arose _before_ reaching this one.

* Upgrade to JUnit 5.10.0

* Make checkstyle happy

* Fix test

* Restore configuration cache tests

* Update baseline versions for config cache
  • Loading branch information
melix authored Jul 27, 2023
1 parent 7bdab7a commit 421b6b6
Show file tree
Hide file tree
Showing 46 changed files with 338 additions and 611 deletions.
10 changes: 3 additions & 7 deletions .github/actions/prepare-environment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ inputs:
description: 'secrets.GITHUB_TOKEN'
required: false
default: ''
graalvm-version:
description: 'GraalVM version to use'
required: false
default: 'dev'
java-version:
description: 'Java version to use'
required: false
default: '11'
default: '17'
push-access:
description: 'Does this workflow require push access?'
required: false
Expand All @@ -26,13 +22,13 @@ inputs:
runs:
using: "composite"
steps:
- name: "🔧 Install GraalVM ${{ inputs.graalvm-version }} (JDK${{ inputs.java-version }})"
- name: "🔧 Install GraalVM (JDK${{ inputs.java-version }})"
uses: graalvm/setup-graalvm@main
with:
components: 'native-image'
github-token: ${{ inputs.github-token }}
java-version: ${{ inputs.java-version }}
version: ${{ inputs.graalvm-version }}
distribution: 'graalvm'
- name: "🔒 Configure push access"
if: ${{ inputs.push-access == '1' }}
shell: "bash"
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/deploy-documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ latest ]
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-20.04 ]
steps:
- name: "☁ Checkout repository"
Expand All @@ -30,7 +29,6 @@ jobs:
uses: ./.github/actions/prepare-environment
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
push-access: 1
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/deploy-snapshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ latest ]
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-22.04 ]
steps:
- name: "☁ Checkout repository"
Expand All @@ -32,7 +31,6 @@ jobs:
uses: ./.github/actions/prepare-environment
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
push-access: 1
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/test-graalvm-metadata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,14 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ 22.3.0 ]
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-20.04 ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Checkstyle"
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/test-junit-platform-native.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,14 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ 22.3.0 ]
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-20.04 ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Checkstyle"
Expand Down
14 changes: 4 additions & 10 deletions .github/workflows/test-native-gradle-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,14 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ latest ] # dev
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-20.04 ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Unit tests and inspections"
Expand All @@ -54,18 +52,16 @@ jobs:
fail-fast: false
matrix:
gradle-version: ["current", "7.4"]
gradle-config-cache-version: ["current", "7.5.1"]
gradle-config-cache-version: ["current", "8.0.1"]
# Following versions are disabled temporarily in order to speed up PR testing
# "7.3.3", "7.2", "7.1", "6.8.3"
graalvm-version: [ latest ] # dev
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-20.04 ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Check and test the plugin"
Expand All @@ -86,15 +82,13 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ latest ]
java-version: [ 11 ]
java-version: [ 17 ]
os: [ windows-latest ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Check and test the Gradle plugin"
Expand Down
8 changes: 2 additions & 6 deletions .github/workflows/test-native-maven-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,13 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ latest ] # dev
java-version: [ 11 ]
java-version: [ 17 ]
os: [ ubuntu-20.04 ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Check and test the plugin"
Expand All @@ -55,15 +53,13 @@ jobs:
strategy:
fail-fast: false
matrix:
graalvm-version: [ latest ] # dev
java-version: [ 11 ]
java-version: [ 17 ]
os: [ windows-latest ]
steps:
- name: "☁️ Checkout repository"
uses: actions/checkout@v2
- uses: ./.github/actions/prepare-environment
with:
graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Check and test the Maven plugin"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ gradlePlugin.testSourceSets(sourceSets.functionalTest)
configurations.functionalTestImplementation.extendsFrom(configurations.testImplementation)

def graalVm = javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(11))
vendor.set(JvmVendorSpec.matching("GraalVM"))
languageVersion.set(JavaLanguageVersion.of(17))
// vendor.set(JvmVendorSpec.matching("Oracle Corporation"))
}

def fullFunctionalTest = tasks.register("fullFunctionalTest")

['functionalTest', 'configCacheFunctionalTest'].each { baseName ->
["current", "7.4", "7.6.2"].each { gradleVersion ->
["current", "7.4", "7.6.2", "8.0.1"].each { gradleVersion ->
String taskName = gradleVersion == 'current' ? baseName : "gradle${gradleVersion}${baseName.capitalize()}"
// Add a task to run the functional tests
def testTask = tasks.register(taskName, Test) {
Expand All @@ -105,6 +105,14 @@ def fullFunctionalTest = tasks.register("fullFunctionalTest")
javaLauncher.set(graalVm)
if (baseName == 'configCacheFunctionalTest') {
systemProperty('config.cache', 'true')
// These args are required so that we can debug tests which
// run with the configuration cache, see https://github.com/gradle/gradle/issues/25898
jvmArgs = [
'--add-opens=java.base/java.lang=ALL-UNNAMED',
'--add-opens=java.base/java.util=ALL-UNNAMED',
'--add-opens=java.base/java.lang.invoke=ALL-UNNAMED',
'--add-opens=java.base/java.net=ALL-UNNAMED',
]
}
}
fullFunctionalTest.configure {
Expand Down
2 changes: 1 addition & 1 deletion common/junit-platform-native/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ dependencies {

apply from: "gradle/native-image-testing.gradle"

test {
tasks.named('test') {
doFirst {
delete { delete testIdsDir.get().asFile }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,25 @@ public void onLoad(NativeImageConfiguration config) {
"org.junit.jupiter.engine.execution.ConditionEvaluator",
"org.junit.jupiter.engine.execution.ExecutableInvoker",
"org.junit.jupiter.params.provider.EnumSource$Mode",
// new in Junit 5.10
"org.junit.platform.launcher.core.LauncherConfig",
"org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter"
};
for (String className : buildTimeInitializedClasses) {
config.initializeAtBuildTime(className);
}

try {
Class <?> executor = Class.forName("org.junit.jupiter.engine.extension.TimeoutExtension$ExecutorResource");
config.registerAllClassMembersForReflection(executor);
} catch (ClassNotFoundException e) {
debug("Failed to register class for reflection. Reason: %s", e);
String[] registeredForReflection = {
"org.junit.jupiter.engine.extension.TimeoutExtension$ExecutorResource",
"org.junit.jupiter.engine.extension.TimeoutInvocationFactory$SingleThreadExecutorResource"
};
for (String className : registeredForReflection) {
try {
Class <?> executor = Class.forName(className);
config.registerAllClassMembersForReflection(executor);
} catch (ClassNotFoundException e) {
debug("Failed to register class for reflection. Reason: %s", e);
}
}
}

Expand Down
41 changes: 41 additions & 0 deletions docs/src/docs/asciidoc/gradle-plugin.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,47 @@ include::../snippets/gradle/kotlin/build.gradle.kts[tags=include-metadata]

For more advanced configurations you can declare a `org.graalvm.buildtools.gradle.tasks.CollectReachabilityMetadata` task and set the appropriate properties.

[[pgo-support]]
== Profile-guided optimizations

The plugin supports building images with https://www.graalvm.org/latest/reference-manual/native-image/guides/optimize-native-executable-with-pgo/[Profile-Guided Optimizations].

It works in 3 phases:

- the first one consists in generating a binary with instrumentation enabled
- the second phase consists in running the binary in order to gather profiling information
- the third phase consists in compiling the binary with the generated profile

In order to generate a binary with instrumentation enabled, you should run the `nativeCompile` command with the `--pgo-instrument` command line option:

`./gradlew nativeCompile --pgo-instrument`

This will generate a binary under `build/native/nativeCompile` with the `-instrumented` suffix.
You can run the binary to gather profiling data:

[source,bash]
----
$ cd build/native/nativeCompile/
$ ./my-application-instrumented`
----

A `default.iprof` file will be generated once the application is stopped.
Alternatively, you can have Gradle both generate and run the instrumented binary in a single command by running:

`./gradlew nativeCompile --pgo-instrument nativeRun`

In which case the profile will automatically be stored into `build/native/nativeCompile`.

The last phase consists in copying the generated profile, so that it's automatically used when building the native binary.
The conventional location for profiles is `src/pgo-profiles/<name of the binary>`.
By default, we're using the `main` binary so the location will be `src/pgo-profiles/main`.
Copy the `default.iprof` file into that directory, then run:

`./gradlew nativeCompile`

The profile will automatically be used and the binary compiled with PGO.
It is possible to include more than one profile, in which case you should rename the `.iprof` files in the `src/pgo-profiles/main` directory.

[[plugin-configurations]]
== Configurations defined by the plugin

Expand Down
6 changes: 6 additions & 0 deletions docs/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ If you are using alternative build systems, see <<alternative-build-systems.adoc
[[changelog]]
== Changelog

=== Release 0.9.24

==== Gradle plugin

* Add support for PGO

=== Release 0.9.23

* Upgrade metadata to 0.3.2
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ mavenEmbedder = "3.8.6"
mavenWagon = "3.4.3"
graalvm = "22.0.0"
jackson = "2.13.3"
junitPlatform = "1.8.1"
junitJupiter = "5.8.1"
junitPlatform = "1.9.3"
junitJupiter = "5.10.0"
aether = "1.1.0"
slf4j = "1.7.9"
groovy = "3.0.11"
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading

0 comments on commit 421b6b6

Please sign in to comment.