Ваш комментарий «слишком много функционала для вычислений в реальном времени, которые могут стать тяжелыми» делает это интересным. Сравнительный анализ и профилирование имеют решающее значение, поскольку вы не хотите писать кучу сложного в обслуживании кода для повышения производительности, а только для того, чтобы понять, что это не критично для производительности вашего приложения. Или, что еще хуже, выясните, что оптимизация производительности ухудшает вашу конкретную рабочую нагрузку.
Наиболее эффективная реализация будет зависеть от вашей специфики (Сколько времени пути? Сколько ядер в системе?) Но я думаю, что сочетание императивного и функционального подходов может дать вам худшее из двух миров. Вы можете потерять и читабельность, и производительность, если не будете осторожны!
Я бы очень немного изменил ответ отсутствующего фактора , чтобы позволить вам получить прирост производительности от параллельных коллекций . Тот факт, что простое добавление .par
может дать вам огромный прирост производительности, демонстрирует способность придерживаться функционального программирования!
def distancePar(wps: collection.GenSeq[Waypoint]): Double = {
val parwps = wps.par
parwps.zip(parwps drop 1).map(Function.tupled(distance)).sum
}
Я предполагаю, что это будет работать лучше всего, если у вас есть несколько ядер для решения проблемы, а wps
имеет тенденцию быть несколько длинным. Если у вас мало ядер или короткие пути, то параллелизм, вероятно, повредит больше, чем поможет.
Другая крайность была бы полностью императивным решением. Написание обязательных реализаций отдельных, критичных к производительности функций обычно приемлемо, если вы избегаете общего изменяемого состояния. Но как только вы привыкнете к FP, вам будет сложнее писать и поддерживать такую функцию. И распараллелить тоже непросто.
def distanceImp(wps: collection.GenSeq[Waypoint]): Double = {
if (wps.size <= 1) {
0.0
} else {
var r = 0.0
var here = wps.head
var remaining = wps.tail
while (!remaining.isEmpty) {
r += distance(here, remaining.head)
here = remaining.head
remaining = remaining.tail
}
r
}
}
Наконец, если вы ищете золотую середину между FP и императивом, вы можете попробовать рекурсию. Я не профилировал его, но я предполагаю, что это будет примерно эквивалентно императивному решению с точки зрения производительности.
def distanceRec(wps: collection.GenSeq[Waypoint]): Double = {
@annotation.tailrec
def helper(acc: Double, here: Waypoint, remaining: collection.GenSeq[Waypoint]): Double =
if (remaining.isEmpty)
acc
else
helper(acc + distance(here, remaining.head), remaining.head, remaining.tail)
if (wps.size <= 1)
0.0
else
helper(0.0, wps.head, wps.tail)
}