Let NUMPY installed by PIP be as fast as that by ANACONDA (on Ubuntu 20.04)

# Abstract
- AnacondaのnumpyはBLAS/LAPACKとしてintel-MKLを使っていて速いらしい
- PIPでnumpyにインストールする際にintel-MKLを指定してビルドすれば同様に速くなるらしい
- Ubuntuの公式リポジトリにinte-MKLが追加されていたのでこれを用いたインストレーションをまとめた


動作確認環境: `Ubuntu 20.04` on `WSL2` on `Windows10 20H2`

1\. intel MKLを公式リポジトリからインストール（ライセンス注意）．なんか選択するとこ出たらデフォルトでok

```bash:input
sudo apt update
sudo apt install intel-mkl
```
2\. 以下のファイルをホームディレクトリ`~`に作る．

```~/numpy-site.cfg
[mkl]
library_dirs = /usr/lib/x86_64-linux-gnu/
include_dirs = /usr/include/mkl
mkl_libs = mkl_rt
lapack_libs =
```
3\. 以下のファイルを`~/.config/pip/`に作る

```~/.config/pip/pip.conf
[install]
no-binary = numpy,scipy
```

4\. ユーザ権限のpipでインストールする．(sudoにしない)

```bash:input
python3 -m pip install numpy
# Defaulting to user installation because normal site-packages is not writeable
# ていわれるけどそれでよい．結局--userオプションつけるのと同じになる（はず）
```

5\. pythonで確認．librariesが`mkl_rt`になってればよし．

```bash:input
python3 -c "import numpy; numpy.show_config()"
```

```bash:output
blas_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
blas_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
lapack_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
lapack_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
```


#Background
Pythonの計算ライブラリであるnumpyが依存している計算ライブラリBLAS/LAPPACKには複数の実装有り [^about-BLAS]．
そのうちintelがintelのCPU用に最適化したintel-MKLという実装がintel系のアーキテクチャで速いらしい [^about-BLAS]<sup>, </sup>[^intel-mkl-is-fast]<sup>, </sup>[^niwaka]．

[^about-BLAS]: [Ubuntu 16.04 上で numpy の環境構築をする > 概要;2018-06-12;](https://ksaw.hatenablog.com/#%E6%A6%82%E8%A6%81)
[^niwaka]: AMDのCPUでも速いらしいので「アーキテクチャで」と書いたがにわか知識
[^intel-mkl-is-fast]: [Anaconda の NumPy が高速みたいなので試してみた; 2020-03-27;](https://tech.morikatron.ai/entry/2020/03/27/100000)


普通に`pip`で`numpy`をインストールするとOpenBLASと呼ばれる実装がインストールされる [^pip-default-numpy]<sup>, </sup>[^pip-default-numpy-scipy]．

一方でANACONDAというPythonディストリビューションをインストールすると，
それについてくる`numpy`のBLASの実装はintel-MKLになっているらしい [^intel-mkl-is-fast]．

[^pip-default-numpy]: [Ubuntu 16.04 上で numpy の環境構築をする > apt (default);2018-06-12;](https://ksaw.hatenablog.com/#pip-default)
[^pip-default-numpy-scipy]: [numpyで MKL を使う; 2018年10月13日; numpy, scipyがMKLにリンクされているか確認](https://qiita.com/f0o0o/items/69d9b766008091a6e698#numpy-scipy-%E3%81%8C-mkl-%E3%81%AB%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B%E7%A2%BA%E8%AA%8D)


このAnacondaはPython本体と周りのライブラリとかを一括して提供する
PythonディストリビューションでもともとはWindows向けの簡単なPython導入環境であった．

ただ，これをLinuxでインストールしようとすると，いちいちWebサイトを開いて，
パッケージをDLしてインストール・コンフィギュレーションしなければいけないっぽく，
またPIPとは別にライブラリ管理をしており，
且つAnacondaですべてのライブラリを網羅しているわけではないらしく，
パッケージ管理周りが大変になりがち [^anaconda-messing]．

[^anaconda-messing]: 頻繁に使う人は慣れて間違いづらいかもしれないが，忘れたころにまた使うということになりがちな人にとっては，ライブラリ管理が少しでも複雑なのは嫌．

もう少し調べてみると，ちょっと工夫すれば`pip`でも`intel-mkl`実装の`numpy`がインストールできるらしいことを知った [^about-BLAS]<sup>, </sup>[^mkl-numpy-scipy-install]<sup>, </sup>[^numpy-using-mkl]．
少し古い参考では`intel MKL`のインストールがちょっと複雑であったが，
新しめ（すくなくとも20.04以降）のUbuntuでは公式リポジトリに`intel-mkl`がありシンプルになった．

ただそのぶん過去のインストレーション方法に記述の設定を丸コピしてもうまくいかず，
パッケージ管理の仕様もよく分からなかったため，
またわりとあんまり文献がなかったため，備忘録の意味も込めて記事にした．

[^mkl-numpy-install]: [mkl numpyのインストール方法; 2016年05月26日; ](https://qiita.com/unnonouno/items/8ab453a1868d77a93679)
[^mkl-numpy-scipy-install]: [NumPy/SciPy で Intel MKL を利用する (2019年11月版); 2019年11月13日; ](https://qiita.com/osanshouo/items/7a729aabf65a70ad6762)
[^numpy-using-mkl]: [numpy で MKL を使う; 2018年10月13日;](https://qiita.com/f0o0o/items/69d9b766008091a6e698)

# Details of Installations
## Intel-MKLのインストール
普通に`apt install`できるのでそれを使う [^mkltolinux]．一応アップデートしておく．


```bash:input
sudo apt update
sudo apt install intel-mkl
```

初回インストール時はライセンスの確認 [^lisence]やリンクの確認がある．
リンクの確認についてはデフォルトのままでok．多分．一応画面の説明に従ってください．

IntelからパッケージをDLしてパッケージ登録して～とかする場合は
`update-alternative`設定や`lpconfig`設定が必要 [^pip-mkl-numpy]<sup>, </sup>[^ldconfig]だったが，
前者はaptでインストールするときに半自動でやってくれるし，
後者は公式リポジトリなので一切不要

## ライブラリのインストールフォルダの確認
さて，インストールフォルダを確認しようと`dpkg -L intel-mkl`としても
ドキュメントしかインストールされてないように見える．

しかし実際の実装は依存している別のパッケージによって管理されている．

```bash:input
apt-cache depends intel-mkl
```

```bash:output
intel-mkl
    Depends: libmkl-dev
```

よって今度は`libmkl-dev`のインストールディレクトリを確認するとヘッダファイル(`.h`)やライブラリファイル(`.so`)がみえる．

```bash:input    
> dpkg -L libmkl-dev
```

```bash:output
...
# header files
/usr/include/mkl/mkl.h
/usr/include/mkl/mkl_blacs.h
/usr/include/mkl/mkl_blas.f90
/usr/include/mkl/mkl_blas.fi
/usr/include/mkl/mkl_blas.h
/usr/include/mkl/mkl_cblas.h
/usr/include/mkl/mkl_cdft.f90
/usr/include/mkl/mkl_cdft.h
/usr/include/mkl/mkl_cdft_types.h
...
# library files. but actually these are just symbolic links
/usr/lib/x86_64-linux-gnu/mkl/libblas.so
/usr/lib/x86_64-linux-gnu/mkl/libblas64.so
/usr/lib/x86_64-linux-gnu/mkl/liblapack.so
/usr/lib/x86_64-linux-gnu/mkl/liblapack64.so
...
```

しかしじつはこの`.so`ファイルはファイルではなくシンボリックリングになっており，
すべて，一個上の親dirにある`libmkl_rt.so`にリンクされている．
ちなみに以下`/lib`となっているのはすべて`/usr/lib`に同じ．というのも歴史的な互換性のためにシンボリックリンクになっている [^usr-lib].

[^usr-lib]: [Linuxのディレクトリについて; 2016年10月12日に更新;](https://qiita.com/usizou/items/be52822007d3c7ff639f)

```bash:input
ls /lib/x86_64-linux-gnu/mkl/* -l
```

```bash:output
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/libblas.so -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/libblas.so.3 -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/libblas64.so -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/libblas64.so.3 -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/liblapack.so -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/liblapack.so.3 -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/liblapack64.so -> ../libmkl_rt.so
lrwxrwxrwx 1 root root 15 Feb 17  2020 /lib/x86_64-linux-gnu/mkl/liblapack64.so.3 -> ../libmkl_rt.so
```
つまりBLAS/LAPACKはどちらも一個のファイルにまとめられている．
ほかの実装だとたぶん異なる．

そしてこの`/lib/x86_64-linux-gnu/libmkl_rt.so`こそがライブラリの実態である．

```bash:input
ls /lib/x86_64-linux-gnu/libmkl_rt.so -l
```

```text:output
-rw-r--r-- 1 root root 6581530 Nov 23  2019 /lib/x86_64-linux-gnu/libmkl_rt.so
```

しかし，この`/lib/x86_64-linux-gnu/libmkl_rt.so`というのは先ほどの`libmkl-dev`パッケージには含まれていなかった．
そこでまた`libmkl-dev`パッケージの依存関係を調べてみると

```bash:input
apt-cache depends libmkl-dev
```

```bash:output
libmkl-dev
 |Depends: debconf
  Depends: <debconf-2.0>
    cdebconf
    debconf
  Depends: libmkl-rt
...
  Depends: pkg-config
    pkgconf
...
```
とのことで，いろいろ表示されたが`Depends: libmkl-rt`とまさにドンピシャなパッケージ名が見えたので，
これのインストール先を調べてみると，

```bash:input
dpkg -L libmkl-rt
```

```bash:output
/.
/usr
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/libmkl_rt.so #<-----------これがメインのものと思われる．
/usr/lib/x86_64-linux-gnu/mkl
/usr/share
/usr/share/doc
/usr/share/doc/libmkl-rt
/usr/share/doc/libmkl-rt/copyright
/usr/share/lintian
/usr/share/lintian/overrides
/usr/share/lintian/overrides/libmkl-rt
/usr/lib/x86_64-linux-gnu/mkl/libblas.so.3     #<---ここから4つはlibmkl-devのものと似ているが，微妙に拡張子が違う．
/usr/lib/x86_64-linux-gnu/mkl/libblas64.so.3   #    が，同様に上のlibmkl_rt.soを指している
/usr/lib/x86_64-linux-gnu/mkl/liblapack.so.3
/usr/lib/x86_64-linux-gnu/mkl/liblapack64.so.3 #<---ここまで
/usr/share/doc/libmkl-rt/changelog.Debian.gz
```


[^lisence]: ライセンスに関しては各自の責任において確認してください．
[^mkltolinux]: [Intel MKLの各Linuxへのインストール方法; 2020年08月28日;](https://qiita.com/termoshtt/items/6fb0f81c876de3ed1617/#ubuntu-2004)
[^pip-mkl-numpy]: [Ubuntu 16.04 上で numpy の環境構築をする > apt (mkl);2018-06-12;](https://ksaw.hatenablog.com/#pip-mkl)
[^ldconfig]: ldconfig関連の操作は今回は必要ないです．参考サイト等ではデフォルトでないディレクトリ，ホームやオプショナルにインストールしているため，その管理をシステムに伝える必要がありました．が今回はUbuntu公式リポジトリで管理されているので，デフォルトのライブラリディレクトリ`/lib ==> /usr/lib`にインストーられます．[「/lib」「/usr/lib」ディレクトリとライブラリ](https://linuc.org/study/knowledge/545/)



## numpyをintel-mklをつかってビルドするための設定
以上で知りえた情報をもとに`pip`君に

- もともとビルドしてあるバイナリを使わずに
- intel-mklを使って新たにnumpyビルドして

とオーダーするための設定ファイルを書く． 

以下のファイルを`~/.config/pip/`に作る，
これは`numpy`と`scipy`のインストールにおいてはもともとビルドしてあるやつは使わんといてという注文書になる．
このファイルを作らなくても`pip install`じに`--no-binary`オプションをつけるのとまったく同じ．

```~/.config/pip/pip.conf
[install]
no-binary = numpy,scipy
```

以下のファイルをホームディレクトリ`~`に作る．
ここで先ほどの情報を用いる．

- `library_dirs`に代入するのは`libmkl_rt.so`が存在するディレクトリ，（`libblas.so`とかではない！）
- `include_dirs`に代入するのはヘッダファイルが存在しているディレクトリである．

意外とこの情報が手に取りづらく，そもそもaptやpipの仕組みも全然わかってなかったので苦戦した．

```~/numpy-site.cfg
[mkl]
library_dirs = /usr/lib/x86_64-linux-gnu/
include_dirs = /usr/include/mkl
mkl_libs = mkl_rt
lapack_libs =
```
`lapack_libs`が空欄なのは多分LAPACKもBLASとおなじファイルに含められているから．

## numpyインストール

ユーザ権限のpipでインストールする．(sudoにしない)

```bash:input
python3 -m pip install numpy
# Defaulting to user installation because normal site-packages is not writeable
# ていわれるけどそれでよい．結局--userオプションつけるのと同じになる（はず）
```
以上でインストールは完了のはず．

## numpy実装の内容確認
ほんとにintel MKLでnumpyが入ってるかをpythonで確認．librariesが`mkl_rt`になってればよし．

```bash:input
python3 -c "import numpy; numpy.show_config()"
```

```bash:output
blas_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
blas_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
lapack_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
lapack_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['/usr/lib/x86_64-linux-gnu/']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['/usr/include/mkl']
```

# Conclusion
苦戦したけどできてしまえばとても簡単．