web-dev-qa-db-ja.com

ベンチマーク(PythonとBLASを使用したc ++)および(numpy)

BLASおよびLAPACK線形代数機能を広範囲に使用するプログラムを作成したいと思います。パフォーマンスが問題であるため、ベンチマークを実施しましたが、知りたいのですが、私が取ったアプローチが合法である場合。

いわば、3人の出場者がいて、単純な行列-行列乗算でパフォーマンスをテストしたいです。出場者は次のとおりです。

  1. dotの機能のみを使用するNumpy。
  2. Python、共有オブジェクトを介してBLAS機能を呼び出します。
  3. C++、共有オブジェクトを介してBLAS機能を呼び出します。

シナリオ

さまざまな次元iにマトリックス-マトリックス乗算を実装しました。 iは5から500まで5の増分で実行され、行列m1およびm2は次のように設定されます。

m1 = numpy.random.Rand(i,i).astype(numpy.float32)
m2 = numpy.random.Rand(i,i).astype(numpy.float32)

1.ナンピー

使用されるコードは次のようになります。

tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))

2. Python、共有オブジェクトを介してBLASを呼び出す

機能付き

_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):

    no_trans = c_char("n")
    n = c_int(i)
    one = c_float(1.0)
    zero = c_float(0.0)

    _blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref(n), byref(n), 
            byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n), 
            m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero), 
            r.ctypes.data_as(ctypes.c_void_p), byref(n))

テストコードは次のようになります。

r = numpy.zeros((i,i), numpy.float32)
tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul")
rBlas.append((i, tBlas.repeat(20, 1)))

3. c ++、共有オブジェクトを介してBLASを呼び出す

これで、c ++コードは自然に少し長くなるので、情報を最小限に抑えます。
関数をロードします

void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");

次のようにgettimeofdayで時間を測定します。

gettimeofday(&start, NULL);
f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim);
gettimeofday(&end, NULL);
dTimes[j] = CalcTime(start, end);

ここで、jは20回実行されるループです。経過時間を計算します

double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}

結果

結果は以下のプロットに示されています。

enter image description here

ご質問

  1. 私のアプローチは公平だと思いますか、それとも回避できる不必要なオーバーヘッドがありますか?
  2. 結果が、c ++とpythonアプローチとの間に大きな矛盾を示すと思いますか?両方とも計算に共有オブジェクトを使用しています。
  3. プログラムにpythonを使用したいので、BLASまたはLAPACKルーチンを呼び出すときにパフォーマンスを向上させるにはどうすればよいですか?

ダウンロード

完全なベンチマークは here からダウンロードできます。 (J.F.セバスチャンはそのリンクを可能にした^^)

104
Woltan

ベンチマーク を実行しました。私のマシンでは、C++とnumpyに違いはありません。

woltan's benchmark

私のアプローチは公平だと思いますか、それとも回避できる不必要なオーバーヘッドがありますか?

結果に違いがないため、公平に思えます。

結果が、c ++とpythonアプローチとの間に大きな矛盾を示すと思いますか?両方とも計算に共有オブジェクトを使用しています。

いや.

プログラムにpythonを使用したいので、BLASまたはLAPACKルーチンを呼び出すときにパフォーマンスを向上させるにはどうすればよいですか?

Numpyがシステム上の最適化されたバージョンのBLAS/LAPACKライ​​ブラリを使用していることを確認してください。

54
jfs

更新(2014年7月30日):

新しいHPCでベンチマークを再実行します。ハードウェアとソフトウェアスタックの両方が、元の回答のセットアップから変更されました。

結果を googleスプレッドシート に入れました(元の回答の結果も含まれています)。

ハードウェア

HPCには、Intel Sandy Bridge CPUと新しいIvy Bridge CPUを備えた2つの異なるノードがあります。

Sandy(MKL、OpenBLAS、ATLAS):

  • [〜#〜] cpu [〜#〜]:2 x 16 Intel(R)Xeon(R)E2560 Sandy Bridge @ 2.00GHz(16コア)
  • [〜#〜] ram [〜#〜]:64 GB

Ivy(MKL、OpenBLAS、ATLAS):

  • [〜#〜] cpu [〜#〜]:2 x 20 Intel(R)Xeon(R)E2680 V2 Ivy Bridge @ 2.80GHz(20コア、HT = 40コア)
  • [〜#〜] ram [〜#〜]:256 GB

ソフトウェア

ソフトウェアスタックは、両方のノード用です。 GotoBLAS2の代わりに、OpenBLASが使用され、8スレッド(ハードコード化)に設定されたマルチスレッドATLAS BLASもあります。

  • [〜#〜] os [〜#〜]:スーゼ
  • Intelコンパイラ:ictce-5.3.0
  • Numpy: 1.8.0
  • OpenBLAS: 0.2.6
  • ATLAS::3.8.4

ドット積ベンチマーク

ベンチマークコードは以下と同じです。ただし、新しいマシンでは、マトリックスサイズ50および80のベンチマークも実行しました。
以下の表には、元の回答のベンチマーク結果が含まれています(名前を変更:MKL-> Nehalem MKL、Netlib Blas-> Nehalem Netlib BLASなど)

Matrix multiplication (sizes=[1000,2000,3000,5000,8000])

シングルスレッドパフォーマンス: single threaded performance

マルチスレッドパフォーマンス(8スレッド): multi-threaded (8 threads) performance

スレッドとマトリックスサイズ(Ivy Bridge MKL): Matrix-size vs threads

ベンチマークスイート

benchmark suite

シングルスレッドパフォーマンス: enter image description here

マルチスレッド(8スレッド)パフォーマンス: enter image description here

結論

新しいベンチマークの結果は、元の回答の結果と似ています。 OpenBLASおよび[〜#〜] mkl [〜#〜]は、Eigenvalueテストを除き、同じレベルで実行します。 Eigenvalueテストは、OpenBLASシングルスレッドモードでのみ十分に実行されます。マルチスレッドモードでは、パフォーマンスが低下します。

"Matrix size vs threadsチャート"は、MKLおよびOpenBLASは一般にコア/スレッドの数に応じて適切にスケーリングされますが、マトリックスのサイズに依存することも示しています。小さなマトリックスの場合、コアを追加してもパフォーマンスはあまり向上しません。

Sandy BridgeからIvy Bridgeへのパフォーマンスも約30%向上しています。これは、クロックレートが高い(+ 0.8 Ghz)か、アーキテクチャが優れているか、またはその両方による可能性があります。


元の回答(2011年4月10日):

しばらく前に、numpyとBLASを使用してpythonで記述された線形代数の計算/アルゴリズムを最適化する必要があったため、異なるnumpy/BLAS構成のベンチマーク/テストを行いました。

具体的にテストしました:

  • ATLASでのナンピー
  • GotoBlas2 を含むNumpy(1.13)
  • MKLでのNumpy(11.1/073)
  • Numpy with Accelerate Framework(Mac OS X)

2つの異なるベンチマークを実行しました。

  1. サイズの異なる行列の単純な内積
  2. here にあるベンチマークスイート。

私の結果は次のとおりです。

機械

Linux(MKL、ATLAS、No-MKL、GotoBlas2):

  • [〜#〜] os [〜#〜]:Ubuntu Lucid 10.4 64ビット。
  • [〜#〜] cpu [〜#〜]:2 x 4 Intel(R)Xeon(R)E5504 @ 2.00GHz(8コア)
  • [〜#〜] ram [〜#〜]:24 GB
  • Intelコンパイラ:11.1/073
  • Scipy:0.8
  • Numpy:1.5

Mac Book Pro(フレームワークの高速化):

  • [〜#〜] os [〜#〜]:Mac OS X Snow Leopard(10.6)
  • [〜#〜] cpu [〜#〜]:1 Intel Core 2 Duo 2.93 Ghz(2コア)
  • [〜#〜] ram [〜#〜]:4 GB
  • Scipy:0.7
  • Numpy:1.3

Macサーバー(フレームワークの高速化):

  • [〜#〜] os [〜#〜]:Mac OS X Snow Leopard Server(10.6)
  • [〜#〜] cpu [〜#〜]:4 X Intel(R)Xeon(R)E5520 @ 2.26 Ghz(8コア)
  • [〜#〜] ram [〜#〜]:4 GB
  • Scipy:0.8
  • Numpy:1.5.1

ドット製品ベンチマーク

コード

import numpy as np
a = np.random.random_sample((size,size))
b = np.random.random_sample((size,size))
%timeit np.dot(a,b)

結果

システム|サイズ= 1000 |サイズ= 2000 |サイズ= 3000 | 
 netlib BLAS | 1350ミリ秒| 10900ミリ秒| 39200ミリ秒| 
 ATLAS(1 CPU)| 314ミリ秒| 2560ミリ秒| 8700ミリ秒| 
 MKL(1 CPU)| 268ミリ秒| 2110ミリ秒| 7120ミリ秒| 
 MKL(2 CPU)| -| -| 3660ミリ秒| 
 MKL(8 CPU)| 39ミリ秒| 319ミリ秒| 1000ミリ秒| 
 GotoBlas2(1 CPU)| 266ミリ秒| 2100ミリ秒| 7280ミリ秒| 
 GotoBlas2(2 CPU)| 139ミリ秒| 1009ミリ秒| 3690ミリ秒| 
 GotoBlas2(8 CPU)| 54ミリ秒| 389ミリ秒| 1250ミリ秒| 
 Mac OS X(1 CPU)| 143ミリ秒| 1060ミリ秒| 3605ミリ秒| 
 Macサーバー(1 CPU)| 92ミリ秒| 714ミリ秒| 2130ミリ秒| 

Dot product benchmark - chart

ベンチマークスイート

コード
ベンチマークスイートの詳細については、 here を参照してください。

結果

システム|固有値| svd | det | inv |ドット| 
 netlib BLAS | 1688ミリ秒| 13102ミリ秒| 438ミリ秒| 2155ミリ秒| 3522ミリ秒| 
 ATLAS(1 CPU)| 1210ミリ秒| 5897ミリ秒| 170ミリ秒| 560ミリ秒| 893ミリ秒| 
 MKL(1 CPU)| 691ミリ秒| 4475ミリ秒| 141ミリ秒| 450ミリ秒| 736ミリ秒| 
 MKL(2 CPU)| 552ミリ秒| 2718ミリ秒| 96ミリ秒| 267ミリ秒| 423ミリ秒| 
 MKL(8 CPU)| 525ミリ秒| 1679ミリ秒| 60ミリ秒| 137ミリ秒| 197ミリ秒| 
 GotoBlas2(1 CPU)| 2124ミリ秒| 4636ミリ秒| 147ミリ秒| 456ミリ秒| 743ミリ秒| 
 GotoBlas2(2 CPU)| 1560ミリ秒| 3278ミリ秒| 116ミリ秒| 295ミリ秒| 460ミリ秒| 
 GotoBlas2(8 CPU)| 741ミリ秒| 2914ミリ秒| 82ミリ秒| 262ミリ秒| 192ミリ秒| 
 Mac OS X(1 CPU)| 948ミリ秒| 4339ミリ秒| 151ミリ秒| 318ミリ秒| 566ミリ秒| 
 Macサーバー(1 CPU)| 1033ミリ秒| 3645ミリ秒| 99ミリ秒| 232ミリ秒| 342ミリ秒| 

Benchmark suite - chart

Installation

[〜#〜] mkl [〜#〜]のインストールには、完全なIntel Compiler Suiteのインストールが含まれていますが、これは非常に簡単です。ただし、MKLサポートを使用してnumpyを構成およびコンパイルするいくつかのバグ/問題のため、少し面倒でした。

GotoBlas2は、共有ライブラリとして簡単にコンパイルできる小さなパッケージです。ただし、 bug があるため、numpyで使用するためには、ビルド後に共有ライブラリを再作成する必要があります。
このビルドに加えて、複数のターゲットプラットフォーム用のビルドは、何らかの理由で機能しませんでした。そのため、最適化されたlibgoto2.soファイルが必要なプラットフォームごとに.soファイルを作成する必要がありました。

Ubuntuのリポジトリからnumpyをインストールすると、[〜#〜] atlas [〜#〜]を使用するようにnumpyが自動的にインストールおよび構成されます。ソースから[〜#〜] atlas [〜#〜]をインストールするには時間がかかり、追加の手順(Fortranなど)が必要になります。

NumpyをFinkまたはMac Portsを使用してMac OS Xマシンにインストールする場合、[〜#〜] atlas [〜#〜]を使用するようにnumpyを構成します_またはAppleのAccelerateフレームワークnumpy.core._dotblasファイルでlddを実行するか、numpy.show_config()を呼び出すことで確認できます。

結論

[〜#〜] mkl [〜#〜]は、GotoBlas2に続く最高のパフォーマンスを発揮します。
eigenvalueテストでは、GotoBlas2のパフォーマンスは予想よりも驚くほど悪くなっています。なぜそうなのかわからない。
AppleのAccelerateフレームワークは、特に他のBLAS実装と比較して、シングルスレッドモードで非常に優れたパフォーマンスを発揮します。

GotoBlas2[〜#〜] mkl [〜#〜]の両方は、スレッド数に応じて非常にうまくスケーリングします。したがって、複数のスレッドで実行する大きな行列を処理する必要がある場合は、非常に役立ちます。

いずれにしても、デフォルトのnetlib blas実装を使用しないでください。深刻な計算作業には遅すぎるためです。

クラスターにはAMDのACMLもインストールしましたが、パフォーマンスは[〜#〜] mkl [〜#〜]およびGotoBlas2に似ていました。難しい数字はありません。

個人的にはGotoBlas2を使用することをお勧めします。インストールが簡単で、無料だからです。

C++/Cでコーディングする場合は、 Eigen3 もチェックアウトします。これは、 casesMKL/GotoBlas2を上回ると思われます。かなり使いやすいです。

72
Ümit

別のベンチマークを次に示します(Linuxでは、makeと入力するだけです): http://dl.dropbox.com/u/5453551/blas_call_benchmark.Zip

http://dl.dropbox.com/u/5453551/blas_call_benchmark.png

Numpy、Ctypes、Fortranの間では、大きな行列のさまざまな方法に本質的な違いはありません。 (C++の代わりにFortran ---そしてこれが重要な場合、ベンチマークはおそらく壊れています。)

C++のCalcTime関数に符号エラーがあるようです。 ... + ((double)start.tv_usec))は、代わりに... - ((double)start.tv_usec))にする必要があります。 おそらく、ベンチマークには他のバグもあります。たとえば、異なるBLASライブラリ間、スレッド数などの異なるBLAS設定間、またはリアルタイムとCPU時間の比較などです。

[〜#〜] edit [〜#〜]CalcTime関数の中括弧のカウントに失敗しました-それは問題ありません。

ガイドラインとして:ベンチマークを行う場合は、必ずどこかにallコードを投稿してください。特に驚くべきことに、完全なコードを持たずにベンチマークにコメントすることは、通常生産的ではありません。


どのBLAS Numpyがリンクされているかを確認するには、次のようにします。

 $ python 
 Python 2.7.2+(デフォルト、2011年8月16日07:24:41)
 [GCC 4.6.1] on linux2 
 Type "詳細については、「ヘルプ」、「著作権」、「クレジット」または「ライセンス」。
 >>> import numpy.core._dotblas 
 >>> numpy.core._dotblas .__ file __ 
 '/ usr/lib/pymodules/python2.7/numpy/core/_dotblas.so' 
 >>> 
 $ ldd /usr/lib/pymodules/python2.7/numpy/ core/_dotblas.so 
 linux-vdso.so.1 =>(0x00007fff5ebff000)
 libblas.so.3gf => /usr/lib/libblas.so.3gf(0x00007fbe618b3000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6(0x00007fbe61514000)

[〜#〜] update [〜#〜]:numpyをインポートできない場合。 core._dotblas、NumpyはBLASの内部フォールバックコピーを使用していますが、これはより低速で、パフォーマンスコンピューティングで使用するためのものではありません。以下の@Woltanからの返信は、これがNumpyとCtypes + BLASで見られる違いの説明であることを示しています。

状況を修正するには、ATLASまたはMKLのいずれかが必要です---これらの指示を確認してください: http://scipy.org/Installing_SciPy/Linux ほとんどのLinuxディストリビューションにはATLASが同梱されているため、最適なオプションはインストールすることです彼らのlibatlas-devパッケージ(名前は異なる場合があります)。

20
pv.

分析で示した厳密さを考えると、これまでの結果には驚かされます。これを「回答」としていますが、それはコメントには長すぎて、可能性を提供するからです(ただし、あなたはそれを検討したと思いますが)。

複雑さが増すにつれて、pythonが参加する割合は小さくなるはずだからです。グラフの右側の結果にもっと興味がありますが、そこに示されている桁違いの矛盾は不穏なものです。

Numpyが活用できる最高のアルゴリズムを使用しているのだろうか。 Linuxのコンパイルガイドから:

「FFTWのビルド(3.1.2):SciPyバージョン> = 0.7およびNumpy> = 1.2:ライセンス、構成、およびメンテナンスの問題のため、FFTWのサポートはSciPy> = 0.7およびNumPy> = 1.2のバージョンで削除されました。 fftpackの組み込みバージョン。分析に必要な場合、FFTWの速度を利用する方法がいくつかあります。サポートを含むNumpy/Scipyバージョンにダウングレードします。FFTWの独自のラッパーをインストールまたは作成します。 http://developer.berlios.de/projects/pyfftw/ は承認されていない例です。」

Mklでnumpyをコンパイルしましたか? ( http://software.intel.com/en-us/articles/intel-mkl/ )。 Linuxで実行している場合、numpyをmklでコンパイルする手順は次のとおりです。 http://www.scipy.org/Installing_SciPy/Linux#head-7ce43956a69ec51c6f2cedd894a4715d5bfff974 (URLにもかかわらず)。重要な部分は次のとおりです。

[mkl]
library_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/lib/intel64
include_dirs = /opt/intel/composer_xe_2011_sp1.6.233/mkl/include
mkl_libs = mkl_intel_lp64,mkl_intel_thread,mkl_core 

Windowsを使用している場合は、mklでコンパイル済みのバイナリを取得できます(また、pyfftw、およびその他の多くの関連アルゴリズムも取得できます): http://www.lfd.uci.edu/~gohlke/pythonlibs / 、カリフォルニア大学アーバイン校蛍光ダイナミクス研究所のChristoph Gohlke氏に感謝の意を表します。

どちらの場合でも、注意すべき多くのライセンス問題などがありますが、インテルのページでそれらについて説明しています。繰り返しますが、これを検討したと思いますが、ライセンス要件を満たしている場合(Linuxでは非常に簡単です)、これにより、FFTWを使用せずに単純な自動ビルドを使用する場合に比べて、numpy部分が大幅に高速化されます。このスレッドをフォローして、他の人の考えを見てみたいと思います。とにかく、優れた厳格さと優れた質問。投稿してくれてありがとう。

9
Profane