Mam tę funkcję Haskella, która powoduje ponad 50% wszystkich alokacji mojego programu, powodując, że 60% mojego czasu pracy jest pobierane przez GC . Używam małego stosu (-K10K
), więc nie ma przepełnienia stosu, ale czy mogę uczynić tę funkcję szybszą, z mniejszą alokacją?Zoptymalizuj funkcję listy, która tworzy zbyt dużo śmieci (nie przepełnienie stosu)
Celem jest obliczenie iloczynu macierzy przez wektor. Nie mogę użyć na przykład hmatrix
, ponieważ jest to część większej funkcji przy użyciu pakietu ad
Automatic Differentiation, więc potrzebuję użyć list z Num
. W czasie pracy sądzę, że użycie modułu Numeric.AD
oznacza, że moje typy muszą być 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)
Zasadniczo pętla poprzez mnożenie macierzy i dodanie akumulator, aż do osiągnięcia końca wektorze przechowywania wyników, a dalej znowu ponownym wektor. Mam test quickcheck
sprawdzający, czy otrzymuję taki sam wynik, jak produkt macierzy/wektor w hmatrix.
Próbowałem z foldl
, foldr
, itp. Nic nie próbowałem sprawia, że funkcja jest szybsza (i niektóre rzeczy jak foldr
powodują wyciek przestrzeni).
Biegając z profilowaniem mówi mi, oprócz faktu, że ta funkcja jest gdzie większość czasu spędza się i przydziału, że istnieje mnóstwo Cells
tworzone, Cells
jest typ danych z pakietu ad
.
Prosty test do uruchomienia:
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
to na moim komputerze mówi mi GC jest zajęty 47% czasu.
Wszelkie pomysły?
Więcej informacji! Jak uruchamiasz ten program? Gdzie jest twoja uprząż testowa? Jakie konkretne typy używasz? Jakie są flagi i wersja twojego kompilatora Haskell? –
Dodano trochę informacji. Ta funkcja jest wywoływana za pośrednictwem funkcji ad grad, która wykorzystuje własne typy (wystąpienia Num). Profilowanie pokazuje alokacje "Komórek". –
Kilka naiwnych sugestii: czy rozważałeś użycie stanu zmiennego z 'ST'? I [stream-fusion] (https://hackage.haskell.org/package/stream-fusion-0.1.2.5/docs/Data-List-Stream.html)/[conduit] (https: //hackage.haskell. org/package/conduit)/[pipes] (https://hackage.haskell.org/package/pipes)? Może (?) Może nawet warto przekształcić twoją listę wektorów w coś innego, np. a [unboxed vector] (http://lambda.jstolarek.com/2013/04/haskell-as-fast-as-c-a-case-study/)? Nie mam doświadczenia z żadną z tych technik, ale może linki mogą ci pomóc dalej. –