Ho la funzione Haskell, che causa oltre il 50% di tutte le allocazioni del mio programma, causando il 60% del mio tempo di esecuzione da parte del GC . Corro con un piccolo stack (-K10K
) quindi non c'è overflow dello stack, ma posso rendere questa funzione più veloce, con meno allocazione?Ottimizza una funzione di lista che crea troppa garbage (non stack overflow)
L'obiettivo qui è calcolare il prodotto di una matrice mediante un vettore. Ad esempio, non posso usare hmatrix
perché fa parte di una funzione più grande che utilizza il pacchetto ad
Automatic Differentiation, quindi ho bisogno di usare gli elenchi di Num
. A runtime suppongo che l'uso del modulo Numeric.AD
significhi che i miei tipi devono essere Scalar Double
.
listMProd :: (Num a) => [a] -> [a] -> [a]
listMProd mdt vdt = go mdt vdt 0
where
go [] _ s = [s]
go ls [] s = s : go ls vdt 0
go (y:ys) (x:xs) ix = go ys xs (y*x+ix)
Fondamentalmente ciclo attraverso la matrice, moltiplicando l'aggiunta di un accumulatore fino a raggiungere l'estremità del vettore, memorizzando il risultato, quindi continuando a riavviare il vettore. Ho un test quickcheck
che verifica che ottengo lo stesso risultato del prodotto matrice/vettore in hmatrix.
Ho provato con foldl
, foldr
, ecc. Niente di quello che ho provato rende la funzione più veloce (e alcune cose come foldr
causano una perdita di spazio).
Esecuzione con profilatura mi dice, in cima al fatto che questa funzione è dove la maggior parte del tempo e l'allocazione è speso, che ci sono un sacco di Cells
essere creato, Cells
essendo un tipo di dati dal pacchetto ad
.
Un semplice test da eseguire:
import Numeric.AD
main = do
let m :: [Double] = replicate 400 0.2
v :: [Double] = replicate 4 0.1
mycost v m = sum $ listMProd m v
mygrads = gradientDescent (mycost (map auto v)) (map auto m)
print $ mygrads !! 1000
questo sulla mia macchina mi dice GC è occupato il 47% del tempo.
Qualche idea?
Maggiori informazioni! Come stai correndo questo programma? Dov'è la tua imbracatura di prova? Che tipi di cemento stai usando? Quali sono le bandiere e la versione sul tuo compilatore Haskell? –
Aggiunte alcune informazioni. Quella funzione è chiamata tramite la funzione ad grad che usa i propri tipi (istanze di Num). Il profilo mostra allocazioni di "Celle". –
Alcuni suggerimenti semi-informati: hai considerato di utilizzare lo stato mutabile con 'ST'? E [stream-fusion] (https://hackage.haskell.org/package/stream-fusion-0.1.2.5/docs/Data-List-Stream.html)/[conduit] (https: //hackage.haskell. org/pacchetto/conduit)/[] tubi (https://hackage.haskell.org/package/pipes)? Forse (?) Potrebbe anche valere la pena trasformare la tua lista vettoriale in qualcos'altro, ad es. un [unboxed vector] (http://lambda.jstolarek.com/2013/04/haskell-as-fast-as-c-a-case-study/)? Non ho esperienza con nessuna di queste tecniche, ma forse i collegamenti possono aiutarti ulteriormente. –