Transparent Huge Pages(THP)と巨大ページの内部
巨大ページでTLBミスを減らし性能を上げたいが、断片化やフォルト遅延が怖い。THPの自動昇格と割当のしくみ、hugetlbfsとの使い分けを内部から理解できます。
- 1.THPは4KiBの代わりに2MiB等の巨大ページを透過的に使う仕組みで、1エントリでカバーする範囲が広がりTLBミスとページウォークを減らす。アプリ改修なしで効く。
- 2.巨大ページの供給経路は二つ。フォルト時にその場で2MiBを割り当てる直接割当と、khugepagedが走査して連続した4KiB群を後から1枚へ昇格(collapse)する経路がある。
- 3.代償は断片化とフォルト遅延。2MiBの連続物理メモリ確保には圧縮(compaction)が要り、フォルトが詰まることがある。事前予約で確実性が要るならhugetlbfsを選ぶ。
なぜ巨大ページなのか:TLB とページウォークの圧
仮想アドレスを物理アドレスへ変換するたび、CPU は TLB(translation lookaside buffer) を引きます。ヒットすれば 1 サイクル級で済みますが、ミスすると ページウォーク(ページテーブルを段階的に辿る処理)が走り、最悪で数十〜百サイクルかかります。標準ページが 4KiB のとき、1 つの TLB エントリがカバーするのはわずか 4KiB です。数 GiB のヒープを総なめするようなワークロードでは、TLB が到底足りず TLB ミスが慢性化 します。
ここで効くのが 巨大ページ(huge page) です。x86-64 では 2MiB(PMD レベル)や 1GiB(PUD レベル)のページを使えます。2MiB ページなら 1 つの TLB エントリが 4KiB の 512 倍の範囲をカバー するため、同じ TLB 容量でカバーできるメモリ(TLB reach)が桁違いに広がります。さらにページテーブルが 1 段浅くなり、ページウォーク自体のコストも下がります。アドレス変換の機構そのものは 仮想記憶のアドレス変換とMMU/TLBの内部 を参照してください。
巨大ページが効くのは、広いメモリにランダムに触れて TLB ミスが性能を律速するケース(大規模な配列・KVS・JVM ヒープ・データベースのバッファプール等)です。逆に作業集合が小さく TLB に収まるなら、巨大ページにしても変換コストはもともと小さく、利得はほぼありません。
THP とは:透過的に巨大ページを使う
巨大ページを使う古典的な方法は後述の hugetlbfs で、アプリが専用 API で明示的に確保します。これは確実ですが、アプリ改修が要りメモリの事前予約も必要で、扱いが面倒です。
THP(Transparent Huge Pages) は、この巨大ページの利用を カーネルが透過的に(アプリ無改修で)肩代わり する仕組みです。アプリは普通に malloc/mmap で 4KiB 前提のコードを書くだけで、カーネルが裏で条件の合う領域を 2MiB ページに置き換えます。対象は主に 匿名メモリ(ヒープ・スタック・MAP_ANONYMOUS)で、近年は tmpfs やファイルマッピングへの拡張も進んでいます。
THP の動作は /sys/kernel/mm/transparent_hugepage/enabled で切り替えます。
always : 可能な限り常に THP を試みる
madvise : madvise(MADV_HUGEPAGE) で明示要求した領域だけ THP 化(既定として推奨されやすい)
never : THP を無効化
always は何もしなくても効く反面、後述の断片化やレイテンシの副作用を全域で被ります。madvise は アプリが「ここは巨大ページが効く」と宣言した領域に限定 するため、副作用を抑えつつ効果を狙えます。
供給経路その1:フォルト時の直接割当
THP がページを巨大化する経路は二つあります。一つ目は ページフォルト時の直接割当(huge page fault) です。
THP 対象の匿名領域に 初めてアクセス してページフォルトが起きると、カーネルは「ここを 4KiB で埋めるか、2MiB 一枚で埋めるか」を判断します。条件が揃えば、そのフォルトの場で 2MiB の物理ページを一括割当 し、PMD エントリ一つで 2MiB 全体をマップします。フォルト処理の基本は デマンドページングとページフォルト処理 を参照。
直接割当が成立する条件は厳しめです。
- 仮想アドレスが 2MiB 境界に整列 し、2MiB 分の連続した仮想範囲が同じ VMA に収まること。
- 2MiB の連続した物理メモリ が(後述の buddy アロケータから)今すぐ取れること。
ここに フォルトレイテンシのトレードオフ が現れます。連続 2MiB がすぐ取れないとき、カーネルは メモリ圧縮(compaction) を試みて連続領域を作ろうとします。これはフォルトを起こしたスレッドの内部で同期的に走り得るため、1 回のフォルトが重くなる。これを制御するのが defrag 設定です。
/sys/kernel/mm/transparent_hugepage/defrag
always : フォルト時に同期 compaction まで頑張る(割当成功率↑、レイテンシ↑)
defer : すぐ取れなければ 4KiB で埋め、compaction は kcompactd に非同期で委ねる
madvise : MADV_HUGEPAGE 領域でだけ積極的に頑張る
defer+madvise : 両者の組合せ(よく使われる)
never : フォルト時に頑張らない
enabled=always かつ defrag=always だと、巨大ページ確保のための同期 compaction がフォルト経路に割り込み、スループットは上がるのに p99/p999 レイテンシが跳ねる という症状が起きがちです。レイテンシ重視のサービスでは madvise 運用にする、あるいは defrag=defer 系にして同期圧縮を避けるのが定石です。
供給経路その2:khugepaged による昇格(collapse)
二つ目の経路が、カーネルスレッド khugepaged による事後的な 昇格(collapse) です。フォルト時には 4KiB で埋まってしまった領域でも、後から条件が整えば巨大ページにまとめ直せます。
khugepaged は定期的に起きて、THP 対象の VMA を走査します。ある 2MiB 整列範囲を構成する 4KiB ページ群が十分に埋まって(常駐して)いる のを見つけると、次の手順で 1 枚の 2MiB ページへ畳み込みます。
collapse_huge_page の概略:
1. 連続した 2MiB の空き巨大ページを 1 枚確保する(必要なら compaction)
2. 元の 512 枚の 4KiB ページの内容を、その巨大ページへコピーする
3. 該当範囲を一時的にアンマップし、PMD を巨大ページ 1 枚に張り替える
4. 元の 512 枚の 4KiB ページを解放する
khugepaged の挙動は走査ページ数やスリープ間隔のチューノブで制御します。
/sys/kernel/mm/transparent_hugepage/khugepaged/
pages_to_scan : 1 回の起床で走査する 4KiB ページ数
scan_sleep_millisecs: 走査と走査の間のスリープ
max_ptes_none : 2MiB 範囲のうち「空き(未常駐)」を何ページまで許して昇格するか
max_ptes_none が大きいほど、まばらにしか触れていない領域も積極的に巨大化されます。ただし 未使用部分まで物理メモリを実体化 してしまうため、後述の内部断片化(メモリ膨張)の原因になります。
直接割当は「初回フォルトで一発で巨大ページ化」を狙う即時経路、khugepaged は「フォルト時に間に合わなかった、あるいは後から条件が整った」領域を回収する非同期経路です。直接割当をレイテンシ理由で抑えても(defrag=defer)、khugepaged が裏で昇格してくれるので、巨大ページ化のチャンスは失われません。
トレードオフ:断片化・膨張・スプリット
THP の利得(TLB reach 向上)には、はっきりした代償が伴います。
| コスト | 中身 | 影響 |
|---|---|---|
| 外部断片化 | 2MiB の連続物理メモリが必要。空きはあっても飛び飛びだと作れない | compaction が走り、フォルトや khugepaged が重くなる |
| 内部断片化(膨張) | 数十KiBしか使わない領域も 2MiB 実体化されうる | RSS が膨らみ、実メモリ使用量が想定超過する |
| スプリットコスト | mprotect/部分unmap/一部スワップ等で 2MiB を 512枚へ戻す必要が出る | split 処理のオーバーヘッド。巨大ページの利点も失う |
| コピーコスト | khugepaged の collapse は 2MiB 分の memcpy を伴う | CPU を消費。書込集中域では割に合わないことも |
最大の論点は 外部断片化 です。巨大ページは物理的に連続した 2MiB を要求しますが、システムが長く動くと空きメモリは細切れになり、空き総量は十分でも連続 2MiB が作れない 状態に陥ります。これを解消するのが compaction(使用中ページを動かして空きを寄せ集める処理)で、同期実行されればフォルト遅延、非同期でも CPU 消費という形で跳ね返ります。空きページの管理単位の話は ページングとスワップ の延長で捉えると整理しやすいです。
スプリット(split) も重要です。2MiB ページの一部にだけ mprotect で別属性を付けたり、一部だけアンマップ・スワップしようとすると、PMD 一枚では表現できなくなり、カーネルは巨大ページを 512 枚の 4KiB へ分割 します。分割が頻発する領域では、せっかくの巨大化が無駄打ちになります。
hugetlbfs との違い:透過 vs 明示・予約
巨大ページのもう一つの実装が hugetlbfs(および MAP_HUGETLB)です。THP との設計思想の違いを押さえることが、選択の決め手になります。
| 観点 | THP | hugetlbfs |
|---|---|---|
| 利用方法 | 透過的。アプリ無改修(または madvise ヒント) | 明示的。専用 mount / mmap(MAP_HUGETLB) / SHM が必要 |
| メモリ確保 | 通常の buddy から動的に確保・昇格 | 起動時等に hugepage プールを事前予約(nr_hugepages) |
| 供給の確実性 | 断片化で割当に失敗・遅延しうる | 予約済みプールから確実に取れる |
| ページの可動性 | 可動。compaction/スワップ/split の対象になりうる | ピン留め。スワップ対象外で動かない |
| 1GiB ページ | 基本は 2MiB 中心 | 1GiB を明示利用しやすい |
| 主な用途 | 汎用ワークロードの底上げ | DB/VM/HPC 等、確実な巨大ページが要る基盤 |
核心は 「動的・透過・可動」の THP と 「静的・明示・予約・ピン留め」の hugetlbfs という対比です。THP は手軽さと汎用性が魅力ですが、断片化のせいで 欲しい瞬間に巨大ページが取れない不確実性 を内包します。一方 hugetlbfs は起動時に vm.nr_hugepages でプールを確保し、そのメモリは スワップされず移動もしない(ピン留め)ため、確実に・予測可能に 巨大ページを供給します。
そのため、データベースの共有バッファ、ハイパーバイザのゲストメモリ、HPC のように 巨大ページの確保が成否や性能予測に直結する基盤 では hugetlbfs を選びます。逆に、性能を底上げしたいが個別チューニングは避けたい一般的なアプリには THP が向きます。NUMA 環境では巨大ページがノードをまたぐ局所性にも影響するため、NUMAとメモリアクセス局所性 も併せて検討すると配置の誤りを避けられます。
(1)巨大ページの本質的利得は「1 エントリの TLB reach 拡大」と「ページウォーク短縮」であること。(2)THP の供給経路が直接割当と khugepaged の二系統であること。(3)代償は外部断片化・内部膨張・split・compaction 遅延で、defrag=always がテイルレイテンシを悪化させ得ること。(4)THP=透過/動的/可動、hugetlbfs=明示/予約/ピン留め という対比。この 4 点が頻出です。
まとめ
- 巨大ページ は 1 つの TLB エントリでカバーする範囲(TLB reach)を 4KiB の数百倍に広げ、TLB ミスとページウォークを削減する。効くのは変換コストが律速する広域アクセスのワークロード。
- THP はアプリ無改修で巨大ページを使わせる透過機構。供給は フォルト時の直接割当 と khugepaged の事後昇格(collapse) の二系統で、
enabled/defragで挙動を制御する。 - 代償は 外部断片化(連続 2MiB 確保)・内部膨張・split・compaction によるフォルト遅延。
defrag=alwaysは p99 を悪化させやすく、レイテンシ重視ならmadvise運用が定石。 - hugetlbfs は明示確保・事前予約・ピン留めで巨大ページを 確実に 供給する。DB・VM・HPC など確実性が要る基盤向き。THP は手軽さ重視の汎用向き、という住み分けになる。
前提として 仮想記憶のアドレス変換とMMU/TLBの内部 と デマンドページングとページフォルト処理 を、配置の話は NUMAとメモリアクセス局所性、空きメモリ管理の土台は ページングとスワップ を合わせて読むと、巨大ページの全体像が繋がります。
OS Article
Transparent Huge Pages(THP)と巨大ページの内部を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
Transparent Huge Pages
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
巨大ページの供給経路は二つ。フォルト時にその場で2MiBを割り当てる直接割当と、khugepagedが走査して連続した4KiB群を後から1枚へ昇格(collapse)する経路がある。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「Transparent Huge Pages / 巨大ページ」に近いか確認する。
- 強みである「THPは4KiBの代わりに2MiB等の巨大ページを透過的に使う仕組みで、1エントリでカバーする範囲が広がりTLBミスとページウォークを減らす。アプリ改修なしで効く。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。