Ticket #3034 (new bug)

Opened 4 years ago

Last modified 8 months ago

divInt# floated into a position which leads to low arity

Reported by: batterseapower Owned by:
Priority: lowest Milestone: 7.6.2
Component: Compiler Version: 6.10.1
Keywords: Cc: twhitehead@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Runtime performance bug Difficulty: Unknown
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Tyson Whitehead saw this in the Core output of one of his programs compiled using the MTL StateT:

$wdigit_s1GR [ALWAYS LoopBreaker Nothing] :: GHC.Prim.Int#
                                           -> GHC.Types.Int
                                           -> Control.Monad.State.Strict.StateT
                                                GHC.Types.Int
                                                (Control.Monad.Error.ErrorT
                                                   GHC.Base.String
                                                   Control.Monad.Identity.Identity)
                                                (GHC.Types.Int, GHC.Types.Int)
[Arity 1
Str: DmdType L]
$wdigit_s1GR =
\ (ww_X1H6 :: GHC.Prim.Int#) ->
  let {
    lvl_s1H5 [ALWAYS Just D(T)] :: GHC.Types.Int
    [Str: DmdType]
    lvl_s1H5 =
      case GHC.Prim.-# 2147483647 ww_X1H6 of wild2_a1xs [ALWAYS Just L] {
        __DEFAULT ->
          case GHC.Base.divInt# wild2_a1xs 10
          of wild21_a1xt [ALWAYS Just L] { __DEFAULT ->
          GHC.Types.I# wild21_a1xt
          };
        (-2147483648) -> lvl_s1Ha
      } } in
  (\ (eta_X1nK :: GHC.Types.Int) (eta_s1Dl :: GHC.Types.Int) ->
     case eta_s1Dl
     of y_XrP [ALWAYS Just A] { GHC.Types.I# ipv_s19d [ALWAYS Just L] ->
     case GHC.Prim.<=# ipv_s19d 214748363
     of wild_a19h [ALWAYS Dead Just A] {
       GHC.Bool.False ->
         case lvl_s1H5
         of wild1_X1zB [ALWAYS Just A]
         { GHC.Types.I# y_X1zG [ALWAYS Just L] ->
         case GHC.Prim.<=# ipv_s19d y_X1zG
         of wild_X1z [ALWAYS Dead Just A] {
           GHC.Bool.False ->
             a_s1Hk
             `cast` (right

This REALLY SHOULD have arity 3 because that allows:

  • More worker/wrapper
  • Less sharing of trivial partial applications elsewhere in his program

Here is my reply to him, explaining why it all happens:

Yes - GHC wants to share the work of (maxBound-x)`div`10 between
several partial applications of "digit". This is usually a good idea,
but in this case it sucks because it's resulted in a massively
increased arity. IMHO GHC should fix this by:
* Marking divInt# INLINE in the base library. This would result in
your code would just containing uses of quotInt#
* Making some operations cheap even if they may fail
(PrimOp.primpOpIsCheap should change). Though this might mean that we
turn non-terminating programs into terminating ones (such operations
get pushed inside lambdas) but this is consistent with our treatment
of lambdas generally.

Actually, your divInt# call wouldn't even usually be floated out to
between two lambdas, but at the time FloatOut runs there is something
in between the \x lambda and the lambdas from the state monad - the
monadic bind operator! So FloatOut feels free to move the computation
for "x" up even though that >>= will go away as soon as we run the
simplifier. What a disaster!

So one of FloatOut? and primOpIsCheap probably needs to be fixed.

I've attached a program that can reproduce this issue.

Attachments

Parse.hs Download (2.2 KB) - added by batterseapower 4 years ago.

Change History

Changed 4 years ago by batterseapower

Changed 4 years ago by batterseapower

  • cc twhitehead@… added

Changed 4 years ago by twhitehead

For some reason, using quot instead of div produces the desired code. This is despite quotInt# not being cheap according to ghci 6.10.1

Prelude> PrimOp?.primOpIsCheap PrimOp?.IntQuotOp? False

Maybe it has something to do with there being a primitive IntQuotOp? but not a corresponding IntDivOp? (tidy core gives GHC.Prim.quotInt# versus GHC.Base.divInt#)

Changed 4 years ago by twhitehead

I should avoiding the division altogether through quotRem generates really bad code as well

where digit :: Int -> Int -> ParseInt? Int Int

digit !x = do !y <- lift get

( if y < ym y==ym && x<=xm

then lift (put $ y*10+x) >> s2 else throwError "integer overflow" )

where (ym,xm) = maxBoundquotRem10

Putting separate "ym = maxBoundquot10" and "xm = maxBoundrem10" bindings gives good code.

Examining the assembler for a seperate routine like

demo :: Int -> Int -> Int demo x y = q+r

where q = quot x y

r = rem x y

shows this isn't a really great option in general because the GHC does it through two identical idivs on the x86_64. I am guessing the underlying issue is much the same due to no IntQuotRem? prim op.

Changed 4 years ago by igloo

  • difficulty set to Unknown
  • milestone set to 6.12 branch

Changed 4 years ago by simonmar

  • failure set to Runtime performance bug

Changed 3 years ago by igloo

  • milestone changed from 6.12 branch to 6.12.3

Changed 3 years ago by igloo

  • priority changed from normal to low
  • milestone changed from 6.12.3 to 6.14.1

Changed 2 years ago by igloo

  • milestone changed from 7.0.1 to 7.0.2

Changed 2 years ago by igloo

  • milestone changed from 7.0.2 to 7.2.1

Changed 20 months ago by igloo

  • milestone changed from 7.2.1 to 7.4.1

Changed 15 months ago by igloo

  • priority changed from low to lowest
  • milestone changed from 7.4.1 to 7.6.1

Changed 8 months ago by igloo

  • milestone changed from 7.6.1 to 7.6.2
Note: See TracTickets for help on using tickets.