Scala 为什么headOption更快

Scala 为什么headOption更快,scala,optimization,queue,option,Scala,Optimization,Queue,Option,我对一些代码进行了更改,速度提高了4.5倍。我想知道为什么。过去基本上是: def doThing(queue: Queue[(String, String)]): Queue[(String, String)] = queue match { case Queue((thing, stuff), _*) => doThing(queue.tail) case _ => queue } 我把它改成这样,以获得巨大的速度提升: def doThing(queue: Queue

我对一些代码进行了更改,速度提高了4.5倍。我想知道为什么。过去基本上是:

def doThing(queue: Queue[(String, String)]): Queue[(String, String)] = queue match {
  case Queue((thing, stuff), _*) => doThing(queue.tail)
  case _ => queue
}
我把它改成这样,以获得巨大的速度提升:

def doThing(queue: Queue[(String, String)]): Queue[(String, String)] = queue.headOption match {
  case Some((thing, stuff)) => doThing(queue.tail)
  case _ => queue
}

\u*
是做什么的?为什么它比headOption昂贵?

\u*
用于指定varargs参数,所以在第一个版本中,您要做的是将队列解构为一对字符串,以及适当数量的其他字符串对-即,即使您只关心第一个元素,也要解构整个队列

如果你只是删除星号,给

def doThing(queue: Queue[(String, String)]): Queue[(String, String)] = queue match {
  case Queue((thing, stuff), _) => doThing(queue.tail)
  case _ => queue
}

然后,您只需将队列解构为一对字符串和一个余数(因此不需要完全解构)。这应该与您的第二个版本在相当的时间内运行(不过我自己还没有计时)。

您的代码示例之间应该没有显著差异

case队列((thing,stuff),*)
实际上由编译器转换为
head
apply(0)
)方法的调用。您可以使用
scalac-Xprint:patmat
来调查这一点:

<synthetic> val p2: (String, String) = o9.get.apply(0);
if (p2.ne(null))
  matchEnd6(doThing(queue.tail))
您还可以将
(东西、东西)
替换为
。在这种情况下,编译器将只生成
lengthCompare
调用,而不生成
head
tail

if (o9.get != null && o9.get.lengthCompare(1) >= 0)

使用
-Xprint:all
运行scalac后,我猜想在
队列匹配{case queue((thing,stuff),*=>doThing(queue.tail)}
示例中的patmat末尾,我看到调用了以下方法(为简洁起见进行了编辑):

因此,以可能优化的方式比较集合的长度。对于
队列
,它创建一个迭代器并迭代一次。所以这应该有点快。另一方面,还构造一个迭代器,跳过一个元素并将其余元素添加到结果队列中,这样在集合的大小上是线性的


headOption
示例更简单,它检查列表是否为空(两次比较),如果不是,则返回一个
Some(head)
,然后将其
\u 1
\u 2
分配给
thing
stuff
。因此不会创建迭代器,集合的长度也不会是线性的。

队列((东西,东西),
是两个元素的集合模式。@senia Oops,你说得对-我认为队列是像列表一样构造的。尽管如此,围绕为什么
.*
花费这么长时间的解构论点仍然适用(但坚持使用headOption或dequeue或类似方法)。
队列实际上是使用两个列表构建的。但是
List(a,b)
也是两个元素列表的模式。这是可变
队列还是不可变队列?您的编译器版本是什么?Scala版本是2.10.2tanks!我也要试试。但是为什么我的例子中存在差异?@kelloti:你确定两个例子的表现不一样吗?您的代码可能会被jit优化为无用。正如我从
scalac-Xprint:patmat
中所看到的,您的两个代码示例之间没有显著差异。@senia,我不认为
head
tail
是O(n)。看见它确实利用了结构共享,在我看来似乎是O(1)。对不起,我把O(1)拿回去了,里面有一个
,反面是
。但是
dequeue
也是如此。你是对的,它是
O(1)
。我可以看到唯一的选择:您的基准不正确。在您的示例中,最多有2个
in.reverse
,1个
dequeue
。这并没有显著的性能差异。您的编译器版本是什么?我使用的是
2.10.1
2.11.0-M3
,没有
p3
@senia,2.10.0。你在检查不可变队列吗?@senia,我明白你的意思,我确实明白了
o9.get.apply(0)
和更新版本,没有
drop
。。我想这是版本>
2.10.0
的改进。
if (o9.get != null && o9.get.lengthCompare(1) >= 0)
val o9 = scala.collection.immutable.Queue.unapplySeq[(String, String)](x1);
if (o9.isEmpty.unary_!)
  if (o9.get.!=(null).&&(o9.get.lengthCompare(1).>=(0)))
    {
      val p2: (String, String) = o9.get.apply(0);
      val p3: Seq[(String, String)] = o9.get.drop(1);