Tôi đã viết một bài kiểm tra ngây thơ để đo lường hiệu suất của ba loại thực hiện giai thừa: loop dựa, không đuôi đệ quy và đuôi đệ quy.Scala đệ quy vs vòng lặp: hiệu suất và thời gian chạy cân nhắc
Đáng ngạc nhiên với tôi performant tồi tệ nhất là những vòng lặp («khi» được dự kiến sẽ có hiệu quả hơn vì vậy tôi cung cấp cả hai) rằng chi phí gần gấp đôi so với đuôi thay thế đệ quy.
ĐÁP: sửa chữa thực hiện vòng lặp tránh * = điều hành Mà làm tốt hơn tồi tệ nhất với bigint do ruột của nó «loops» trở thành nhanh nhất như mong đợi
Một «woodoo» hành vi của tôi đã trải nghiệm là trường hợp ngoại lệ StackOverflow không được ném sơ bộ cho cùng một mục nhập trong trường hợp trường hợp triển khai đệ quy không đuôi. Tôi có thể phá vỡ StackOverlow bằng việc tiếp tục gọi hàm với lớn hơn và lớn hơn giá trị ... Tôi cảm thấy điên :) Trả lời: JVM yêu cầu hội tụ trong quá trình startup, sau đó hành vi là chặt chẽ và có hệ thống
Đây là mã :
final object Factorial {
type Out = BigInt
def calculateByRecursion(n: Int): Out = {
require(n>0, "n must be positive")
n match {
case _ if n == 1 => return 1
case _ => return n * calculateByRecursion(n-1)
}
}
def calculateByForLoop(n: Int): Out = {
require(n>0, "n must be positive")
var accumulator: Out = 1
for (i <- 1 to n)
accumulator = i * accumulator
accumulator
}
def calculateByWhileLoop(n: Int): Out = {
require(n>0, "n must be positive")
var accumulator: Out = 1
var i = 1
while (i <= n) {
accumulator = i * accumulator
i += 1
}
accumulator
}
def calculateByTailRecursion(n: Int): Out = {
require(n>0, "n must be positive")
@tailrec def fac(n: Int, acc: Out): Out = n match {
case _ if n == 1 => acc
case _ => fac(n-1, n * acc)
}
fac(n, 1)
}
def calculateByTailRecursionUpward(n: Int): Out = {
require(n>0, "n must be positive")
@tailrec def fac(i: Int, acc: Out): Out = n match {
case _ if i == n => n * acc
case _ => fac(i+1, i * acc)
}
fac(1, 1)
}
def comparePerformance(n: Int) {
def showOutput[A](msg: String, data: (Long, A), showOutput:Boolean = false) =
showOutput match {
case true => printf("%s returned %s in %d ms\n", msg, data._2.toString, data._1)
case false => printf("%s in %d ms\n", msg, data._1)
}
def measure[A](f:()=>A): (Long, A) = {
val start = System.currentTimeMillis
val o = f()
(System.currentTimeMillis - start, o)
}
showOutput ("By for loop", measure(()=>calculateByForLoop(n)))
showOutput ("By while loop", measure(()=>calculateByWhileLoop(n)))
showOutput ("By non-tail recursion", measure(()=>calculateByRecursion(n)))
showOutput ("By tail recursion", measure(()=>calculateByTailRecursion(n)))
showOutput ("By tail recursion upward", measure(()=>calculateByTailRecursionUpward(n)))
}
}
sau đây là một số đầu ra từ SBT giao diện điều khiển (Trước «khi» thực hiện):
scala> example.Factorial.comparePerformance(10000)
By loop in 3 ns
By non-tail recursion in >>>>> StackOverflow!!!!!… see later!!!
........
scala> example.Factorial.comparePerformance(1000)
By loop in 3 ms
By non-tail recursion in 1 ms
By tail recursion in 4 ms
scala> example.Factorial.comparePerformance(5000)
By loop in 105 ms
By non-tail recursion in 27 ms
By tail recursion in 34 ms
scala> example.Factorial.comparePerformance(10000)
By loop in 236 ms
By non-tail recursion in 106 ms >>>> Now works!!!
By tail recursion in 127 ms
scala> example.Factorial.comparePerformance(20000)
By loop in 977 ms
By non-tail recursion in 495 ms
By tail recursion in 564 ms
scala> example.Factorial.comparePerformance(30000)
By loop in 2285 ms
By non-tail recursion in 1183 ms
By tail recursion in 1281 ms
Sau đây là một số đầu ra từ SBT giao diện điều khiển (Sau «khi» thực hiện):
scala> example.Factorial.comparePerformance(10000)
By for loop in 252 ms
By while loop in 246 ms
By non-tail recursion in 130 ms
By tail recursion in 136 ns
scala> example.Factorial.comparePerformance(20000)
By for loop in 984 ms
By while loop in 1091 ms
By non-tail recursion in 508 ms
By tail recursion in 560 ms
Sau đây là một số đầu ra từ SBT giao diện điều khiển (sau khi «trở lên» đuôi thực hiện đệ quy) thế giới đến trở lại lành mạnh:
scala> example.Factorial.comparePerformance(10000)
By for loop in 259 ms
By while loop in 229 ms
By non-tail recursion in 114 ms
By tail recursion in 119 ms
By tail recursion upward in 105 ms
scala> example.Factorial.comparePerformance(20000)
By for loop in 1053 ms
By while loop in 957 ms
By non-tail recursion in 513 ms
By tail recursion in 565 ms
By tail recursion upward in 470 ms
sau đây là một số đầu ra từ SBT console sau khi sửa bigint nhân trong «vòng»: thế giới là hoàn toàn sa ne:
scala> example.Factorial.comparePerformance(20000)
By for loop in 498 ms
By while loop in 502 ms
By non-tail recursion in 521 ms
By tail recursion in 611 ms
By tail recursion upward in 503 ms
bigint overhead và ngu ngốc thực hiện bởi tôi đeo mặt nạ hành vi mong đợi.
Thanks guys
PS .: Cuối cùng tôi nên lại tiêu đề bài này để «Một bài học lernt trên bigints»
câu hỏi là gì? – Dan
"Loops nên tránh" Đây là gây hiểu nhầm, cho vòng thường chậm hơn so với tương đương trong khi vòng trong scala. Đuôi đệ quy thường nhanh hơn đệ quy không đuôi, tôi sẽ đi ra ngoài một chi và nói nó chậm hơn vì sự đóng cửa mà bạn đang tạo ra trước khi hàm bắt đầu. –
Ai có thể giúp tôi hiểu hành vi này chỉ cho tôi một tham chiếu có thẩm quyền. –