[GHC] #13851: Change in specialisation(?) behaviour since 8.0.2 causes 6x slowdown

GHC ghc-devs at haskell.org
Tue Jun 20 16:13:08 UTC 2017


#13851: Change in specialisation(?) behaviour since 8.0.2 causes 6x slowdown
-------------------------------------+-------------------------------------
        Reporter:  mpickering        |                Owner:  (none)
            Type:  bug               |               Status:  new
        Priority:  normal            |            Milestone:
       Component:  Compiler          |              Version:  8.2.1-rc2
      Resolution:                    |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by RyanGlScott):

 Here's a version with no dependencies:

 {{{#!hs
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE UndecidableInstances #-}
 module Main (main) where

 -- | Benchmarks for various effect system implementations

 -- import Criterion.Main
 import Data.Bits
 import Data.Int
 import Data.IORef
 import Data.Ratio
 import Data.Time ( getCurrentTime, utctDayTime )

 import Control.Exception
 import Control.Monad
 import Control.Monad.Trans.Class
 import qualified Control.Monad.Trans.State.Strict as S
 import Control.Monad.Trans.Reader

 import System.CPUTime   ( getCPUTime )
 import System.IO.Unsafe

 -- Use only state, lift variable number of effects over/under
 --------------------------------------------------------------------------------

 test1mtl :: MonadState Int m => Int -> m Int
 test1mtl n = foldM f 1 [0..n] where
   f acc x | x `rem` 5 == 0 = do
               s <- get
               put $! (s + 1)
               pure $! max acc x
           | otherwise = pure $! max acc x

 main = do

   -- Used to definitively disable bench argument inlining
   -- !n <- randomRIO (1000000, 1000000) :: IO Int
   !m <- randomRIO (0, 0) :: IO Int

   let runRT = (`runReaderT`  (m :: Int))
   let runS  = (`S.runState`  (m :: Int))

   replicateM_ 100 $ do
     !n <- randomRIO (1000000, 1000000) :: IO Int
     evaluate $ (runS . runRT . test1mtl) n
   replicateM_ 100 $ do
     !n <- randomRIO (1000000, 1000000) :: IO Int
     evaluate $ (runS . runRT . test1mtl) n
   replicateM_ 100 $ do
     !n <- randomRIO (1000000, 1000000) :: IO Int
     evaluate $ (runS . runRT . runRT . test1mtl) n

 -----
 -- Auxiliary
 ----

 class Monad m => MonadState s m | m -> s where
     get :: m s
     get = state (\s -> (s, s))

     put :: s -> m ()
     put s = state (\_ -> ((), s))

     state :: (s -> (a, s)) -> m a
     state f = do
       s <- get
       let ~(a, s') = f s
       put s'
       return a
     {-# MINIMAL state | get, put #-}

 instance MonadState s m => MonadState s (ReaderT r m) where
     get = lift get
     put = lift . put
     state = lift . state

 instance Monad m => MonadState s (S.StateT s m) where
     get = S.get
     put = S.put
     state = S.state

 class Random a where
   randomR :: RandomGen g => (a,a) -> g -> (a,g)
   -- random  :: RandomGen g => g -> (a, g)
   randomRIO :: (a,a) -> IO a
   randomRIO range  = getStdRandom (randomR range)

 instance Random Int        where randomR = randomIvalIntegral -- ; random
 = randomBounded

 randomIvalIntegral :: (RandomGen g, Integral a) => (a, a) -> g -> (a, g)
 randomIvalIntegral (l,h) = randomIvalInteger (toInteger l, toInteger h)

 {-# SPECIALIZE randomIvalInteger :: (Num a) =>
     (Integer, Integer) -> StdGen -> (a, StdGen) #-}

 randomIvalInteger :: (RandomGen g, Num a) => (Integer, Integer) -> g ->
 (a, g)
 randomIvalInteger (l,h) rng
  | l > h     = randomIvalInteger (h,l) rng
  | otherwise = case (f 1 0 rng) of (v, rng') -> (fromInteger (l + v `mod`
 k), rng')
      where
        (genlo, genhi) = genRange rng
        b = fromIntegral genhi - fromIntegral genlo + 1

        q = 1000
        k = h - l + 1
        magtgt = k * q

        f mag v g | mag >= magtgt = (v, g)
                  | otherwise = v' `seq`f (mag*b) v' g' where
                         (x,g') = next g
                         v' = (v * b + (fromIntegral x - fromIntegral
 genlo))

 class RandomGen g where
    next     :: g -> (Int, g)
    genRange :: g -> (Int,Int)
    genRange _ = (minBound, maxBound)

 data StdGen
  = StdGen !Int32 !Int32

 instance RandomGen StdGen where
   next  = stdNext
   genRange _ = stdRange

 stdRange :: (Int,Int)
 stdRange = (1, 2147483562)

 stdNext :: StdGen -> (Int, StdGen)
 stdNext (StdGen s1 s2) = (fromIntegral z', StdGen s1'' s2'')
  where  z'   = if z < 1 then z + 2147483562 else z
         z    = s1'' - s2''

         k    = s1 `quot` 53668
         s1'  = 40014 * (s1 - k * 53668) - k * 12211
         s1'' = if s1' < 0 then s1' + 2147483563 else s1'

         k'   = s2 `quot` 52774
         s2'  = 40692 * (s2 - k' * 52774) - k' * 3791
         s2'' = if s2' < 0 then s2' + 2147483399 else s2'

 getStdRandom :: (StdGen -> (a,StdGen)) -> IO a
 getStdRandom f = atomicModifyIORef' theStdGen (swap . f)
   where swap (v,g) = (g,v)

 theStdGen :: IORef StdGen
 theStdGen  = unsafePerformIO $ do
    rng <- mkStdRNG 0
    newIORef rng

 mkStdRNG :: Integer -> IO StdGen
 mkStdRNG o = do
     ct          <- getCPUTime
     (sec, psec) <- getTime
     return (createStdGen (sec * 12345 + psec + ct + o))

 createStdGen :: Integer -> StdGen
 createStdGen s = mkStdGen32 $ fromIntegral s

 mkStdGen32 :: Int32 -> StdGen
 mkStdGen32 sMaybeNegative = StdGen (s1+1) (s2+1)
   where
     s       = sMaybeNegative .&. maxBound
     (q, s1) = s `divMod` 2147483562
     s2      = q `mod` 2147483398

 getTime :: IO (Integer, Integer)
 getTime = do
   utc <- getCurrentTime
   let daytime = toRational $ utctDayTime utc
   return $ quotRem (numerator daytime) (denominator daytime)
 }}}

 {{{
 $ /opt/ghc/8.0.2/bin/ghc MultiBench2.hs -O2 -fforce-recomp
 [1 of 1] Compiling Main             ( MultiBench2.hs, MultiBench2.o )
 Linking MultiBench2 ...
 $ time ./MultiBench2

 real    0m2.954s
 user    0m2.952s
 sys     0m0.000s
 $ /opt/ghc/8.2.1/bin/ghc MultiBench2.hs -O2 -fforce-recomp
 [1 of 1] Compiling Main             ( MultiBench2.hs, MultiBench2.o )
 Linking MultiBench2 ...
 $ time ./MultiBench2

 real    0m12.335s
 user    0m12.292s
 sys     0m0.048s
 }}}

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13851#comment:2>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list