インテル® Xeon Phi™ プロセッサーのメモリーモード・プログラミング (MCDRAM) 要約

同カテゴリーの次の記事

インテル® Xeon Phi™ プロセッサー向けプログラミング特集

この記事は、inside HPC に公開されている「Intel Xeon Phi Memory Mode Programming (MCDRAM) in a Nutshell」の日本語参考訳です。


この記事では、スペシャルゲスト、James Reinders(ジェイムス・レインダース)氏が、インテル® Xeon Phi™ プロセッサーのメモリーモードについて解説しています。


James Reinders、並列プログラミング・エンスージアスト

インテル® Xeon Phi™ プロセッサーは、今後 HPC システムの設計で注目されるであろう 2 つの機能をサポートしています。それは、高帯域幅メモリーとメニーコアを部分領域に細分化する機能です。

最初の記事、『インテル® Xeon Phi™ プロセッサー向けプログラミング要約』では、インテル® Xeon Phi™ プロセッサー (開発コード名 Knights Landing) にどうアプローチするか、いくつかの方法を説明しました。

この記事では、インテル® Xeon Phi™ プロセッサーが提供するこれまでにない構成の変更を可能にする “モード” オプションの 1 つである、メモリーモードについて説明します。これは、プログラマーにとってプログラミングに革新をもたらす可能性を秘めた、考慮すべきオプションです。次の記事ではそのほかのモード (クラスターモード) について説明します。

メモリーモードは、MCDRAM を高帯域幅キャッシュ、高帯域幅メモリー、またはそれらを混在して使用することを可能にします。

帯域幅に注目する MCDRAM

計算集約型のプログラムはしばしば帯域幅に制限されます。もちろん、計算エンジンとの間でデータをやり取りできないなら、最高のパフォーマンスは得られません。インテル® Xeon Phi™ プロセッサーは、メインメモリー (DDR) と同じ階層レベルのメモリーとして、アプリケーションにより高い帯域幅を提供するため、”パッケージ上に” MCDRAM を搭載しています。

メモリーモード (表を参照) は、ブート時にプロセッサーのメモリー構成を設定するのを可能にします。MCDRAM は、通常のメモリー (DDR) の代替高帯域幅メモリーと見なすことができます。実際、システムは DDR がなくても起動可能です。DDR を搭載するシステムでは、MCDRAM は DDR メモリーの高帯域幅キャッシュとして機能することも可能です。

MCDRAM は、”Multichannel” DRAM であり、Multichannel (複数チャンネル) はより高い帯域幅をもたらします。DRAM へのチャンネルが増えることは、より多くの同時アクセス (高い帯域幅) が可能であることを意味します。

MCDRAM は “オンパッケージ” であり、プロセッサーと同じパッケージに含まれますが、プロセッサーと同じシリコン (ダイ) とは分離されています。一般に、ダイを分離すると単一のダイよりも多くのトランジスターを集積することが可能となり (プロセッサーはコアを増やせ、メモリーは容量を増やせる)、プロセッサーを汎用ロジックの構成にチューニングしながら、メモリーチップをメモリーにチューニングすることができます。その他の点では、”オンパッケージ” は “シングルダイ” に対して技術的には同じく機能します。

高帯域幅は高速を意味するわけではありません。MCDRAM と DDR に対する個々のアクセス時間は、実際にはパフォーマンスは同等です。同時にキャッシュラインを必要とする場合にのみ “高速” となります。MCDRAM は複数の応答を即座に返すことができますが、DDR は順番に応答します。これは、帯域幅に余裕があるだけです。

メモリーモードを変更する前に並列処理をチューニング

私がインテル® Xeon Phi™ プロセッサー向けのプログラミング・セミナーを行うと、すべての参加者はこのプロセッサーに固有な機能をすべて学ぶことを望んでいるようでした。私は、インテル® Xeon Phi™ プロセッサーが並列プロセッサーであることを、最初に強調することが重要であることを理解しています。そして、デフォルト設定の並列プロセッサーであってもかなり優れています。私達の最も重要な目的は、効率良い並列プログラムを持つことです。私は、Knights Landing (開発コード名) 向けにプログラミングを行う規則としてこれを強調しています。

インテル® Xeon Phi™ プロセッサー向けプログラミングの最初の規則:
最初に、ほとんどのアプリケーションでは、メモリーとクラスターモードを調整するよりも、並列処理のファイン・チューニングから利点を得るべきです。

例えば、プログラムは MCDRAM をキャッシュモードで使用して、ベクトル化のチューニングを行います。コードをベクトル化すると、メモリー・アクセス・パターンが変わる可能性があります。これは、開発過程で誤ったアクセスパターンのメモリー・チューニング (異なるモードを試行) を避けることが可能であることを意味します。

この記事では、プログラマーの視点からメモリーモードについて説明したいと思います。書籍の 4 章と 6 章では、基盤となるハードウェアをさらに理解したい開発者向けの説明を記載しています。ここでは、メモリーモードとクラスターモードを使用する堅固な基礎を構築するため、プログラマーにとって鍵となるインターフェイスに注目します。

MCDRAM キャッシュモード: デフォルトで簡単、そして効果的

(フラット・メモリー・モードを使用して) MCDRAM を直接管理するのが最適だと思われるなら、”キャッシュ” モードと “フラット” モードの結果を比較してみます。私は、優秀な何人かのプログラマーが、”キャッシュ” モードの使用に不満を漏らす状況を見てきました。この課題の要点は、プロセッサーがキャッシュ全体を使用することです。”フラット” モードを選択した場合は、利点を得るためすべてのメモリーを使用する必要があります。アイドル状態のメモリーは、”キャッシュ” モードでの効果的な実装を容易にします。

MCDRAM の一部またはすべてがキャッシュとして構成されていることを仮定すると、プログラムはキャッシュを使用するため特別なことをする必要はありません。もちろん、通常のキャッシュ最適化は、他のレベルのキャッシュやシステムと L3 キャッシュに適用されます。

つまり、メモリーモードの “新機能” で注目するのは、フラットモードやハイブリッド・モードで実行する時のフラットな領域に関するものであることを意味します。

フラットまたはハイブリッド・モードを使用するメモリーとしての MCDRAM

“フラット” モードの MCDRAM の利点を引き出すには、3 つの方法があります。1 つ目の方法は、numactl ユーティリティーを使用してメモリー要求を MCDRAM にマップすることです。2 つ目の方法は、メモリー要求をインターセプトして、MCDRAM へマップすることです (サイズに依存)。3 つ目の方法は、特殊なメモリー・アロケーターを使用するか、変数や割り当て時にコンパイラーの制御を使用する方法です。”numactl” は “NUMA control” の略です。NUMA は、Non-Uniform Memory Access の頭字語であり、この場合、特にメインメモリー (DDR) と MDDRAM メモリーへのアクセスが異なる特性を持ち、メモリーを使用するアプリケーションから見ると動作が一定でないという事実を表します。今後の記事で、SNC-2 または SNC-4 クラスターモードを使用する際の、DDR と MCDRAM 内の NUMA の動作について説明する予定です。numactl ユーティリティーは、アプリケーションのメモリー要求をオペレーティング・システムが各種メモリーへ割り当てるのを制御するのを可能にします。

numactl

インテルは、BIOS が作成したテーブルを介して、意図的に MCDRAM メモリーが BIOS とオペレーティング・システムによって最も望ましいメモリーとして認識されないようにセットアップしています。そうでなければ、アプリケーションを起動する前に、オペレーティング・システムのカーネルが MCDRAM 内に格納されてしまいます。プログラムを実行する際に numactl を使用して MCDRAM に切り替えることができます。

numactl ユーティリティーがシステムにない場合、インストールする必要があります。

numactl ユーティリティーはいくつかのオプションを持っていますが、重要なことはプログラムのデータ割り当てに “MCDRAM の使用” を指示することです。

このコマンドは、NUMA ノード 1 にデータ (スタックを含む) を配置します。どの NUMA ノードがどの MCDRAM に割り当てられるかを理解するには、クラスターモードについて知っている必要があります。これに関しては、ブログで議論する予定です。クラスターモードがデフォルト (4 分割) のままであると仮定すると、プロセッサーは単一のプロセッサー集合として実行され、唯一 NUMA であるのは DDR に対する MCDRAM となります。DDR が装着されているとすると、DDR は NUMA ノード 0 となり、MCDRAM は NUMA ノード 1 となり、私たちはメモリーモードがキャッシュ以外に設定されているとを想定できます。”numactl –H” コマンドは、システムの NUMA ノードに関する情報を表示します。

優先 (-p) と必須 (-m)

MCDRAM を使用する際に最も重要なことは、MCDRAM を必須とする (numactl -m) か、優先的に使用する (numactl -p) かということです。一般的に、プログラマーが正確にメモリーレイアウトを計画していない場合に、MCDRAM を必須としデバッグしようとしているようです。これは名案のように思われますが、2 つの問題を引き起こします。第 1 に、オペレーティング・システムやライブラリーは常にすべてをクリーンアップするわけではなく、16GB が 15.9GB となる可能性があるため、16GB すべての要求は正しく行われないでしょう。オペレーティング・システムが 16GB すべてを私たちに利用させるかどうか確認する方法は分かっています。第 2 に複雑性をもたらします。メモリーのオーバーサブスクリプションは、メモリー割り当て時ではなく、ページ割り当て時にのみ発生します。これを理解するのは容易ではありませんが、”必須割り当て” のメモリー要求は、メモリーを使い果たすと Linux* がプログラムをアボートすることになります。メモリーを使い果たすと、オペレーティング・システムはどのように振る舞うでしょう? 私はこのようなインターフェイスが発展していくと予想していますが、現在は、オーバーサブスクリプションが起こると、割り当て後しばらくして MCDRAM 要求がプログラムをアボートします。MCDRAM の残りが少なるなると、優先要求された MCDRAM は暗黙的に DDR へ切り替わります。

autohbw

autohbw ライブラリーは中継ライブラリーであり、アプリケーションが標準ヒープ・アロケーター (malloc()、 calloc()、 realloc()、 posix_memalign ()、 そして free() など) を利用すると MCDRAM へリダイレクトします。リダイレクトするかどうかの選択は、アプリケーション内の特定の割り当てサイズを元に判断されます。次の例は、 50K から 1M のサイズの割り当てを要求するアプリケーション実行する際に、MCDRAM から割り当てる方法を示しています。

簡単ですが機能は限られています。numactl は、小さな割り当てと異常に大きな割り当てを避けることできる良い解決策です。numactl のように、autohbw もアプリケーション・コードの変更は必要ありません。

autohbw はオープンソースです。より適したアイディアがあれば実装してみてください。

完全な指示: Memkind, hbw_malloc, FASTMEM
C/C++ における MCDRAM メモリーの完全な制御は割り当てルーチンによって行われ、Fortran ではディレクティブを ALLOCATE() に変更することで行われます。

Cプログラマーは、memkind オープンソース・プロジェクトによって提供される、hbw_malloc()、hbw_calloc()、hbw_realloc()、hbw_free()、hbw_posix_memalign()、そして hbw_posix_memalign_psize() といった関数を介して、MCDRAM 版の標準ヒープ割り当てルーチンを使用できます。これらの関数の動作は、標準ルーチン (同じ名前で “hbw_” のプリフィクスを取ったもの) に良く似ています。

hbw ルーチンは、高帯域幅メモリー向けです。memkind API は、より一般的なインターフェイスで明示的に DDR に割り当てることに加え、将来的にはほかのメモリータイプもサポートします。hbw ルーチンの利点は、”hbw_” というプリフィクスをよく使われるヒープ割り当てライブラリーの呼び出し (すなわち、malloc()、 calloc()、 realloc()、 posix_memalign()、および free()) に加えるだけという、最小限のコード変更で済むということです。memkind インターフェイスには、より柔軟にメモリータイプの追加ができるという利点もあります。特に固有の方法で行う必要がないのであれば、hbw ルーチンを使用した方がいいでしょう。

これらのルーチンのデフォルトのポリシーは、MCDRAM “優先” であり、必要ではないないということです。hbw_set_policy (HBW_POLICY_BIND) を使用して異なるポリシーを設定することもできます。アプリケーション内で、割り当てが完了する前に 1 回だけ設定できます。2 回以上ポリシーを設定すると失敗します。

C++ プログラマー向けに、memkind ライブラリーはいくつかの優れたサンプルコードが付属しています。その 1 つには MCDRAM 内に割り当てるタイプを作成するため、new をオーバーライドする方法を示したものがあります。examples ディレクトリー下で、memkind_allocated_example.cpp と memkind_allocated_example.hpp を探してください。

FORTRAN プログラマーは、ALLOCATE() を使用して割り当てを行ってください。ディレクティブは、Fortran で “FASTMEM” (MCDRAM) を使用する際に、ALLOCATE() の動作を制御するために使用されます。また、ポリシーを設定するより高度な方法があります (優先割り当てと要求割り当て)。

MCDRAM はダイレクト・マップ・キャッシュ

MCDRAM がキャッシュとして使用される場合、それがダイレクト・マップ・キャッシュであることを知っておくと良いでしょう。これは、最大限のパフォーマンスを引き出す場合に重要となります。設計の容易さと利用可能なテクノロジーから、高いパフォーマンスと多くの容量を実現するダイレクトマップ方式が選択されています。キャッシュの連想性を持つことは良いかもしれませんが、パフォーマンス上のコストを伴い、アプリケーションのパフォーマンスを低下させる可能性があります。複数の配列を同時にアクセスする場合、ダイレクトマップのキャッシュ特性から競合ミスが発生する可能性があります。これは、データのアライメントを移動することにより回避できます。これは私が好む最適化手法ではありませんが、必要とされれば有効です。

まとめ

MCDRAM は、インテル® Xeon Phi™ プロセッサー (開発コード名 Knights Landing) のパッケージ内に、16GB の高帯域幅メモリーとして搭載されています。また、8GB メモリー + 8GB キャッシュ、12GB メモリー + 4GB キャッシュ、または 16GB キャッシュもしくはメモリーのみなど、柔軟な構成を提供しています。

メモリーとして使用する場合、アプリケーション自身を変更することなく、MCDRAM の配分を自動的に変えるいくつかの方法があります。もちろん、MCDRAM メモリーに割り当てを行うよう、アプリケーションを修正して直接制御を行うことができます。この記事は、インテル® Xeon Phi™ プロセッサーに関連する 2 番目の記事です。 最初の『インテル® Xeon Phi™ プロセッサー向けプログラミング要約』では、インテル® Xeon Phi™ プロセッサー (開発コード名 Knights Landing) にどうアプローチするか、いくつかの方法を説明しました。次の記事でクラスターモードについて説明し、その後インテル® アドバンスト・ベクトル・エクステンション 512 (インテル® AVX-512) に触れたいと思います

すべての図表は、lotsodcores.com の承認の下、『インテル® Xeon Phi™ コプロセッサー・ハイパフォーマンス・プログラミング – Knights Landing エディション』の書籍から抜粋されています。

関連記事

Part 1: インテル® Xeon Phi™ プロセッサー向けプログラミング要約
Part3 : インテル® Xeon Phi™ プロセッサーのクラスターモード・プログラミング (およびメモリーモードとの相互作用) 要約
Part 4: インテル® Xeon Phi™ プロセッサーにおけるインテル® AVX-512 プログラミング要約

関連記事