Tengo problemas para que GHC especialice una función con una restricción de clase. Tengo un ejemplo mínimo de mi problema aquí: Foo.hs y Main.hs . Los dos archivos se compilan (GHC 7.6.2, ghc -O3 Main
) y se ejecutan.
NOTA:
Foo.hs
está realmente despojado. Si desea ver por qué se necesita la restricción, puede ver un poco más de código aquí . Si pongo el código en un solo archivo o hago muchos otros cambios menores, GHC simplemente alinea la llamada plusFastCyc
. Esto no sucederá en el código real porque plusFastCyc
es demasiado grande para que GHC lo conecte, incluso cuando está marcado INLINE
. El punto es especializar la llamada a plusFastCyc
, no en línea. plusFastCyc
se llama en muchos lugares en el código real, por lo que duplicar una función tan grande no sería deseable incluso si pudiera forzar a GHC a hacerlo.
El código de interés es el plusFastCyc
en Foo.hs
, reproducido aquí:
{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc ::
forall m . (Factored m Int) =>
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) #-}
-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in my real program because the phantom type M is reified
-- {-# SPECIALIZE plusFastCyc ::
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int #-}
plusFastCyc :: (Num (t r)) => (FastCyc t r) -> (FastCyc t r) -> (FastCyc t r)
plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
El Main.hs
archivo tiene dos controladores: vtTest
que se ejecuta en ~ 3 segundos y fcTest
que se ejecuta en ~ 83 segundos cuando se compila con -O3 utilizando la forall
especialización 'd.
El núcleo muestra que para la vtTest
prueba, el código de adición se está especializando en Unboxed
vectores sobre Int
s, etc., mientras que se usa el código vectorial genérico fcTest
. En la línea 10, se puede ver que no GHC escribir una versión especializada de plusFastCyc
, en comparación con la versión genérica de la línea 167. La regla para la especialización está en la línea 225. Creo que esta regla debe disparar en la línea 270. ( main6
llamadas iterate main8 y
, por lo que main8
es donde plusFastCyc
debe estar especializado)
Mi objetivo es hacerlo fcTest
tan rápido como vtTest
especializándome plusFastCyc
. He encontrado dos formas de hacer esto:
- Llamada explícita
inline
desdeGHC.Exts
adentrofcTest
. - Eliminar la
Factored m Int
restricción enplusFastCyc
.
La opción 1 no es satisfactoria porque en la base del código real plusFastCyc
es una operación de uso frecuente y una función muy grande, por lo que no se debe incluir en cada uso. Más bien, GHC debería llamar a una versión especializada de plusFastCyc
. La opción 2 no es realmente una opción porque necesito la restricción en el código real.
He probado una variedad de opciones usando (y no usar) INLINE
, INLINABLE
y SPECIALIZE
, pero nada parece funcionar. ( EDITAR : puede que me haya quitado demasiado plusFastCyc
para hacer que mi ejemplo sea pequeño, por lo que INLINE
podría hacer que la función esté en línea. Esto no sucede en mi código real porque plusFastCyc
es muy grande). En este ejemplo en particular, no estoy obteniendo alguna match_co: needs more cases
o RULE: LHS too complicated to desugar
(y aquí ) advertencias, aunque recibí muchas match_co
advertencias antes de minimizar el ejemplo. Presumiblemente, el "problema" es la Factored m Int
restricción en la regla; si hago cambios a esa restricción, se fcTest
ejecuta tan rápido como vtTest
.
¿Estoy haciendo algo que a GHC simplemente no le gusta? ¿Por qué no se especializa GHC plusFastCyc
y cómo puedo hacerlo?
ACTUALIZAR
El problema persiste en GHC 7.8.2, por lo que esta pregunta sigue siendo relevante.
m
, a saberM
. Esto hizo el trabajo, pero no puedo especializarme para tipos fantasmas específicos en el programa real ya que están reificados.Respuestas:
GHC también ofrece una opción para
SPECIALIZE
una declaración de instancia de tipo de clase. Intenté esto con el código (expandido) deFoo.hs
, poniendo lo siguiente:Sin embargo, este cambio no logró la aceleración deseada. Lo que logró esa mejora en el rendimiento fue agregar manualmente una instancia especializada para el tipo
VT U.Vector m Int
con las mismas definiciones de funciones, como sigue:Esto requiere la adición
OverlappingInstances
yFlexibleInstances
enLANGUAGE
.Curiosamente, en el programa de ejemplo, la aceleración obtenida con la instancia superpuesta permanece incluso si elimina todo
SPECIALIZE
yINLINABLE
pragma.fuente