Constructor specialization requires eta expansion
I recently encountered a slightly tricky performance issue[1] in
bytestring
's Builder
implementation which required explicit eta
expansion in order for GHC to perform ConstrSpec.
We have
data BufferRange = BufferRange {-# UNPACK #-} !(Ptr Word8) -- First byte of range
{-# UNPACK #-} !(Ptr Word8) -- First byte /after/ range
data BuildSignal a = ... -- Just a vanilla ADT
type BuildStep a = BufferRange -> IO (BuildSignal a)
We end up with Core (full version https://gist.github.com/bgamari/091e3dac9c45ee9accf1#file-slow-hs-L1) that looks like this,
Main.$wa
:: GHC.Prim.Word#
-> GHC.Prim.Int#
-> forall r_aKUt.
Data.ByteString.Builder.Internal.BuildStep r_aKUt
-> Data.ByteString.Builder.Internal.BuildStep r_aKUt
Main.$wa =
\ (ww_s10YU :: GHC.Prim.Word#)
(ww1_s10YY :: GHC.Prim.Int#)
(@ r_aKUt)
(w_s10YR :: Data.ByteString.Builder.Internal.BuildStep r_aKUt) ->
case ww1_s10YY of wild_XE {
__DEFAULT -> (
\ (eta1_X1Q :: Data.ByteString.Builder.Internal.BufferRange) ->
case eta1_X1Q of BufferRange a b ->
-- A bunch of code eventually ending in a recursive call to $wa
)
0 -> w_s10YR
}
If w_s10YR
is eta-expanded GHC will run ConstrSpec, eliminating the
fields of BufferRange
to be unpacked between iterations of $wa
and
substantially improving performance.
In [1] we had to manually eta-expand the empty case to ensure that this would happen. It would be great if GHC would identify cases like this.
[1] https://github.com/haskell/bytestring/pull/40
Trac metadata
Trac field | Value |
---|---|
Version | 7.8.4 |
Type | Bug |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |