関連するソリューション
サイバーセキュリティ
ID-Ashura/セキュリティサービス
永続メモリってなんだろう?
永続メモリ (persistent memory)
コンピュータの中でデータを記憶するものといえば、ご存知の通り、揮発性の DRAM と不揮発性の SSD やハードディスクがよく知られています。
揮発性 (volatile) というのは、電源を切ったら中身が消えてしまうことを指します。一方、不揮発 (non-volatile) というのは、電源を切っても中身が消えないことを指します。
DRAM は電源を切ると中身がなくなってしまうものの、SSD などに比べてずっと高速にアクセスすることができます。
一方、不揮発性の SSD やハードディスクは、電源を切っても中身が無くなりません。その代わり、DRAM に比べたらアクセス速度はずっと遅くなります。もう一つ、両者には大事な違いがあります。DRAM はアドレスを指定してバイト単位での読み書きが可能です。
この性質を専門用語でバイト・アドレッサブル (byte addressable) であるという言い方をします。一方、SSD などの記憶デバイスはブロックデバイスともいわれ、たとえば 512 バイト単位での読み書きしかできません。
1 バイトのデータを書き換えようと思ったら、512 バイトのデータを読み出して 1 バイトの書き換えを行い、ふたたび 512 バイトを書き出すことになります。仮に DRAM と同じ速度で読み書きが可能になったとしても、これではメモリのように使うことは難しいでしょう。
最近になって、永続メモリ (persistent memory) といわれる新しい記憶デバイスが実際に使われるようになってきました。この永続メモリというのは、DRAM と SSD のいいところ取りをした記憶デバイスです。その性質を列挙してみると、
- DRAM に匹敵するアクセス速度で読み書きが可能
- SSD と同じように電源を切っても中身が消えない
- DRAM と同じようにバイト単位での読み書きが可能
というものです。
少し専門的になりますが、DRAM よりも記憶密度が高く高集積化が可能で、消費電力も少ないそうです。このように書いてくると、永続メモリは夢のようなメモリです。
技術的に永続メモリが作れるということはかなり以前から言われていました。そのため、永続メモリが実用的に使われるようになったことを想定して、次世代のファイルシステムやキーバリューストアなどの研究が盛んに行われてきました。これらの研究は、物性物理学の検知から理論的に予測される性能を前提としたものです。
2017 年の 4 月に Intel Optane Pesistent Memory という永続メモリがリリースされました。だいぶ前置きが長くなってしまいましたが、今回のコラムでは、この Intel Optane Persistent Memory が内部的にどのような構造になっており、実際にはどのような性能特性を示し、その結果、使いこなすのにコツのいるものだということを解説していこうと思います。
Intel Optane Persistent Memory の内部構造
永続メモリの特性を理解するには、その内部構造についての知識が必要になります。Intel 社が公開してくれている情報だけでは不十分で、世界中の研究者がいろいろと調査した結果、次のようになっていると言われています。
永続メモリへの書き込みは何段階かのステップを踏みます。永続メモリへの書き込みは、CPU から CPU キャッシュに対して行われます。
次に、CPU キャッシュからメモリコントローラ (iMC) 内の WPQ (write pending queue) に対して書き込みが行われます。そして、メモリコントローラ内の WPQ から永続メモリコントローラ内のバッファ (buffer) に対して書き込みが行われます。
最後に、永続メモリコントローラ内のバッファから永続メモリへと書き込みが行われるという段取りになります。下の図にこの関係を図示しておきます。
また、キャッシュを介さずに直接メモリに書き込む命令として、nontemporal store 命令という命令が用意されています。
キャッシュラインを書き戻します。
書き戻されたキャッシュラインは、invalid になります。複数の CLFLUSH 命令があった場合、それらは逐次的に処理されていきます。
CLFLUSHOPT命令:
キャッシュラインを書き戻し、書き戻されたキャッシュラインは invalid になるというところまでは、上記の CLFLUSH 命令と同じです。
複数の CLFLUSHOPT 命令があった場合、並列的に処理されるという点で異なっています。つまり、CLFLUSH 命令を最適化した(OPT は最適化を意味する OPTimize の最初の 3 文字)ものになります。
CLWB命令:
キャッシュラインを書き戻しますが、書き戻されたキャッシュラインは valid のままになります。この命令は永続メモリのために導入された新しい命令です。
通常はキャッシュラインを書き出すのは、そのキャッシュラインにアクセスする必要がなくなったときです。そのため、書き出しキャッシュラインは無効化して invalid にしています。
しかし、永続メモリの場合、これから先もアクセスするんだけれども、今の時点でその内容を永続化しておきたいということがあります。
このようなときに CLWB 命令を使います。複数の CLWB 命令があった場合、並列的に処理されます。なお、CLWB は Cache Line Write Back という意味になります。
上記のいずれの命令を用いた場合でも、メモリ書き込みの順序を保証するために SFENCE 命令を用いてメモリフェンスをはる必要があります。
CPU からみるとバイト単位で読み書きできるように見える Intel Optane Persistent Memory ですが、実際には 256 バイト単位で読み書きを行なっています。
つまり、1 バイトのデータを更新するときは 256 バイトのデータを読み込み、1 バイトの更新を行い、それから 256 バイト全体を書き戻すことになります。そのため、バイト単位でアクセス可能といっても、実際には 256 バイト単位でアクセスを行うブロックデバイスに近い性質を示します。ただし、永続メモリ内部にバッファを持っており、隣接した書き込みは一度に行われるようになっているようです。
他にもメモリのインターリーブに関する話があります。現状では、ひとつの CPU パッケージに 6 枚の永続メモリを接続することができます。メモリアクセスの仕方を工夫すると、これら 6 枚の永続メモリに同時にアクセスできるようになります。
Optane の性能特性
さて、では Intel Optane Persistent Memory はどれくらいの性能が出るのでしょうか。研究室で実際に測った数値もありますが、ここでは文献 [1] に掲載されたものを転記しておきます。評価に使った環境や方法については、ここではバッサリ説明を省略します。興味があれば文献 [1] を参照していただくのがいいかと思います。
ここでお伝えしたいことは、DRAM とはまったく違う性能特性を示すということです。
データの読み出し性能:
- バンド幅: 40 GB/s
- 遅延時間: 50 ns (連続的な読み出しの場合)、450 ns (ランダムな読み出しの場合)
一方で、DRAM ではどのようになるかというと、
データの読み出し性能:
- バンド幅: 100 GB/s
- 遅延時間: 40 ns (連続的な読み出しの場合)、190 ns (ランダムな読み出しの場合)
のようになります。
書き込みの性能がどのようになるのかも見てみましょう。
データの読み出し性能:
- バンド幅: 13 GB/s
- 遅延時間: 230 ns (連続的な書き込みの場合)、900 ns (ランダムな書き込みの場合)
一方で、DRAM ではどのようになるかというと、
データの書き込み性能:
- バンド幅: 70 GB/s
- 遅延時間: 110 ns (連続的な書き込みの場合)、170 ns (ランダムな書き込みの場合)
書き込み性能でも、永続メモリの場合、やはり連続的な書き込みとランダムな書き込みでは、その差が DRAM よりもずっと大きくなります。
このようになってしまう理由は、永続メモリの内部構造を踏まえれば納得がいくでしょう。とびとびの位置に書き込みを行うランダムな書き込みでは、永続メモリの内部で頻繁に 256 バイト単位での読み書きが発生してしまうため、このようになっているのだろうと想像が付きます。
このように、プログラム上で書き込んだデータよりも多くのデータをデバイスに書き込む現象をWrite Amplification といいます。
永続メモリを上手に使うには Write Amplification を起こさないように、うまく工夫してあげる必要があります。
障害発生時の対応
もうひとつ考えておかないといけないことは、障害発生時への対応です。永続メモリに書き込まれたデータは永続化されます。しかし、データを永続化している最中に、突然、電源が落ちてしまう可能性があります。
電源が落ちても永続メモリ内のデータが失われることはありません。そう考えると、電源が突然落ちてしまうようなことがあっても何も問題がないように感じます。実際には、それほど甘くありません。
上図の左上に示したようなリスト構造を永続メモリ内で管理していたとしましょう。このリスト構造に C という要素を追加するには矢印の順序にしたがって、赤色のポインタをつなぎかえていく必要があります。
この途中で電源が落ちたとします。そうすると、永続メモリ上のリスト構造はポインタが中途半端につながれたおかしな状態のままになります。電源が落ちた瞬間のレジスタやキャッシュの状態がすべて永続化されているなら、電源の落ちた直後から再開すれば問題は起きません。
しかし、いくら永続メモリを使ってもレジスやキャッシュの状態は失われてしまいます。もし、この壊れたリスト構造を何らかの形で意味のある状態に戻してあげない限り、このリスト構造は使い物にならず、結果として永続メモリ上に保存されたデータ構造がそのまま意味のない壊れたものとなってしまいます。ファイルシステムが壊れたのと同じような状態になってしまうわけです。
実際にはこのような状態にならないように慎重なプログラミングが求められます。Intel 社から公開されているPMDK (Persistent Memory Development Kit) を使うと、(正しくプログラムを書けば) このような問題は起こらないようになっています。
しかし、汎用性の高い機能を提供しているため、特定の用途に使うにはオーバーヘッドが大きく、実際にはプログラマの責任でもっと微妙なコードを書いていることが多いようです。
最後に
夢のように見えた永続メモリも、実際に使いこなそうとすると、ちょっと変わった性能特性のために期待したような性能が出なかったり、障害発生時にひどいことになったりします。
今、多くの研究者がこうした問題を解決するための方策を発表しています。もうしばらくすれば、そうした研究成果が実用化されストレージを取り巻くコンピュータシステムの世界が大きく変わっていくかもしれません。
ここでは具体的なプログラミングの方法については触れませんでした。興味のある方は Intel PMDK や Linux DAX などを調べてみると情報が得られると思います。
参考文献:
[1] Lawrence Benson, Hendrik Makait and Tilmann Rabl,
"Viper: An Efficient Hybrid PMem-DRAM Key-Value Store",
VLDB Endow., Vol. 14, No. 9, pp. 1544--1556, 2021.