[GHC] #10678: integer-gmp's runS seems unnecessarily expensive
GHC
ghc-devs at haskell.org
Tue Aug 4 15:34:45 UTC 2015
#10678: integer-gmp's runS seems unnecessarily expensive
-------------------------------------+-------------------------------------
Reporter: rwbarton | Owner:
Type: bug | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 7.10.1
(CodeGen) |
Resolution: | Keywords:
Operating System: Unknown/Multiple | Architecture:
Type of failure: Runtime | Unknown/Multiple
performance bug | Test Case:
Blocked By: | Blocking:
Related Tickets: | Differential Revisions:
-------------------------------------+-------------------------------------
Comment (by rwbarton):
Replying to [comment:7 simonpj]:
> Good! Can you give a small example of your last para? I bet it's
something like
> {{{
> case (runRW (\s -> I# x)) of (# _ , I# y) -> blah
> }}}
Right, so in integer-gmp, where I previously had
{{{
runS :: S RealWorld a -> a -- type S s a = State# s -> (# State# s, a
#)
runS m = case runRW# m of (# _, a #) -> a
}}}
when `a = BigNat` (`data BigNat = BN# ByteArray#`), I can instead use
{{{
runS_BigNat :: S RealWorld BigNat -> BigNat
runS_BigNat m = case runRW# (\s -> case m s of (# s', BN# ba #) -> (# s',
ba #)) of
(# _, ba #) -> BN# ba
}}}
The idea is
* `m` usually simplifies to something like
{{{
\s0 -> case newByteArray# n s0 of
(# s1, arr #) -> case someGmpFunction arr s1 of
s2 -> case unsafeFreezeByteArray# arr s2 of
(# s3, farr #) -> (# s3, BN# farr #)
}}}
so in the "inner case" in `runS_BigNat`, the constructor `BN#` will
cancel
* Now the caller of `runS_BigNat` (say, `plusBigNat`) has the CPR property
because the `BN#` constructor is visible outside the `runRW#`
* Its caller in turn (say, `plusInteger`) looks like
{{{
plusInteger (Jp# x) (Jp# y) = Jp# (plusBigNat x y)
}}}
but `Jp#`'s argument is unboxed for size reasons
{{{
data Integer = ... | Jp# {-# UNPACK #-} !BigNat | ...
}}}
so `Jp# (plusBigNat x y)` would have meant extracting the field from a
`BN#` and repacking it into a `Jp#`. But by calling the unboxed worker we
can avoid ever allocating an intermediate `BN#` constructor.
Note that the transformation `runS -> runS_BigNat` is always valid for any
`m`, even though it looks like a nested CPR situation. The reason is that
the context of the `runRW#` is strict in the second argument anyways. To
determine when the transformation is a good idea for a particular argument
`m`, we can use regular CPR analysis on the function `\s -> case m s of (#
_, r #) -> r`.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10678#comment:9>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list