python的hashmap_为什么Java的Hashmap比Python的dictionary慢得多?

简短回答:

如果按照题主给出的Java和Python代码,在常见环境里按照默认配置来跑测试,确实会发现Python版例子比Java版例子跑得快。这自然也算是Java性能的一个坑啦——从来就没有什么“理论上Java应该比Python快”的理论,而是在知道常见性能坑、知道best practice的前提下,纯Java程序在较大规模的运算上有可能比纯Python程序有更好的性能,并且相对来说更scalable。

话说回来,这个例子要让Java比Python跑得快无需修改代码,只要稍微修改一下Java的启动参数即可。另外我也相信这个小例子不一定充分反映了题主说的“需要3个小时以上”的那个Java程序的性能问题。那个程序或许也只需要稍微调整一下启动参数就可以大幅提高性能,也可能要修改代码去掉一些不好的做法。

所以具体到题主这个例子,是什么造成Java比Python慢得多?是HashMap写得没dict好?是因为HashMap用Java实现而dict用C实现?是因为JIT的预热开销?还是别的?

假定题主运行Java和Python的环境都是常见的,例如说Oracle JDK vs 原装CPython,那么在Java一侧讨论的对象就是Oracle JDK里的HotSpot VM的性能。

答案是:是因为题主没有设置好GC的参数,而HotSpot VM默认的GC参数在这个例子上非常不适用,导致默认参数下Java版的性能非常糟糕。Java的HashMap自身并不慢,特别是经过JIT编译后它其实不会比CPython的dict慢,至少不是造成这个例子的性能差异的原因。

在我的Mac OS X 10.9 / x86-64上,用Oracle JDK8u101来跑题主给出的Java版测试,我可以看到以下的用时情况:

# 默认参数

$ java TestHashMap

...

Round 0: Time duration: 8480ms

...

Round 1: Time duration: 4849ms

...

Round 2: Time duration: 3789ms

# 添加GC参数,设定Java堆的大小

$ java -Xmx3200m -Xms3200m -Xmn3g TestHashMap

...

Round 0: Time duration: 1801ms

...

Round 1: Time duration: 1810ms

...

Round 2: Time duration: 1122ms

而同一环境中我用CPython 2.7.5(是的这个有点老…)来运行题主的Python脚本的话,用时情况是:

$ time python test_dict.py

...

real0m9.082s

user0m8.192s

sys0m0.825s

(是的这个版本有点老;是的我偷懒了,这样计时跟我这边用的Java版的计时方式不一样所以并不公平,这边会包括了CPython自身的解释器初始化开销,但CPython解释器的初始化开销非常低,至少比HotSpot VM的VM初始化要低多了。这样计时不会引起量级上的差异。)

我用的测试代码是直接将题主的Python代码放进test_dict.py里,Java版则是直接将题主给的代码包装进一个方法里然后从main()中循环调用3次:

import java.util.*;

public class TestHashMap {

public static void doTest() {

Map testmap = new HashMap();

for (int i = 0; i < 10000000; i++) {

String s = Integer.toString(i);

testmap.put(s, i);

int t = testmap.get(s);

if (t % 100000 == 0) {

System.out.println(t);

}

}

}

public static void main(String[] args) {

for (int i = 0; i < 3; i++) {

long start = System.currentTimeMillis();

doTest();

long end = System.currentTimeMillis();

System.out.printf("Round %d: Time duration: %dms\n", i, (end - start));

// System.gc(); }

}

}

这样的Java版测试程序,在Oracle JDK8u101上以默认参数运行时,3次计时的差异的主要来源其实不是JIT编译的影响,而是我的环境中HotSpot VM默认选择的ParallelGC的自适应逻辑的初始化——它需要通过头几次GC来逐渐“学习”当前Java应用的行为并调整自己的GC参数,所以头几次GC本身就是会比较慢。外加默认的GC堆大小对这个程序来说太小了,导致会多次触发GC(包括多次Full GC),这就会严重降低Java程序的性能。

只要正确设置GC参数就可以完全避开这些问题。如何给Java程序正确设置GC参数也算是Java进阶学习的一门必修课了(残念

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值