Spécialisation avec contraintes

J’ai des problèmes pour que GHC spécialise une fonction avec une contrainte de classe. J’ai un exemple minimal de mon problème ici: Foo.hs et Main.hs. Les deux fichiers comstacknt (GHC 7.6.2, ghc -O3 Main ) et s’exécutent.

NOTE: Foo.hs est vraiment dépouillé. Si vous voulez voir pourquoi la contrainte est nécessaire, vous pouvez voir un peu plus de code ici . Si je mets le code dans un seul fichier ou apporte de nombreuses modifications mineures, GHC insiste simplement sur l’appel à plusFastCyc . Cela ne se produira pas dans le code réel, car plusFastCyc est trop grand pour que GHC soit en ligne, même s’il est marqué INLINE . Le point est de spécialiser l’appel à plusFastCyc , pas de l’inline. plusFastCyc est appelé à de nombreux endroits dans le code réel, donc dupliquer une telle fonction ne serait pas souhaitable, même si je pouvais forcer GHC à le faire.

Le code d’intérêt est le plusFastCyc dans Foo.hs , reproduit ici:

 {-# 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 (tr)) => (FastCyc tr) -> (FastCyc tr) -> (FastCyc tr) plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2 

Le fichier Main.hs a deux pilotes: vtTest , qui s’exécute en ~ 3 secondes, et fcTest , qui s’exécute en ~ 83 secondes lorsqu’il est compilé avec -O3 en utilisant la spécialisation forall ‘d.

Le kernel montre que pour le test vtTest , le code d’addition est spécialisé dans les vecteurs Unboxed sur Int s, etc., tandis que le code vectoriel générique est utilisé pour fcTest . Sur la ligne 10, vous pouvez voir que GHC écrit une version spécialisée de plusFastCyc , comparée à la version générique de la ligne 167. La règle pour la spécialisation se trouve à la ligne 225. Je pense que cette règle devrait main6 sur la ligne 270. iterate main8 y , alors main8 est l’endroit où plusFastCyc devrait être spécialisé.)

Mon but est de rendre fcTest aussi rapide que vtTest en spécialisant plusFastCyc . J’ai trouvé deux façons de le faire:

  1. Appel d’explicite depuis GHC.Exts en fcTest .
  2. Supprimez la contrainte plusFastCyc Factored m Int sur plusFastCyc .

L’option 1 n’est pas satisfaisante car dans la base de code actuelle, plusFastCyc est une opération fréquemment utilisée et une très grande fonction, elle ne doit donc pas être intégrée à chaque utilisation. Au contraire, GHC devrait appeler une version spécialisée de plusFastCyc . L’option 2 n’est pas vraiment une option car j’ai besoin de la contrainte dans le code réel.

J’ai essayé une variété d’options utilisant (et n’utilisant pas) INLINE , INLINABLE et SPECIALIZE , mais rien ne semble fonctionner. ( EDIT : J’ai peut-être enlevé trop de plusFastCyc pour que mon exemple soit petit, donc INLINE pourrait faire en sorte que la fonction soit intégrée. Cela ne se produit pas dans mon code réel, car plusFastCyc est si grand.) n’obtiens aucun match_co: needs more cases ou RULE: LHS too complicated to desugar (et ici ) des avertissements, bien que je reçois beaucoup d’avertissements match_co avant de minimiser l’exemple. Vraisemblablement, le “problème” est la contrainte Factored m Int dans la règle; Si je modifie cette contrainte, fcTest s’exécute aussi vite que vtTest .

Est-ce que je fais quelque chose que GHC n’aime pas? Pourquoi GHC ne spécialise-t-il pas le plusFastCyc et comment puis-je le faire?

METTRE À JOUR

Le problème persiste dans GHC 7.8.2, donc cette question est toujours pertinente.

GHC offre également une option pour SPECIALIZE une déclaration d’instance de classe de type. J’ai essayé ceci avec le code (étendu) de Foo.hs , en mettant ce qui suit:

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

Ce changement n’a cependant pas permis d’accélérer le rythme. L’amélioration des performances a permis d’append manuellement une instance spécialisée pour le type VT U.Vector m Int avec les mêmes définitions de fonction, comme suit:

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

Cela nécessite l’ajout de OverlappingInstances et de FlexibleInstances dans LANGUAGE .

Il est intéressant de noter que dans l’exemple de programme, l’accélération obtenue avec l’instance de chevauchement persiste même si vous supprimez chaque pragma SPECIALIZE et INLINABLE .