热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

简短回答:如果按照题主给出的Java和Python代码,在常见环境里按照默认配置来跑测试,确实会发现Python版例子比Java版例子跑得

简短回答:

如果按照题主给出的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 &#61; 0; i <10000000; i&#43;&#43;) {

String s &#61; Integer.toString(i);

testmap.put(s, i);

int t &#61; testmap.get(s);

if (t % 100000 &#61;&#61; 0) {

System.out.println(t);

}

}

}

public static void main(String[] args) {

for (int i &#61; 0; i <3; i&#43;&#43;) {

long start &#61; System.currentTimeMillis();

doTest();

long end &#61; System.currentTimeMillis();

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

// System.gc(); }

}

}

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

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



推荐阅读
author-avatar
w3shuajiang2
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有