Haskellで行列計算をしようと思ったときに、どのライブラリを使ったら良いのか迷ったので、代表的なライブラリを使って行列積の計算時間を測定してみました。
Haskellの行列計算ライブラリ
代表的な下記の3ライブラリを対象にします。
行列積の計算プログラム
各ライブラリを使って、要素が1の1000行1000列の行列同士の積を計算して、900行900列目の値を表示するプログラムを用意しました。
matirx
matrix.hs
import Data.Matrix
main = do
let
a = matrix 1000 1000 $ \_ -> 1 :: Matrix Double
b = matrix 1000 1000 $ \_ -> 1 :: Matrix Double
print $ getElem 900 900 (a `multStd` b)
hmatirx
hmatrix.hs
import Numeric.LinearAlgebra
main = do
let
a = (1000 >< 1000) $ replicate (1000 * 1000) 1 :: Matrix Double
b = (1000 >< 1000) $ replicate (1000 * 1000) 1 :: Matrix Double
print $ (a <> b) `atIndex` (900, 900)
repa
repa.hs
import Data.Array.Repa
import Data.Array.Repa.Algorithms.Matrix
main = do
let
a = fromListUnboxed (Z :. 1000 :. 1000 :: DIM2) $ replicate (1000 * 1000) 1 :: Array U DIM2 Double
b = fromListUnboxed (Z :. 1000 :. 1000 :: DIM2) $ replicate (1000 * 1000) 1 :: Array U DIM2 Double
m <- (a `mmultP` b)
print $ m ! (Z :. 900 :. 900)
計算時間の比較
stackのghcでコンパイルして実行した結果は下記です。
matirx
$ stack ghc matrix.hs
$ time ./matrix
( 1000.0 )
real 0m0.615s
user 0m0.486s
sys 0m0.092s
hmatirx
$ stack ghc hmatrix.hs
$ time ./hmatrix
1000.0
real 0m0.169s
user 0m0.240s
sys 0m0.028s
repa
ghcオプションに-threaded
を指定して、実行オプションに+RTS -N
を指定すると、自動的にCPUの全コアを使って並列計算してくれるみたいです。
$ stack ghc -- repa.hs -threaded
$ time ./repa
1000.0
real 0m2.796s
user 0m2.594s
sys 0m0.112s
$ time ./repa +RTS -N
1000.0
real 0m1.652s
user 0m4.552s
sys 0m0.639s
実行環境
GHCのバージョン
$ stack ghc -- --version
...
The Glorious Glasgow Haskell Compilation System, version 8.0.1
PCのスペック
$ system_profiler SPHardwareDataType
Hardware:
Hardware Overview:
Model Name: MacBook Pro
Model Identifier: MacBookPro11,1
Processor Name: Intel Core i5
Processor Speed: 2.4 GHz
Number of Processors: 1
Total Number of Cores: 2
L2 Cache (per Core): 256 KB
L3 Cache: 3 MB
Memory: 4 GB
...
まとめ
結果的にはhmatrixが圧倒的に速かったです。マルチコアの並列計算が可能なrepaに期待していたのですが、あまり速度は出ませんでした。repaの強みは2次元以上の配列を使う場合、もっと多くのコアを使った場合に出てくるのかもしれません。ひとまず、hmatrixを使うのが無難なのかなと思いました。