SpecConstr increasing allocation. Maybe by triggering reboxing
I noticed that nofib/real/ben-raytrace seems to increase allocations quite significantly with -fspec-constr compared to without.
In nofib/real/ben-raytracer we have this function:
hitTestMany :: [Figure] -> Ray3 -> Interval -> Maybe Hit
hitTestMany figs ray = go_hit figs Nothing
where
go_hit :: [Figure] -> Maybe Hit -> Interval -> Maybe Hit
go_hit [] accum !_ = accum
go_hit (fig:rest) accum int =
case hitTest fig ray int of
Nothing -> go_hit rest accum int
Just hit ->
case accum of
Nothing
-> go_hit rest (Just hit) (int {iUpper=hitDistance hit})
Just oldHit
| hitDistance hit < hitDistance oldHit
-> go_hit rest (Just hit) (int {iUpper=hitDistance hit})
| otherwise
-> go_hit rest accum int
Without spec-constr we get somewhat straight forward core:
joinrec {
$wgo_hit_s2L4 [InlPrag=[2],
Occ=LoopBreaker,
Dmd=SCS(C1(C1(C1(L))))]
:: [Figure]
-> Maybe Hit -> GHC.Prim.Double# -> GHC.Prim.Double# -> Maybe Hit
[LclId[JoinId(4)], Arity=4, Str=<1L><1L><L><L>, Unf=OtherCon []]
$wgo_hit_s2L4 (ds_s2KW :: [Figure])
(accum_s2KX :: Maybe Hit)
(ww9_X2 :: GHC.Prim.Double#)
(ww10_X3 :: GHC.Prim.Double#)
= case ds_s2KW of {
[] -> accum_s2KX;
: fig_aSB rest_aSC ->
case fig_aSB of { Figure ds1_d27m ds2_d27n ->
case ds2_d27n ray_aSy (Interval.Interval ww9_X2 ww10_X3)
of wild4_X6 {
Nothing -> jump $wgo_hit_s2L4 rest_aSC accum_s2KX ww9_X2 ww10_X3;
Just hit_aSF ->
case accum_s2KX of wild5_X7 {
Nothing ->
case hit_aSF of
{ Hit bx_d2f3 ds3_d27x ds4_d27y ds5_d27z ds6_d27A ->
jump $wgo_hit_s2L4 rest_aSC wild4_X6 ww9_X2 bx_d2f3
};
Just oldHit_aSG ->
case hit_aSF of
{ Hit bx_d2f3 ds3_d27x ds4_d27y ds5_d27z ds6_d27A ->
case oldHit_aSG of { Hit bx1_Xa ds7_Xb ds8_Xc ds9_Xd ds10_Xe ->
case GHC.Prim.<## bx_d2f3 bx1_Xa of {
__DEFAULT -> jump $wgo_hit_s2L4 rest_aSC wild5_X7 ww9_X2 ww10_X3;
1# -> jump $wgo_hit_s2L4 rest_aSC wild4_X6 ww9_X2 bx_d2f3
}
}
}
}
}
}
}; } in
Not that we case on the accumulator accum_s2KX
but pass it along boxed and as-is.
However if we enable SpecConstr instead then we get the code hidden below the spoiler tag:
joinrec {
$s$wgo_hit_s2Pr [Occ=LoopBreaker,
Dmd=LCL(C1(C1(C1(C1(C1(C1(C1(L))))))))]
:: GHC.Prim.Double#
-> GHC.Prim.Double#
-> GHC.Prim.Double#
-> Pt Vec3
-> Vec3
-> Pt Vec2
-> Material
-> [Figure]
-> Maybe Hit
[LclId[JoinId(8)],
Arity=8,
Str=<L><L><L><L><L><L><L><1L>,
Unf=OtherCon []]
$s$wgo_hit_s2Pr (sc_s2Pp :: GHC.Prim.Double#)
(sc1_s2Po :: GHC.Prim.Double#)
(sc2_s2Pj :: GHC.Prim.Double#)
(sc3_s2Pk :: Pt Vec3)
(sc4_s2Pl :: Vec3)
(sc5_s2Pm :: Pt Vec2)
(sc6_s2Pn :: Material)
(sc7_s2Pi :: [Figure])
= case sc7_s2Pi of {
[] ->
GHC.Maybe.Just
@Hit (Figure.Hit sc2_s2Pj sc3_s2Pk sc4_s2Pl sc5_s2Pm sc6_s2Pn);
: fig_aSC rest_aSD ->
case fig_aSC of { Figure ds_d27n ds1_d27o ->
case ds1_d27o ray_aSz (Interval.Interval sc1_s2Po sc_s2Pp) of {
Nothing ->
jump $s$wgo_hit_s2Pr
sc_s2Pp
sc1_s2Po
sc2_s2Pj
sc3_s2Pk
sc4_s2Pl
sc5_s2Pm
sc6_s2Pn
rest_aSD;
Just hit_aSG ->
case hit_aSG of
{ Hit bx_d2f4 ds2_d27y ds3_d27z ds4_d27A ds5_d27B ->
case GHC.Prim.<## bx_d2f4 sc2_s2Pj of {
__DEFAULT ->
jump $s$wgo_hit_s2Pr
sc_s2Pp
sc1_s2Po
sc2_s2Pj
sc3_s2Pk
sc4_s2Pl
sc5_s2Pm
sc6_s2Pn
rest_aSD;
1# ->
jump $s$wgo_hit_s2Pr
bx_d2f4
sc1_s2Po
bx_d2f4
ds2_d27y
ds3_d27z
ds4_d27A
ds5_d27B
rest_aSD
}
}
}
}
}; } in
joinrec {
$s$wgo_hit1_s2Pq [Occ=LoopBreaker, Dmd=LCL(C1(C1(L)))]
:: GHC.Prim.Double# -> GHC.Prim.Double# -> [Figure] -> Maybe Hit
[LclId[JoinId(3)], Arity=3, Str=<L><L><1L>, Unf=OtherCon []]
$s$wgo_hit1_s2Pq (sc_s2Ph :: GHC.Prim.Double#)
(sc1_s2Pg :: GHC.Prim.Double#)
(sc2_s2Pf :: [Figure])
= case sc2_s2Pf of {
[] -> GHC.Maybe.Nothing @Hit;
: fig_aSC rest_aSD ->
case fig_aSC of { Figure ds_d27n ds1_d27o ->
case ds1_d27o ray_aSz (Interval.Interval sc1_s2Pg sc_s2Ph) of {
Nothing -> jump $s$wgo_hit1_s2Pq sc_s2Ph sc1_s2Pg rest_aSD;
Just hit_aSG ->
case hit_aSG of
{ Hit bx_d2f4 ds2_d27y ds3_d27z ds4_d27A ds5_d27B ->
jump $s$wgo_hit_s2Pr
bx_d2f4
sc1_s2Pg
bx_d2f4
ds2_d27y
ds3_d27z
ds4_d27A
ds5_d27B
rest_aSD
}
}
}
}; } in
case figs_s2L7 of { :| a1_a28j as_a28k ->
case a1_a28j of { Figure ds_d27n ds1_d27o ->
case ds1_d27o ray_aSz wild1_X1 of {
Nothing -> jump $s$wgo_hit1_s2Pq ww8_s2L2 ww7_s2L1 figs1_s2CL;
Just hit_aSG ->
case hit_aSG of
{ Hit bx_d2f4 ds2_d27y ds3_d27z ds4_d27A ds5_d27B ->
jump $s$wgo_hit_s2Pr
bx_d2f4
ww7_s2L1
bx_d2f4
ds2_d27y
ds3_d27z
ds4_d27A
ds5_d27B
figs1_s2CL
}
}
SpecConstr seems to specialise on the accumulator and in the Just
case also unboxes the Hit constructor.
However we eventually want to return the boxed Just (Hit ...)
constructor just the way we got it. So we eventually end up reboxing it (and the Just it's contained in).
This is quite annoying. I guess this could eventually be tied into the boxity analysis @sgraf812 mentions in #19871 (closed).