我对一些代码进行了更改,速度提高了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
}
与{head}相比,_*
做了什么,为什么这么贵?
答案 0 :(得分:4)
我在使用-Xprint:all
运行scalac后的猜测是,在queue match { case Queue((thing, stuff), _*) => doThing(queue.tail) }
示例中 patmat 的末尾,我看到调用了以下方法(为简洁起见而编辑):< / p>
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);
所以lengthCompare
以可能优化的方式比较集合的长度。对于Queue
,它创建一个迭代器并迭代一次。所以这应该有点快。另一方面,drop(1)
也构造一个迭代器,跳过一个元素并将其余元素添加到结果队列中,因此它在集合的大小上是线性的。
headOption
示例更简单,它检查列表是否为空(两次比较),如果不是则返回Some(head)
,然后只有_1
和{{} 1}}分配给_2
和thing
。因此,没有创建迭代器,并且集合的长度没有线性。
答案 1 :(得分:2)
您的代码示例之间应该没有显着差异。
case Queue((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))
head
的费用和headOption
的费用几乎相同。
方法head
,tail
和dequeue
可能会reverce
List
Queue
O(n)
(费用为reverce
) 。在两个代码示例中,最多会有2 dequeue
次调用。
您应该像这样使用reverce
来获得最多一次def doThing(queue: Queue[(String, String)]): Queue[(String, String)] =
if (queue.isEmpty) queue
else queue.dequeue match {
case (e, q) => doThing(q)
}
电话:
(thing, stuff)
您也可以将_
替换为lengthCompare
。在这种情况下,编译器将仅生成head
tail
或if (o9.get != null && o9.get.lengthCompare(1) >= 0)
的调用:
{{1}}
答案 2 :(得分:0)
_*
用于指定varargs参数,因此您在第一个版本中所做的是将Queue解构为一对字符串,以及适当数量的其他字符串对 - 即您正在解构整个即使你只关心第一个元素,也要排队。
如果您只是删除星号,请提供
def doThing(queue: Queue[(String, String)]): Queue[(String, String)] = queue match {
case Queue((thing, stuff), _) => doThing(queue.tail)
case _ => queue
}
然后你只是将队列解构为一对字符串和一个余数(因此不需要完全解构)。这应该在与你的第二个版本相当的时间内运行(尽管我自己没有计时)。