HashMap性能Java 9比Java 8低25%?

rmu*_*ler 8 performance-testing jmh java-9

注意:这与性能问题无关.我只观察到无法解释/理解的性能差异.

在对一些针对Java 9的新开发的代码进行基准测试时,我发现了一些奇怪的东西.HashMap带有5个密钥的(非常)简单基准测试表明,Java 9比Java 8慢得多.这可以解释一下,还是我的(基准)代码完全错误?

码:

@Fork(
    jvmArgsAppend = {"-Xmx512M", "-disablesystemassertions"}
)
public class JsonBenchmark {

    @State(Scope.Thread)
    public static class Data {

        final static Locale RUSSIAN = new Locale("ru");
        final static Locale DUTCH = new Locale("nl");

        final Map<Locale, String> hashmap = new HashMap<>();

        public Data() {
            hashmap.put(Locale.ENGLISH, "Flat flashing adjustable for flat angled roof with swivel");
            hashmap.put(Locale.FRENCH, "Solin pour toit plat inclinée");
            hashmap.put(Locale.GERMAN, "Flachdachkragen Flach Schrägdach");
            hashmap.put(DUTCH, "Plakplaat vlak/hellend dak inclusief glijschaal");
            hashmap.put(RUSSIAN, "?????? ????? ??????? ?????? ???????. ??? ????. ??????");
        }

    }

    @Benchmark
    public int bmHashMap(JsonBenchmark.Data data) {
        final Map<Locale, String> m = data.hashmap;
        int sum = 0;
        sum += m.get(Data.RUSSIAN).length();
        sum += m.get(Locale.FRENCH).length();
        sum += m.get(Data.DUTCH).length();
        sum += m.get(Locale.ENGLISH).length();
        sum += m.get(Locale.GERMAN).length();
        return sum;
    }    

}
Run Code Online (Sandbox Code Playgroud)

结果:

  • Java 8_151:JsonBenchmark.bmHashMap thrpt 40 47948546.439±560763.711 ops/s
  • Java 9_181:JsonBenchmark.bmHashMap thrpt 40 34962904.479±276045.691 ops/s(- / - 27%!)

UPDATE

感谢您的回答和好评.

  1. @Holger的建议.我的第一反应是:那一定是解释.但是,如果我只对String#length()功能进行基准测试,则性能没有显着差异.而且,当我只对HashMap#get()方法进行基准测试时(如@Eugene所示),仍然存在大约10-12%的差异.

  2. @Eugene的建议.我改变了参数(更多的热身迭代,更多的内存),但我无法重现你的结果.然而,我将堆增加到了4G.但这无法解释差异,不是吗?

  3. @Alan Bateman的建议.是的,这提高了性能!但是,仍然相差20%左右.

Hol*_*ger 9

您正在测试的不仅仅是HashMap.你不只是打电话HashMap.get,而是暗中打电话Locale.hashCodeLocale.equals.而且,你在打电话String.length.

现在,所有四个都可以改变它们的性能特征,因此您需要更多的测试来推断哪些方法表现出不同的性能.

但最热门的候选人是String.length.在Java 9中,String该类不再使用char[]数组,而是使用byte[]数组,仅使用每个字符一个字节对Latin 1字符串进行编码,从而大大减少了典型应用程序的内存占用.然而,这意味着长度不再总是与阵列长度相同.因此,此操作的复杂性已发生变化.

但请记住,您的结果是微基准测试的差异大约为77 ns .这还不足以估计对实际应用的影响......

  • 好答案.另外要提到的是-XX:-CompactStrings,以便在不使用紧凑字符串时看到性能不同. (3认同)
  • @Eugene:当它有一个`offset`和`length`字段(在Java7之前,update6)时,它仍然比当时的字段少. (3认同)
  • @nullpointer:旧实现类似于`array.length`,新实现类似于`arrays.length&gt;&gt;coder`,其中coder 为零或一,具体取决于字符串。实际应用程序的影响取决于实际调用 `length()` 的频率以及 latin1 字符串和其他字符串之间的比率。对于 latin1 字符串,其他一些字符串操作可能会更快(需要处理的字节更少),这可能不仅仅是补偿。 (2认同)
  • @Holger 我喜欢 Aleksey(从事此工作的同一个人和 `jol`)说添加 `coder` 字段不会使 String 类更大,因为 8 字节对齐,它的空间已经存在。 (2认同)

Eug*_*ene 5

我有一个提示,这是关于jmh设置,更多的是关于HashMap.正如已经指出的那样,你在测量不仅仅是HashMap::get在这里.但即便如此,我还是怀疑java-9会慢得多,所以我自己测量了(最新的jmh构建源自java-8和9).

我没有更改你的代码 - 只是添加了更多堆(10GB)和更热身的方式,从而减少你看到的"错误" ±

使用java-8:

Benchmark            Mode  Cnt   Score   Error  Units
SOExample.bmHashMap  avgt   25  22.059 ± 0.276  ns/op
Run Code Online (Sandbox Code Playgroud)

使用java-9:

Benchmark            Mode  Cnt   Score   Error  Units
SOExample.bmHashMap  avgt   25  23.954 ± 0.383  ns/op
Run Code Online (Sandbox Code Playgroud)

如你所见,结果几乎没有明显差异(毕竟这些是纳秒).此外,如果你真的想测试只是HashMap::get你的方法可以简单地返回它的调用,如下所示:

@Benchmark
@Fork(5)
public int bmHashMap(SOExample.Data data) {
    return data.hashmap.get(data.key); // where key is a random generated possible key
}
Run Code Online (Sandbox Code Playgroud)