Результаты теста (после уменьшения размера массива до 20000000)
Под Java 1.6.22
я получаю 151 and 122 ms
для хвостовой рекурсии и цикла while соответственно.
Под Java 1.7.0
Я получаю 55 and 101 ms
Так что в Java 6 ваш цикл while на самом деле быстрее; оба улучшили производительность под Java 7, но хвостовая рекурсивная версия обогнала цикл.
Объяснение
Разница в производительности связана с тем, что в цикле вы условно добавляете 1 к сумме, а для рекурсии вы всегда добавляете либо 1, либо 0. Таким образом, они не эквивалентны. Эквивалентный цикл while для вашего рекурсивного метода:
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) {
gt += (if (values(pos) > v) 1 else 0)
lt += (if (values(pos) < v) 1 else 0)
eq += (if (values(pos) == v) 1 else 0)
pos += 1
}
(lt, eq, gt)
}
и это дает точно такое же время выполнения, что и рекурсивный метод (независимо от версии Java).
Обсуждение
Я не эксперт по поводу того, почему виртуальная машина Java 7 (HotSpot) может оптимизировать это лучше, чем ваша первая версия, но я предполагаю, что это потому, что она каждый раз проходит по одному и тому же пути через код (а не разветвляется вдоль if
/ else if
путей), поэтому байт-код может быть встроен более эффективно.
Но помните, что это не так в Java 6. Почему один цикл while выигрывает у другого, это вопрос внутренних функций JVM. К счастью для программиста Scala, версия, созданная на основе идиоматической хвостовой рекурсии, является более быстрой в последней версии JVM.
Разница также может возникать на уровне процессора. См. этот вопрос , который объясняет, как код замедляется, если он содержит непредсказуемое ветвление.