Плохая производительность fold
Наткнулся на старый тикет, в котором Travis Brown померял производительность вызова fold по сравнению с обычным паттерн-матчингом. Замеры он делал для cats.data.Validated
, но результаты актуальны и для аналогичных контейнеров (Either
, Option
). А результаты неутешительные — fold
медленнее заинлайненного паттерн-матчинга в четыре раза.
fold
для этих типов реализован тривиально и фактически принимает две функции, чтобы засунуть их в паттерн-матчинг:
sealed abstract class Validated[+E, +A] {
// ...
def fold[B](fe: E => B, fa: A => B): B = this match {
case Invalid(e) => fe(e)
case Valid(a) => fa(a)
}
// ...
}
Validated // <- медленно
.valid(42)
.fold(e => println(s"error $e"), res => println(s"result $res"))
Validated.valid(42) match { // <- быстро
case Invalid(e) => println(s"error $e")
case Valid(res) => println(s"result $res")
}
Просадка производительности связана с тем, как в jvm реализованы лямбды: для каждой лямбды аллоцируется объект в куче. Соотвественно fold
только и делает, что аллоцирует лямбды для своих аргументов.
Что с этим делать? Да ничего. Если у вас есть горячий код с фолдами, то вы, скорее всего, уже давно всё заинлайнили. Если код холодный, то оверхед вы и не заметите.