[GHC] #8032: Worker-wrapper transform triggers bad reboxing behavior
#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@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
participants (1)
-
GHC