この記事は,高速な数値計算ライブラリIntel Math Kernel Library(Intel MKL)を,Windows Subsystem for Linux(WSL)上でJavaから呼び出すまでのまとめ記事です.WSLがインストールされている前提で話を進めます.
以下の順にまとめていきます.
- Intel MKLのインストール
- Intel MKLのC++からの利用
- Intel MKLの共有ライブラリへのラッピング
- 共有ライブラリのJavaからの利用
- まとめ
Intel MKLのインストール
本節では,Intel MKLをWSLのUbuntu上のユーザの$HOME/programs/intelにインストールする手順について説明します.
インストーラのダウンロード
- このページにアクセスし,”Free Download”をクリック.
- 以下のページに移動する.適宜登録を済ませ,ログインした状態でユーザ情報を”Submit”する.
- 以下のページに移動する.”Intel Products”をクリック.
- 以下のページに移動する.Intel Performance Library for Linuxの隣にある”Version 2018”をクリック.
- Choose a Versionから”2018 Update 1”を選択し,Choose a Download Option内の”Intel Math Kernel Libray(Intel MKL)”をクリックし,インストーラ(l_mkl_2018.1.163.tar.gz)をダウンロード
インストール
- l_mkl_2018.1.163.tar.gzを$HOME/programs/に移動
- tar xvf l_mkl_2018.1.163.tar.gz で解凍
- $HOME/programs/l_mkl_2018.1.163/install.sh を実行
- インストール対象をユーザに設定し,インストールディレクトリに$HOME/programs/intelを指定
Intel MKLのC++からの利用
本節では,前節でインストールしたIntel MKLをWSL上でC++プログラムから呼び出す手順について説明します. ライブラリパス,インクルードパス,LD_LIBRARY_PATHを適切に指定することでIntel MKLを呼び出すことができます.
行列x行列の演算を行う関数である,cblas_dgemmを呼び出すプログラム (test.cpp) を用いて説明します.
1.以下のようにtest.cppを作成.
#include <iostream>
#include "mkl.h"
void A_TB(const int m, const int n, const int k,
const double A[], const double B[], double C[])
{
double alpha = 1.0, beta = 0.0;
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, m, n, k,
alpha, A, k,
B, n, beta,
C, m);
}
int main(){
std::cout << "Intel MKL test" << std::endl;
const double A[] = {1, 2,
3, 4};
const double B[] = {1, 3,
4, 2};
double C[] = {0, 0, 0, 0};
const int m = 2, n = 2, k = 2;
std::cout << "A" << std::endl;
std::cout << A[0] << ", " << A[1] << std::endl;
std::cout << A[2] << ", " << A[3] << std::endl;
std::cout << "B" << std::endl;
std::cout << B[0] << ", " << B[1] << std::endl;
std::cout << B[2] << ", " << B[3] << std::endl;
A_TB(m, n, k, A, B, C);
std::cout << "calculate AB" << std::endl;
std::cout << C[0] << ", " << C[1] << std::endl;
std::cout << C[2] << ", " << C[3] << std::endl;
return 0;
}
2.コンパイルに必要なライブラリをファイルに書き込んでおく.ここでは,liblistというファイルに以下の内容を書き込む.
-lmkl_avx2
-lmkl_avx512_mic
-lmkl_avx512
-lmkl_avx
-lmkl_core
-lmkl_def
-lmkl_mc3
-lmkl_mc
-lmkl_vml_avx2
-lmkl_vml_avx512_mic
-lmkl_vml_avx512
-lmkl_vml_avx
-lmkl_vml_cmpt
-lmkl_vml_def
-lmkl_vml_mc2
-lmkl_vml_mc3
-lmkl_vml_mc
-lmkl_rt
3.以下のように,ライブラリパスとインクルードパスを設定し,g++でビルドを行う.
g++ -std=c++11 -L$HOME/programs/intel/mkl/lib/intel64 -I$HOME/programs/intel/mkl/include test.cpp `cat liblist` -o test.out
4.LD_LIBRARY_PATHの設定.
export LD_LIBRARY_PATH=$HOME/programs/intel/mkl/lib/intel64
5.test.outの実行.
./test.out
以下の出力が得られれば,無事にIntel MKLをC++から呼び出せているはずです.
Intel MKL test
A
1, 2
3, 4
B
1, 3
4, 2
calculate AB
9, 7
19, 17
Intel MKLの共有ライブラリへのラッピング
本節では,Intel MKLを共有ライブラリにラッピングする手順について説明します.共有ライブラリにラッピングすることで,C++以外からもIntel MKLを呼び出すことが可能になります.
1.以下のように,共有ライブラリを作成するためのC++ファイルとヘッダーファイルを作成する. このように,Intel MKL内の使用したい関数を用いた関数を新たに定義する.
mkl_blas_for_java.cpp
#include "mkl.h"
#include "mkl_blas_for_java.hpp"
/* MKL CBlas Level 3 */
extern "C" void aM1_TM2_PbM0(const int M1M0_row, const int M2M0_col, const int M1_colM2_row,
const double a, const double M1[], const double M2[], const double b, double M0[])
{
const CBLAS_LAYOUT Layout = CblasRowMajor;
const CBLAS_TRANSPOSE trans = CblasNoTrans;
cblas_dgemm(Layout, trans, trans, M1M0_row, M2M0_col, M1_colM2_row,
a, M1, M1_colM2_row, M2, M2M0_col, b, M0, M2M0_col);
}
mkl_blas_for_java.hpp
#ifndef MKL_BLAS_FOR_JAVA_HPP
#define MKL_BLAS_FOR_JAVA_HPP
/* MKL Blas Level 3 */
extern "C" void aM1_TM2_PbM0(const int M1M0_row, const int M2M0_col, const int M1_colM2_row,
const double a, const double M1[], const double M2[], const double b, double M0[]);
#endif
2.以下のようにして,共有ライブラリを作成する.
g++ -fPIC -std=c++11 -Ofast -L$HOME/programs/intel/mkl/lib/intel64 -I$HOME/programs/intel/mkl/include -shared -o libmkljava.so mkl_blas_for_java.cpp `cat liblist`
libmkljava.soが作成されれば成功です.
共有ライブラリのJavaからの利用
本節では,前節で作成した共有ライブラリ(libmkljava.so)をJavaから呼び出す手順について説明します. 共有ライブラリをJavaから呼び出すことで,共有ライブラリ内で使用しているIntel MKL関数をJavaで利用できるようになります. 共有ライブラリの呼び出しには,jna-4.1.0のライブラリを使用します.
1.以下のように,Javaインターフェースファイルを作成.
mklFunction.java
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface mklFunction extends Library {
mklFunction INSTANCE = (mklFunction) Native.loadLibrary("mkljava", mklFunction.class);
/* MKL Blas Level 3 */
void aM1_TM2_PbM0(final int M1M0_row, final int M2M0_col, final int M1_colM2_row,
final double a, final double M1[], final double M2[], final double b, double M0[]);
}
2.インターフェース内の関数を呼び出すJavaファイルを作成.
mklTest.java
public class mklTest2 {
public static void main(String[] args) {
final mklFunction2 func = mklFunction2.INSTANCE;
double M1[] = new double[]{1, 2,
3, 4};
double M2[] = new double[]{1, 3,
4, 2};
double M0[] = new double[]{0, 0, 0, 0};
int m = 2, n = 2, k = 2;
double a = 1.0, b = 0.0;
System.out.println("Intel MKL test");
func.aM1_TM2_PbM0(m, n, k, a, M1, M2, b, M0);
System.out.println(M0[0] + ", " + M0[1]);
System.out.println(M0[2] + ", " + M0[3]);
}
}
3.以下のようにして,Javaクラスファイルを作成.
javac -cp ./jna-4.1.0.jar mklTest.java mklFunction.java
4.以下のようにして,Javaプログラムを実行.java -cp 以下には,libmkljava.soのあるフォルダを指定.ここでは,現在のフォルダを指定している.
export LD_LIBRARY_PATH=$HOME/programs/intel/mkl/lib/intel64
java -cp .:./jna-4.1.0.jar mklTest
以下の出力が得られれば,無事にIntel MKLをJavaから呼び出せているはずです.
Intel MKL test
9.0, 7.0
19.0, 17.0
まとめ
以上で,WSL上でJavaからIntel MKLを呼び出す手順についての説明は終わりです. これで,Javaからでもなかなか高速な行列計算ができるようになります.うれしいですね.
私の体感では,の計算(行列x行列とか逆行列の計算)は圧倒的に早くなる気がします.それ以外のベクトルx行列とかはあんまり早くなってる気がしませんでした.元々Javaの段階で結構早いからなのでしょうか?いまいちわかりません.わかる人がいたら教えてください.
まあ,この関数を利用して,1000x1000ぐらいのサイズの行列の積をたくさん計算させると,CPU使用率が100%になって気持ちがいいです.
もちろん,上記で示した以外の関数も,同様の手順でJavaから呼び出すことが可能です.
参考までに,私が実装した分はGitHubに置いておきます.