洋食の日記

洋食のことではなく、技術メモを書きます。たまにどうでも良いことも書きます。

MacではRubyのNMatrixでOpenBLASを使わなくてもLAPACKで十分に速かった

はじめに

Ruby線形代数機械学習アルゴリズムの実装を行う場合、SciRubyのNMatrixが最適だろう。 このNMatrixで、 RやPythonのNumpyと同様に、後ろで叩いているBLAS/LAPACKを、OpenBLASやIntel MKLに変えることで、高速化することを考えた。 しかし、Macでは、BLAS/LAPACKはAccelerate Frameworkに含まれていて、OpenBLASに変えるまでもなく十分に速かった。 環境は、MacBookIntel Core m3 1.1Ghz、メモリ8G、macOS Sierra 10.12.6 である。

行列積での実行速度の計測

まずは、素のNMatrixと、MacBLAS/LAPACKを使う場合の実行速度を計測した。 インストールは、gemからもできるが、測定条件を一致させるため最新版をビルドする方法を採用した。

$ git clone https://github.com/SciRuby/nmatrix.git
$ cd nmatrix/
$ bundle install
$ bundle exec rake compile nmatrix_plugins=lapacke
$ bundle exec rake spec nmatrix_plugins=lapacke
$ bundle exec rake install nmatrix_plugins=lapacke

単純な行列積で、実行速度を計測する。行列積が速くなると色々と嬉しい。

require 'nmatrix'
require 'benchmark'

Benchmark.bm do |x|
  x.report do
    50.times do
      a = NMatrix.random [1000, 1000]
      b = NMatrix.random [1000, 1000]
      c = a.dot b
    end
  end
end

これを、実行すると以下の様になる。

$ ruby test_nmat.rb
       user     system      total        real
  53.790000   1.400000  55.190000 ( 56.935680)

さらにLAPACKプラグインの実行速度を確認するため、require ‘nmatrix'を、

require 'nmatrix/lapacke'

と変更する。これを実行すると、素のNMatrixと比較してグンと速くなった。

$ ruby test_nmat.rb
       user     system      total        real
  23.050000   1.090000  24.140000 ( 18.840261)

OpenBLASに差し替えた場合での実行速度

OpenBLASは、Homebrewでインストールできる。 「–build-from-source」や「–with-openmp」といったオプションも指定できるが、 何も指定しないものが、行列積では最も速かった。

brew install openblas

「ext/nmatrix_lapacke/extconf.rb」を編集して、OpenBLASを使用するようにする。

...
#ldefaults = {lapack: ["/usr/local/lib"].delete_if { |d| !Dir.exists?(d) } }
ldefaults = {lapack: ["/usr/local/opt/openblas/lib"] }
...
#$libs += " -llapack "
$libs += " -L/usr/local/opt/openblas/lib -lopenblas "
...

NMatrixを再インストールする。

$ gem uninstall namtrix nmatix-lapacke
$ bundle exec rake clean
$ bundle exec rake compile nmatrix_plugins=lapacke
$ bundle exec rake spec nmatrix_plugins=lapacke
$ bundle exec rake install nmatrix_plugins=lapacke

OpenBLASを叩くLAPACKプラグインという状態なので、

require 'nmatrix/lapacke'

とする。実行してみると、LAPACKの結果と大差ないものとなった。

$ ruby test_nmat.rb
       user     system      total        real
  27.400000   4.530000  31.930000 ( 17.920017)

特異値分解での実行速度の計測

以下の特異値分解を含むプログラムでも比較を行った。

require 'nmatrix/lapacke'
require 'benchmark'

Benchmark.bm do |x|
  x.report do
    50.times do
      a = NMatrix.random [200, 2000]
      b = a.dot a.transpose
      #
      b.gesvd
    end
  end
end

LAPACKを使用した場合では、

$ ruby test_nmat2.rb
       user     system      total        real
   6.040000   0.300000   6.340000 (  5.908795)

となり、OpenBLASを使用した場合では、

$ ruby test_nmat2.rb
       user     system      total        real
  27.220000   4.920000  32.140000 ( 16.595134)

となった。OpenBLASよりもLAPACKの方が速く、 MacではAccelerate FrameworkのBLAS/LAPACKを使用した方が良いことがわかる。

ちなみに、Intel MKLでも試したが、Accelerate FrameworkのBLAS/LAPACKの方が速かった。 また、Intel MKLでは、rake spec した際にコケてしまった。

おわりに

MacでNMatrixを使用する場合は、

gem install nmatrix nmatrix-lapacke

として、

require 'nmatrix/lapacke'

とすれば良い。