Ответ 1
Это связано с тем, как вычисляется фиксированная точка fix
.
Это было указано выше @duplode (и я сам в связанном вопросе). В любом случае, мы можем суммировать этот вопрос следующим образом.
Мы имеем, что
fix f = f (fix f)
работает, но делает новый вызов fix f
при каждой рекурсии. Вместо
fix f = go
where go = f go
вычисляет ту же фиксированную точку, что и избежать вызова. В библиотеках fix
реализовано более эффективным способом.
Вернемся к вопросу, рассмотрим следующие три реализации cata
:
cata :: Functor f => Algebra f b -> Fix f -> b
cata alg' = alg' . fmap (cata alg') . unFix
cata2 :: Functor f => Algebra f b -> Fix f -> b
cata2 alg' = go
where
go = alg' . fmap go . unFix
fixcata :: Functor f => Algebra f b -> Fix f -> b
fixcata alg' = fix $ \f -> alg' . fmap f . unFix
Первая делает вызов cata alg'
при каждой рекурсии. Второй - нет. Третий также не работает, так как библиотека fix
эффективна.
И действительно, мы можем использовать Criterion для подтверждения этого, даже используя тот же тест, который использует OP:
benchmarking cata/short input/cata
time 16.58 us (16.54 us .. 16.62 us)
1.000 R² (1.000 R² .. 1.000 R²)
mean 16.62 us (16.58 us .. 16.65 us)
std dev 111.6 ns (89.76 ns .. 144.0 ns)
benchmarking cata/short input/cata2
time 1.746 us (1.742 us .. 1.749 us)
1.000 R² (1.000 R² .. 1.000 R²)
mean 1.741 us (1.736 us .. 1.744 us)
std dev 12.69 ns (10.50 ns .. 17.31 ns)
benchmarking cata/short input/fixcata
time 2.010 us (2.003 us .. 2.016 us)
1.000 R² (1.000 R² .. 1.000 R²)
mean 2.006 us (2.001 us .. 2.011 us)
std dev 16.40 ns (14.05 ns .. 19.27 ns)
Длинные входы также показывают улучшение.
benchmarking cata/long input/cata
time 119.3 ms (113.4 ms .. 125.8 ms)
0.996 R² (0.992 R² .. 1.000 R²)
mean 119.8 ms (117.7 ms .. 121.7 ms)
std dev 2.924 ms (2.073 ms .. 4.064 ms)
variance introduced by outliers: 11% (moderately inflated)
benchmarking cata/long input/cata2
time 17.89 ms (17.43 ms .. 18.36 ms)
0.996 R² (0.992 R² .. 0.999 R²)
mean 18.02 ms (17.49 ms .. 18.62 ms)
std dev 1.362 ms (853.9 us .. 2.022 ms)
variance introduced by outliers: 33% (moderately inflated)
benchmarking cata/long input/fixcata
time 18.03 ms (17.56 ms .. 18.50 ms)
0.996 R² (0.992 R² .. 0.999 R²)
mean 18.17 ms (17.57 ms .. 18.72 ms)
std dev 1.365 ms (852.1 us .. 2.045 ms)
variance introduced by outliers: 33% (moderately inflated)
Я также экспериментировал с ana
, наблюдая, что производительность аналогично улучшенного ana2
согласуется с fixana
. Здесь нет сюрпризов.