[GHC] #1349: Generalise the ! and UNPACK mechanism for data types, to unpack function arguments

GHC cvs-ghc at haskell.org
Wed Aug 4 18:34:53 EDT 2010


#1349: Generalise the ! and UNPACK mechanism for data types, to unpack function
arguments
---------------------------------+------------------------------------------
    Reporter:  simonpj           |        Owner:              
        Type:  task              |       Status:  new         
    Priority:  low               |    Milestone:  6.14.1      
   Component:  Compiler          |      Version:  6.6.1       
    Keywords:                    |     Testcase:              
   Blockedby:                    |   Difficulty:  Unknown     
          Os:  Unknown/Multiple  |     Blocking:              
Architecture:  Unknown/Multiple  |      Failure:  None/Unknown
---------------------------------+------------------------------------------

Comment(by kolmodin):

 I thought I might share my story too, as I ran into this as well, and got
 aware of this ticket.
 3 years after Duncan wrote his mail about the need for speed in binary I
 ran into the same trouble, again with the binary package.

 Duncan wrote about the Put module, which creates lazy Bytestrings from an
 DSL.
 The Get module reads such Bytestrings with a similar DSL.

 I've rewritten the Get module to use CPS, which allows some useful tricks
 when parsing.

 The Get data type from the non-CPS Get module looks like this:

 {{{
 -- | The parse state
 data S = S {-# UNPACK #-} !B.ByteString  -- current chunk
            L.ByteString                  -- the rest of the input
            {-# UNPACK #-} !Int64         -- bytes read

 -- | The Get monad is just a State monad carrying around the input
 ByteString
 newtype Get a = Get { unGet :: S -> (# a, S #) }
 }}}

 While the CPS version looks like this (heavily influenced by attoparsec):

 {{{
 data S = S { input      :: !B.ByteString
            , next_input :: !B.ByteString
            , read_all   :: !Bool
            } deriving Show

 data Result a = Fail S [String] String
               | Partial (Maybe B.ByteString -> Result a)
               | Done S a

 type Failure   r = S -> [String] -> String -> Result r
 type Success a r = S -> a -> Result r


 -- unrolled codensity/state monad
 newtype Get a = C { runCont :: forall r.
                                S ->
                                Failure   r ->
                                Success a r ->
                                Result    r }
 }}}

 Unfortunately, this yields terrible performance.
 The old Get gives me about 26mb/s (Core 2 Duo, 1.6Ghz) when reading chunks
 of 1 byte at a time, while the CPS version only gives up to 1mb/s! Much
 time seems to be spent doing GC, as the bigger Bytestring I read the worse
 performance I get.

 After reasoning a bit about what GHC actually does, I unpacked the state
 into the Get data type:

 {{{
 type Failure   r = B.ByteString -> B.ByteString -> Bool -> [String] ->
 String -> Result r
 type Success a r = B.ByteString -> B.ByteString -> Bool -> a -> Result r

 -- unrolled codensity/state monad
 newtype Get a = C { runCont :: forall r.
                                B.ByteString ->
                                B.ByteString ->
                                Bool ->
                                Failure   r ->
                                Success a r ->
                                Result    r }
 }}}

 This at least gave me 12mb/s (I think I've even been able to get the same
 26mb/s, when further trying to optimize). The price to pay is that the
 code is now not as maintainable, it truly looks horrible.

 So it still seems this ticket is important in some cases.

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


More information about the Glasgow-haskell-bugs mailing list