Performance regression from GHC 8.4.4 to GHC 8.6.4
Summary
Function becomes slower when compiled with latest GHC
Steps to reproduce
Here is a sample program:
{-# LANGUAGE DeriveFunctor #-}
module Main (main) where
import Criterion.Main
data ListF a b = Cons a b
| Nil
deriving (Functor)
elgot :: Functor f => (f a -> a) -> (b -> Either a (f b)) -> b -> a
elgot φ ψ = h where h = either id (φ . fmap h) . ψ
collatzLength :: Integral a => a -> a
collatzLength = elgot algebra elgotCoalgebra
where algebra Nil = 0
algebra (Cons _ x) = x + 1
elgotCoalgebra 1 = Left 1
elgotCoalgebra n
| n `mod` 2 == 0 = Right $ Cons n (div n 2)
| otherwise = Right $ Cons n (3 * n + 1)
main :: IO ()
main =
defaultMain [ bgroup "collatzLength"
[ bench "collatzLength" $ nf collatzLength (837799 :: Int) ]
]
The relevant bits are in that module proper but I used criterion for benchmarks.
When run with GHC 8.6.4 I get
Benchmark elgot-bench-bench: RUNNING...
benchmarking collatzLength/collatzLength
time 1.453 μs (1.447 μs .. 1.460 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 1.448 μs (1.442 μs .. 1.456 μs)
std dev 23.46 ns (19.43 ns .. 30.54 ns)
variance introduced by outliers: 16% (moderately inflated)
Benchmark elgot-bench-bench: FINISH
whereas with GHC 8.4.4 I get
Benchmark elgot-bench-bench: RUNNING...
benchmarking collatzLength/collatzLength
time 1.110 μs (1.103 μs .. 1.117 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 1.106 μs (1.104 μs .. 1.110 μs)
std dev 11.54 ns (9.112 ns .. 16.53 ns)
Benchmark elgot-bench-bench: FINISH
Expected behavior
I would expect the code compiled with GHC 8.6.4 to have similar performance to the code compiled with GHC 8.4.4.
Environment
- GHC version used: GHC 8.6.4
Optional:
- Operating System: Lubuntu
- System Architecture: x86_64
(Aside: there's a darcs repo I'm using for other purposes that anyone wishing to verify may use)