Optimize remainders by powers of two for Integer and Natural
It appears that GHC does not optimise even (n :: Integer)
to a bitwise check, as it does for Int
and Word
(#14437 (closed)). Here is a benchmark:
import Data.Time.Clock
evenRem :: Integer -> Bool
evenRem n = fromIntegral n `rem` 2 == (0 :: Word)
lim :: Integer
lim = 100000000
main :: IO ()
main = do
t0 <- getCurrentTime
print $ maximum $ filter even [1..lim]
t1 <- getCurrentTime
putStrLn "even"
print $ diffUTCTime t1 t0
t0 <- getCurrentTime
print $ maximum $ filter evenRem [1..lim]
t1 <- getCurrentTime
putStrLn "evenRem"
print $ diffUTCTime t1 t0
$ ghc -O2 Even.hs
[1 of 1] Compiling Main ( Even.hs, Even.o )
Linking Even ...
$ ./Even
100000000
even
6.367393s
100000000
evenRem
4.35664s
The reason is that even (n :: Integer)
results in remInteger
call in CMM, which remains unoptimized.
One possible solution is to add a special case of divisor 2 to GHC.Integer.Type.remInteger
. Or perhaps even something like
remInteger n (S# d#)
| isPowerOfTwo (I# d#) = fromIntegral (fromIntegral n `rem` I# d#)
isPowerOfTwo :: Int -> Bool
isPowerOfTwo d = d /= 0 && d .&. (complement d + 1) == d
Since remInteger
is not inlined during Core pipeline, such implementation of even
will pattern-match in runtime, which is a bit suboptimal. On my machine it benchmarks halfway between even
and evenRem
above.
Alternative approach is to add new rules to PrelRules.builtinIntegerRules
to avoid any possible runtime slowdown. Is is appropriate?
Trac metadata
Trac field | Value |
---|---|
Version | 8.4.3 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Core Libraries |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | |
Architecture |