mpi_allgather操作の計算コストは​​、ギャザー/スキャッター操作と比較してどうですか?


11

単一のmpi_allgather操作または1つのmpi_scatterと1つのmpi_gather操作を使用して並列化できる問題に取り組んでいます。これらの操作はwhileループ内で呼び出されるため、何度も呼び出される場合があります。

MPI_allgatherスキームを使用した実装では、重複する行列を解決するために、分散ベクトルをすべてのプロセスに収集しています。もう1つの実装では、分散ベクトルを単一のプロセッサ(ルートノード)に収集し、このプロセッサで線形システムを解き、ソリューションベクトルをすべてのプロセスに分散させます。

収集操作のコストが、分散操作と収集操作を合わせたものよりもかなり大きいかどうか知りたいです。メッセージの長さは、その複雑さにおいて重要な役割を果たしていますか?mpiの実装によって異なりますか?

編集:


通信の構造と関連するサイズを説明してください。にMPI_Scatter続くMPI_Gatherは、と同じ通信セマンティックを提供しませんMPI_Allgather。どちらかの方法で操作を表現するときに、おそらく冗長性が関係していますか?
ジェドブラウン

ポールは、ジェドは右である、もしかしてMPI_Gather続くのMPI_Bcast
アロンアフマディア

@JedBrown:もう少し情報を追加しました。
ポール

@AronAhmadia:ベクター全体ではなく、各プロセスにベクターの一部を送信するため、MPI_Bcastを使用する必要はないと思います。私の理論的根拠は、一般に、短いメッセージは大きいメッセージよりも速く送信されるということです。これは理にかなっていますか?
ポール

マトリックスはすでに冗長に分散されていますか?すでにファクタリングされていますか?複数のプロセスが同じキャッシュとメモリバスを共有していますか?(それは、冗長システムの解決速度に影響します。)システムはどれほど大きく/高価ですか?なぜ連続的に解決するのですか?
ジェドブラウン

回答:


9

まず、正確な答えは、(1)使用法、つまり関数の入力引数、(2)MPI実装の品質と詳細、(3)使用しているハードウェアに依存します。多くの場合、ハードウェアベンダーがネットワークのMPIを最適化する場合など、(2)と(3)は関連しています。

一般に、MPIコレクティブの融合は小さなメッセージには適しています。起動コストは自明ではなく、コール間の計算時間が異なる場合は、コレクティブのブロックに伴う同期を最小限に抑える必要があるためです。大きなメッセージの場合、送信するデータの量を最小限にすることが目標です。

たとえば、理論的にMPI_Reduce_scatter_blockは、がMPI_Reduce続くよりも優れている必要がありますMPI_Scatterが、前者は後者に関して実装されることが多いため、実際の利点はありません。MPIのほとんどの実装では、実装の品質と使用頻度との間に相関関係があり、ベンダーは明らかに、マシン契約でこれが必要とされる機能を最適化します。

一方、Blue GeneをMPI_Reduce_scatter_block使用している場合MPI_Allreduce、を使用して行うMPI_Reduceと、MPI_Scatter結合よりも多くの通信が行われ、実際にはかなり高速になります。これは私が最近発見したものであり、MPIのパフォーマンスの自己整合性の原則に対する興味深い違反です(この原則の詳細については、「自己整合性のあるMPIパフォーマンスガイドライン」を参照)。

スキャッター+ギャザー対オールギャザーの特定のケースでは、前者ではすべてのデータが単一のプロセスに出入りする必要があり、それがボトルネックになりますが、オールギャザーではデータはすべてのランクにすぐに出入りできることを考慮してください、すべてのランクには他のすべてのランクに送信するデータがあるためです。ただし、一部のネットワークでは、すべてのノードから一度にデータを送信することは必ずしも良い考えではありません。

最後に、この質問に答える最良の方法は、コードで次のことを行い、実験によって質問に答えることです。

#ifdef TWO_MPI_CALLS_ARE_BETTER_THAN_ONE
  MPI_Scatter(..)
  MPI_Gather(..)
#else
  MPI_Allgather(..)
#endif

さらに優れたオプションは、最初の2回の反復中にコードで実験的に測定し、残りの反復で速い方を使用することです。

const int use_allgather = 1;
const int use_scatter_then_gather = 2;

int algorithm = 0;
double t0 = 0.0, t1 = 0.0, dt1 = 0.0, dt2 = 0.0;

while (..)
{
    if ( (iteration==0 && algorithm==0) || algorithm==use_scatter_then_gather )
    {
        t0 = MPI_Wtime();
        MPI_Scatter(..);
        MPI_Gather(..);
        t1 = MPI_Wtime();
        dt1 = t1-t0;
    } 
    else if ( (iteration==1 && algorithm==0) || algorithm==use_allgather)
    {
        t0 = MPI_Wtime();
        MPI_Allgather(..);
        t1 = MPI_Wtime();
        dt2 = t1-t0;
    }

    if (iteration==1)
    {
       dt2<dt1 ? algorithm=use_allgather : algorithm=use_scatter_then_gather;
    }
}

それは悪い考えではありません...両方の時間を計り、どちらが速いかを決定します。
ポール

最新のHPC環境のハードウェアは、多くのMPI呼び出しを最適化します。これは信じられないほどの高速化につながることもあれば、非常に不透明な動作につながることもあります。注意してください!
meawoppl

@ジェフ:私は重要な詳細を1つ省略したことに気付きました...私はテキサスアドバンストコンピューティングセンターのクラスターで作業しており、そこではファットツリートポロジネットワークを使用しています。それは、オールギャザーアプローチとギャザーブロードキャストアプローチのパフォーマンスの違いに影響しますか?
ポール

@Paulトポロジはここでは支配的な要因ではありませんが、ファットツリーはかなりの2分割帯域幅を持っているため、集会者を安くする必要があります。ただし、ギャザーは常にオールギャザーよりも安くする必要があります。大きなメッセージのためにかかわらず、それが2倍未満であるかもしれない
ジェフ

5

確認する唯一の方法についてのジェフの絶対的な権利は測定することです-結局のところ、私たちは科学者であり、これは経験的な質問です-そして、そのような測定を実装する方法について優れたアドバイスを与えます。ここで、反対の(または、おそらく補完的な)見解を示します。

広く使用されるコードを記述することと、特定の目的に合わせて調整することとの間には区別があります。一般的には、最初に-a)さまざまなプラットフォームで使用できるようにコードを構築します。b)コードは今後何年間も保守および拡張可能です。しかし、時には他のことをやっています-大きなマシンで1年分の割り当てがあり、必要な大規模なシミュレーションのセットに立ち上げており、実行中に必要なことを行うためにパフォーマンスの特定のベースラインが必要です付与された割り当ての時間。

コードを作成するとき、特定のマシンで実行時間を数パーセント削減するよりも、コードを広く使用および保守可能にすることがはるかに重要です。この場合、行うべき正しいことは、ほとんどの場合、あなたがしたいことを最もよく説明するルーチンを使用することです。これは一般に、あなたがしたいことを行う最も具体的な呼び出しです。たとえば、まっすぐなallgatherまたはallgathervが必要な処理を行う場合は、スキャッター/ガター操作から独自にロールアウトするのではなく、それを使用する必要があります。その理由は次のとおりです。

  • コードは、あなたがやろうとしていることをより明確に表し、翌年あなたのコードに来る次の人がより理解しやすくなります。
  • このより具体的なケースでは、より一般的なケースではないMPIレベルで最適化が利用できるため、MPIライブラリが役立ちます。そして
  • 自分で転がそうとすると、逆効果になる可能性があります。MPI実装Y.ZZを備えたマシンXでパフォーマンスが向上したとしても、別のマシンに移動したり、MPI実装をアップグレードしたりすると、パフォーマンスが大幅に低下する可能性があります。

このかなり一般的なケースでは、MPI集団がマシン上で不当にゆっくり動作することがわかった場合、最善の方法はmpiベンダーにバグレポートを提出することです。MPIライブラリレベルで適切に修正すべきものをアプリケーションコードで回避しようとして、独自のソフトウェアを複雑にしたくない場合。

しかし。「チューニング」モードの場合-動作するコードがある場合、短時間で非常に大きなスケール(たとえば、1年間の割り当て)にランプアップする必要があり、コードのプロファイルを作成しました。コードのこの特定の部分がボトルネックであることがわかったので、これらの非常に具体的なチューニングを実行することは理にかなっています。それらがコードの長期的な部分にならないことを願っています-理想的には、これらの変更はリポジトリのプロジェクト固有のブランチに残りますが、それを行う必要があるかもしれません。その場合、プリプロセッサディレクティブによって区別される2つの異なるアプローチのコーディング、または特定の通信パターンの「自動チューニング」アプローチは、非常に意味があります。

したがって、私はジェフに反対していません。そのような相対的なパフォーマンスの質問に十分に関心を持ち、それを処理するためにコードを変更する必要がある場合についてコンテキストを追加したいだけです。


私はこの時点で最適化よりも移植性に関心があると思いますが、移植性が同等で高速な別の実装があるかどうかを常に知りたいと思っています:
ポール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.