De la documentation pour GHC 7.6:
[V] ous n'avez souvent même pas besoin du pragma SPECIALIZE en premier lieu. Lors de la compilation d'un module M, l'optimiseur de GHC (avec -O) considère automatiquement chaque fonction surchargée de niveau supérieur déclarée dans M, et la spécialise pour les différents types auxquels il est appelé dans M. L'optimiseur considère également chaque fonction surchargée INLINABLE importée, et le spécialise pour les différents types auxquels il est appelé en M.
et
De plus, étant donné un pragma SPECIALIZE pour une fonction f, GHC créera automatiquement des spécialisations pour toutes les fonctions surchargées de classe de type appelées par f, si elles sont dans le même module que le pragma SPECIALIZE, ou si elles sont INLINABLE; et ainsi de suite, de manière transitoire.
Ainsi, GHC devrait spécialiser automatiquement certaines fonctions / la plupart / toutes (?) Marquées INLINABLE
sans pragma, et si j'utilise un pragma explicite, la spécialisation est transitive. Ma question est: l' auto- spécialisation est- elle transitive?
Plus précisément, voici un petit exemple:
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 spécialise l'appel à plus
, mais ne se spécialise pas(+)
dans l' Qux
Num
instance qui tue les performances.
Cependant, un pragma explicite
{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}
se traduit par une spécialisation transitive comme l'indiquent les documents, il (+)
est donc spécialisé et le code est 30 fois plus rapide (tous deux compilés avec -O2
). Est-ce un comportement attendu? Dois-je seulement m'attendre (+)
à être spécialisé de manière transitoire avec un pragma explicite?
MISE À JOUR
Les documents pour 7.8.2 n'ont pas changé, et le comportement est le même, donc cette question est toujours d'actualité.
plus
n'était pas marqué comme INLINABLE et 2) simonpj a indiqué qu'il y avait un certain alignement en cours avec le code du ticket, mais le noyau de mon exemple montre qu'aucune des fonctions n'était en ligne (en particulier, je ne pouvais pas me débarrasser du deuxième Foo
constructeur, sinon les choses en ligne de GHC).
plus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2
, afin que le LHS soit pleinement appliqué sur le site d'appel? Est-il aligné puis la spécialisation se déclenche-t-elle?
plus
appliquer pleinement en raison de ces liens, mais en fait j'ai eu moins de spécialisation: l'appel à plus
n'était pas spécialisé non plus. Je n'ai aucune explication à cela, mais j'avais l'intention de le laisser pour une autre question, ou j'espère que cela sera résolu dans une réponse à celle-ci.