最近、マルチプロセッシング モジュールと mpi4py を通信ツールとして使用して並列アプリケーションのパフォーマンスを測定したところ、奇妙な効果が見られました。
アプリケーションは、一連のデータに対して進化的アルゴリズムを実行します。評価を除いて、ほとんどの操作は順次実行されます。すべての進化的演算子が適用された後、すべての個人が新しいフィットネス値を受け取る必要があります。これは評価中に行われます。基本的には、float のリスト (python のもの) に対して実行される単なる数学的な計算です。評価の前に、データセットは mpi のスキャッターまたは python の Pool.map によって分散され、次に並列評価が行われ、後で mpi の収集または再び Pool.map メカニズムによってデータが返されます。
私のベンチマーク プラットフォームは、Core i7 (4/8 コア)、8 GB の RAM、SSD ドライブで Open MPI 1.4.3 を搭載した Ubuntu 11.10 を実行する仮想マシン (virtualbox) です。
本当に驚くべきことは、通信ツールによっては、一定のプロセスのしきい値を超えるとパフォーマンスが低下することです。以下の写真で説明できます。
y 軸 - 処理時間
x 軸 - プロセスの
数 色 - 各個体のサイズ (float の数)
1) multiprocessing モジュールの使用 - Pool.map
2) mpi の使用 - スキャッター/ギャザー
3) 両方の写真を重ね合わせる
最初は、ハイパースレッディングのせいだと思っていました。大規模なデータ セットでは、4 つのプロセス (4 つの物理コア) に達すると遅くなるためです。ただし、マルチプロセッシングの場合にも表示されるはずですが、そうではありません。私の別の推測では、mpi 通信方式は python 通信方式よりもはるかに効果的ではありませんが、信じがたいと思います。
これらの結果について誰か説明がありますか?
追加した:
結局のところ、それはハイパースレッディングのせいだと思い始めています。コア i5 (2/4 コア) を搭載したマシンでコードをテストしましたが、3 つ以上のプロセスでパフォーマンスが低下しました。私が思いつく唯一の説明は、私が使用している i7 には、ハイパースレッディングと同時に評価を計算するのに十分なリソース (キャッシュ?) がなく、4 つの物理コアで実行するために 4 つ以上のプロセスをスケジュールする必要があるということです。
ただし、興味深いのは、mpi htop を使用すると、8 つの論理コアすべてが完全に使用されていることが示されることです。これは、上記のステートメントが正しくないことを示唆しているはずです。一方、Pool.Map を使用すると、すべてのコアが完全に使用されるわけではありません。1つまたは2つを最大限に使用し、残りは部分的にしか使用しません。なぜこのように動作するのかわかりません。明日、この動作を示すスクリーンショットを添付します。
私はコードで特別なことをしていません。それは本当に単純です (コード全体を提供していないのは、それが秘密だからではなく、DEAP のような追加のライブラリをインストールする必要があるためです。誰かが本当に問題に興味があり、準備ができている場合DEAP をインストールするには、簡単な例を用意できます)。MPI のコードは、population コンテナー (リストから継承) を処理できないため、少し異なります。もちろん多少のオーバーヘッドはありますが、大したことはありません。以下に示すコードを除いて、残りの部分は同じです。
プールマップ:
def eval_population(func, pop):
for ind in pop:
ind.fitness.values = func(ind)
return pop
# ...
self.pool = Pool(8)
# ...
for iter_ in xrange(nr_of_generations):
# ...
self.pool.map(evaluate, pop) # evaluate is really an eval_population alias with a certain function assigned to its first argument.
# ...
MPI - スキャッター/ギャザー
def divide_list(lst, n):
return [lst[i::n] for i in xrange(n)]
def chain_list(lst):
return list(chain.from_iterable(lst))
def evaluate_individuals_in_groups(func, rank, individuals):
comm = MPI.COMM_WORLD
size = MPI.COMM_WORLD.Get_size()
packages = None
if not rank:
packages = divide_list(individuals, size)
ind_for_eval = comm.scatter(packages)
eval_population(func, ind_for_eval)
pop_with_fit = comm.gather(ind_for_eval)
if not rank:
pop_with_fit = chain_list(pop_with_fit)
for index, elem in enumerate(pop_with_fit):
individuals[index] = elem
for iter_ in xrange(nr_of_generations):
# ...
evaluate_individuals_in_groups(self.func, self.rank, pop)
# ...
追加 2:
前述したように、i5 マシン (2/4 コア) でいくつかのテストを行った結果は次のとおりです。
また、2 つの xeon (2x 6/12 コア) を搭載したマシンを見つけて、ベンチマークを繰り返しました。
これで、同じ動作の例が 3 つあります。物理コアよりも多くのプロセスで計算を実行すると、計算が悪化し始めます。リソースが不足しているため、同じ物理コア上のプロセスを同時に実行できないためだと思います。