para pagina online oficial mac instalar descargar compiler como haskell ghc

haskell - pagina - Transitividad de la Auto-Especialización en GHC



haskell pagina oficial (1)

De los documentos para GHC 7.6:

[A menudo ni siquiera necesitas el pragma ESPECIALIZAR 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 a los que se llama en M.

y

Además, dado un pragma SPECIALIZE para una función f, GHC creará automáticamente las especializaciones para cualquier función sobrecargada de clase de tipo llamada por f, si están en el mismo módulo que el pragma SPECIALIZE, o si son INLINABLES; Y así sucesivamente, transitivamente.

Por lo tanto, GHC debería especializar automáticamente some/most/all(?) funciones some/most/all(?) INLINABLE como INLINABLE sin pragma, y ​​si uso un pragma explícito, la especialización es transitiva. Mi pregunta es: ¿es transitivo la autoespecialización?

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 especializa la llamada a plus , pero no se especializa (+) en la instancia de Qux Num que mata el rendimiento.

Sin embargo, un pragma explícito.

{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}

los resultados en la especialización transitiva como indican los documentos, por lo que (+) está especializado y el código es -O2 más rápido (ambos compilados con -O2 ). ¿Es este el comportamiento esperado? ¿Debo esperar que (+) se especialice 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.


Respuestas cortas:

Los puntos clave de la pregunta, tal como los entiendo, son los siguientes:

  • ¿Es transitivo la autoespecialización?
  • ¿Debo esperar que (+) se especialice transitivamente con un pragma explícito?
  • (aparentemente destinado) ¿Es este un error de GHC? ¿Es inconsistente con la documentación?

AFAIK, las respuestas son No, la mayoría sí, pero hay otros medios, y No.

La inclusión de código y la especialización de aplicaciones de tipo es una compensación entre velocidad (tiempo de ejecución) y tamaño de código. El nivel predeterminado obtiene un poco de aceleración sin inflar el código. La elección de un nivel más exhaustivo se deja a la discreción del programador a través del programa SPECIALISE .

Explicación:

El optimizador también considera cada función sobrecargada INLINABLE importada, y la especializa para los diferentes tipos a los que se llama en M.

Supongamos que f es una función cuyo tipo incluye una variable de tipo a restringida por una clase de tipo C a . Por defecto, GHC especializa f con respecto a una aplicación de tipo (sustituyendo a por t ) si f se llama con esa aplicación de tipo en el código fuente de (a) cualquier función en el mismo módulo, o (b) si f está marcado como INLINABLE entonces cualquier otro módulo que importe f desde B . Por lo tanto, la auto-especialización no es transitiva, solo toca INLINABLE funciones INLINABLE importadas y solicitadas en el código fuente de A

En su ejemplo, si reescribe la instancia de Num siguiente manera:

instance (Num r, Unbox r) => Num (Qux r) where (+) = quxAdd quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y

  • quxAdd no es importado específicamente por Main . Main importa el diccionario de instancia de Num (Qux Int) , y este diccionario contiene quxAdd en el registro para (+) . Sin embargo, aunque el diccionario es importado, los contenidos utilizados en el diccionario no lo son.
  • plus no llama a quxAdd , usa la función almacenada para el registro (+) en el diccionario de instancia de Num t . Este diccionario se establece en el sitio de la llamada (en Main ) por el compilador.