MKL版NumPy, SciPy をビルドする方法(お手軽バージョン)

## MKLについて
[Intel® oneAPI Math Kernel Library](https://software.intel.com/content/www/us/en/develop/tools/oneapi/components/onemkl.html#gs.3xggx4) （以下単にMKLと略します）は高度にベクトル化およびスレッド化された線形代数、高速フーリエ変換 (FFT)、ベクトル演算関数、統計関数を含む数値演算ライブラリです。
アプリケーションがBLASやLAPACKのルーチンを呼び出しているのであれば、MKLに置き換えることで性能を大幅に向上させることが期待できます。

## まずは結論から
次のスクリプトを実行するだけです。
numpy と scipy をMKLをリンクしてリビルドしてくれます。


```bash:Setup_NumpyScipy.sh
#!/bin/bash

PYPI_CACHE_DIR=$HOME/cache/pypi

# ---- YOU MAY NOT NEED TO EDIT BELLOWS ----------

__AUTHOR__="Goichi Iisaka"
__VERSION__="1.0"
__DATE__="Jun 17 2021, Sunny day"

python -m pip install -U pip wheel

# for Build numpy and scipy
cat <<_EOF_ > $HOME/.numpy-site.cfg
[mkl]
library_dirs = $HOME/.local/lib
include_dirs = $HOME/.local/include
mkl_libs = mkl_rt
runtime_library_dirs = $HOME/.local/lib
lapack_libs =
extra_link_args = -Wl,--rpath,$HOME/.local/lib -Wl,--no-as-needed -lmkl_rt -ldl -lpthread -lm
_EOF_

export LD_LIBRARY_PATH=$HOME/.local/lib:${LD_LIBRARY_PATH}

[ -d "${PYPI_CACHE_DIR}" ] || mkdir -p "${PYPI_CACHE_DIR}"

pip download -d ${PYPI_CACHE_DIR} \
    mkl-devel mkl-include pybind11 cython
pip install --no-index --find-link ${PYPI_CACHE_DIR} \
    mkl-devel mkl-include pybind11 cython

# Fix MKL libs
for F in $HOME/.local/lib/lib*.so.[0-9]
do
    [ -f ${F%.*} ] || ln -s $F ${F%.*}
done

pip wheel --no-binary :all: numpy && {
    rm -f ${PYPI_CACHE_DIR}/numpy-*.whl
    mv numpy-*whl ${PYPI_CACHE_DIR}
    pip install --no-index --find-link ${PYPI_CACHE_DIR} --force-reinstall numpy
}

pip wheel --no-deps --no-binary :all: scipy && {
    rm -f ${PYPI_CACHE_DIR}/scipy-*.whl
    mv scipy-*whl ${PYPI_CACHE_DIR}
    pip install --no-index --find-link ${PYPI_CACHE_DIR} --force-reinstall scipy
}

pip wheel --no-deps --no-binary :all: bottleneck && {
    rm -f ${PYPI_CACHE_DIR}/Rottleneck-*
    mv Bottleneck-*whl ${PYPI_CACHE_DIR}
    pip install --no-index --find-link ${PYPI_CACHE_DIR} --force-reinstall bottleneck
}
```


## MKLがリンクされているかを確認

```bash
$ python -c "import numpy; numpy.show_config()"
blas_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/home/iisaka/.local/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/home/iisaka/.local/include']
    extra_link_args = ['-Wl,--rpath,/home/iisaka/.local/lib', '-Wl,--no-as-needed', '-lmkl_rt', '-ldl', '-lpthread', '-lm']
blas_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/home/iisaka/.local/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/home/iisaka/.local/include']
    extra_link_args = ['-Wl,--rpath,/home/iisaka/.local/lib', '-Wl,--no-as-needed', '-lmkl_rt', '-ldl', '-lpthread', '-lm']
lapack_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/home/iisaka/.local/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/home/iisaka/.local/include']
    extra_link_args = ['-Wl,--rpath,/home/iisaka/.local/lib', '-Wl,--no-as-needed', '-lmkl_rt', '-ldl', '-lpthread', '-lm']
lapack_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/home/iisaka/.local/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/home/iisaka/.local/include']
    extra_link_args = ['-Wl,--rpath,/home/iisaka/.local/lib', '-Wl,--no-as-needed', '-lmkl_rt', '-ldl', '-lpthread', '-lm']
```

blas_mkl_info や lapack_mkl_info が定義されていればOKです。

## 再度インストールするとき
再度インストールするときは次のようにキャッシュディレクトリを指示します。


```bash
$ pip install --no-index -f $HOME/cache/pypi numpy
```


## ビルドに必要なパッケージ

VPSサーバなどでディプロイ直後の状態では最低限次のパッケージをインストールしておけばOKです。

テストは Ubuntu 20.04 で行っています。

```bash:Setup_BuildEnv.sh
#!/bin/bash

sudo apt install -y \
	build-essential gfortran python-is-python3 python3-pip \
	pkg-config autoconf automake libtool \
	liblapack-dev libfftw3-dev 
```

## なぜこのスクリプト作ろうとしたのかという経緯
なにげにPYPIを見ていると oneAPI 2021.02 版のランタイムライブラリが登録されていることに気づきました。pip だけでMKLがインストールができるのはとても簡単です。
実際、oneAPI のBaseKit と HPCKit をインストールするとなれば、
結構大げさなことになってしまいます。
ディスクも21GB消費してしまいます。

```bash
isaka@dev02:~$ du -sh /opt/intel/oneapi
21G /opt/intel/oneapi

iisaka@dev02:~$ du -sh /opt/intel/oneapi/mkl
3.1G    /opt/intel/oneapi/mkl

iisaka@dev02:~$ ls /opt/intel/oneapi/mkl/2021.2.0
benchmarks  documentation  examples  interfaces  licensing    tools
bin         env            include   lib         modulefiles
```

インテルコンパイラを使い続けるのであれば、何も問題ないかもしれませんが、
numpy や scipy をリビルドするために必要なライブラリは libmkl_rt.so.1 だけなので、クラウドで構築するような場合など少しディスクがもったいないと感じたわけです。

## PIPでインストールできるMKLの不具合
pip でインストールされるMKLでは共有ライブラリ名がおかしくて、
*.so　になっていないため、リンカーがうまく共有ライブラリを見つけてくれません。
これを修復することもスクリプトにした理由のひとつでした。

## 既知の制限
scipy をビルドするためには2GBメモリのサーバでは、メモリ不足でビルドに失敗します。



## 参考資料
 * [NumPy/SciPy で Intel MKL を利用する (2019年11月版)](https://qiita.com/osanshouo/items/7a729aabf65a70ad6762)
 * [Anacondaの商用利用有償化に際して: pipでIntel Math Kernel Library (MKL)をサクッと入れる (Windows)](https://qiita.com/Koshka/items/9226ef69676d18216fa1)
 * [numpy で MKL を使う](https://qiita.com/f0o0o/items/69d9b766008091a6e698)