更新: 2009-05-29
谢谢你所有的建议和建议。我采纳了你的建议,让我的产品代码平均执行速度比几天前我的最佳结果快了2.5倍。,最后,我能够让代码变得最快。
课程:
java -Xmx1024m SpeedTest
但是,如果您按如下方式设置初始堆大小,您将获得巨大的改进:
java -Xms1024m -Xmx1024m SpeedTest
这个简单的更改将执行时间减少了50%以上。所以我的SpeedTest的最终结果是Python9.6秒。Java 6.5秒。
原始问题:
我有以下python代码:
import time
import sys
def main(args):
iterations = 10000000
counts = set()
startTime = time.time();
for i in range(0, iterations):
counts.add(i)
totalTime = time.time() - startTime
print 'total time =',totalTime
print len(counts)
if __name__ == "__main__":
main(sys.argv)
它在我的机器上执行大约3.3秒,但我想让它更快,所以我决定用java编写它。我假设因为java是编译的,并且通常被认为比python更快,所以我会看到一些很大的回报。
下面是java代码:
import java.util.*;
class SpeedTest
{
public static void main(String[] args)
{
long startTime;
long totalTime;
int iterations = 10000000;
HashSet counts = new HashSet((2*iterations), 0.75f);
startTime = System.currentTimeMillis();
for(int i=0; i<iterations; i++)
{
counts.add(i);
}
totalTime = System.currentTimeMillis() - startTime;
System.out.println("TOTAL TIME = "+( totalTime/1000f) );
System.out.println(counts.size());
}
}
所以这段java代码基本上和python代码做了同样的事情。但它的执行时间是8.3秒,而不是3.3秒。
我从一个真实的例子中提取了这个简单的例子来简化事情。关键的元素是我有(set或hashSet),它有很多成员,就像这个例子一样。
以下是我的问题:
更新:
感谢到目前为止做出贡献的所有人。请允许我补充一些细节。
我没有包含我的产品代码,因为它相当复杂。会让人分心。我上面给出的例子是最简单的。我的意思是,java put调用似乎比python set`s的add()慢得多。
产品代码的java实现也比python版本慢2.5 -3倍--就像上面一样。
我不关心vm预热或启动开销。我只想将startTime中的代码与totalTime中的代码进行比较。请不要为其他事情操心。
我使用了足够多的存储桶来初始化哈希集,这样它就永远不需要重新进行哈希了。(我总是提前知道集合最终将包含多少元素。)我想有人会争辩说,我应该把它初始化为iterations/0.75。但是如果你尝试一下,你会发现执行时间没有明显的影响。
我为那些好奇的人设置了Xmx1024m (我的机器有4 4GB的内存)。
我使用的是java版本: Java(TM) SE Runtime Environment (build 1.6.0_13-b03)。
在生产版本的中,我在hashSet中存储了一个字符串(2-15个字符),所以我不能使用原语,尽管这是一个有趣的情况。
我已经运行代码很多很多次了。我有很高的信心,python代码比java代码快2.5到3倍。
发布于 2009-05-28 09:26:12
您实际上并不是在测试Java和Python,而是使用自动装箱的整数和Python的原生集合和整数处理来测试java.util.HashSet
。
显然,在这个特定的微基准测试中,Python端确实更快。
我尝试用来自GNU trove的TIntHashSet
替换HashSet,获得了3到4倍的加速比,使Java略微领先于Python。
真正的问题是,您的示例代码是否真的像您认为的那样代表您的应用程序代码。您是否运行过分析器,并确定大部分CPU时间都花在将大量in放入HashSet中?如果不是,这个例子就无关紧要了。即使唯一的区别是您的生产代码存储的是in以外的其他对象,它们的创建和hashcode的计算也很容易支配set插入(并完全破坏Python在特别处理in方面的优势),从而使整个问题变得毫无意义。
发布于 2009-05-28 08:46:55
我怀疑这是因为Python使用整数值本身作为其哈希,而基于哈希表的set实现直接使用该值。从source中的注释
这不一定是坏事!相反,在大小为2**i的表中,将低阶i位作为初始表索引是非常快的,并且对于由连续的int范围索引的dict根本不存在冲突。当键是“连续”字符串时,情况也大致相同。因此,在常见情况下,这提供了比随机更好的行为,这是非常可取的。
此微基准测试在某种程度上是Python的最佳情况,因为它恰好会导致零散列冲突。然而,如果Javas HashSet重新散列密钥,它必须执行额外的工作,并且在冲突中的行为也会变得更糟糕。
如果将范围(迭代)存储在一个临时变量中,并在循环之前对其执行random.shuffle,则运行时会慢2倍以上,即使随机播放和列表创建是在循环之外完成的。
发布于 2009-05-27 23:06:53
另一种可能的解释是,Python中的集合是在C代码中本地实现的,而Java中的HashSet是在Java本身中实现的。因此,Python中的集合应该会更快。
https://stackoverflow.com/questions/918359
复制相似问题