Especialización con restricciones

156

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 plusFastCyces demasiado grande para que GHC lo conecte, incluso cuando está marcado INLINE. El punto es especializar la llamada a plusFastCyc, no en línea. plusFastCycse 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 plusFastCycen 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.hsarchivo tiene dos controladores: vtTestque se ejecuta en ~ 3 segundos y fcTestque se ejecuta en ~ 83 segundos cuando se compila con -O3 utilizando la forallespecialización 'd.

El núcleo muestra que para la vtTestprueba, el código de adición se está especializando en Unboxedvectores sobre Ints, 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. ( main6llamadas iterate main8 y, por lo que main8es donde plusFastCycdebe estar especializado)

Mi objetivo es hacerlo fcTesttan rápido como vtTestespecializándome plusFastCyc. He encontrado dos formas de hacer esto:

  1. Llamada explícita inlinedesde GHC.Extsadentro fcTest.
  2. Eliminar la Factored m Intrestricción en plusFastCyc.

La opción 1 no es satisfactoria porque en la base del código real plusFastCyces 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, INLINABLEy SPECIALIZE, pero nada parece funcionar. ( EDITAR : puede que me haya quitado demasiado plusFastCycpara hacer que mi ejemplo sea pequeño, por lo que INLINEpodría hacer que la función esté en línea. Esto no sucede en mi código real porque plusFastCyces muy grande). En este ejemplo en particular, no estoy obteniendo alguna match_co: needs more caseso RULE: LHS too complicated to desugar(y aquí ) advertencias, aunque recibí muchas match_coadvertencias antes de minimizar el ejemplo. Presumiblemente, el "problema" es la Factored m Intrestricción en la regla; si hago cambios a esa restricción, se fcTestejecuta tan rápido como vtTest.

¿Estoy haciendo algo que a GHC simplemente no le gusta? ¿Por qué no se especializa GHC plusFastCycy cómo puedo hacerlo?

ACTUALIZAR

El problema persiste en GHC 7.8.2, por lo que esta pregunta sigue siendo relevante.

crockeea
fuente
3
Acabo de intentar especializarme en un específico m , a saber M. Esto hizo el trabajo, pero no puedo especializarme para tipos fantasmas específicos en el programa real ya que están reificados.
crockeea
También envié un informe de error de GHC ghc.haskell.org/trac/ghc/ticket/8668 pero el problema aún está abierto. El proceso de informe de errores me ayudó a aclarar un poco la pregunta, así que espero que sea más fácil descubrir qué está sucediendo.
crockeea
@monojohnny Lamento escuchar eso, creo que puedes marcarlo como tal. Creo que le estoy pidiendo a GHC que haga algo bastante razonable, y no lo hará. No estoy seguro de si lo estoy haciendo mal, o si esto es una idiosincrasia con el compilador que podría tener una solución. He visto soluciones para la especialización y las reglas en alguna biblioteca específica sobre piratería que se me escapa en este momento, por lo que espero que alguien en la comunidad con más experiencia en GHC que yo pueda saber cómo lograr la especialización.
crockeea
1
Pido disculpas por el tono de mi comentario, no es mi mejor contribución a este sitio, realmente no hay nada de malo en su publicación (¡Supongo que es mi falta de comprensión la fuente de mi molestia!)
monojohnny
@monojohnny Disculpa aceptada, pero es una pena que el voto negativo esté bloqueado ahora ;-)
crockeea

Respuestas:

5

GHC también ofrece una opción para SPECIALIZEuna declaración de instancia de tipo de clase. Intenté esto con el código (expandido) de Foo.hs, poniendo lo siguiente:

instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where 
    {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-}
    VT x + VT y = VT $ V.zipWith (+) x y

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 Intcon las mismas definiciones de funciones, como sigue:

instance (Factored m Int) => Num (VT U.Vector m Int) where 
    VT x + VT y = VT $ V.zipWith (+) x y

Esto requiere la adición OverlappingInstancesy FlexibleInstancesen LANGUAGE.

Curiosamente, en el programa de ejemplo, la aceleración obtenida con la instancia superpuesta permanece incluso si elimina todo SPECIALIZEy INLINABLEpragma.

Diego E. Alonso-Blas
fuente
Definitivamente no es óptimo, pero es la primera solución que realmente logra el objetivo, así que supongo que lo tomaré por ahora ...
crockeea