Word type to Double or Float conversions are slower than Int conversions
We have int2Double# and int2Float# primitives, but not equivalent ones for Word types. We may need word2Double# too, for Words* to be fully first-class performance-wise.
This means we have to do extra tests in the Num instances for Word types to implement 'fromIntegral':
toInteger (W# x#)
| i# >=# 0# = smallInteger i#
| otherwise = wordToInteger x#
where i# = word2Int# x#
Now, for some types, we work around this:
"fromIntegral/Int->Word" fromIntegral = \(I# x#) -> W# (int2Word# x#)
"fromIntegral/Word->Int" fromIntegral = \(W# x#) -> I# (word2Int# x#)
"fromIntegral/Word->Word" fromIntegral = id :: Word -> Word
and so on for other Word/Int types. And all is fine.
The problem comes up for Float and Double. For Int, we can write:
"fromIntegral/Int->Float" fromIntegral = int2Float
"fromIntegral/Int->Double" fromIntegral = int2Double
int2Float :: Int -> Float
int2Float (I# x) = F# (int2Float# x)
int2Double :: Int -> Double
int2Double (I# x) = D# (int2Double# x)
But we can't write these rules for Word types.
The result is a slow down on Word conversions, consider this program:
main = print . sumU
. mapU (fromIntegral::Int->Double)
$ enumFromToU 0 100000000
When in lhs is Int, we get this nice code:
$wfold :: Double# -> Int# -> Double#
$wfold =
\ (ww_s18k :: Double#) (ww1_s18o :: Int#) ->
case ># ww1_s18o 100000000 of wild_a14T {
False ->
$wfold
(+## ww_s18k (int2Double# ww1_s18o)) (+# ww1_s18o 1);
True -> ww_s18k
But for Word types, we get:
$wfold :: Double# -> Word# -> Double#
$wfold =
\ (ww_s1gN :: Double#) (ww1_s1gR :: Word#) ->
case gtWord# ww1_s1gR __word 100000000 of wild_a1do {
False ->
case case >=# (word2Int# ww1_s1gR) 0 of wild1_a1cS {
False ->
case word2Integer# ww1_s1gR of wild11_a1d9 { (# s_a1db, d_a1dc #) ->
case {__ccall __encodeDouble Int#
-> ByteArray#
-> Int#
-> State# RealWorld
-> (# State# RealWorld, Double# #)}_a1bT
s_a1db d_a1dc 0 realWorld#
of wild12_a1bX { (# ds1_a1bZ, ds2_a1c0 #) ->
ds2_a1c0
}
};
True -> int2Double# (word2Int# ww1_s1gR)
}
of wild1_a1bM { __DEFAULT ->
$wfold
(+## ww_s1gN wild1_a1bM) (plusWord# ww1_s1gR __word 1)
};
True -> ww_s1gN
}
Which is to be expected, and the running time goes from:
$ time ./henning
5.00000000067109e17
./henning 1.53s user 0.00s system 99% cpu 1.534 total
To:
$ time ./henning
5.00000000067109e17
./henning 4.57s user 0.00s system 99% cpu 4.571 total
So not too bad, but still, principle of least surprise says Word and Int should behave the same.
Should we have a word2Double# primop?
Trac metadata
Trac field | Value |
---|---|
Version | 6.8.2 |
Type | FeatureRequest |
TypeOfFailure | OtherFailure |
Priority | normal |
Resolution | Unresolved |
Component | Compiler |
Test case | |
Differential revisions | |
BlockedBy | |
Related | |
Blocking | |
CC | |
Operating system | Unknown |
Architecture | Unknown |