Specializzazione con vincoli


156

Ho problemi a far sì che GHC specializzi una funzione con un vincolo di classe. Ho un esempio minimo del mio problema qui: Foo.hs e Main.hs . I due file vengono compilati (GHC 7.6.2, ghc -O3 Main) ed eseguiti.

NOTA: Foo.hs è davvero ridotto. Se vuoi vedere perché è necessario il vincolo, puoi vedere un po 'più di codice qui . Se inserisco il codice in un singolo file o apporto molte altre modifiche minori, GHC semplifica semplicemente la chiamata a plusFastCyc. Ciò non accadrà nel codice reale perché plusFastCycè troppo grande per essere integrato in GHC, anche se contrassegnato INLINE. Il punto è specializzare la chiamata a plusFastCyc, non incorporarla. plusFastCycviene chiamato in molti punti del codice reale, quindi duplicare una funzione così grande non sarebbe desiderabile anche se potessi forzare GHC a farlo.

Il codice di interesse è il plusFastCycin Foo.hs, riprodotto qui:

{-# 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

Il Main.hsfile ha due driver :, vtTestche viene eseguito in ~ 3 secondi, e fcTest, che viene eseguito in ~ 83 secondi se compilato con -O3 usando la forallspecializzazione d.

Il nucleo mostra che per il vtTesttest, il codice addizionale è specializzato in Unboxedvettori su Ints, ecc., Mentre per il codice vettoriale generico viene utilizzato fcTest. Sulla riga 10, puoi vedere che GHC scrive una versione specializzata della versione plusFastCycgenerica sulla riga 167. La regola per la specializzazione è sulla riga 225. Credo che questa regola dovrebbe attivarsi sulla riga 270. ( main6chiama iterate main8 y, così main8è dove plusFastCycdovrebbe essere specializzato.)

Il mio obiettivo è fcTestvelocizzare quanto vtTestspecializzandomi plusFastCyc. Ho trovato due modi per farlo:

  1. Chiamata esplicita inlineda GHC.Extsdentro fcTest.
  2. Rimuovere il Factored m Intvincolo attivo plusFastCyc.

L'opzione 1 non è soddisfacente perché nella base di codice attuale plusFastCycè un'operazione utilizzata di frequente e una funzione molto grande, quindi non dovrebbe essere incorporata ad ogni utilizzo. Piuttosto, GHC dovrebbe chiamare una versione specializzata di plusFastCyc. L'opzione 2 non è in realtà un'opzione perché ho bisogno del vincolo nel codice reale.

Ho provato una varietà di opzioni di utilizzo (e non usando) INLINE, INLINABLEe SPECIALIZE, ma nulla sembra funzionare. ( EDIT : potrei essermi spogliato troppo plusFastCycper rendere piccolo il mio esempio, quindi INLINEpotrebbe essere inline la funzione. Questo non accade nel mio codice reale perché plusFastCycè così grande.) In questo esempio particolare, non lo sono ricevere eventuali match_co: needs more caseso RULE: LHS too complicated to desugar(e qui ) avvisi, anche se stavo ricevendo molti match_coavvisi prima di minimizzare l'esempio. Presumibilmente, il "problema" è il Factored m Intvincolo della regola; se apporto modifiche a quel vincolo, fcTestcorre veloce quanto vtTest.

Sto facendo qualcosa che a GHC non piace? Perché GHC non è specializzato in plusFastCyc, e come posso farlo?

AGGIORNARE

Il problema persiste in GHC 7.8.2, quindi questa domanda è ancora rilevante.


3
Ho appena provato a specializzarmi per uno specifico m , vale a dire M. Questo ha portato a termine il lavoro, ma non posso specializzarmi per specifici tipi di fantasmi nel programma reale in quanto sono reificati.
crockeea,

Ho anche inviato una segnalazione bug GHC ghc.haskell.org/trac/ghc/ticket/8668 ma il problema è ancora aperto. Il processo di segnalazione dei bug mi ha aiutato a chiarire un po 'la domanda, quindi spero che sia più facile capire cosa sta succedendo.
crockeea,

@monojohnny Mi dispiace sentirlo, credo che tu possa segnalarlo come tale. Penso che sto chiedendo a GHC di fare qualcosa di abbastanza ragionevole, e non lo farà. Non sono sicuro se sto sbagliando, o se questa è un'idiosincrasia con il compilatore che potrebbe avere una soluzione alternativa. Ho visto soluzioni alternative per la specializzazione e le regole in una libreria specifica di hacking che mi sfugge al momento, quindi spero che qualcuno nella comunità con più esperienza GHC di quanto io stesso possa sapere come raggiungere la specializzazione.
crockeea,

1
Mi scuso per il tono del mio commento - non è il mio miglior contributo a questo sito - non c'è davvero niente di sbagliato nel tuo post (è la mia mancanza di comprensione che è stata la fonte del mio fastidio immagino!)
monojohnny

@monojohnny Le scuse hanno accettato, ma è un peccato che il downvote sia bloccato adesso ;-)
crockeea

Risposte:


5

GHC offre inoltre un'opzione per SPECIALIZEuna dichiarazione di istanza di tipo-classe. Ho provato questo con il codice (espanso) di Foo.hs, mettendo il seguente:

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

Questa modifica, tuttavia, non ha raggiunto la velocità desiderata. Ciò che ha ottenuto questo miglioramento delle prestazioni è stato l' aggiunta manuale di un'istanza specializzata per il tipo VT U.Vector m Intcon le stesse definizioni di funzione, come segue:

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

Ciò richiede l'aggiunta OverlappingInstancese FlexibleInstancesin LANGUAGE.

È interessante notare che, nel programma di esempio, lo speedup ottenuto con l'istanza sovrapposta rimane anche se si rimuove ogni SPECIALIZEe INLINABLEpragma.


Sicuramente non ottimale, ma è la prima soluzione che in realtà raggiunge l'obiettivo, quindi credo che lo prenderò per ora ...
crockeea,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.