问题 Scala中的替代项的顺序是否与性能相关?


特别是关于模式匹配和案例类。考虑以下:

abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

object Expr {
  def simplify(expr: Expr): Expr = expr match {
    // Some basic simplification rules...
    case UnOp("-", UnOp("-", e)) => simplify(e) // Double negation
    case BinOp("+", e, Number(0)) => simplify(e) // Adding zero
    case BinOp("-", e, Number(0)) => simplify(e) // Subtracting zero
    case BinOp("*", e, Number(1)) => simplify(e) // Multiplying by one
    case BinOp("*", e, Number(0)) => Number(0) // Multiplying by zero
    case _ => expr // Default, could not simplify given above rules
  }
}

如果有任何样品电话,比方说, simplify(UnOp("-", UnOp("-", UnOp("-", UnOp("-", Var("x")))))) (结果是 Var("x")),匹配表达式中备选方案的顺序对性能有影响吗?

旁注,有点相关(我自己的观察): 有一件事真的让我感到震惊 simplify 它是一个递归函数,虽然不像我写的/处理的其他递归函数,基本情况是最后的,以避免提前终止。


7559
2017-08-17 17:43


起源

我希望,它需要更多的案例,以及更长,更长的测试用例来衡量可重现的差异。 - user unknown


答案:


理论上是的,因为匹配测试是按顺序完成的。

但在实践中,差异可能无关紧要。我使用Caliper和你的例子运行微基准测试。我加了前缀 Var("x") 100'000 Unop为了让它更大。

结果是:

[info]  0% Scenario{vm=java, trial=0, benchmark=ForFirst} 240395.82 ns; σ=998.55 ns @ 3 trials
[info] 50% Scenario{vm=java, trial=0, benchmark=ForLast} 251303.52 ns; σ=2342.60 ns @ 5 trials
[info] 
[info] benchmark  us linear runtime
[info]  ForFirst 240 ============================
[info]   ForLast 251 ==============================

在第一次测试中, UnOp case是第一个,第二个测试是最后一个(就在默认情况之前)。

正如你所看到的,它并不重要(慢不到5%)。或许这样,有一个庞大的案例清单,订单很重要,但它也可能是重构的候选人。

完整代码在这里: https://gist.github.com/1152232 (经过 斯卡拉-基准模板)。


8
2017-08-17 18:24





像上面这样的匹配语句被转换成字节码中的一堆if语句:

public Expr simplify(Expr);
  Code:
   0:   aload_1
   1:   astore_3
   2:   aload_3
   3:   instanceof  #17; //class UnOp
   6:   ifeq    110
   . . .

   110: aload_3
   111: instanceof  #35; //class BinOp
   114: ifeq    336
   . . .

所以它实际上等同于按顺序运行一堆if语句。因此,对于if语句,首先放置常见案例可能有所帮助。编译器在折叠类似测试方面做得相当不错,但它并不完美,所以有时候它可以更好地捕获多个案例(甚至使用嵌套的if语句),并且可以使用某种类型的决策树。仍然是编译器  做得相当不错,所以不要浪费你的时间,除非你知道这是瓶颈。


7
2017-08-17 18:54



+1:“[...]不要浪费你的时间,除非你知道这是瓶颈“ - paradigmatic


列出案例的顺序不一定是它们的测试顺序。包括null的常量由编译器排序,以便在其中快速搜索。因此,在处理常数时按照使用频率对案例进行排序是没有意义的。但是,在匹配类型时,顺序至关重要:匹配的第一个类型将被使用,即使它们之后是更好的匹配(不太通用)。因此,最具体的类型应该是第一位的。


0
2017-07-18 13:43