[GHC] #8032: Worker-wrapper transform triggers bad reboxing behavior
GHC
ghc-devs at haskell.org
Wed Jul 3 02:31:45 CEST 2013
#8032: Worker-wrapper transform triggers bad reboxing behavior
------------------------------------+---------------------------------------
Reporter: ezyang | Owner:
Type: bug | Status: new
Priority: normal | Component: Compiler
Version: 7.7 | Keywords:
Os: Unknown/Multiple | Architecture: Unknown/Multiple
Failure: Runtime performance bug | Blockedby:
Blocking: | Related:
------------------------------------+---------------------------------------
When we worker-wrapper transform functions, we tend to be to eager to
unbox all of our arguments and pass them to the worker. This backfires
when the boxed version of the argument is needed:
{{{
module Gnam where
data KST = KST {
ke :: {-# UNPACK #-} !Int
, ka :: {-# UNPACK #-} !Int
}
data KStop r = KNeedInput Int !(KC r)
data Result r = Result r
type KC r = KST -> Int -> Result r
newtype K r a = K {
unK :: (KST -> KStop r -> Result r) -> KC r
}
skipWhileK :: K () ()
skipWhileK =
K $ \kf kst at KST{ ke = e } i0 ->
let loop i rw0 -- Note: removing rw0 argument gets rid of re-boxing
behavior
| i < e = loop (i + 1) rw0
| otherwise = unK recurse kf kst i
in loop i0 (0 :: Int)
where -- Note that without NOINLINE, we get an unnecessary eager
-- closure allocation even when closure is never used. This
-- is unfortunate because recurse is the slow path (e.g.,
-- called 1/4000 of the time in the real code).
{-# NOINLINE recurse #-}
recurse = K $ \kf kst i -> kf kst $ KNeedInput i $ unK skipWhileK
kf
}}}
skipWhileK is an important loop which we would like to avoid performing
heap allocation on. However, this code is compiled by HEAD with an
allocation which reboxes KST:
{{{
Gnam.$wa
:: (Gnam.KST -> Gnam.KStop () -> Gnam.Result ())
-> GHC.Prim.Int#
-> GHC.Prim.Int#
-> GHC.Prim.Int#
-> Gnam.Result ()
[GblId,
Arity=4,
Caf=NoCafRefs,
Str=DmdType <C(C(S)),U><L,U><L,U><L,U>,
Unf=OtherCon []] =
\r srt:SRT:[] [w_srG ww_srz ww1_srA ww2_srK]
let {
wild_srB :: Gnam.KST
[LclId, Str=DmdType, Unf=OtherCon []] =
NO_CCS Gnam.KST! [ww_srz ww1_srA];
} in ...
}}}
The worker function takes i and the unboxed KST as arguments, as one might
hope, but when it discovers that it needs KST on the inside, it has no
choice but to reconstruct KST inside. What should be done instead is
wild_srB (produced by the case analysis on KST here:
{{{
\r srt:SRT:[] [w_srV w1_srO w2_srS]
case w1_srO of _ { // <--- wild_ here please!
Gnam.KST ww1_srW ww2_srX ->
case w2_srS of _ {
GHC.Types.I# ww4_srY -> Gnam.$wa w_srV ww1_srW ww2_srX
ww4_srY;
};
};
}}}
This is perhaps a tradeoff: passing the evaluated version of things that
were unpacked costs an extra argument; however, unboxing things can result
in a lot of extra arguments! (And GHC doesn't seem to eliminate unused
arguments, even when the rebox is not necessary.)
--
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/8032>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list