nan*_*nda 10 performance scala scala-collections
在Daniel Korzekwa撰写的文章中,他表示以下代码的表现:
list.map(e => e*2).filter(e => e>10)
Run Code Online (Sandbox Code Playgroud)
比使用Java编写的迭代解决方案更糟糕.
有谁能解释为什么?什么是Scala中此类代码的最佳解决方案(我希望它不是Scala-fied的Java迭代版本)?
Rex*_*err 15
原因在于特定的代码是缓慢的,因为它的工作对原语,但它使用通用的操作,因此,元都被装箱.(如果List它的祖先是专门的,这可以得到改善.)这可能会使事情减慢5倍左右.
此外,从算法上讲,这些操作有点昂贵,因为你制作了一个完整的列表,然后创建一个全新的列表,抛弃中间列表的一些组件.如果你一举做到这一点,那么你会更好.你可以这样做:
list collect (case e if (e*2>10) => e*2)
Run Code Online (Sandbox Code Playgroud)
但如果计算e*2真的很贵怎么办?那你可以
(List[Int]() /: list)((ls,e) => { val x = e*2; if (x>10) x :: ls else ls }
Run Code Online (Sandbox Code Playgroud)
除了这会出现倒退.(如果需要,你可以反转它,但这需要创建一个新列表,这在算法上也不是理想的.)
当然,如果你使用的是单链表,那么你在Java中就会遇到同样的算法问题 - 你的新列表最终会被倒退,或者你必须创建两次,首先是反向然后转发,或者你有与(非尾)递归(这是很容易在Scala中,但不妥当的这种事情在任何一种语言,因为你会耗尽堆栈)建造它,或者你必须创建一个可变的列表,然后假装以后,它的不可变的.(顺便说一下,你也可以在Scala中做 - 看看mutable.LinkedList.)
Wil*_*ger 13
基本上它是两次遍历列表.一次将每个元素乘以两个.然后另一次过滤结果.
可以把它想象成一个while循环,生成一个带有中间结果的LinkedList.然后应用过滤器的另一个循环产生最终结果.
这应该更快:
list.view.map(e => e * 2).filter(e => e > 10).force
Run Code Online (Sandbox Code Playgroud)
解决方案主要在于JVM.虽然Scala在图中有一个解决方法,但@specialization它大大增加了任何专业类的大小,并且只解决了一半的问题 - 另一半是临时对象的创建.
JVM实际上很好地优化了很多,或者性能会更糟糕,但Java不需要Scala所做的优化,因此JVM不提供它们.我希望通过SAM在Java中引入非实时闭包来改变某种程度.
但是,最终,它归结为平衡需求.同样的while循环,它的Java和Scala这样做的速度远远超过了Scala的功能相当于在C.更快尚未完成然而,尽管什么微基准测试告诉我们,人们使用Java.