注意:这是关于性能问题的不。我只观察到无法解释/理解的表现差异。
在对一些针对Java 9的新开发的代码进行基准测试时,我发现了一些奇怪的东西。带有5个密钥的HashMap
的(非常)简单基准测试表明,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;
}
}
结果:
更新
感谢您的回答和好评。
@Holger的建议。我的第一反应是:那一定是解释。但是,如果我只对String#length()
函数进行基准测试,则性能没有显着差异。而且,当我只对HashMap#get()
方法进行基准测试时(如@Eugene所建议的那样)仍有大约10 - 12%的差异。
@Eugene的建议。我改变了参数(更多的热身迭代,更多的内存),但我无法重现你的结果。然而,我将堆增加到了4G。但这不能解释差异,不是吗?
@Alan Bateman的建议。是的,这提高了性能!但是,差异仍在20%左右。
答案 0 :(得分:9)
您正在测试的不只是HashMap
。您不仅要致电HashMap.get
,还隐式调用Locale.hashCode
和Locale.equals
。此外,您正在呼叫String.length
。
现在,所有四个都可能改变了它们的性能特征,因此您需要更多的测试来推断哪些方法表现出不同的性能。
但最热门的候选人是String.length
。在Java 9中,String
类不再使用char[]
数组,而是使用byte[]
数组,仅使用每个字符一个字节对Latin 1字符串进行编码,从而大大减少了内存占用典型应用。然而,这意味着长度不再总是与阵列长度相同。因此,此操作的复杂性已发生变化。
但请记住,您的结果是微基准测试中<77>的差异 。这还不足以估计对实际应用的影响......
答案 1 :(得分:5)
我有一个关于jmh
设置的提示,而不仅仅是HashMap
。正如已经提到的,你在这里测量很多而不仅仅是HashMap::get
。但即便如此,我还是怀疑java-9会慢得多,所以我自己测量了(最新的jmh构建源自java-8和9)。
我还没有改变你的代码 - 只是添加了更多的堆(10GB)和更多的热身,从而减少了#34;错误&#34;你看到±
使用java-8:
Benchmark Mode Cnt Score Error Units
SOExample.bmHashMap avgt 25 22.059 ± 0.276 ns/op
使用java-9:
Benchmark Mode Cnt Score Error Units
SOExample.bmHashMap avgt 25 23.954 ± 0.383 ns/op
结果几乎没有明显差异(毕竟这些都是纳秒),如你所见。此外,如果真的想要仅仅测试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
}