メッセージパッシングインターフェース(MPI)は、並列コンピューティングアーキテクチャ上で動作するように設計された移植可能なメッセージパッシング標準です。[ 1 ] MPI標準は、 C、C++、Fortranで移植可能なメッセージパッシングプログラムを作成する幅広いユーザーにとって有用なライブラリルーチンの構文と意味論を定義します。オープンソースのMPI実装がいくつかあり、並列ソフトウェア産業の発展を促進し、移植可能でスケーラブルな大規模並列アプリケーションの開発を奨励しました。
メッセージ パッシング インターフェースの取り組みは、1991 年の夏に少数の研究者がオーストリアの山小屋で議論を始めたことから始まった。その議論から、1992 年 4 月 29 ~ 30 日にバージニア州ウィリアムズバーグで分散メモリ環境におけるメッセージ パッシングの標準に関するワークショップが開催された。[ 2 ]ウィリアムズバーグの参加者は、標準的なメッセージ パッシング インターフェースに不可欠な基本機能について議論し、標準化プロセスを継続するためのワーキング グループを設立した。ジャック ドンガラ、トニー ヘイ、デビッド W. ウォーカーは、1992 年 11 月に予備的なドラフト提案「MPI1」を提出した。1992 年 11 月にミネアポリスで MPI ワーキング グループの会議が開催され、標準化プロセスをより正式な基盤に置くことが決定された。 MPIワーキンググループは、1993年の最初の9か月間、6週間ごとに会合を開きました。MPI標準の草案は、1993年11月のSupercomputing '93会議で発表されました。[ 3 ]パブリックコメント期間を経て、MPIにいくつかの変更が加えられ、MPIバージョン1.0が1994年6月にリリースされました。これらの会合と電子メールでの議論は、高性能コンピューティングコミュニティのすべてのメンバーに開かれたMPIフォーラムを構成しました。
MPIの取り組みには、主に米国とヨーロッパの40の組織から約80人が参加した。主要な並列コンピュータベンダーのほとんどがMPIの取り組みに参加し、大学、政府研究所、および産業界の研究者と協力した。
MPIは、並列ハードウェアベンダーに対し、効率的に実装可能な明確に定義された基本ルーチンセットを提供します。その結果、ハードウェアベンダーは、この標準的な低レベルルーチン群を基に、自社の並列マシンに搭載される分散メモリ通信環境向けの高レベルルーチンを作成できます。MPIは、初心者ユーザーにとって使いやすいポータブルなインターフェースを提供すると同時に、プログラマが高度なマシンで利用可能な高性能メッセージパッシング操作を活用できるほど強力な機能も備えています。
メッセージパッシングの普遍的な標準規格を作成するため、研究者たちは単一のシステムをベースにするのではなく、IBM、Intel、nCUBE、PVM、Express、P4、PARMACSなどが設計した複数のシステムの最も有用な機能を統合しました。メッセージパッシングパラダイムは、移植性の高さから魅力的であり、分散メモリ型および共有メモリ型マルチプロセッサ、ワークステーションネットワーク、そしてこれらの要素の組み合わせにおける通信に利用できます。このパラダイムは、ネットワーク速度やメモリアーキテクチャに関係なく、様々な環境に適用可能です。
MPI会議への支援は、DARPA、米国国立科学財団(NSF)の助成金ASC-9310330、NSF科学技術センター協力協定番号CCR-8809615、および欧州委員会のEspritプロジェクトP6643を通じて行われた。テネシー大学もMPIフォーラムに資金援助を行った。
その後、MPI 標準は、複数のメジャーおよびマイナーな改訂を経て進化し、それぞれで新しい機能と改善が導入されました。たとえば、MPI-3.0 では、ノンブロッキング集合演算、片方向通信の強化、および更新された言語バインディングが導入されました。MPI-4.0 では、大規模ルーチン、永続集合、および改良された初期化メソッドが追加されました。MPI-5.0 では、実装間の相互運用性を向上させるために、標準化されたアプリケーションバイナリインターフェイス (ABI) が導入されました。[ 4 ]
MPI は並列コンピュータをプログラミングするための通信プロトコルです[ 5 ] 。ポイントツーポイント通信と集合通信の両方がサポートされています。MPI は「メッセージパッシングアプリケーションプログラマインターフェースであり、その機能が実装においてどのように動作しなければならないかに関するプロトコルおよび意味仕様を備えています」[ 6 ] 。MPI の目標は、高性能、スケーラビリティ、移植性です。MPI は2006 年時点で高性能コンピューティングで使用される主要なモデルであり続けました[ 7 ]。
この標準の目標には、並列コンピューティングアーキテクチャ全体にわたる高性能、スケーラビリティ、移植性が含まれます。正式な国際標準化団体によって管理されているわけではありませんが、MPIは高性能コンピューティングアプリケーションにおけるメッセージパッシングの事実上の標準として広く認識されています。[ 8 ]
MPIは主要な標準化団体によって正式に承認されているわけではありませんが、分散メモリシステム上で実行される並列プログラムをモデル化したプロセス間の通信における事実上の標準となっています。コンピュータクラスタなどの実際の分散メモリ型スーパーコンピュータでは、しばしばこのようなプログラムが実行されます。
主要な MPI-1 モデルには共有メモリの概念がなく、MPI-2 には限定的な分散共有メモリの概念しかありません。それにもかかわらず、MPI プログラムは共有メモリ コンピュータで定期的に実行され、MPICHとOpen MPI の両方が、共有メモリが利用可能であればメッセージ転送に共有メモリを使用できます。[ 9 ] [ 10 ] MPI モデルに基づいてプログラムを設計すること (明示的な共有メモリモデルとは対照的に) は、MPI がメモリの局所性を促進するため、NUMAアーキテクチャで実行する場合に利点があります。明示的な共有メモリ プログラミングは MPI-3 で導入されました。[ 11 ] [ 12 ] [ 13 ]
MPIはOSI参照モデルのレイヤー5以上に属するが、実装によってはほとんどのレイヤーをカバーでき、トランスポート層ではソケットと伝送制御プロトコル(TCP)が使用される。
ほとんどの MPI 実装は、 C、C++、Fortran (つまり API) およびC#、Java、Pythonなど、そのようなライブラリとインターフェースできるあらゆる言語から直接呼び出し可能な特定のルーチン群で構成されています。MPI が従来のメッセージパッシングライブラリよりも優れている点は、移植性 (MPI はほぼすべての分散メモリアーキテクチャ向けに実装されているため) と速度 (各実装は原則として実行されるハードウェアに合わせて最適化されているため) です。
MPI は、呼び出しと言語バインディングに言語非依存仕様(LIS) を使用します。最初の MPI 標準では、ANSI Cと Fortran 77 のバインディングと LIS が規定されました。ドラフトは Supercomputing 1994 (1994 年 11 月) [ 14 ] で発表され、その後すぐに最終化されました。約 128 個の関数が MPI-1.3 標準を構成しており、これは 2008 年に MPI-1 シリーズの最終版としてリリースされました。[ 15 ]
現在、この標準にはいくつかのバージョンがあります。メッセージ パッシングを重視し、静的ランタイム環境を備えたバージョン1.3 (一般的にMPI-1と略される)、並列 I/O、動的プロセス管理、リモート メモリ操作などの新機能を含む MPI-2.2 (MPI-2) [ 16 ]、非ブロッキング バージョンによる集合操作の拡張と片側操作の拡張を含む MPI-3.1 (MPI-3) [ 17 ]です。 MPI-2 の LIS は 500 を超える関数を規定し、ISO C、ISO C++、Fortran 90の言語バインディングを提供します。また、混合言語のメッセージ パッシング プログラミングを容易にするために、オブジェクト相互運用性も追加されました。 1996 年に完了した MPI-2 の標準化の副次的効果として、MPI-1 標準が明確化され、MPI-1.2 が作成されました。
MPI-2は、一部の機能が非推奨になったものの、基本的にはMPI-1の上位互換です。MPI-1.3プログラムは、MPI-2規格に準拠したMPI実装であれば引き続き動作します。
MPI-3.0では、MPI標準に大幅なアップデートが導入されました。これには、非ブロッキング型の集合演算、片側演算の強化、Fortran 2008バインディングなどが含まれます。また、非推奨となったC++バインディングや、様々な廃止されたルーチンおよびオブジェクトが削除されました。重要な点として、削除された要素を回避した有効なMPI-2.2プログラムは、MPI-3.0でも有効です。
MPI-3.1は、特にFortranバインディングに関する修正と明確化に重点を置いたマイナーアップデートです。MPI_Aint値の操作、非ブロッキング集合I/Oルーチン、MPI_Tパフォーマンス変数のインデックス値を名前で取得するための新しい関数が導入されています。さらに、汎用インデックスも追加されました。有効なMPI-3.0プログラムはすべて、MPI-3.1でも有効です。
MPI-4.0は、多くのルーチンの大規模バージョン、永続的な集合演算、分割通信、および新しいMPI初期化メソッドを導入したメジャーアップデートです。また、アプリケーション情報アサーションの追加、エラー処理定義の改善、その他様々な小規模な機能強化も含まれています。有効なMPI-3.1プログラムはすべてMPI-4.0と互換性があります。
MPI-4.1は、MPI-4.0規格の修正と明確化に重点を置いたマイナーアップデートです。いくつかのルーチン、MPI_HOST属性キー、およびmpif.hFortranインクルードファイルは非推奨となりました。MPIプログラムを実行しているハードウェアを照会するための新しいルーチンが追加されています。有効なMPI-4.0プログラムは、MPI-4.1でも引き続き有効です。
MPI-5.0は、アプリケーションバイナリインターフェースを導入するメジャーアップデートです。これにより、異なる MPI ベンダーの MPI ライブラリの相互運用性が向上し、コンテナ化された環境でのパフォーマンスも向上します。[ 18 ]
MPIは、1989年に開発された人気の分散環境およびメッセージパッシングシステムである並列仮想マシン(PVM)とよく比較されます。PVMは、標準的な並列メッセージパッシングの必要性を生み出したシステムの1つです。スレッド共有メモリプログラミングモデル( PthreadsやOpenMPなど)とメッセージパッシングプログラミング(MPI/PVM)は相互補完的な関係にあり、例えば複数の大規模共有メモリノードを持つサーバーなどで併用されることもあります。
MPIインターフェースは、言語に依存しない方法で、ノード/サーバー/コンピュータインスタンスにマッピングされた一連のプロセス間の仮想トポロジ、同期、および通信機能を提供することを目的としています。言語固有の構文(バインディング)と、いくつかの言語固有の機能も備えています。MPIプログラムは常にプロセスを扱いますが、プログラマは一般的にプロセスをプロセッサと呼びます。通常、最高のパフォーマンスを得るために、各CPU(マルチコアマシンではコア)には単一のプロセスのみが割り当てられます。この割り当ては、MPIプログラムを起動するエージェント(通常はmpirunまたはmpiexecと呼ばれる)によって実行時に行われます。
MPIは、プロセス間でメッセージが直接交換されるポイントツーポイント通信と、ブロードキャストやリダクションなどの操作でプロセス群が協力する集合通信の両方をサポートしています。言語バインディングにより、MPIルーチンを複数のプログラミング言語から使用でき、CとFortranのバインディングが最も一般的です。[ 19 ]
MPIライブラリ関数には、ポイントツーポイントのランデブー型送受信操作、直交座標系またはグラフのような論理プロセストポロジの選択、プロセスペア間のデータ交換(送受信操作)、計算の部分結果の結合(収集および削減操作)、ノードの同期(バリア操作)のほか、計算セッション内のプロセス数、プロセスがマッピングされている現在のプロセッサID、論理トポロジでアクセス可能な隣接プロセスなど、ネットワーク関連情報の取得が含まれますが、これらに限定されません。ポイントツーポイント操作は、同期、非同期、バッファリング、準備完了の形式で提供され、ランデブー送信の同期側面に関して、比較的強い意味と弱い意味の両方を可能にします。ほとんどの実装では、非同期モードで多くの保留操作が可能です。
MPI-1とMPI-2はどちらも通信と計算をオーバーラップさせる実装を可能にしますが、理論と実践は異なります。MPIはスレッドセーフなインターフェースも規定しており、インターフェース内に隠れた状態が生じるのを防ぐための凝集度と結合度に関する戦略を備えています。マルチスレッドのポイントツーポイントMPIコードは比較的簡単に記述でき、一部の実装ではそのようなコードをサポートしています。マルチスレッドの集合通信は、後述するように、Communicatorの複数のコピーを使用することで最適に実現できます。
MPIにはいくつかの機能があります。以下の概念は、それらの機能すべてに関する背景情報を提供し、プログラマがアプリケーションプログラムでどの機能を使用するかを決定するのに役立ちます。MPIの8つの基本概念のうち、4つはMPI-2固有のものです。
コミュニケータオブジェクトは、MPIセッション内のプロセスグループを接続します。各コミュニケータは、含まれる各プロセスに独立した識別子を与え、含まれるプロセスを順序付けられたトポロジに配置します。MPIには明示的なグループもありますが、これらは主に、別のコミュニケータを作成する前にプロセスグループを整理および再編成するのに適しています。MPIは、単一グループ内のコミュニケータ操作と双方向のコミュニケータ間通信を理解します。MPI-1では、単一グループ操作が最も一般的です。双方向操作は主にMPI-2に登場し、集合通信と動的なプロセス内管理が含まれます。
コミュニケータは、いくつかの MPI コマンドを使用して分割できます。これらのコマンドにはMPI_COMM_SPLIT、各プロセスが、その色を宣言することによって、複数の色付きサブコミュニケータのいずれかに参加するコマンドが含まれます。
MPIの重要な機能の多くは、2つの特定のプロセス間の通信を伴います。よく知られた例としてMPI_Send、指定されたプロセスから別の指定されたプロセスへメッセージを送信できる関数があります。このようなポイントツーポイント操作は、パターン化された通信や不規則な通信において特に有用です。例えば、各プロセッサが計算ステップ間で特定の他のプロセッサとデータ領域を定期的に交換するデータ並列アーキテクチャや、マスターが前のタスクが完了するたびに新しいタスクデータをスレーブに送信するマスタースレーブアーキテクチャなどが挙げられます。
MPI-1は、ブロッキング型およびノンブロッキング型のポイントツーポイント通信メカニズム、ならびに、対応する受信要求が既に送信された場合にのみ送信要求を行うことができる、いわゆる「レディセンド」メカニズムを規定している。
集合関数は、プロセスグループ内のすべてのプロセス間の通信を伴います(プロセスグループとは、プロセスプール全体またはプログラムで定義されたサブセットを意味します)。典型的な関数は、呼び出しMPI_Bcast(「ブロードキャスト」の略)です。この関数は、1 つのノードからデータを受け取り、プロセスグループ内のすべてのプロセスに送信します。逆の操作は呼び出しでMPI_Reduce、グループ内のすべてのプロセスからデータを受け取り、演算(合計など)を実行し、結果を 1 つのノードに格納します。は、MPI_Reduce大規模な分散計算の開始時または終了時によく役立ちます。この場合、各プロセッサはデータの一部を処理し、それを組み合わせて結果を作成します。
その他の操作では、より高度なタスクを実行します。たとえば、n個のデータ項目をMPI_Alltoall並べ替えて、 n番目のノードが各データ項目からn番目のデータ項目を取得するようにします。
多くの MPI 関数では、プロセス間で送信されるデータの型を指定する必要があります。これは、MPI が異なるノードで型が異なる形で表現される可能性のある異種環境をサポートすることを目的としているためです[ 20 ] (たとえば、異なるエンディアンを持つ異なる CPU アーキテクチャを実行している場合など)。この場合、MPI 実装はデータ変換を実行できます。[ 20 ] C 言語では型自体をパラメータとして渡すことができないため、MPI は定数MPI_INT、MPI_CHAR、 を、、などMPI_DOUBLEに対応するように事前に定義しています。intchardouble
以下は、すべてのプロセスから s の配列を 1 つのプロセスに渡す C の例ですint。受信する 1 つのプロセスは「ルート」プロセスと呼ばれ、任意の指定プロセスにすることができますが、通常はプロセス 0 になります。すべてのプロセスは、 を使用してルートに配列を送信するように要求しますMPI_Gather。これは、各プロセス (ルート自身を含む) が を呼び出しMPI_Send、ルートが対応する数の順序付きMPI_Recv呼び出しを実行して、これらのすべての配列をより大きな配列に組み立てることに相当します。[ 21 ]
int send_array [ 100 ]; int root = 0 ; /* または任意の値 */ int num_procs , * recv_array ; MPI_Comm_size ( comm , & num_procs ); recv_array = malloc ( num_procs * sizeof ( send_array )); MPI_Gather ( send_array , sizeof ( send_array ) / sizeof ( * send_array ), MPI_INT , recv_array , sizeof ( send_array ) / sizeof ( * send_array ), MPI_INT , root , comm );しかし、100秒ではなく1つのブロックとしてデータを送信する方が望ましい場合もありますint。そのためには、「連続ブロック」派生データ型を定義します。
MPI_Datatype newtype ; MPI_Type_contiguous ( 100 , MPI_INT , & newtype ); MPI_Type_commit ( & newtype ); MPI_Gather ( array , 1 , newtype , receive_array , 1 , newtype , root , comm );クラスまたはデータ構造を渡す場合、以下のようにデータ型MPI_Type_create_structからMPI派生データ型を作成します。MPI_predefined
int MPI_Type_create_struct ( int count , int * blocklen , MPI_Aint * disp , MPI_Datatype * type , MPI_Datatype * newtype )どこ:
countはブロックの数であり、配列blocklen、disp、 およびの長さ(要素数)を指定しますtype。blocklen各ブロック内の要素数を含みます。disp各ブロックのバイト変位を含みます。type各ブロック内の要素の種類が含まれています。newtype(出力)には、この関数によって作成された新しい派生型が含まれます。(変位)配列はデータ構造のアライメントdispに必要です。コンパイラがクラスやデータ構造内の変数をパディングする可能性があるためです。異なるフィールド間の距離を見つける最も安全な方法は、メモリ内のアドレスを取得することです。これは、通常は C の演算子と同じである を使用して行われますが、メモリ分割を扱う場合はそうではない可能性があります。[ 22 ]MPI_Get_address&
データ構造を1つのブロックとして渡す方が、1つの項目を1つずつ渡すよりも大幅に高速です。特に操作を繰り返す場合はその差が顕著です。これは、固定サイズのブロックは転送中にシリアル化を必要としないためです。[ 23 ]
以下のデータ構造が与えられた場合:
struct A { int f ; short p ; };struct B { struct A a ; int pp , vp ; };以下は、MPI由来のデータ型を構築するためのC言語コードです。
static const int blocklen [] = { 1 , 1 , 1 , 1 }; static const MPI_Aint disp [] = { offsetof ( struct B , a ) + offsetof ( struct A , f ), offsetof ( struct B , a ) + offsetof ( struct A , p ), offsetof ( struct B , pp ), offsetof ( struct B , vp ) }; static MPI_Datatype type [] = { MPI_INT , MPI_SHORT , MPI_INT , MPI_INT }; MPI_Datatype newtype ; MPI_Type_create_struct ( sizeof ( type ) / sizeof ( * type ), blocklen , disp , type , & newtype ); MPI_Type_commit ( & newtype );MPI_PutMPI-2では、3つの片方向通信操作(リモートメモリへの書き込み、リモートメモリからの読み出し、複数のタスクにわたる同一メモリに対するリダクション操作)が定義されていますMPI_Get。MPI_Accumulateまた、これらの操作が同期ポイントまで完了することを仕様では保証していないため、この通信を同期するための3つの異なる方法(グローバルロック、ペアワイズロック、リモートロック)も定義されています。
このような呼び出しは、同期が不便なアルゴリズム(例えば、分散行列乗算)や、他のプロセッサがデータを処理している間にタスクが負荷を分散できることが望ましい場合などに役立つことが多い。
重要な点は、「MPI プロセスが新しい MPI プロセスの作成に参加したり、別々に起動された MPI プロセスと通信を確立したりできる能力」です。 MPI-2 仕様では、MPI プロセスが動的に通信を確立できる 3 つの主要なインターフェース (、/、MPI_Comm_spawnおよび)について説明しています。 インターフェースを使用すると、MPI プロセスは指定された MPI プロセスのインスタンスを複数生成できます。新しく生成された MPI プロセスのセットは新しいイントラコミュニケータを形成しますが、関数が返す親およびインターコミュニケータと通信できます。 は、生成された異なるインスタンスが異なる引数を持つ異なるバイナリになることを可能にする代替インターフェースです。[ 24 ]MPI_Comm_acceptMPI_Comm_connectMPI_Comm_joinMPI_Comm_spawnMPI_COMM_WORLDMPI_Comm_spawn_multiple
並列I/O機能はMPI-IO [ 25 ]と呼ばれることもあり、分散システム上のI/O管理をMPIに抽象化し、既存の派生データ型機能を使用してパターン化された方法でファイルに簡単にアクセスできるように設計された一連の関数を指します。
この機能について行われたわずかな研究によると、MPI-IO を使用して高いパフォーマンス向上を得ることは容易ではない可能性があることが示されています。たとえば、MPI I/O ライブラリを使用した疎行列ベクトル乗算の実装では、わずかなパフォーマンス向上という一般的な動作が示されていますが、これらの結果は決定的ではありません。[ 26 ] MPI-IO に集合 I/O [ 27 ]の概念が実装されるまで、MPI-IO は広く採用されることはありませんでした。集合 I/O は、プロセスが小さく非連続な I/O 操作をまとめて大きく連続した操作に変換することで、アプリケーションの I/O 帯域幅を大幅に向上させ、ロックとディスク シークのオーバーヘッドを削減します。その大きなパフォーマンス上の利点により、MPI-IO はHDF5やParallel NetCDFなどの多くの最先端の I/O ライブラリの基盤となる I/O レイヤーにもなりました。その人気は、レイアウトを考慮したI/O [ 28 ]やファイル間集約[ 29 ] [ 30 ]などの集合的I/O最適化に関する研究も引き起こした。
その他多くの取り組みは、MPICH、LAM、およびその他の研究成果から派生したものであり、HPE、Intel、Microsoft、NECなどの商用実装も含まれるが、これらに限定されない。
仕様ではCとFortranのインターフェースが必須とされていますが、MPIの実装に使用される言語は、実行時にサポートする言語と必ずしも一致するとは限りません。ほとんどの実装はC、C++、アセンブリ言語を組み合わせており、C、C++、Fortranのプログラマを対象としています。Perl、Python、R、Ruby、Java、CLなど、他の多くの言語用のバインディングも利用可能です( #言語バインディングを参照)。
MPI実装のABIは、 MPICHとOpen MPIの派生版に大まかに分かれているため、一方のファミリーのライブラリは同じファミリーのライブラリのドロップイン代替として機能しますが、ファミリー間での直接的な代替は不可能です。フランスのCEAは、このような切り替えを容易にするためのラッパーインターフェースを維持しています。[ 31 ]
MPIハードウェア研究は、例えばプロセッサ・イン・メモリ(PIM)を介して、各ノードのRAMチップのマイクロ回路にMPI演算を組み込むなど、ハードウェア上でMPIを直接実装することに重点を置いている。このアプローチは、言語、オペレーティングシステム、CPUに依存しないという利点がある一方で、容易に更新や削除を行うことはできない。
別のアプローチとしては、MPIキューのハードウェア処理や、CPUやOSカーネルの介入なしにメモリとネットワークインターフェースコントローラ間でデータを直接転送するためにRDMAを使用するなど、操作の1つまたは複数の部分にハードウェアアクセラレーションを追加する方法がある。
mpicc(および同様にmpic++、mpif90など)は、既存のコンパイラをラップして、MPIを使用するコードをコンパイルする際に必要なコマンドラインフラグを設定するプログラムです。通常、コードをコンパイルしてMPIライブラリにリンクできるようにするフラグをいくつか追加します。[ 32 ]
バインディングとは、MPICHやOpen MPIなどの既存のMPI実装をラップすることで、他の言語へのMPIサポートを拡張するライブラリのことです。
管理された共通言語インフラストラクチャ.NET実装は、Pure Mpi.NET [ 33 ]と MPI.NET [ 34 ]の2 つです。MPI.NET はインディアナ大学の研究プロジェクトで、 BSDスタイルのライセンスでライセンスされています。Monoと互換性があり、基盤となる低遅延 MPI ネットワーク ファブリックを最大限に活用できます。
Javaには公式のMPIバインディングはありませんが、いくつかのグループが両者を橋渡ししようと試みており、成功度や互換性は様々です。最初の試みの1つは、Bryan CarpenterのmpiJava [ 35 ]で、基本的にはローカルのC MPIライブラリへのJava Native Interface (JNI)ラッパーのセットであり、移植性が限られたハイブリッド実装となり、使用する特定のMPIライブラリに対してコンパイルする必要もあります。
しかし、このオリジナルのプロジェクトでは、mpiJava API [ 36 ] (同等の C++ バインディングに厳密に従った Java の事実上のMPI API ) も定義されており、その後の他の Java MPI プロジェクトで採用されました。あまり使用されていない API の 1 つは MPJ API で、これはオブジェクト指向でSun Microsystemsのコーディング規約に近くなるように設計されました。[ 37 ] API 以外にも、Java MPI ライブラリはローカル MPI ライブラリに依存するか、Java でメッセージ パッシング 機能を実装するかのいずれかであり、 P2P-MPI のようにピアツーピア機能を提供し、異なるプラットフォームでの動作を可能にするものもあります。
Java/MPIの最も難しい部分のいくつかは、明示的なポインタの欠如やオブジェクトのメモリアドレス空間が線形であることなど、Javaの特性に起因しています。これらの特性により、多次元配列や複雑なオブジェクトの転送が非効率になります。通常、回避策としては、一度に1行ずつ転送したり、送信側と受信側の両方で明示的な逆シリアル化とキャストを実行したり、1次元配列を使用してCやFortranのような配列をシミュレートしたり、単一要素配列を使用してプリミティブ型へのポインタをシミュレートしたりすることが挙げられます。そのため、Javaの慣習とは大きく異なるプログラミングスタイルが生まれます。
別の Java メッセージ パッシング システムとして MPJ Express があります。[ 38 ]最新バージョンは、クラスタ構成とマルチコア構成で実行できます。クラスタ構成では、クラスタやクラウド上で並列 Java アプリケーションを実行できます。ここでは、Java ソケットまたはMyrinetのような専用の I/O インターコネクトを使用して、MPJ Express プロセス間のメッセージングをサポートできます。また、ネイティブ デバイスを使用して MPI のネイティブ C 実装を利用することもできます。マルチコア構成では、並列 Java アプリケーションがマルチコア プロセッサ上で実行されます。このモードでは、MPJ Express プロセスは Java スレッドで表されます。
MATLABを用いたMPIの学術的な実装はいくつか存在する。MATLAB自体にも、MPIとPVMを用いて実装された並列拡張ライブラリがある。
OCamlMPIモジュール[ 40 ]はMPI関数の大部分を実装しており、科学計算で活発に使用されています。11,000行のOCamlプログラムは、このモジュールを使用して500行のコードを追加し、若干の構造変更を加えただけで「MPI化」され、スーパーコンピュータの最大170ノードで優れた結果で実行されました。[ 41 ]
PARI/GPは、マルチスレッドエンジンとしてMPIを使用するように構築することができ[ 42 ] 、変更を加えることなくMPIクラスタ上で並列PARIおよびGPプログラムを実行できます。
Python用の活発にメンテナンスされている MPI ラッパーには、mpi4py [ 43 ] 、 numba-mpi [ 44 ]、numba-jax [ 45 ]などがあります。
開発が中止されたものには、pyMPI、pypar、[ 46 ] MYMPI [ 47 ] 、およびScientificPythonの MPI サブモジュールが含まれます。
MPI のRバインディングには、 Rmpi [ 48 ]とpbdMPI [ 49 ]があり、 Rmpi はマネージャとワーカーの並列処理に重点を置いているのに対し、pbdMPI はSPMD並列処理に重点を置いています。どちらの実装もOpen MPIまたはMPICH2 を完全にサポートしています。
以下は、 MPIでC言語で書かれた「Hello, World!」プログラムです。この例では、各プロセッサに「hello」メッセージを送信し、簡単な操作を行い、結果をメインプロセスに返し、メッセージを表示します。
/* "Hello World" MPI テスト プログラム*/ #include <assert.h> #include <stdio.h> #include <string.h> #include <mpi.h>int main ( int argc , char ** argv ) { char buf [ 256 ]; int my_rank , num_procs ;/* 通信に必要なインフラストラクチャを初期化します */ MPI_Init ( & argc , & argv );/* このプロセスを識別する */ MPI_Comm_rank ( MPI_COMM_WORLD , & my_rank );/* アクティブなプロセスの総数を調べます */ MPI_Comm_size ( MPI_COMM_WORLD , & num_procs );/* ここまでは、すべてのプログラムがまったく同じことをしてきました。 ここで、ランクをチェックしてプログラムの役割を区別します */ if ( my_rank == 0 ) { int other_rank ; printf ( "プロセスが %i 個あります。\n " , num_procs );/* 他のすべてのプロセスにメッセージを送信 */ for ( other_rank = 1 ; other_rank < num_procs ; other_rank ++ ) { sprintf ( buf , "Hello %i!" , other_rank ); MPI_Send ( buf , 256 , MPI_CHAR , other_rank , 0 , MPI_COMM_WORLD ); }/* 他のすべてのプロセスからメッセージを受信する */ for ( other_rank = 1 ; other_rank < num_procs ; other_rank ++ ) { MPI_Recv ( buf , 256 , MPI_CHAR , other_rank , 0 , MPI_COMM_WORLD , MPI_STATUS_IGNORE ); printf ( "%s \n " , buf ); }}それ以外{/* プロセス #0 からメッセージを受信 */ MPI_Recv ( buf , 256 , MPI_CHAR , 0 , 0 , MPI_COMM_WORLD , MPI_STATUS_IGNORE ); assert ( memcmp ( buf , "Hello " , 6 ) == 0 );/* プロセス #0 にメッセージを送信 */ sprintf ( buf , "プロセス %i が任務に就きます。" , my_rank ); MPI_Send ( buf , 256 , MPI_CHAR , 0 , 0 , MPI_COMM_WORLD );}/* 通信インフラストラクチャを破棄する */ MPI_Finalize (); return 0 ; }4 つのプロセスで実行すると、次の出力が生成されます: [ 50 ]
$ mpicc example.c && mpiexec -n 4 ./a.out 当社には4つのプロセスがあります。 プロセス1:勤務開始の報告。 プロセス2:勤務開始の報告。 プロセス3:勤務開始の報告。
ここで、 は、4 つのプロセスmpiexecでサンプル プログラムを実行するために使用されるコマンドです。各プロセスは実行時にプログラムの独立したインスタンスであり、ランク (つまり数値 ID) 0、1、2、3 が割り当てられます。 という名前は MPI 標準で推奨されていますが、一部の実装では という名前で同様のコマンドを提供しています。 は、すべてのプロセスで構成されるコミュニケータです。mpiexecmpirunMPI_COMM_WORLD
これにより、単一プログラム、複数データ ( SPMD ) プログラミング モデルが容易に実現できますが、必須ではありません。多くの MPI 実装では、同じ MPI ジョブで複数の異なる実行可能ファイルを起動できます。各プロセスは、独自のランク、ワールド内のプロセスの総数、およびポイントツーポイント (送受信) 通信またはグループ間の集合通信によるプロセス間の通信機能を持ちます。MPI はMPI_COMM_WORLD、独自のランクとワールドのサイズを持つ SPMD スタイルのプログラムを提供することで、アルゴリズムが何をするかを決定できるようになります。より現実的な状況では、I/O はこの例よりも慎重に管理されます。MPI は、特定のシステムで標準 I/O (stdin、stdout、stderr) がどのように動作するかを規定していません。通常、ランク 0 のプロセスでは期待どおりに動作し、一部の実装では他のプロセスからの出力もキャプチャして転送します。
MPIはプロセッサではなくプロセスという概念を用います。プログラムのコピーはMPIランタイムによってプロセッサにマッピングされます。つまり、並列マシンは1つの物理プロセッサ、あるいは利用可能なプロセッサ数N個のプロセッサ、またはその中間のプロセッサにマッピングできます。並列処理の速度を最大化するには、より多くの物理プロセッサを使用します。この例では、ワールドのサイズNに応じて動作を調整するため、各サイズの変化に対してコンパイルなしでランタイム構成にスケーリングしようとしますが、ランタイムの決定は利用可能な並列処理の絶対量によって異なる場合があります。
MPI-1.2は特にクラスタコンピューティングにおいて広く採用されているが、MPI-2.1の普及は限定的である。問題点としては以下のようなものがある。
MPI の将来については、確実な部分もあれば、そうでない部分もあるようです。MPI フォーラムは 2007 年に再招集され、MPI-2 の問題を明確にし、MPI-3 の可能性に向けた開発を検討しました。その結果、MPI-3.0 (2012 年 9 月) [ 51 ]と MPI-3.1 (2015 年 6 月) [ 52 ]がリリースされました。開発は続き、2021 年 6 月 9 日に MPI-4.0 が承認されました[ 53 ]。MPI-4.1 は 2023 年 11 月 2 日に承認されました[ 54 ] 。MPI-5.0 は 2025 年 6 月 5 日に承認され、標準アプリケーションバイナリインターフェイス(ABI)の追加など、重要な新機能がもたらされました[ 55 ]。
アーキテクチャは、内部並行性の向上(マルチコア)、よりきめ細かい並行性制御(スレッド、アフィニティ)、およびメモリ階層のレベルの増加に伴い変化しています。マルチスレッドプログラムは、シングルスレッドアプリケーションよりもこれらの発展を容易に活用できます。これにより、対称マルチプロセッシングのための独立した補完的な標準、すなわちOpenMP が既に生まれています。MPI-2 は、標準に準拠した実装がマルチスレッドの問題をどのように処理すべきかを定義しますが、実装がマルチスレッドであること、あるいはスレッドセーフであることさえ要求しません。MPI-3 は、ノード内で共有メモリ並列処理を使用する機能を追加します。Adaptive MPI、Hybrid MPI、Fine-Grained MPI、MPC などの MPI 実装は、MPI のさまざまな課題に対処する MPI 標準の拡張機能を提供します。
天体物理学者のジョナサン・ダーシは、MPIは時代遅れだと論説し、Chapel言語、Unified Parallel C、Hadoop、Spark、Flinkなどの新しい技術を指摘した。[ 56 ]同時に、エクサスケールコンピューティングプロジェクトのほぼすべてのプロジェクトは明示的にMPIをベースに構築されており、MPIは2020年代初頭の時点で最大のマシンにまで拡張できることが示されており、今後も長期間にわたって関連性を保つと広く考えられている。