Scala表现问题

时间:2011-02-04 14:28:01

标签: performance scala scala-collections

article written by Daniel Korzekwa中,他表示以下代码的表现:

list.map(e => e*2).filter(e => e>10)

比使用Java编写的迭代解决方案更糟糕。

任何人都可以解释原因吗?什么是Scala中此类代码的最佳解决方案(我希望它不是Scala-fied的Java迭代版本)?

7 个答案:

答案 0 :(得分:15)

特定代码很慢的原因是因为它正在处理基元但它使用泛型操作,因此原语必须加框。 (如果List及其祖先是专门的,这可以得到改善。)这可能会使事情减慢5倍左右。

此外,从算法上讲,这些操作有点贵,因为你制作了一个完整的列表,然后创建一个全新的列表,抛弃中间列表的一些组件。如果你一举做到这一点,那么你会更好。你可以这样做:

list collect (case e if (e*2>10) => e*2)

但如果计算e*2真的很贵怎么办?那么你可以

(List[Int]() /: list)((ls,e) => { val x = e*2; if (x>10) x :: ls else ls }

除了这会出现倒退。 (如果需要,你可以反转它,但这需要创建一个新的列表,这在算法上也不是理想的。)

当然,如果您使用的是单链表,那么您在Java中会遇到相同类型的算法问题 - 您的新列表将以向后的方式结束,或者您必须创建两次,首先是反向,然后是向前,或者你必须使用(非尾部)递归来构建它(这在Scala中很容易,但在任何一种语言中都不建议使用,因为你会耗尽堆栈),或者你必须创建一个可变列表然后假装事后说它不可变。 (顺便说一句,您也可以在Scala中执行此操作 - 请参阅mutable.LinkedList。)

答案 1 :(得分:13)

基本上它是两次遍历列表。一次将每个元素乘以两个。然后另一次过滤结果。

将其视为一个while循环,生成带有中间结果的LinkedList。然后应用过滤器的另一个循环产生最终结果。

这应该更快:

list.view.map(e => e * 2).filter(e => e > 10).force

答案 2 :(得分:6)

解决方案主要在于JVM。虽然Scala在@specialization的图形中有一个解决方法,但它大大增加了任何专业类的大小,并且只解决了一半的问题 - 另一半是临时对象的创建。

JVM实际上很好地优化了很多,或者性能会更糟糕,但Java不需要Scala所做的优化,因此JVM不提供它们。我希望通过在Java中引入SAM非实际闭包来改变某种程度。

但是,最终,它归结为平衡需求。 Java和Scala比Scala的函数等效快得多的while循环可以在C中更快地完成。然而,尽管微基准测试告诉我们,人们使用Java。

答案 3 :(得分:4)

Scala方法更抽象,更通用。因此很难优化每一个案例。

我可以想象,如果HotSpot JIT编译器发现未使用直接结果,可能会在未来对代码应用流和循环融合。

此外,Java代码还有更多功能。

如果您真的只想改变数据结构,请考虑transform。 它看起来有点像map但不会创建新的集合,例如。 G:

val array = Array(1,2,3,4,5,6,7,8,9,10).transform(_ * 2)
// array is now WrappedArray(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

我真的希望将来会增加一些额外的就地操作......

答案 4 :(得分:3)

为了避免遍历列表两次,我认为for语法在这里是个不错的选择:

val list2 = for(v <- list1; e = v * 2; if e > 10) yield e

答案 5 :(得分:2)

Rex Kerr正确地指出了主要问题:在不可变列表上操作,所陈述的代码段在内存中创建了中间列表。请注意,这不一定比等效的Java代码慢;你永远不会在Java中使用不可变数据结构。

Wilfried Springer有一个很好的Scala idomatic解决方案。使用view,不会创建整个列表的(操作)副本。

请注意,使用视图可能并不总是理想的。例如,如果您的第一个调用是filter,预计会丢弃大部分列表,则可能值得明确创建较短版本并仅在此之后使用view以提高内存位置以后的迭代。

答案 6 :(得分:1)

list.filter(e =&gt; e * 2&gt; 10).map(e =&gt; e * 2)

此尝试首先减少List。所以第二次遍历是在较少的元素上。