module Main where -- This test works efficiently because the full laziness -- pass now floats out applications -- \x -> f y (x+1) -- It'll float out the (f y) if that's a redex loop :: Double -> [Int] -> Double {-# NOINLINE loop #-} loop x [] = x loop x (n:ns) = x `seq` loop (x ^ n) ns main = print $ loop 1 (replicate 10000000 5) ---------------------------------------------------- {- Roman's message of May 2010 I tried running nofib with -fno-method-sharing (we discussed this at some point). These are the results: -------------------------------------------------------------------------------- Program Size Allocs Runtime Elapsed -------------------------------------------------------------------------------- Min -0.3% -25.0% -12.5% -9.9% Max +0.2% +159.1% +90.0% +84.7% Geometric Mean -0.0% +2.2% +6.8% +5.1% This is the worst program: simple +0.2% +159.1% +65.3% +63.9% I looked at it a bit and came up with this small example: ---- loop :: Double -> [Int] -> Double {-# NOINLINE loop #-} loop x [] = x loop x (n:ns) = x `seq` loop (x ^ n) ns main = print $ loop 1 (replicate 10000000 5) ---- This is over 2x slower with -fno-method-sharing. The culprit is, of course, (^). Here is the difference: Without -fno-method-sharing: ---- ^_rVB :: GHC.Types.Double -> GHC.Types.Int -> GHC.Types.Double ^_rVB = GHC.Real.^ @ GHC.Types.Double @ GHC.Types.Int GHC.Float.$fNumDouble GHC.Real.$fIntegralInt Main.loop [InlPrag=NOINLINE (sat-args=2), Occ=LoopBreaker] :: GHC.Types.Double -> [GHC.Types.Int] -> GHC.Types.Double Main.loop = \ (x1_aat :: GHC.Types.Double) (ds_drG :: [GHC.Types.Int]) -> case ds_drG of _ { [] -> x1_aat; : n_aav ns_aaw -> case x1_aat of x2_aau { GHC.Types.D# ipv_srQ -> Main.loop (^_rVB x2_aau n_aav) ns_aaw } } ---- With: ---- Main.loop [InlPrag=NOINLINE (sat-args=2), Occ=LoopBreaker] :: GHC.Types.Double -> [GHC.Types.Int] -> GHC.Types.Double Main.loop = \ (x1_aat :: GHC.Types.Double) (ds_drD :: [GHC.Types.Int]) -> case ds_drD of _ { [] -> x1_aat; : n_aav ns_aaw -> case x1_aat of x2_aau { GHC.Types.D# ipv_srN -> Main.loop (GHC.Real.^ @ GHC.Types.Double @ GHC.Types.Int GHC.Float.$fNumDouble GHC.Real.$fIntegralInt x2_aau n_aav) ns_aaw } } ---- This is a bit disappointing. I would have expected GHC to float out the application of (^) to the two dictionaries during full laziness (note that (^) has arity 2 so the application is oversaturated). Why doesn't that happen? SetLevels (if this is the right place to look) has this: -}