線形代数演算ライブラリインストール・利用方法
BLAS は、ベクトルや行列の基本的な線形代数演算を行うサブルーチンを集めたライブラリで、インターフェース互換を維持してこれを高速化したものが OpenBLAS です。
このため、 BLAS 用に作成されたプログラムであれば、再コンパイル・リンクを行うだけでソースプログラムを修正することなく OpenBLAS を使用してアプリケーションを高速化することが可能です。
また OpenBLAS は、pthreadを使用するスレッド並列実行に対応しており、 BLAS のサブルーチンを並列化で更に高速実行することが可能です。
以降では、 BLAS と OpenBLAS をHPCワークロード向け Intel Ice Lake プロセッサを搭載するベアメタルシェイプ BM.Optimized3.36 にインストール・セットアップし、 BLAS の倍精度行列・行列積サブルーチン(DGEMM)を使用するFortranのサンプルプログラムをコンパイル後、 BLAS とスレッド数を変化させたときの OpenBLAS の実行性能を比較・検証します。
この性能比較詳細は、 3-0. 概要 に記載していますが、これを抜粋している下表からも性能は圧倒的に OpenBLAS が良いことがわかり、特に本テクニカルTipsで使用している BM.Optimized3.36 の全コアを利用して線形代数演算を実行するようなワークロードでは、 OpenBLAS を利用するメリットが大きいと言えます。
| ライブラリ | スレッド数 | 速度向上比 |
|---|---|---|
| BLAS | 1 | 1 |
| OpenBLAS | 1 | 34.6 |
| 36 | 742.7 |
本テクニカルTipsでは、各ソフトウェアに以下のバージョンを使用しています。
- イメージ : プラットフォーム・イメージ Oracle-Linux-9.6-2025.10.23-0
- BLAS :3.9.0
- OpenBLAS :0.3.30
- Fortranコンパイラ: GNU Fortran 11.5.0
なお、本テクニカルTipsで使用するインスタンスを作成する手順は、 OCIチュートリアル の その3 - インスタンスを作成する を参照してください。
またこのインスタンスは、スレッド並列実行時の性能を最大化するためSMTを無効化して作成しますが、BIOS設定でこれを適用する場合は OCI HPCパフォーマンス関連情報 の パフォーマンスに関連するベアメタルインスタンスのBIOS設定方法 を参照してください。
本章は、 BLAS と OpenBLAS をインストールします。
以下コマンドをopcユーザで実行し、 BLAS をインストールします。
$ sudo yum-config-manager --enable ol9_codeready_builder
$ sudo dnf install -y blas blas-devel
次に、以下コマンドをopcユーザで実行し、 OpenBLAS を /opt/OpenBLAS ディレクトリにインストールします。
$ mkdir ~/`hostname` && cd ~/`hostname` && wget https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.30/OpenBLAS-0.3.30.tar.gz
$ tar -xvf ./OpenBLAS-0.3.30.tar.gz
$ cd OpenBLAS-0.3.30 && make -j 36 TARGET=SKYLAKEX && sudo make install TARGET=SKYLAKEX
本章は、 BLAS の倍精度行列・行列積サブルーチン(DGEMM)を使用するFortranのサンプルプログラムを BLAS と OpenBLAS を使用してコンパイルし、実行バイナリを作成します。
以下のファイルを dgemm_timer.f90 で作成します。
このFortranプログラムは、サイズが5,000x5,000の正方行列を乱数で初期化してその行列積をサブルーチンdgemmを使用して求め、その結果を標準出力に出力します。またdgemmの所要時間をタイマーで計測し、これを標準エラー出力に出力します。
program main
implicit none
external dgemm
integer*8 i, j, l, m, n
integer*8 lda, ldb, ldc
integer t1, t2, t_rate, t_max, diff
double precision, allocatable :: a(:,:), b(:,:), c(:,:)
double precision alpha, beta
l=5000; m=l; n=l
lda=l; ldb=n; ldc=l
alpha=1.0; beta=0.0
allocate(a(lda,n), b(ldb,m), c(ldc,m))
call random_number(a)
call random_number(b)
a = a - 0.5d0
b = b - 0.5d0
call system_clock(t1)
call dgemm('n','n',l,m,n,alpha,a,lda,b,ldb,beta,c,ldc)
call system_clock(t2, t_rate, t_max)
if ( t2 < t1 ) then
diff = (t_max - t1) + t2 + 1
else
diff = t2 - t1
endif
write(0,'(a,f10.3)') "Elapse time(sec):", diff/dble(t_rate)
do i = lbound(c,1), ubound(c,1)
write(*,*) (c(i,j), j=lbound(c,2), ubound(c,2))
end do
end program main
次に、以下コマンドをopcユーザで実行し、 BLAS を使用してサンプルプログラムをコンパイルします。
$ gfortran -O3 -lblas -o blas ./dgemm_timer.f90
次に、以下コマンドをopcユーザで実行し、 OpenBLAS を使用してサンプルプログラムをコンパイルします。
$ gfortran -O3 -L/opt/OpenBLAS/lib -lopenblas -o openblas ./dgemm_timer.f90
本章は、先に作成した実行バイナリを使用し、 BLAS と OpenBLAS を使用するサンプルプログラムをそれぞれ実行、その性能を比較します。
下表は、この実行結果をまとめています。
| No. | ライブラリ | スレッド数 | 所要時間 | 速度向上比 (※1) |
並列化効率 (※1) |
GFLOPS (※2, 3) |
|---|---|---|---|---|---|---|
| 1 | BLAS | 1 | 87.637 秒 | 0.029 | - | 2.9 |
| 2 | OpenBLAS | 1 | 2.530 秒 | 1 | - | 99.1 |
| 3 | 2 | 1.285 秒 | 1.969 | 98.4 % | 195.1 | |
| 4 | 4 | 0.650 秒 | 3.892 | 97.3 % | 385.7 | |
| 5 | 8 | 0.339 秒 | 7.463 | 93.3 % | 739.5 | |
| 6 | 16 | 0.178 秒 | 14.213 | 88.8 % | 1,408.4 | |
| 7 | 32 | 0.128 秒 | 19.766 | 61.8 % | 1,958.6 | |
| 8 | 36 | 0.118 秒 | 21.441 | 59.6 % | 2,124.6 |
※1)No.2のテストケースをベースに算出しています。
※2)算出に必要な浮動小数点演算数( 250.7 GFLOP )は、本サンプルプログラムのdgemm部分を PAPI で計測して求めています。
※3)BM.Optimized3.36 の倍精度浮動小数点演算の理論性能は、ベースクロックの3.0GHz動作時でコア当たり 96 GFLOPS 、ノード当たり 3,456 GFLOPS です。
以下コマンドをopcユーザで実行し、 BLAS 版実行バイナリでdgemmの所要時間を計測します。
$ ./blas > /dev/null
Elapse time(sec): 87.637
$
以下コマンドをopcユーザで実行し、 OpenBLAS 版実行バイナリでdgemmの所要時間を計測します。
ここでは、環境変数 OMP_NUM_THREADS の値を BM.Optimized3.36 の搭載コア数(36)の範囲で変化させて、その所要時間の変化を確認しています。なお、環境変数 OMP_NUM_THREADS の指定が無い場合は、実行するインスタンスの搭載コア数と同じスレッド数で実行します。
$ export LD_LIBRARY_PATH=/opt/OpenBLAS/lib:$LD_LIBRARY_PATH
$ for nt in `echo 1 2 4 8 16 32 36`; do echo $nt; export OMP_NUM_THREADS=$nt; ./openblas > /dev/null; done
1
Elapse time(sec): 2.530
2
Elapse time(sec): 1.285
4
Elapse time(sec): 0.650
8
Elapse time(sec): 0.339
16
Elapse time(sec): 0.178
32
Elapse time(sec): 0.128
36
Elapse time(sec): 0.118
$