Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
  • Sign in / Register
  • GHC GHC
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Locked Files
  • Issues 4,872
    • Issues 4,872
    • List
    • Boards
    • Service Desk
    • Milestones
    • Iterations
  • Merge requests 457
    • Merge requests 457
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
    • Test Cases
  • Deployments
    • Deployments
    • Releases
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Code review
    • Insights
    • Issue
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Glasgow Haskell Compiler
  • GHCGHC
  • Issues
  • #20321
Closed
Open
Created Sep 01, 2021 by Andreas Klebinger@AndreasKDeveloper

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).

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking