20

最近、マルチプロセッシング モジュールと 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 つあります。物理コアよりも多くのプロセスで計算を実行すると、計算が悪化し始めます。リソースが不足しているため、同じ物理コア上のプロセスを同時に実行できないためだと思います。

4

1 に答える 1

6

MPI は実際にはノード間通信を行うように設計されているため、ネットワークを介して他のマシンと通信します。同じノードで MPI を使用すると、スレッド化などと比較して、送信する必要があるすべてのメッセージに対して大きなオーバーヘッドが発生する可能性があります。

mpi4py は、分散メモリの使用を対象としているため、すべてのメッセージのコピーを作成します。OpenMPI がノード内通信に共有メモリを使用するように構成されていない場合、このメッセージはカーネルの tcp スタックを介して送信され、別のプロセスに配信されるため、再びオーバーヘッドが追加されます。

同じマシン内でのみ計算を行う場合は、ここで mpi を使用する必要はありません。

これの一部は、このスレッドで議論されています。

更新ipc-benchmarkプロジェクトは 、さまざまな通信タイプがさまざまなシステムでどのように機能するかを理解しようとしています。(マルチコア、マルチプロセッサ、共有メモリ) そして特に、これが仮想化されたマシンにどのように影響するか!

仮想化されたマシンで ipc-benchmark を実行し、結果を投稿することをお勧めします。このベンチマークのように見える場合は、tcp、ソケット、およびパイプの違いについて大きな洞察を得ることができます。

于 2013-06-21T13:32:15.723 に答える