[GHC] #15519: Minor code refactoring leads to drastic performance degradation

GHC ghc-devs at haskell.org
Tue Aug 14 18:55:04 UTC 2018


#15519: Minor code refactoring leads to drastic performance degradation
-------------------------------------+-------------------------------------
           Reporter:  danilo2        |             Owner:  (none)
               Type:  bug            |            Status:  new
           Priority:  high           |         Milestone:  8.6.1
          Component:  Compiler       |           Version:  8.4.3
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 Hi!
 I've just observed an important performance problem. This code:

 {{{
 test0 :: Text -> Result
 test0 src = let
     s1 = token 't'
     s2 = token 'e'
     s3 = token 's'
     s4 = token 't'
     p  = many $! s1 <> s2 <> s3 <> s4
     in runTokenParser p src
 {-# NOINLINE test0 #-}
 }}}

 runs over 10 times faster than this one:

 {{{
 testGrammar1 :: Grammar Char
 testGrammar1 = let
     s1 = token 't'
     s2 = token 'e'
     s3 = token 's'
     s4 = token 't'
     in many $! s1 <> s2 <> s3 <> s4
 {-# INLINE testGrammar1 #-}

 test1 :: Text -> Result
 test1 = runTokenParser testGrammar1
 {-# NOINLINE test1 #-}
 }}}

 I've also observed another thing here, namely the former code runs also
 over 10 times faster than this code:

 {{{
 test2 :: Text -> Result
 test2 src = let
     s1 = token 't'
     s2 = token 'e'
     s3 = token 's'
     s4 = token 't'
     p  = X $! many $! s1 <> s2 <> s3 <> s4
     in runTokenParser p src
 {-# NOINLINE test2 #-}
 }}}

 The only difference here is the `X` wrapper, while the `runTokenParser` is
 defined as `runTokenParser (X !a) = runTokenParser a`.

 I've created sample project for it here: https://github.com/wdanilo/ghc-
 bug-peg-optimization/blob/master/src/Main.hs

 In order to run it execute `stack build --exec test`.

 The results are:

 {{{
 benchmarking test0
 time                 420.0 μs   (417.6 μs .. 422.9 μs)
                      1.000 R²   (0.999 R² .. 1.000 R²)
 mean                 421.0 μs   (419.2 μs .. 425.3 μs)
 std dev              9.286 μs   (4.239 μs .. 15.30 μs)
 variance introduced by outliers: 14% (moderately inflated)

 benchmarking test1
 time                 6.069 ms   (6.022 ms .. 6.123 ms)
                      0.999 R²   (0.998 R² .. 1.000 R²)
 mean                 6.065 ms   (6.037 ms .. 6.117 ms)
 std dev              114.5 μs   (74.30 μs .. 183.4 μs)

 benchmarking test2
 time                 6.070 ms   (6.007 ms .. 6.137 ms)
                      0.999 R²   (0.998 R² .. 1.000 R²)
 mean                 6.067 ms   (6.039 ms .. 6.129 ms)
 std dev              123.0 μs   (63.88 μs .. 220.1 μs)

 benchmarking native
 time                 428.0 μs   (421.5 μs .. 437.4 μs)
                      0.998 R²   (0.995 R² .. 1.000 R²)
 mean                 427.1 μs   (424.1 μs .. 434.7 μs)
 std dev              15.18 μs   (5.678 μs .. 26.26 μs)
 variance introduced by outliers: 29% (moderately inflated)
 }}}

 Where "native" is just standard `Text.takeWhile ...`.

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


More information about the ghc-tickets mailing list