はじめに
Numo::OpenBLASのBLIS版を作成した。Numo::OpenBLASを作ったのが、だいたい1年前だった。
インストール時にOpenBLASをビルドしてNumo::Linalgのバックグラウンドライブラリに使用するGemを作成した - 洋食の日記
BLISは、最適化BLASの一種で、線形代数計算に対するモダンなAPIも提供している。条件によっては、OpenBLASよりも高速に計算できる様だ。
GitHub - flame/blis: BLAS-like Library Instantiation Software Framework
blis/Performance.md at master · flame/blis · GitHub
作ったきっかけは、Numo::OpenBLASに寄せられた「BLISをつかうオプションがあると良いんじゃない?」というIssueで、同じ層のNumo::OpenBLASにBLISを混ぜるのは文脈的にややこしいので、別のGemにした。最初は、Numo::Linalg::LoaderでBLISを読み込めるようにするPull Requestを作ろうと思ったが、各OSのパッケージでBLISのビルドのオプションにバラつきがあったので、Numo::OpenBLASのように、Gemのインストール時にダウンロードとビルドを行う専用のモノを用意することにした。
使い方
普通にgemコマンドでインストールできる。インストール時に、BLISとLAPACKをダウンロードしてビルドするので、ちょっと時間が必要になる。
$ gem install numo-blis
使用方法はNumo::BLISをrequireするのみで、内部的にNumo::NArrayとNumo::Linalgがrequireされて、ビルドしたBLISとLAPACKがバックグラウンドライブラリに使用される。
require 'numo/blis' x = Numo::DFloat.new(5, 2).rand c = x.transpose.dot(x) eig_val, eig_vec = Numo::Linalg.eigh(c)
Numo::BLISとしては、これだけである。
Numo::BLISが行っていること
Numo::BLISがインストール時に行っていることを、手動でやってみる。BLIS・LAPACKのビルドと、Numo::Linalgでの読み込みである。
まず、BLISとLAPACKをGithubからダウンロードする。
$ wget https://github.com/flame/blis/archive/refs/tags/0.8.1.tar.gz $ tar xvzf 0.8.1.tar.gz $ wget https://github.com/Reference-LAPACK/lapack/archive/refs/tags/v3.10.0.tar.gz $ tar xvzf v3.10.0.tar.gz
BLISではconfigureでビルドオプションを指定する。helpオプションでどういうものがあるか確認する。
$ cd blis-0.8.1 $ ./configure --help ... -t MODEL, --enable-threading[=MODEL], --disable-threading Enable threading in the library, using threading model MODEL={openmp,pthreads,no}. If MODEL=no or --disable-threading is specified, threading will be disabled. The default is 'no'. ... --enable-cblas, --disable-cblas Enable (disabled by default) building the CBLAS compatibility layer. This automatically enables the BLAS compatibility layer as well. ...
重要なのが、マルチスレッドとCBLAS互換に関するオプションがデフォルトで無効になっている点である。特に、CBLAS互換がないとNumo::Linalgでうまく動作してくれないので、これは有効にする必要がある。そんなわけで、configureは次のようになる。後ろについている「auto」は、CPUアーキテクチャを自動で検出してね、というモノである。
$ mkdir build; cd build $ ../configure --enable-cblas --enable-threading=pthreads --prefix=インストール先ディレクトリ auto ... $ make $ make install
BLISのビルドができたので、次はLAPACKをビルドする。LAPACKではcmakeを使う。
$ cd lapack-3.10.0 $ mkdir build; cd build
BLASに先程ビルドしたBLISを指定する必要がある。CMakeのFindBLASを使っている様なので、BLAS_LIBRARIESでパスを指定すればよい。 その他のオプションは、CMakeLists.txtを見て確認した。Numo::LinalgはCインターフェースのLAPACKEを必要とするので、LAPACKEオプションをONにする。
$ cmake -DLAPACKE=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX='インストール先ディレクトリ' -DBLAS_LIBRARIES='先程ビルドしたBLISをlibblas.soとかまで指定する' ../ ... $ make $ make install
あとはこれらをNumo::Linalgで読み込むと、使えるようになる。
require 'fiddle' require 'numo/linalg/linalg' Fiddle.dlopen('/hoge/fuga/lib/libblis.so') Fiddle.dlopen('/hoge/fuga/lib/liblapacke.so') Numo::Linalg::Blas.dlopen('/hoge/fuga/lib/libblis.so') Numo::Linalg::Lapack.dlopen('/hoge/fuga/lib/liblapacke.so')
ここで、注意が必要なのが、BLISは環境変数でスレッド数を指定しないといけないことである。指定しないとシングルスレッドになってしまう。
BLIS_NUM_THREADS=8 ruby hoge.rb
Numo::BLISでは、ENV['BLIS_NUM_THREADS']で確認していて、これがnilであれば、Etc.nprocessorsをセットしている。
おわりに
Numo::OpenBLASをベースにすれば良いので、BLISのビルド方法を調べて、一日でガッと作った。未だ腱鞘炎に悩まされていて、大した開発ができてないのだが、久しぶりにgemを作って、良いストレス解消になった。