NUMA:理論と実践

かつてNUMA(Non-Uniform Memory Access)は、巨大なマルチプロセッシング・マシンを構築しようとする人以外には無縁の概念だった。しかし、2GHzを超える強力なプロセッサや、IntelのItanium 2やAMDのOpteronなどのような、x86ベースの64ビットチップが登場した現在では、より速く強力なメモリ管理が求められている。SMP(対称型マルチプロセッシング)、クラスタリング、そして分散コンピューティングが一般的になるにしたがって、よりよいメモリ管理への必要性が高まっている。

40年もの間語り継がれてきたコンピュータ・エンジニアリングの経験則、アムダールの法則を考えてみれば、これがなぜだか分かるはずだ。アムダールの法則とは、「システムは、1命令/秒ごとに1ビット/秒のI/Oを必要とする」というものだ。つまり、1メガバイト/秒のスループットがあれば、MIPS(million instructions per second)は8になる計算だ。

MIPSはこれまで、CPUのベンチマークにおいて重要視されてこなかったが、システム全体のパフォーマンスを測るうえで有用な基準となり得る。MIPSの数値は、メモリとCPUの総合的なパフォーマンスを判断する際に、興味深い視点を提供してくれる。たとえば、BiT-Technologiesのsystem speed demonによれば、2.4GHzのPentium 4の動作は、Rambusのメモリ(166.83MIPS)においてよりも、DDR-SDRAM(Double Data Rate-Synchronous DRAM)メモリにおいての方がわずかに速かった(168.73MIPS)。

この結果からは、2つの教訓を導き出すことができる。1つ目は、単一の32ビットプロセッサさえも、標準のメモリ・パフォーマンスの限界を超えようとしていること。2つ目は、メモリ・アーキテクチャに少し変更を加えるだけで、パフォーマンスが変わるということ。大きなI/O帯域幅を想定してデザインされたNUMAは、大きな変革をもたらすはずだ。

NUMAの仕組み

メモリは何でも一緒というわけではない。一般的に言って、プロセッサに対してメモリが(アクセス時間的に見て)近ければ近いほど、システムの総合的なI/Oは速くなり、あらゆる面においてシステムのパフォーマンスが向上する。

一番分かりやすい例は、頻繁にアクセスされるデータを、ハードドライブからキャッシュに格納するという方法だろう。たとえ世界最速のハードドライブであっても、アクセス速度の面では低速なメモリにさえかなわないから、これがシステム全体の速度向上につながる。

同じ考え方を、プロセッサとメモリにも当てはめることができる。チップそのもの(L1またはプライマリ・キャッシュ)で、あるいはCPU(L2またはセカンダリ・キャッシュ)のすぐ隣で、少数の高速なRAMを使うのだ。ハードドライブ・キャッシュを使用することでディスクのパフォーマンスを向上させるのと同じように、この方法でメイン・メモリを高速化することができる。

NUMAは、キャッシュのメモリ配置の考え方を応用して、マルチプロセッサ・システムが、ローカルのメモリだけでなく、別のバスにあるメモリや、高速なネットワークで接続されたプロセッサを有効利用できるようにするものだ。

NUMAを利用することで、各プロセッサ、または一連のプロセッサが、他のCPUのメモリにアクセスすることになる。もちろん、ローカルのメモリにもアクセスは可能だが、メモリを共有することで、並列処理をより効果的に行うことができ、データを大量に使用するタスクを手ごろなサイズに分割することができる。

どこかで聞いた話だと思った読者もいるだろう。SMPやクラスタリングも、システム・バス、バックプレーン、あるいは高速なネットワーク接続を介して、同じことを実現しようとするものだからだ。NUMAがこの2つのテクノロジと違うのは、メモリ管理を行うことでメモリ・アクセスを改善し、システム全体のパフォーマンス向上を狙っている点だ。

たとえば、SMPでは伝送メモリは相互接続バスによって共有されるのが一般的だ。プロセッサの数に比例してバスのトラフィックは増え、したがってスループットは低下する。

しかしNUMAマシンは、メモリを管理するために複数のバスを使うため、バスに大量のデータを送り込むことでシステムの速度が低下するのを防ぐことができる。また、NUMAはリニア・メモリ・アドレス空間を提供するので、プロセッサはすべてのメモリを直接アドレス指定することができる。同様の目的を持った技術として分散メモリがあるが、データのレプリケーションに伴うオーバーヘッドがより高い。

NUMAシステムの内部には、多くの場合、L3キャッシュと呼ばれる高速なメモリの領域がある。バス上にあるすべてのプロセッサは、まずこのキャッシュにアクセスした後で、バスのメイン・メモリ・リソースでデータを探す。

NUMAマシンでは、UMA(Uniform Memory Access)領域がメイン・メモリに当たる。ローカルの一連のプロセッサと、そのUMAをあわせて「ノード」と呼ぶ。たとえばSMPシステムでは、ノード内で各プロセッサが共有メモリ領域またはローカル・メモリを共有している。このメモリは、当然だが、ノードのCPUへの最も速い非キャッシュのメモリ・アクセスを提供する。このノードが複数集まったものがNUMAマシンである。ノード間のメモリ移動を担当するのはルータだ。ルータ経由でしかアクセスできないメモリは「リモート・メモリ」と呼ばれる。

言うまでもないことだが、NUMAの最終目的は、このようなリモート・メモリの移動を可能な限りスピードアップして、ノードのプロセッサが期限切れのデータを処理してしまうのを避けること、つまりキャッシュの一貫性を保つことだ。

NUMAに対応したプログラミング

キャッシュの一貫性に問題が起きるのを避ける方法としては、ノード上のローカル・メモリへの参照を最大にし、リモート・メモリへの参照を最小にするというものが考えられる。リモート・メモリへのアクセスは、優れたNUMAシステムにおいてさえ、ローカル・メモリへのアクセスに比べて3倍から5倍の時間がかかる可能性があるからだ。この点が、NUMAマシンで効率的にプログラミングをするために重要となる。これは、言うのは簡単だが、実現するのは難しい。

現在、多くのNUMA開発者が使っているのが、MultipathのFast Matrix Solver(FMS)のようなツールだ。このようなツールを使うと、I/Oスレッドを特定のプロセッサやノードにバインドし、ローカル・ノードのメモリに要素を明示的に配置するのがずっと簡単になる。もちろん、NUMAアーキテクチャやOSが効率的なメモリ割り当てを行ってくれるが、このメモリ管理は一般的なケースに最適化されており、特定のアプリケーションにおいても最適だとは限らない。つまり、このメモリ・アーキテクチャのメリットを最大限に享受するには、やはりNUMA対応の開発ツールが必要なのだ。自力でNUMAに対応したコーディングをしようと思うなら、LinuxとWindows 2003 Server向けの、NUMA対応の新しい汎用プログラミング・ツールとSDKが登場するまでに(いくつかは間もなく発表になるはずだ)、CやC++のメモリ管理に精通し、自分が使っているNUMAのバージョンが、リモートとローカルのメモリをどのように識別しているかを完全に理解しておく必要がある。

しかも、残念なことに、NUMAは魔法ではない。アプリケーションは、巨大なメモリ空間があることは分かっても、それを適切に利用できるかどうかは別問題なのだ。

では、NUMAに対応したアプリケーションを書くにはどうしたらよいのだろうか。仮想メモリに手を出す場面を減らすためにオーバーレイを使うアプリケーションを開発した経験があるなら、そのテクニックはリモート・メモリを扱うのに役立つだろう。

たとえば、アプリケーションが、呼び出したある種のデータを待機し続けることができる場合には、そのデータ配列はリモートに格納し、アプリケーションがより頻繁にアクセスすることが分かっているデータを、手近なUMAに置いておくのも一案だ。

リモート・メモリを扱いやすくするため、NUMAは、コンポーネント(CPU、ローカル・メモリ、リモート・メモリなど)間の「距離」という概念を利用する。Linuxの場合、この距離はホップと呼ばれている。また、本来は正確でないが、帯域幅やレイテンシといった用語もよく使われる。呼び方はさまざまだが、一般的に言って、距離(ホップ数)が近ければ近いほど、ローカル・プロセッサはより速くコンポーネントにアクセスできる。

さて、ここまで読んできて、厄介そうだと思われただろうか。しかし、手を煩わせる価値はある。NUMAは、SMPや、結びつきの強いクラスタ・アプリケーションのスケーリングをずっと容易にしてくれる。また、気象モデリングなど、スーパーコンピュータが活躍するような高並列計算にも、NUMAは最適だ。

NUMAが一般的な用途に注目されているのは、これらの要素と、現在のSMPマシンで大量のデータ待ちが発生するようになったことが重なったためだ。NUMAのサポートは、MicrosoftのWindows 2003 ServerなどのサーバOSや、リリースが待たれている2.6カーネルをベースにしたLinuxの各ディストリビューションなどですでに予定されている。NUMA対応のアプリケーションを長期にわたって配備するようになるのは、まだ何年か先のことかもしれないが、アプリケーション開発におけるNUMAのメモリ管理を容易にする開発ツールが登場すれば、NUMAは、ハイエンドのサーバ・アプリケーションの開発者にとって最適なメモリ管理テクニックとなるだろう。

Steven J. Vaughan-NicholsはPractical Technologyの編集者であり、NUMAとキャッシュ一貫性の理論に何年もの間取り組んでいる。