numpy, scipyは数値計算のライブラリ。自分で作らなくてもやりたいことはほぼ揃っています。numpyやscipyの計算部分はCやFortranで作られてるから高速というのは有名な話ですが、とはいえ行列計算はopen blasが使用されているとのこと。Intel CPUつんでるマシンならIntel MKL使ったら早くなるんじゃね?っていうことで、やってみました。
対象マシン
MacBook Pro (15-inch, 2018)
プロセッサ 2.9 GHz Intel Core i9
メモリ 32 GB 2400 MHz DDR4
numpy scipy with openblas
open blasが紐付いてるかどうか確認してみる。
>>> import numpy
>>> numpy.show_config()
blas_mkl_info:
NOT AVAILABLE
blis_info:
NOT AVAILABLE
openblas_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
blas_opt_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
lapack_mkl_info:
NOT AVAILABLE
openblas_lapack_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
lapack_opt_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
>>> import scipy
>>> scipy.show_config()
lapack_mkl_info:
NOT AVAILABLE
openblas_lapack_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
lapack_opt_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
blas_mkl_info:
NOT AVAILABLE
blis_info:
NOT AVAILABLE
openblas_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
blas_opt_info:
libraries = ['openblas', 'openblas']
library_dirs = ['/usr/local/lib']
language = c
define_macros = [('HAVE_CBLAS', None)]
とりあえず下記のコードでタイム測定してみました。
import numpy as np
import time
import scipy.linalg.blas
N = 10000
A = np.random.rand(N,N)
B = np.random.rand(N,N)
t1 = time.time()
C = scipy.linalg.blas.dgemm(alpha=1.0, a=A, b=B)
t2 = time.time()
print(t2-t1)
結果は以下。
$ python test.py
12.556735038757324
$ python test.py
12.65819001197815
$ python test.py
12.273000955581665
平均で12.496秒でした。
numpy scipy with Intel MKL
では、Intel MKLを試してみましょう。ちなみにIntel MKLのインストールは以下参照。
こういうときvenvが便利です。globalの環境を汚しません。下記のように、”.numpy-site.cfg”にmklのパスを記載してあげてからインストールします。
$ python -m venv venv
$ source ./venv/bin/activate
(venv) $ echo "[mkl]
library_dirs = /opt/intel/mkl/lib/intel64
include_dirs = /opt/intel/mkl/include
mkl_libs = mkl_rt
lapack_libs =" > .numpy-site.cfg
(venv) $ pip install --no-binary :all: numpy
(venv) $ pip install --no-binary :all: scipy
インストール終了後、intel MKLに紐付いているか確認。
>>> import numpy
>>> numpy.show_config()
blas_mkl_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
blas_opt_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
lapack_mkl_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
lapack_opt_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
>>> import scipy
>>> scipy.show_config()
lapack_mkl_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
lapack_opt_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
blas_mkl_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
blas_opt_info:
libraries = ['mkl_rt', 'pthread']
library_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
include_dirs = ['/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/include', '/opt/intel/compilers_and_libraries_2019.1.144/mac/mkl/lib']
上と同じコードで計算を走らせてみましょう。
(venv) $ python test.py
11.162160873413086
(venv) $ python test.py
11.657418966293335
(venv) $ python test.py
11.576632022857666
平均時間は11.465秒でした。少し早くなってように思いますが、誤差の範囲でしょうか?期待していたほど早くはなりませんでした。
C/C++での計算結果
同じコードをC/C++で計算させたものが下記です。
-O2オプションだとpythonとほぼ同じですね。python素晴らしい。
「C++、お前の本気はこんなものなのか!?」ってことで、-O3 オプションをつけてコンパイルしてみました。結果は、約7.5秒!やりました。C++の面目を保ちました。
$ c++ -DMKL_ILP64 -m64 -I${MKLROOT}/include ${MKLROOT}/lib/libmkl_intel_ilp64.a ${MKLROOT}/lib/libmkl_intel_thread.a ${MKLROOT}/lib/libmkl_core.a -liomp5 -lpthread -lm -ldl -O3 -std=c++11 -o dgemm_mkl dgemm_mkl.cpp
$ ./dgemm_mkl
cblas_dgemm with 10000 * 10000 matrix.
Realtime: 7.507 sec.
CPU time: 44.7587sec.
まとめ
numpy, scipyをintel MKLでインストールしてみましたら、ちょっとだけ早くなりました。が、思ってたほど劇的には変わりませんでした。Openblasってすごいんですね。コンパイルに時間かかるので、あんまりコスパは高くないかと。まぁ、コンパイルは最初の一回だけなんですが。