在通过类变量调用时,Java 9中的Class.cast和Class.isInstance方法实际上是内在的吗?

时间:2017-11-06 22:29:23

标签: java performance java-9 microbenchmark

我正在研究Java 9中的新内在方法,现在发现方法SELECT DISTINCT p.Id, UNIX_TIMESTAMP(p.PostCreationTime) AS PostCreationTime, p.Content AS Content, p.Bu AS Bu, p.Se AS Se, UNIX_TIMESTAMP(p.PostCreationTime) AS PostTimeOrder FROM Post p WHERE (p.Id IN (SELECT pc.PostId FROM PostCreator pc WHERE (pc.UserId IN (SELECT uf.FollowedId FROM UserFollowing uf WHERE uf.FollowingId = '100') OR pc.UserId = '100') )) OR (p.Id IN (SELECT pum.PostId FROM PostUserMentions pum WHERE (pum.UserId IN (SELECT uf.FollowedId FROM UserFollowing uf WHERE uf.FollowingId = '100') OR pum.UserId = '100') )) OR (p.Id IN (SELECT ssp.PostId FROM SStreamPost ssp WHERE (ssp.SStreamId IN (SELECT ssf.SStreamId FROM SStreamFollowing ssf WHERE ssf.UserId = '100')) )) OR (p.Id IN (SELECT psm.PostId FROM PostSMentions psm WHERE (psm.StockId IN (SELECT sf.StockId FROM StockFollowing sf WHERE sf.UserId = '100' )) )) UNION ALL SELECT DISTINCT p.Id AS Id, UNIX_TIMESTAMP(p.PostCreationTime) AS PostCreationTime, p.Content AS Content, p.Bu AS Bu, p.Se AS Se, UNIX_TIMESTAMP(upe.PostEchoTime) AS PostTimeOrder FROM Post p INNER JOIN UserPostE upe on p.Id = upe.PostId INNER JOIN UserFollowing uf on (upe.UserId = uf.FollowedId AND (uf.FollowingId = '100' OR upe.UserId = '100')) ORDER BY PostTimeOrder DESC; is intrinsic以及Class.cast

我做了简单的基准测试来检查:

Class.isInstance

结果:

@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;
    }

}

ENV:

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

结果似乎有些出乎意料。通过类变量调用它们时,# JMH version: 1.19 # VM version: JDK 9, VM 9+181 # VM invoker: /usr/lib/jvm/java-9-oracle/bin/java Class.cast都会变慢。我的理解是内在应该使这种方法与替代方法一样快。 变量会破坏内在优化吗?或者这个开销是预期的吗?

1 个答案:

答案 0 :(得分:6)

事实上,方法是内在的,而且它们对变量而不是常量的工作速度较慢。

考虑表达式Math.min(a, 2)Math.min(a, b)。方法Math.min在两种情况下都是固有的。但是,第一个表达式显然更快,因为不需要加载变量b,编译器可以直接在cmp指令中内联常量2。

isInstance/cast方法也是如此。当type不是常量时,JVM仍然需要加载变量,检查它是否为非空,然后从java.lang.Class实例加载VM Klass指针。

您可以通过明确禁用内在函数来验证内在函数是否有效。在这种情况下,基准将更慢。

@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);
}

结果:

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