Since [JDK-8251462: Simplify compilation policy](https://bugs.openjdk.org/browse/JDK-8251462), introduced in JDK 17, no native wrappers are generated any more if running with `-XX:-TieredCompilation` (i.e. native methods are not compiled any more).
The attached JMH benchmark demonstrate that native method calls became twice as expensive with JDK 17:
```
public static native void emptyStaticNativeMethod();
@Benchmark
public static void baseline() {
}
@Benchmark
public static void staticMethodCallingStatic() {
emptyStaticMethod();
}
@Benchmark
public static void staticMethodCallingStaticNative() {
emptyStaticNativeMethod();
}
@Benchmark
@Fork(jvmArgsAppend = "-XX:-TieredCompilation")
public static void staticMethodCallingStaticNativeNoTiered() {
emptyStaticNativeMethod();
}
@Benchmark
@Fork(jvmArgsAppend = "-XX:+PreferInterpreterNativeStubs")
public static void staticMethodCallingStaticNativeIntStub() {
emptyStaticNativeMethod();
}
```
JDK 11
======
```
Benchmark Mode Cnt Score Error Units
NativeCall.baseline avgt 5 0.390 ± 0.016 ns/op
NativeCall.staticMethodCallingStatic avgt 5 1.693 ± 0.053 ns/op
NativeCall.staticMethodCallingStaticNative avgt 5 10.287 ± 0.754 ns/op
NativeCall.staticMethodCallingStaticNativeNoTiered avgt 5 9.966 ± 0.248 ns/op
NativeCall.staticMethodCallingStaticNativeIntStub avgt 5 20.384 ± 0.444 ns/op
```
JDK 17 & 21
===========
```
Benchmark Mode Cnt Score Error Units
NativeCall.baseline avgt 5 0.390 ± 0.017 ns/op
NativeCall.staticMethodCallingStatic avgt 5 1.852 ± 0.272 ns/op
NativeCall.staticMethodCallingStaticNative avgt 5 10.648 ± 0.661 ns/op
NativeCall.staticMethodCallingStaticNativeNoTiered avgt 5 20.657 ± 1.084 ns/op
NativeCall.staticMethodCallingStaticNativeIntStub avgt 5 22.429 ± 0.991 ns/op
```
The issue can bee seen if we run with `-XX:+PrintCompilation -XX:+PrintInlining`. With JDK 11 we get the following output for `-XX:+TieredCompilation`:
```
172 111 b 3 io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes)
@ 0 io.simonis.NativeCall::emptyStaticNativeMethod (0 bytes) native method
172 112 n 0 io.simonis.NativeCall::emptyStaticNativeMethod (native) (static)
173 113 b 4 io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes)
@ 0 io.simonis.NativeCall::emptyStaticNativeMethod (0 bytes) native method
173 111 3 io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes) made not entrant
```
As you can see, the native wrapper for `NativeCall::emptyStaticNativeMethod()` gets compiled with compiled id 112. If we run with `-XX:-TieredCompilation`:
```
117 5 b io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes)
@ 0 io.simonis.NativeCall::emptyStaticNativeMethod (0 bytes) native method
117 6 n io.simonis.NativeCall::emptyStaticNativeMethod (native) (static)
```
There's still a native wrapper created with compile id 6.
With JDK 17 and later, the `-XX:+PrintCompilation` output looks similar for the default `-XX:+TieredCompilation` case:
```
56 26 b 3 io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes)
@ 0 io.simonis.NativeCall::emptyStaticNativeMethod (0 bytes) native method
56 27 n 0 io.simonis.NativeCall::emptyStaticNativeMethod (native) (static)
56 28 b 4 io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes)
@ 0 io.simonis.NativeCall::emptyStaticNativeMethod (0 bytes) native method
56 26 3 io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes) made not entrant
```
But with `-XX:-TieredCompilation`, we don't generate the native wrapper any more:
```
58 5 b io.simonis.NativeCall::staticMethodCallingStaticNative (4 bytes)
@ 0 io.simonis.NativeCall::emptyStaticNativeMethod (0 bytes) native method
```
Which basically means that we're always invoking the native method through the interpreter stub.