De los documentos para GHC 7.6:
[Y] a menudo ni siquiera necesitas el pragma SPECIALIZE en primer lugar. Al compilar un módulo M, el optimizador de GHC (con -O) considera automáticamente cada función sobrecargada de nivel superior declarada en M, y la especializa para los diferentes tipos a los que se llama en M. El optimizador también considera cada función sobrecargada INLINABLE importada, y lo especializa para los diferentes tipos en los que se llama en M.
y
Además, dado un pragma SPECIALIZE para una función f, GHC creará automáticamente especializaciones para cualquier función sobrecargada de tipo-clase llamada por f, si están en el mismo módulo que el pragma SPECIALIZE, o si son INLINABLES; y así sucesivamente.
Por lo tanto, GHC debería especializar automáticamente algunas / más / todas (?) Funciones marcadas INLINABLE
sin un pragma, y si uso un pragma explícito, la especialización es transitiva. Mi pregunta es: ¿la transespecialización automática es transitiva?
Específicamente, aquí hay un pequeño ejemplo:
Main.hs:
import Data.Vector.Unboxed as U
import Foo
main =
let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
(Bar (Qux ans)) = iterate (plus y) y !! 100
in putStr $ show $ foldl1' (*) ans
Foo.hs:
module Foo (Qux(..), Foo(..), plus) where
import Data.Vector.Unboxed as U
newtype Qux r = Qux (Vector r)
-- GHC inlines `plus` if I remove the bangs or the Baz constructor
data Foo t = Bar !t
| Baz !t
instance (Num r, Unbox r) => Num (Qux r) where
{-# INLINABLE (+) #-}
(Qux x) + (Qux y) = Qux $ U.zipWith (+) x y
{-# INLINABLE plus #-}
plus :: (Num t) => (Foo t) -> (Foo t) -> (Foo t)
plus (Bar v1) (Bar v2) = Bar $ v1 + v2
GHC se especializa en la llamada a plus
, pero no se especializa (+)
en la Qux
Num
instancia que mata el rendimiento.
Sin embargo, un pragma explícito
{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}
da como resultado la especialización transitiva como indican los documentos, por lo que (+)
está especializado y el código es 30 veces más rápido (ambos compilados con -O2
). ¿Es este comportamiento esperado? ¿Debería esperar solo (+)
estar especializado transitivamente con un pragma explícito?
ACTUALIZAR
Los documentos para 7.8.2 no han cambiado, y el comportamiento es el mismo, por lo que esta pregunta sigue siendo relevante.
plus
estaba marcado como INLINABLE y 2) simonpj indicó que había algo en línea con el código del boleto, pero el núcleo de mi ejemplo muestra que ninguna de las funciones estaba en línea (en particular, no pude deshacerme del segundo constructor, de lo contrario GHC en línea). Foo
plus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2
, para que el LHS se aplique completamente en el sitio de la llamada? ¿Se alinea y luego comienza la especialización?
plus
aplicar completamente debido específicamente a esos enlaces, pero de hecho obtuve menos especialización: la llamada a plus
tampoco estaba especializada. No tengo ninguna explicación para eso, pero tenía la intención de dejarlo para otra pregunta, o espero que se resuelva en una respuesta a esta.