Are Class.cast and Class.isInstance methods in Java 9 actually intrinsic when invoking via class variable?

Viewed 308

I was looking through new intrinsic methods in Java 9 and found that now the method Class.cast is intrinsic too along with Class.isInstance.

I did simple benchmark to check that:

@BenchmarkMode(Mode.Throughput)
@Fork(1)
@State(Scope.Thread)
@Warmup(iterations = 10, time = 1, batchSize = 1000)
@Measurement(iterations = 20, time = 1, batchSize = 1000)
public class Casting {

    public Object msg;
    public Class<String> type;

    @Setup
    public void setup(BenchmarkParams params) {
        type = String.class;
        msg = "123";
    }

    @Benchmark
    public boolean isInstanceMethod() {
        return type.isInstance(msg);
    }

    @Benchmark
    public boolean isInstanceMethodExplicit() {
        return String.class.isInstance(msg);
    }

    @Benchmark
    public boolean isInstanceRegular() {
        return msg instanceof String;
    }

    @Benchmark
    public String castMethod() {
        return type.cast(msg);
    }

    @Benchmark
    public String castMethodExplicit() {
        return String.class.cast(msg);
    }

    @Benchmark
    public String castRegular() {
        return (String) msg;
    }

}

Results:

Benchmark                          Mode  Cnt       Score       Error  Units
Casting.castMethod                thrpt   20  254604.793 ±  3863.859  ops/s
Casting.castMethodExplicit        thrpt   20  336046.787 ± 10059.986  ops/s
Casting.castRegular               thrpt   20  344369.229 ±  4855.492  ops/s
Casting.isInstanceMethod          thrpt   20  325867.697 ±  6511.731  ops/s
Casting.isInstanceMethodExplicit  thrpt   20  415387.363 ±  2993.788  ops/s
Casting.isInstanceRegular         thrpt   20  396613.371 ± 16799.378  ops/s

Env:

# JMH version: 1.19
# VM version: JDK 9, VM 9+181
# VM invoker: /usr/lib/jvm/java-9-oracle/bin/java

The result seems a bit unexpected. Both Class.cast and Class.isInstance are slower when invoking them via class variable. My understanding was that intrinsic should make both this method as fast as alternatives. Does variable breaks intrinsic optimization? Or this overhead is expected?

1 Answers

There is no contradiction between the fact that methods are intrinsics and that they work slower for variables rather than for constants.

Consider the expressions Math.min(a, 2) and Math.min(a, b). Method Math.min is intrinsic in both cases. However, the first expression is obviously faster, since there is no need to load variable b, and the compiler can inline constant 2 directly in cmp instruction.

The same holds for isInstance/cast methods. When type is not a constant, JVM still needs to load the variable, check that it is non-null, and then load VM Klass pointer from java.lang.Class instance.

You can verify that intrinsics work by explicitly disabling them. In this case the benchmark will be much slower.

@Benchmark
public String castMethod() {
    return type.cast(msg);
}

@Benchmark
@Fork(jvmArgs = {
        "-XX:+UnlockDiagnosticVMOptions",
        "-XX:DisableIntrinsic=_Class_cast,_isInstance",
})
public String castMethodNoIntrinsic() {
    return type.cast(msg);
}

Results:

Benchmark                      Mode  Cnt   Score   Error  Units
Casting.castMethod             avgt   10   4,777 ± 0,065  ns/op
Casting.castMethodNoIntrinsic  avgt   10  28,557 ± 0,124  ns/op
Related