
#16122: `round :: Double -> Int64` much slower than `fromIntegral @Int @Int64 . round` -------------------------------------+------------------------------------- Reporter: Fuuzetsu | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.6.3 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Description changed by Fuuzetsu: Old description:
Consider this simple code:
{{{#!hs {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -ddump-simpl -ddump-to-file -ddump-rule-firings #-} module C (f, g) where
import Data.Int (Int64)
f :: Double -> Int64 f = round
g :: Double -> Int64 g = fromIntegral @Int @Int64 . round }}}
There is a rule in {{{GHC.Int}}} that should translate {{{f}}} into {{{g}}}
{{{#!hs "round/Double->Int64" round = (fromIntegral :: Int -> Int64) . (round :: Double -> Int) }}}
If I compile the above module, I see
{{{ Rule fired: round/Double->Int64 (GHC.Int) Rule fired: fromIntegral/a->Int64 (GHC.Int) Rule fired: fromIntegral/Int->Int (GHC.Real) Rule fired: fromIntegral/a->Int64 (GHC.Int) Rule fired: fromIntegral/Int->Int (GHC.Real) Rule fired: round/Double->Int (GHC.Float) }}}
however in Core the functions end up different: {{{g}}} has an extra dictionary passed around:
{{{#!hs -- RHS size: {terms: 11, types: 6, coercions: 0, joins: 0/0} f :: Double -> Int64 [GblId, Arity=1, Str=
m, Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False) Tmpl= \ (x_a1sG [Occ=Once!] :: Double) -> case x_a1sG of { GHC.Types.D# ww1_a28u [Occ=Once] -> case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of { GHC.Types.I# x#_a1Rr [Occ=Once] -> GHC.Int.I64# x#_a1Rr } }}] f = \ (x_a1sG :: Double) -> case x_a1sG of { GHC.Types.D# ww1_a28u -> case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of { GHC.Types.I# x#_a1Rr -> GHC.Int.I64# x#_a1Rr } }-- RHS size: {terms: 12, types: 14, coercions: 0, joins: 0/0} g :: Double -> Int64 [GblId, Arity=1, Caf=NoCafRefs, Str=
m, Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False) Tmpl= \ (x_X1sX [Occ=Once!] :: Double) -> case x_X1sX of { GHC.Types.D# ds1_a28C [Occ=Once] -> case {__pkg_ccall base-4.12.0.0 Double# -> State# RealWorld -> (# State# RealWorld, Double# #)}_a28B ds1_a28C GHC.Prim.realWorld# of { (# _ [Occ=Dead], ds3_a28H [Occ=Once] #) -> GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H) } }}] g = \ (x_X1sX :: Double) -> case x_X1sX of { GHC.Types.D# ds1_a28C -> case {__pkg_ccall base-4.12.0.0 Double# -> State# RealWorld -> (# State# RealWorld, Double# #)}_a28B ds1_a28C GHC.Prim.realWorld# of { (# ds2_a28G, ds3_a28H #) -> GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H) } } }}}This makes it over an order of magnitude slower than what it should be!
{{{ benchmarked f time 111.1 ns (109.1 ns .. 113.9 ns) 0.997 R² (0.996 R² .. 0.998 R²) mean 111.1 ns (110.4 ns .. 112.0 ns) std dev 2.724 ns (2.195 ns .. 3.271 ns)
benchmarked g time 7.160 ns (7.104 ns .. 7.224 ns) 1.000 R² (0.999 R² .. 1.000 R²) mean 7.196 ns (7.171 ns .. 7.231 ns) std dev 100.5 ps (71.97 ps .. 153.8 ps) }}}
I don't know why the dictionary is present in {{{f}}} but not in {{{g}}}.
New description:
Consider this simple code:
{{{#!hs
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -ddump-simpl -ddump-to-file -ddump-rule-firings #-}
module C (f, g) where
import Data.Int (Int64)
f :: Double -> Int64
f = round
g :: Double -> Int64
g = fromIntegral @Int @Int64 . round
}}}
There is a rule in {{{GHC.Int}}} that should translate {{{f}}} into
{{{g}}}
{{{#!hs
"round/Double->Int64"
round = (fromIntegral :: Int -> Int64) . (round :: Double -> Int)
}}}
If I compile the above module, I see
{{{
Rule fired: round/Double->Int64 (GHC.Int)
Rule fired: fromIntegral/a->Int64 (GHC.Int)
Rule fired: fromIntegral/Int->Int (GHC.Real)
Rule fired: fromIntegral/a->Int64 (GHC.Int)
Rule fired: fromIntegral/Int->Int (GHC.Real)
Rule fired: round/Double->Int (GHC.Float)
}}}
however in Core the functions end up different: {{{f}}} has an extra
dictionary passed around:
{{{#!hs
-- RHS size: {terms: 11, types: 6, coercions: 0, joins: 0/0}
f :: Double -> Int64
[GblId,
Arity=1,
Str=m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_a1sG [Occ=Once!] :: Double) ->
case x_a1sG of { GHC.Types.D# ww1_a28u [Occ=Once] ->
case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt
ww1_a28u of
{ GHC.Types.I# x#_a1Rr [Occ=Once] ->
GHC.Int.I64# x#_a1Rr
}
}}]
f = \ (x_a1sG :: Double) ->
case x_a1sG of { GHC.Types.D# ww1_a28u ->
case GHC.Float.$w$cround @ Int GHC.Real.$fIntegralInt ww1_a28u of
{ GHC.Types.I# x#_a1Rr ->
GHC.Int.I64# x#_a1Rr
}
}
-- RHS size: {terms: 12, types: 14, coercions: 0, joins: 0/0}
g :: Double -> Int64
[GblId,
Arity=1,
Caf=NoCafRefs,
Str=m,
Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
Tmpl= \ (x_X1sX [Occ=Once!] :: Double) ->
case x_X1sX of { GHC.Types.D# ds1_a28C [Occ=Once] ->
case {__pkg_ccall base-4.12.0.0 Double#
-> State# RealWorld -> (# State# RealWorld,
Double# #)}_a28B
ds1_a28C GHC.Prim.realWorld#
of
{ (# _ [Occ=Dead], ds3_a28H [Occ=Once] #) ->
GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H)
}
}}]
g = \ (x_X1sX :: Double) ->
case x_X1sX of { GHC.Types.D# ds1_a28C ->
case {__pkg_ccall base-4.12.0.0 Double#
-> State# RealWorld -> (# State# RealWorld,
Double# #)}_a28B
ds1_a28C GHC.Prim.realWorld#
of
{ (# ds2_a28G, ds3_a28H #) ->
GHC.Int.I64# (GHC.Prim.double2Int# ds3_a28H)
}
}
}}}
This makes it over an order of magnitude slower than what it should be!
{{{
benchmarked f
time 111.1 ns (109.1 ns .. 113.9 ns)
0.997 R² (0.996 R² .. 0.998 R²)
mean 111.1 ns (110.4 ns .. 112.0 ns)
std dev 2.724 ns (2.195 ns .. 3.271 ns)
benchmarked g
time 7.160 ns (7.104 ns .. 7.224 ns)
1.000 R² (0.999 R² .. 1.000 R²)
mean 7.196 ns (7.171 ns .. 7.231 ns)
std dev 100.5 ps (71.97 ps .. 153.8 ps)
}}}
I don't know why the dictionary is present in {{{f}}} but not in {{{g}}}.
--
--
Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/16122#comment:2
GHC http://www.haskell.org/ghc/
The Glasgow Haskell Compiler