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

nak*_*hli 9 performance functional-programming scala

我正在比较两个atoi实现的性能.第一个是迭代输入字符串使用chars 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 */ }
}
Run Code Online (Sandbox Code Playgroud)

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

oxb*_*kes 21

这个问题与内联无关,它与你使用时发生的拳击/取消装箱有关.CharfoldLeft

你得到foldLeftString由隐式转换到StringOps,这是不专业.char字符串中的每个都必须被装入一个java.lang.Character以便传递到Function2(参数foldLeft),然后将未装箱(便宜得多)传递到value函数体内的方法中,然后再次装箱以进入下一次迭代折叠.

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


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

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

(也就是说,除非你已经确定了可归因于拳击特定和不可接受的性能下降,否则你不应该担心它.)

如果您确定存在需要解决的问题,请避免使用集合和for理解(使用foreach和引用flatMap).如果您正在使用循环,请使用while.

  • +1,这是使用Scala时[很多](http://www.nescala.org/2011/#performance-considerations)性能考虑因素之一.(句法)糖真的很胖:) (5认同)