Intel MKLのようなネイティブな数学ライブラリでSpark機械学習アルゴリズムを加速する

最近、仕事上でSpark MLlibのSVD（特異値分解）でMovieLensの推薦を実装するアプリの性能調査をしております。思った以上遅くて困っていました。SparkのDriver, Executorのメモリ、Executor数、OverHeadなどいろんなSparkパラメーターを調整して試してきましたが、なかなか改善できていません。

そして更に調査したところ、そもそもSpark MllibのSVDの実装が古いらしく、性能が出ない情報がネット上で結構上がっています。Workaroundとしては、SVDの代わりにAlternating Least Squares（ALS）などを使うか、あるいはネイティブな数学演算ライブラリを使って高速化するか　などがあります。

今回はHDP上でIntel MKLを利用するための設定方法をご紹介します。

まず、ClouderaのBlogを参照しています。CDHのクラスタの場合はMKLのParcelsファイルが提供されているため、Cloudera Managerから簡単にインストールすることができます。
Using Native Math Libraries to Accelerate Spark Machine Learning Applications
https://docs.cloudera.com/documentation/guru-howto/data_science/topics/ght_native_math_libs_to_accelerate_spark_ml.html#using_native_math_libs_with_spark_machine_learning

# Native Math Libraries for Spark ML
Spark MLlibは、Breeze線形代数パッケージを使用しています。Breezeは最適化な数値処理を行うにはnetlib-javaというライブラリに依存する。 netlib-javaは、低レベルのBLAS、LAPACK、およびARPACKライ​​ブラリのラッパーです。しかし、ライセンス関連の制限でCDHのSpark,あるいはApache CommunityのSparkに既定でnetlib-javaのネイティブなプロキシが含まれていません。特に手動で何も設定していない場合、netlib-javaはF2JというJavaベースのライブラリを使います。

ネイティブな数学ライブラリを使っているか、JavaベースのF2Jを使っているか　Spark-shellで確認できます。

```
scala> import com.github.fommil.netlib.BLAS
import com.github.fommil.netlib.BLAS

scala> println(BLAS.getInstance().getClass().getName())
com.github.fommil.netlib.F2jBLAS
```
com.github.fommil.netlib.F2jBLASが表示されている場合は、F2jを使っていることを確認できます。

# Intel MKLのインストールと設定
Intel MKL（Intel’s Math Kernel Library）を利用することで、(Alternating Least Squares (ALS) algorithm),Latent Dirichlet Allocation (LDA), Primary Component Analysis (PCA),　Singular Value Decomposition (SVD) といったアルゴリズムを使ったモデル訓練を高速化することができます。

## Intel MKLインストール
各Sparkノードで実施
https://software.intel.com/en-us/articles/installing-intel-free-libs-and-python-yum-repo

```
# curl -O https://yum.repos.intel.com/mkl/setup/intel-mkl.repo > /etc/yum.repos.d/intel-mkl.repo
# sudo yum -y install intel-mkl-2019.3-062
intel-mkl-<VERSION>.<UPDATE>-<BUILD_NUM>の形式で指定
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110403/f33f3e5f-dab0-0c58-a21b-0e3dc54496b8.png)
MKLのインストール先は/opt/intel/mkl になります。

## mklWrapperのダウンロード
mklWrapper.jarとmkl_wrapper.soを以下のリンクからダウンロードする
https://github.com/Intel-bigdata/mkl_wrapper_for_non_CDH　

```
Copy mkl_wrapper.jar to /opt/intel/mkl/wrapper/mkl_wrapper.jar
Copy mkl_wrapper.so to /opt/intel/mkl_wrapper/mkl_wrapper.so
```
## Spark Configuration

```
# echo "/opt/intel/mkl/lib/intel64" > /etc/ld.so.conf.d/mkl_blas.conf
# ldconfig
```
## AmbariでSpark-default設定を追加

| propety | value |
| --- | ---|
| spark.driver.extraClassPath | /opt/intel/mkl/wrapper/mkl_wrapper.jar |
| spark.executor.extraClassPath | /opt/intel/mkl/wrapper/mkl_wrapper.jar |
| spark.driver.extraJavaOptions | -Dcom.github.fommil.netlib.BLAS=com.intel.mkl.MKLBLAS -Dcom.github.fommil.netlib.LAPACK=com.intel.mkl.MKLLAPACK |
| spark.executor.extraJavaOptions | -Dcom.github.fommil.netlib.BLAS=com.intel.mkl.MKLBLAS -Dcom.github.fommil.netlib.LAPACK=com.intel.mkl.MKLLAPACK |
| spark.driverEnv.MKL_VERBOSE | 1 |
| spark.executorEnv.MKL_VERBOSE | 1 |

実際の設定 Custom Spark2-defaults
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110403/9db469df-5fac-dc7e-f125-d7bb86c10be4.png)

spark.executor.extraJavaOptionsは Advanced spark2-defaultsにあります。すでにあった値の後ろに追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110403/188e008f-919c-ac1a-22c5-205b74578bf9.png)

## 確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110403/12c7351f-92f4-479c-0eb9-b76b0e521580.png)

com.intel.mkl.MKLBLASが表示されることを確認できます。

そして再度SVDを実行してみたところ、前に比べて３0％〜35%の時間短縮することができました。
アルゴリズムによって加速する度合いが違いますので、ALSの場合は4.3倍も速いという結果が出ています。ぜひ試してみてください。