第15章 メモリ管理サブシステムのチューニング

目次

15.1. メモリの使用方法
15.2. メモリ使用率の削減
15.3. 仮想メモリマネージャ (VM) におけるチューニングパラメータ
15.4. 不均一型メモリアクセス (Non-Uniform Memory Access (NUMA))
15.5. VM 動作の監視

カーネルにおけるメモリ管理の動作を理解したりチューニングしたりするには、 まずメモリ管理機能の動作概要を理解し、他のサブシステムとどのように協調 動作するのかを知っておくことが重要です。

メモリ管理サブシステムは仮想メモリマネージャとも呼ばれ、以降の記述では VM と略されます。 VM はカーネル全体とユーザプログラム が利用する物理メモリ (RAM) の割り当てを管理する役割を持っています。また、 ユーザプロセスに対して仮想メモリ環境の提供 (Linux 拡張機能付きの POSIX API 経由) も行なっています。それ以外にも、 VM はメモリが不足したような場合に、 キャッシュメモリの解放や 匿名 メモリのスワップアウトなどを 利用することで、メモリの空き容量を増やす作業も行ないます。

VM の調査やチューニングを行なう際に最もよく知っておくべきことは、それらの キャッシュがどのように管理されているのかです。 VM キャッシュについて目指す べきゴールは、スワップやファイルシステム (ネットワークファイルシステム を含む) 操作で発生する I/O を最小化することにあります。これは I/O そのものを 回避することでも実現できますし、最適な方法で I/O を送信することでも実現 できます。

空きメモリは、必要であればそれらのキャッシュとして使用されます。キャッシュや 匿名メモリ用にさらなるメモリが利用できる場合、さらにキャッシュやスワップを 効率的に行ないます。しかしながら、メモリが不足すると、キャッシュは切り詰められ たり、メモリがスワップアウトされたりします。

処理内容にもよりますが、性能を向上するための対策として第一に、搭載するメモリ量を 増やすという対策があります。これによりキャッシュの削減やスワップアウトの頻度を 減らすことになり、性能を向上させることができます。次にやるべきこととしては、 カーネルのパラメータを変更することでキャッシュ方法を変更するという手段も あります。

最後に、処理内容そのものを調査してチューニングする必要があります。お使いの アプリケーション側で、動作するプロセス数やスレッド数が増えるような場合、 それぞれのプロセスが別々にメモリ管理されるとすると、VM キャッシュの効率は 落ちてしまうほか、メモリのオーバーヘッドも増えてしまいます。また、 アプリケーションが自分自身でバッファやキャッシュを割り当てる場合、その キャッシュを大きくすることは、 VM キャッシュとして利用可能なメモリを減らす ことにもなってしまいます。ただし、プロセス数やスレッド数を増やすことで I/O の多重度やパイプラインを増やすことができるほか、マルチコア環境でよりよい 性能を発揮する場合もあります。つまり、ベストな結果を出すには実験が欠かせない ことになります。

15.1. メモリの使用方法

一般的には、メモリの割り当ては pinned (固定) (unreclaimable (埋め立て不可能) と呼ばれる場合もあります), reclaimable (埋め立て可能), swappable (スワップ可能) の 3 種類に分類することができます。

15.1.1. 匿名メモリ

匿名メモリは一般に、プログラムのヒープメモリやスタックメモリ (たとえば >malloc() などで確保したメモリ) のことを指します。 これは mlock が設定されている場合や、利用可能なスワップ 領域が存在しないような場合を除いて埋め立て可能 (reclaimable) なメモリです。 また匿名メモリは、埋め立て可能な状態になる前に、スワップに書かれなければ なりません。なお、スワップの入出力 (スワップイン/スワップアウトの両方) は、割り当てとアクセスパターンの違いにより、ページキャッシュの I/O よりも 効率が落ちる傾向にあります。

15.1.2. ページキャッシュ

ファイルデータに対するキャッシュのことを指します。ファイルがディスクや ネットワークから読み出されると、その内容はページキャッシュ内に保管されます。 内容がページキャッシュ内で最新の状態にある場合、ディスクやネットワークに 対するアクセスは不要になります。 tmpfs や共有メモリのセグメントはページ キャッシュに有利に働きます。

ファイルに書き込みが行なわれると、ディスクやネットワークに書き込まれる 前に新しいデータがページキャッシュ内に保管されます (つまりライトバック キャッシュになります) 。まだ書き込まれていないデータがページ内に存在する 場合、それは 汚れた (dirty) 状態であると表現します。 汚れた状態として分類されていないページは クリーン (clean) な状態であると表現します。クリーンなページキャッシュのページは、メモリが 不足した場合、単純にそれらを解放することで埋め立てが可能です。汚れたページ の場合は、埋め立てが行なわれる前にまずクリーンな状態にしなければなりません。

15.1.3. バッファキャッシュ

これはブロックデバイス (たとえば /dev/sda など) に対するページキャッシュ です。ファイルシステムの場合、ディスク上にある inode テーブルやアロケーション ビットマップなど、 メタデータ 構造にアクセスする際、バッファ キャッシュを利用します。バッファキャッシュはページキャッシュと同様に 埋め立てることができます。

15.1.4. バッファヘッド

バッファヘッドは小規模な補助構造体で、ページキャッシュアクセス上に割り当て られるものです。一般に、これらはページキャッシュやバッファキャッシュの ページがクリーンである場合、簡単に埋め立てることができます。

15.1.5. ライトバック

アプリケーションがファイルに対して書き込みを行なうと、ページキャッシュ (およびバッファキャッシュ) は汚れた (dirty) 状態になります。ページが一定 時間汚れた状態であり続けた場合や、汚れたメモリ量が RAM 内の一定の割合を 超えた場合は、カーネルがライトバック処理を動作させます。処理のスレッドは 裏側書き込み処理を行なうため、アプリケーションはそのまま動作し続けること ができるようになっています。アプリケーションがページキャッシュを汚して (書き込み処理を行なって) いる速度に追随できない場合、汚れたページ キャッシュは RAM 上で大きな割合を占めるようになってしまいます。このような 場合は、汚れたページキャッシュが閾値を超えないよう、アプリケーション側の 処理が調整されるようになっています。

15.1.6. 先読み

VM はファイルアクセスのパターンを監視し、場合によっては先読みを行ないます。 先読み処理では、まだ要求されていない範囲のデータを、ファイルシステムから ページキャッシュ内に読み込みます。これにより、少ない回数で大きな容量の I/O 要求 (より効率的な読み出し) を実現しているほか、 I/O 処理を並行処理で 行なう (アプリケーションが動作している間に I/O を同時並行で行なう) ようになっています。

15.1.7. VFS キャッシュ

15.1.7.1. Inode キャッシュ

これはそれぞれのファイルシステムに対して構成される、メモリ内に存在する inode 構造のキャッシュです。ファイルサイズやパーミッション、所有権や ファイルデータへのポインタなどの属性が含まれています。

15.1.7.2. ディレクトリエントリのキャッシュ

これはシステム内に存在する、ディレクトリエントリのメモリ内キャッシュです。 ここには名前 (ファイル名) のほか、それが参照する inode や子となるエントリ などが含まれています。このキャッシュは、ディレクトリ構造を参照する際や、 名前でファイルにアクセスする際に使用されます。

15.2. メモリ使用率の削減

15.2.1. malloc (匿名) 使用の削減

openSUSE 13.1 では、アプリケーション側からメモリを割り当てる 際、 openSUSE 10 に比べて多くのメモリを割り当てる場合があります。これは glibc がユーザスペースのメモリを 割り当てる際、その既定の動作が変更になっているためです。詳しくは http://www.gnu.org/s/libc/manual/html_node/Malloc-Tunable-Parameters.html をお読みになり、これらのパラメータについて調整を行なってください。

また、 openSUSE 10 の動作に戻したい場合は、 M_MMAP_THRESHOLD の値は 128*1024 の値に設定してください。これはアプリケーション側から mallopt() を呼び出すことで実施できるほか、アプリケーションの実行前に MALLOC_MMAP_THRESHOLD の環境変数を設定しても実現できます。

15.2.2. カーネルのメモリオーバーヘッドの削減

埋め立て可能なカーネルメモリ (上述のキャッシュなど) は、メモリが不足した 場合には自動的に削減されます。それ以外の多くのカーネルメモリは簡単には 削減できませんが、作業内容によって確保されるメモリ量が変化します。

そのため、ユーザ側での処理で利用する項目を減らす (具体的にはプロセス数を 減らす、ファイルやソケットを開く数を減らすなど) ことで、カーネルのメモリ 使用量を減らすことができます。

15.2.3. メモリコントローラ (メモリの制御グループ)

メモリの制御グループ (cgroup) 機能が不要な場合、カーネルのコマンドラインに cgroup_disable=memory を追加することで、この機能を無効化することができます。 これにより、カーネルのメモリ消費を若干少なくすることができます。

15.3. 仮想メモリマネージャ (VM) におけるチューニングパラメータ

VM をチューニングする場合、チューニング時に変更した項目が実際の処理に対して 完全に反映されるまでには、しばらくの時間が必要であることを理解する必要が あります。また、処理が 1 日を通して変化するような場合は、異なる時間帯では 異なる振る舞いを見せることにも注意が必要です。さらに言うと、ある特定の条件 ではスループットを向上させる変更が、別の条件では逆効果になる場合もあります。

15.3.1. 再生率

/proc/sys/vm/swappiness

この制御は、カーネルが匿名メモリをスワップアウトさせる際、ページキャッシュ やその他のキャッシュと比較した場合の頻度を設定するものです。この値を 増加させると、スワップ処理が多くなります。既定値は 60 です。

スワップの I/O は、一般にその他の I/O に比べて効率が大きく悪いものです。 しかしながら、ページキャッシュ内のページによっては、ほとんど使用されて いない匿名メモリよりも頻繁にアクセスされる場合があります。そのため、 正しいバランスをとる必要があります。

処理が重くなっている際にスワップの動作が観測される場合、このパラメータを 小さく設定すると解決できる場合があります。また、多量の I/O 動作が存在する 環境で、システム内のページキャッシュ量が比較的少ない場合、もしくは 休眠状態にあるアプリケーションが多数起動しているような場合、この値を 増加させることで性能が改善する場合があります。

ただし、より多くのデータがスワップアウトされると、それらのデータが必要に なった場合、スワップからデータを読み出すのに時間を要するようになることに 注意してください。

/proc/sys/vm/vfs_cache_pressure

この値は VFS キャッシュに使用されているメモリを埋め立てる際のカーネルの 動作傾向を、ページキャッシュやスワップとの比率で制御するものです。この 値を増加させると、 VFS キャッシュの埋め立て比率が増加します。

この値について変更実験を行なわず、いつ変更すべきなのかは難しい問題です。 slabtop コマンド (procps パッケージ内) では、カーネル側で使用しているメモリオブジェクトについて、 多く使用されているものを表示することができます。 VFS キャッシュは "dentry" と "*_inode_cache" というオブジェクト名で表示されます。 これらの容量がページキャッシュに比べて大きな容量を消費している場合、 この値を増加させるとよいでしょう。また、この増加により、スワップ処理を 少なくさせることもできます。既定値は 100 です。

/proc/sys/vm/min_free_kbytes

この値は アトミックな 割り当て (埋め立て処理を待機 できない割り当て) など、特殊な予約領域向けに空けておかなければならない メモリ量を制御します。これはメモリ使用に関して注意深くチューニングした ような場合を除いて、小さく設定すべきではない値です (通常はサーバ アプリケーションよりも組み込み機器向けに有用な設定です) 。ログファイル内に ページ割り当ての失敗 (page allocation failure) メッセージとスタックトレースが頻繁に出力されるような場合は、これらの エラーが出なくなる程度まで min_free_kbytes を大きくすることができます。 これらのメッセージがほとんど発生しないような環境では、調整を考える必要は ありません。既定値は RAM の容量によって決まります。

15.3.2. ライトバック (書き戻し) パラメータ

openSUSE 10 以降のバージョンでは、ライトバック動作における重要な変更点 として、ファイルの設定された mmap() メモリが即時に汚れたメモリとして扱われる (書き戻す必要があるものとして扱われる) という点があります。以前のバージョン では mmap() が解除された後や、 msync() のシステムコールが呼び出された際、 またはメモリに対して使用量の圧縮が必要となった場合に書き戻されます。

アプリケーションによっては mmap() に関する上記の仕様変更が期待通りのものでは なく、性能が落ちる場合があります。 Berkeley DB (およびこのソフトウエアを 使用しているアプリケーション) はこの場合に含まれるソフトウエアの 1 つであり、 これによって問題が発生します。この問題に対しては、ライトバック (書き戻し) の割合と回数を増やすことで回避することができます。

/proc/sys/vm/dirty_background_ratio

これは空き容量と埋め立て可能なメモリの割合を設定するものです。汚れた (ディスクに書き込む必要のある) ページキャッシュがここで設定した割合を 超えると、ライトバック (書き戻し) スレッドが起動して汚れたメモリを ディスクに書き戻します。既定値は 10 (%) です。

/proc/sys/vm/dirty_ratio

上記に似た値です。ただし、この値を超過した場合は、ページキャッシュに 書き込むアプリケーションは一時停止させられ、ライトバック (書き込み) 処理が動作するようになります。既定値は 40 (%) です。

これら 2 つの値は、ページキャッシュのライトバック (書き戻し) 動作に影響を 与えるものです。これらの値を増加させると、システム内にさらなる汚れたメモリを 長い時間確保するようになります。システム内にさらなる汚れたメモリが存在する ことになると、ライトバック処理の I/O を避けることによってスループットを 改善できることが期待され、さらに最適な I/O 処理を行なうことができることに なります。ただし、汚れたメモリの量が増えると、メモリを埋め立てなければ ならない場合や、ディスクへの書き戻しの必要が発生した時にデータの整合性 (sync) ポイントで遅延が発生することになります。

15.3.3. 先読みパラメータ

/sys/block/<デバイス>/queue/read_ahead_kb

1 つまたは複数のプロセスがファイルを順に読んでいっている場合、カーネルは プロセス側に対して、データの読み込み待ちが発生しないようにするため、 ファイルを前もって読み込んで (先読みして) おきます。実際の先読み量は 動的に計算される仕組みになっていて、どれだけ I/O が順序通りに読みこまれて いるのかによって決まります。このパラメータでは、単一のファイルに対して カーネルが先読みする最大量を設定します。ファイルの読み込みの際、順序 どおりの読み込みが十分早くないことがわかった場合は、この値を増やすこと で解決する場合があります。この値を大きくしすぎてしまうと、先読みに 使用したページキャッシュが使用されるよりも前に埋め立てられてしまう 場合が考えられるほか、不要な I/O によって動作が遅くなってしまう場合が あります。既定値は 512 (キロバイト) です。

15.3.4. さらなる VM パラメータ

VM のチューニング関連のパラメータについて、完全な一覧は /usr/src/linux/Documentation/sysctl/vm.txt ファイル (kernel-source パッケージ内) に書かれています。

15.4. 不均一型メモリアクセス (Non-Uniform Memory Access (NUMA))

VM についてもう一つ重要になりつつある役割として、適切な NUMA 割り当て 戦略を提供するという役割があります。 NUMA とは不均一型メモリアクセスの 略で、今日におけるマルチソケット型のサーバは NUMA に対応しています。 NUMA はスワップやキャッシュを性能面で管理する際の第二の考慮事項で、 NUMA のメモリ割り当ての改善にあたっては、多数のドキュメントが提供されて います。ページの埋め立てに関わる点では、下記の 1 つのパラメータが 存在します:

/proc/sys/vm/zone_reclaim_mode

このパラメータは、他のノード上で多くのメモリが空きになっている場合でも、 メモリの埋め立て処理をローカルの NUMA ノードで実行するかどうかを制御 します。このパラメータは、さらなる NUMA 属性の宣言が行なわれているマシン であれば、自動的に有効に設定されます。

NUMA マシン上で VM キャッシュがすべてのメモリを占有することが許可されない 場合、 zone_reclaim_mode を 1 に設定することができます。 0 を設定すると、 このような動作を無効にします。

15.5. VM 動作の監視

VM の動作を監視するにあたっては、下記のようなシンプルなツールがあります:

  1. vmstat: このツールでは、 VM が現在行なっている処理について、その概要を 表示させることができます。詳しくは 2.1.1項 「vmstat をお読みください。

  2. /proc/meminfo: このファイルでは、それぞれメモリが どのように使用されているのかを明細に表わしたものを表示できます。 詳しくは 2.4.2項 「詳細なメモリ使用量: /proc/meminfo をお読みください。

  3. slabtop: このツールでは、カーネルの slab メモリやバッファ ヘッド、ディレクトリエントリや inode キャッシュ、 ext3_inode_cache などに ついて、詳細な情報を表示することができます。このコマンドは、 procps パッケージに含まれています。


openSUSE システム分析とチューニングガイド 13.1