Haskell 関数があり、プログラムのすべての割り当ての 50% 以上が発生し、実行時間の 60% が GC に費やされています。小さなスタック ( -K10K
) で実行しているので、スタック オーバーフローはありませんが、少ない割り当てでこの関数を高速化することはできますか?
ここでの目標は、行列とベクトルの積を計算することです。hmatrix
たとえば、これはad
自動微分パッケージを使用するより大きな関数の一部であるため、使用できません。したがって、のリストを使用する必要がありますNum
。実行時に、Numeric.AD
モジュールを使用すると、型が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)
基本的に、行列をループし、ベクトルの最後に到達するまでアキュムレータを乗算および加算し、結果を格納してから、ベクトルを再び再起動し続けます。quickcheck
hmatrix の行列/ベクトル積と同じ結果が得られることを確認するテストがあります。
foldl
、などで試しました。関数を高速化するものは何も試していません (また、スペース リークの原因となるfoldr
ものもあります)。foldr
プロファイリングを使用して実行すると、この関数がほとんどの時間と割り当てが費やされる場所であるという事実に加えて、パッケージからのデータ型でCells
ある作成される負荷があることがわかります。Cells
ad
実行する簡単なテスト:
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
私のマシンでは、GC が 47% の時間ビジーであることがわかります。
何か案は?