为什么Scala的foldLeft性能低于使用字符串索引进行迭代?

时间:2011-07-14 19:32:50

标签: performance scala functional-programming

我正在比较两个atoi实现的性能。第一个是使用charAt迭代输入字符串获取字符;第二个是使用foldLeft

object Atoi {
  def withRandomAccess(str: String, baze: Int): Int = {
      def process(acc: Int, place: Int, str: String, index: Int): Int = 
        if (index >= 0) process(acc + value(str.charAt(index)) * place, place * baze, str, index-1) else acc
      process(0, 1, str, str.length - 1)
    }

  def withFoldLeft(str: String, base: Int): Int = (0/:str) (_ * base + value(_))

  def value(c: Char): Int = { /* omitted for clarity */ }

  def symbol(i: Int): Char = { /* omitted for clarity */ }
}

foldLeft版本慢2到4倍(完整的基准代码为here)。我没想到这一点。你知道为什么吗? Scala是否在处理之前将字符串转换为List?您是否有关于如何提高字符串foldLeft性能的提示?

1 个答案:

答案 0 :(得分:21)

此问题与内联无关,与使用Char时发生的foldLeft装箱/取消装箱有关。< / p>

foldLeft通过隐式转换为String获得StringOps,这不是专门的。字符串中的每个char都必须装入java.lang.Character才能传递到Function2foldLeft的参数),然后取消装箱(便宜得多)传递到函数体内的value方法,然后再次装箱以进入下一次折叠迭代。

拳击涉及创建对象和随后垃圾收集它们的开销。


在避免拳击方面,有一个简短而重要的观点:

  • 你不应该试图避免拳击,概率几乎为1.

(也就是说,除非你已经确定特定且不可接受的性能下降,这可归因于拳击,那么你不应该担心它。) < / p>

如果您确定存在需要解决的问题,请避免收集和for - 理解(使用foreachflatMap)。如果您使用循环,请使用while