Почему моя хвостовая рекурсия Scala быстрее, чем цикл while?

Вот два решения упражнения 4.9 в Scala для нетерпеливых Кея Хорстмана: «Напишите функцию lteqgt (значения: Array [Int], v: Int), которая возвращает тройку, содержащую количество значений меньше v, равных v и больше чем v. "Один использует хвостовую рекурсию, другой использует цикл while. Я думал, что оба будут компилироваться с аналогичным байт-кодом, но цикл while медленнее, чем хвостовая рекурсия почти в 2 раза. Это наводит на мысль, что мой метод while является плохо написано.

import scala.annotation.tailrec
import scala.util.Random
object PerformanceTest {

  def main(args: Array[String]): Unit = {
    val bigArray:Array[Int] = fillArray(new Array[Int](100000000))
    println(time(lteqgt(bigArray, 25)))
    println(time(lteqgt2(bigArray, 25)))
  }

  def time[T](block : => T):T = {
    val start = System.nanoTime : Double
    val result = block
    val end = System.nanoTime : Double
    println("Time = " + (end - start) / 1000000.0 + " millis")
    result
  }

  @tailrec def fillArray(a:Array[Int], pos:Int=0):Array[Int] = {
    if (pos == a.length)
      a
    else {
      a(pos) = Random.nextInt(50)
      fillArray(a, pos+1)
    }
  }

  @tailrec def lteqgt(values: Array[Int], v:Int, lt:Int=0, eq:Int=0, gt:Int=0, pos:Int=0):(Int, Int, Int) = {
    if (pos == values.length)
      (lt, eq, gt)
    else
      lteqgt(values, v, lt + (if (values(pos) < v) 1 else 0), eq + (if (values(pos) == v) 1 else 0), gt + (if (values(pos) > v) 1 else 0), pos+1) 
  }

  def lteqgt2(values:Array[Int], v:Int):(Int, Int, Int) = {
    var lt = 0
    var eq = 0
    var gt = 0
    var pos = 0
    val limit = values.length
    while (pos < limit) {
      if (values(pos) > v)
        gt += 1
      else if (values(pos) < v)
        lt += 1
      else
        eq += 1
      pos += 1
    }
    (lt, eq, gt)
  }
}

Отрегулируйте размер bigArray в соответствии с размером вашей кучи. Вот пример вывода:

Time = 245.110899 millis
(50004367,2003090,47992543)
Time = 465.836894 millis
(50004367,2003090,47992543)

Почему метод while так много ч медленнее, чем tailrec? Наивно, что версия tailrec имеет небольшой недостаток, поскольку она всегда должна выполнять 3 проверки «if» для каждой итерации, тогда как версия while часто выполняет только 1 или 2 теста из-за конструкции else. (NB, изменение порядка, в котором я выполняю два метода, не влияет на результат).

35
задан waifnstray 6 February 2012 в 22:58
поделиться