【发布时间】:2011-06-15 07:23:20
【问题描述】:
我写了第一个Project Euler问题的答案:
将所有小于一千且是 3 或 5 的倍数的自然数相加。
我想到的第一件事是:
(1 until 1000).filter(i => (i % 3 == 0 || i % 5 == 0)).foldLeft(0)(_ + _)
但它很慢(需要 125 毫秒),所以我重写了它,只是考虑“另一种方式”与“更快的方式”
(1 until 1000).foldLeft(0){
(total, x) =>
x match {
case i if (i % 3 == 0 || i % 5 ==0) => i + total // Add
case _ => total //skip
}
}
这要快得多(仅 2 毫秒)。为什么?我猜第二个版本只使用 Range 生成器,并没有以任何方式表现出完全实现的集合,一次完成所有操作,速度更快,内存更少。我说的对吗?
这里是 IdeOne 上的代码:http://ideone.com/GbKlP
【问题讨论】:
-
您如何衡量代码“快几个数量级”?在我的 非常 旧、非常 慢的笔记本电脑上,在 Scala interpreter 中,您声称“慢几个数量级”的版本需要不到 300 µs(即 micro 秒),那么 fast 版本需要多长时间?时间会倒退吗?大多数高性能 JVM 需要大约 5 秒 来预热它们的缓存和东西,甚至在它们达到全速之前。 (HotSpot JVM 中的 C2 编译器,即优化编译器,在运行 20000 次之前甚至不会编译方法。)
-
第二个版本似乎快了大约 3 倍(根据我在 REPL 中使用
(1 to 10000000)的完全不科学的测量)。我不会称其为“数量级”,但仍然如此。 -
@Jörg:您可以在他的 ideone 链接上看到运行时间,但我会将这些信息编辑到问题中,这样即使 ideone 链接消失也不会丢失。
-
你可以从这个伟大的文档中了解这个以及许多其他值得注意的事情:scala-lang.org/docu/files/collections-api/collections.html 每个人都应该在编码之前阅读这个。我知道我应该有。
-
在 IDEOne 上看到代码我注意到了一些严重的问题。每个代码都有一个迭代,“较慢”版本首先出现。在实践中,较慢版本的内部循环是“训练”JVM 用于稍后将由“较快”版本调用的常用功能。看这里,我刚刚倒转了两段代码,哪一个更快:ideone.com/7RrlQ
标签: performance scala scala-collections