VM の memory 性能は、割り当て容量だけでは決まりません。page size、TLB miss、vCPU と memory の NUMA placement、page reclaim、ballooning、KSM によって、同じ容量でも throughput と tail latency が変わります。
この記事では、事前予約する HugeTLB と kernel が自動管理する Transparent HugePages を分け、2 MiB / 1 GiB page の選択、NUMA node ごとの予約、libvirt XML、capacity、起動と migration の検証方法を確認します。
書籍
CPU 仮想化支援、メモリ仮想化、割り込み、仮想デバイスなど、VM の実行モデルを低レイヤから理解する参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。
HugeTLB、THP、通常 page を分ける
| 方式 | allocation | 運用上の特徴 |
|---|---|---|
| 通常 page | 通常の memory allocator が管理する | 柔軟だが、大きな working set では page 数が増える |
| HugeTLB | 専用 pool へ明示的に予約する | 決定性を得やすいが、他用途へ使えず swap もできない |
| Transparent HugePages | kernel が page を自動的に昇格・分割する | 予約 pool は不要だが、allocation と compaction の挙動を考慮する |
HugeTLB と THP は、どちらも大きな page を使う仕組みですが同じものではありません。libvirt の memoryBacking で HugePages を指定する設計と、host の THP policy を有効にする設計を混同しないことが重要です。
page size と TLB の関係を確認する
TLB は、virtual address から physical address への変換結果を保持する cache です。同じ memory 容量なら、page size が大きいほど必要な page 数が減り、限られた TLB entry で広い範囲を扱えます。
| page size | 64 GiB を構成する page 数 | 判断 |
|---|---|---|
| 4 KiB | 16,777,216 | 通常 page。柔軟性が高い |
| 2 MiB | 32,768 | HugeTLB で扱いやすく、細かい capacity 調整が可能 |
| 1 GiB | 64 | page 数は少ないが、予約単位と fragmentation の影響が大きい |
page 数が減ることは TLB miss 削減の可能性を示しますが、必ず workload が速くなるとは限りません。working set、memory access pattern、CPU architecture、nested page table、NUMA remote access を含め、適用前後で測定します。
host が対応する page size と pool を確認する
最初に host が対応する HugeTLB page size、予約総数、空き数、使用数、THP policy を読み取ります。/proc/meminfo の HugePages_* は default size の pool を中心に示すため、複数 size を使う場合は sysfs も確認します。
grep -E 'Huge|AnonHuge' /proc/meminfo
ls -1 /sys/kernel/mm/hugepages
cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
cat /sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages
cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defragsysfs に対象 directory がなければ、その page size を同じ方法では利用できません。1 GiB page を前提にする前に、CPU、kernel、boot parameter、pool の実在を確認します。
NUMA node ごとの HugePages を確認する
NUMA host では、HugeTLB pool の総数だけでなく node ごとの配置が重要です。VM の vCPU が node 0 で動いても、必要な HugePages が node 1 にしかなければ、起動失敗または意図しない memory placement につながります。
lscpu -e=CPU,NODE,SOCKET,CORE,ONLINE
numactl --hardware
numastat -c
cat /sys/devices/system/node/node*/meminfo
cat /sys/devices/system/node/node*/hugepages/hugepages-2048kB/nr_hugepages
cat /sys/devices/system/node/node*/hugepages/hugepages-2048kB/free_hugepages- vCPU を置く CPU と NUMA node
- VM memory を割り当てる node
- node ごとの HugePages 総数と空き数
- NIC、NVMe、HBA が接続された NUMA node
- host OS、QEMU、page cache に残す通常 memory
- 障害時に VM が移動する先の pool
runtime で HugeTLB pool を増やす場合、必要な連続 memory を確保できず、要求数に届かないことがあります。kernel boot の早い段階は memory fragmentation が少ないため、大きな固定 pool は boot parameter で確保する方が再現しやすい場合があります。
VM の memoryBacking と numatune を確認する
libvirt では memoryBacking の hugepages で page size を指定し、numatune で host NUMA node の policy を指定できます。次は guest NUMA node 0 に 2 MiB page を使い、host node 0 へ memory を限定する例です。
<memoryBacking>
<hugepages>
<page size='2' unit='M' nodeset='0'/>
</hugepages>
<nosharepages/>
</memoryBacking>
<numatune>
<memory mode='strict' nodeset='0'/>
</numatune>nodeset の意味は要素ごとに確認します。memoryBacking の page 指定では guest NUMA node との対応を表し、numatune では host NUMA node の memory policy を表します。guest NUMA topology を定義していない VM に複雑な size 分割を入れる前に、XML 全体を確認します。
nosharepages は、その domain で memory merge、つまり KSM を無効にする指定です。locked は別の制約であり、必要性と host 側の lock limit を確認せずに追加しません。
実行中と永続 XML を分けて確認する
実行中の VM と次回起動時の XML が一致しているとは限りません。HugePages の適用確認では、live XML、inactive XML、NUMA policy、balloon、QEMU process の memory map を対応付けます。
virsh dumpxml vm01
virsh dumpxml vm01 --inactive
virsh numatune vm01
virsh dommemstat vm01
virsh domstats vm01 --balloon --memory
virsh vcpupin vm01
virsh emulatorpin vm01XML に hugepages があっても、意図した size と node で実 memory が確保されたかは別に確認します。起動 log、QEMU process の smaps、node ごとの free HugePages の差分を合わせて見ます。
必要 page 数と host capacity を計算する
必要 page 数は、HugePages へ載せる memory 容量を page size で割って求めます。64 GiB を 2 MiB page で構成するなら 32,768 pages、1 GiB page なら 64 pages です。ただし、VM の設定 memory と host の予約総量を機械的に同じにするだけでは不十分です。
| capacity 項目 | 確認すること |
|---|---|
| VM memory | guest が使う memory と memory slot の構成 |
| QEMU overhead | page table、device、process 自体に必要な通常 memory |
| host reserve | kernel、service、page cache、monitoring の余力 |
| NUMA balance | VM ごとの必要 pages が対象 node にあるか |
| failover | 障害時に移動先 host で同じ pool を確保できるか |
| growth | memory hotplug、VM 増設、maintenance 時の余力 |
HugeTLB pages は pool に予約されると通常用途には使えず、memory pressure が高くても swap できません。予約しすぎると host の通常 memory を圧迫し、少なすぎると VM を起動できません。
Transparent HugePages の policy と実使用量を見る
THP は予約 pool ではなく、kernel が anonymous memory などを大きな page へ昇格または分割する仕組みです。always、madvise、never の policy だけでなく、defrag policy、khugepaged、実際の AnonHugePages を確認します。
| 観点 | 確認すること |
|---|---|
| enabled | system-wide policy と size ごとの policy |
| defrag | page fault 時の reclaim / compaction による latency |
| khugepaged | background collapse の進行と CPU 使用 |
| 実使用量 | /proc/meminfo と process の smaps |
| 変更反映 | 既存 mapping では application restart が必要か |
THP を一律に無効または有効にするのではなく、workload と kernel の挙動を測定します。明示的 HugeTLB を利用する VM があっても、host 上の他 process に対する THP policy は別に評価します。
ballooning と KSM の境界を決める
| 機能 | 得られるもの | 専有に近い VM での注意 |
|---|---|---|
| ballooning | guest から memory を回収できる | 実 memory の保証と page pool の前提が曖昧になる |
| KSM | 同一内容の page を merge できる | scan と copy-on-write の負荷、分離境界を確認する |
| HugeTLB | 明示した page pool を利用できる | 共有や回収より reservation を優先する設計になる |
nosharepages | domain の memory merge を無効にする | host 全体の KSM policy とは別に管理する |
best effort VM の集約率を優先する設計と、NFV、DPDK、database、低遅延 VM の memory 決定性を優先する設計を同じ policy にしないことが重要です。VM class ごとに ballooning、KSM、HugeTLB の扱いを決めます。
起動、再起動、migration を検証する
| 試験 | 合格条件 |
|---|---|
| cold start | 対象 node に必要 pages があり、VM が意図した size で起動する |
| restart | 使用後に pages が pool へ戻り、再起動結果を再現できる |
| concurrent start | 複数 VM の起動順序が変わっても必要 pool を確保できる |
| memory pressure | host の通常 memory と service に必要な headroom が残る |
| migration | 移行先の CPU、NUMA、page size、pool、libvirt XML が互換である |
| failover | 片系障害後の収容数でも必要 pages と通常 memory が足りる |
HugePages の性能試験だけでなく、起動可能性と運用可能性を試します。migration できない構成を採用する場合は、それを障害復旧と maintenance の制約として明示します。
適用前後を同じ条件で比較する
- 同じ vCPU、memory、NUMA topology、workload を使う
- throughput と p95 / p99 latency を同時に測る
- TLB miss、page fault、NUMA remote access を比較する
- THP collapse、split、compaction の変化を確認する
- host の free HugePages と通常 memory の余力を記録する
- 改善しない場合に元の XML と pool へ戻せるようにする
page size だけでなく NUMA placement も同時に変えると、どちらが効いたのか分かりません。可能な範囲で変更を分け、同じ負荷区間で counter の差分を比較します。
よくある失敗
| 失敗 | 問題 | 確認すること |
|---|---|---|
| HugeTLB と THP を同じものとして扱う | 予約と自動昇格の挙動を混同する | pool、sysfs policy、XML |
| host 全体の総数だけを見る | 必要 node に pages がない | node ごとの total / free |
| VM memory 分だけ予約する | host の通常 memory が不足する | QEMU overhead と host reserve |
| 1 GiB page を常に選ぶ | 予約単位が大きく capacity が硬直する | working set と実測効果 |
| XML があれば適用済みと考える | size と node が意図どおりでない | smaps、pool 差分、起動 log |
| 性能試験だけ行う | 再起動と failover で起動できない | 運用 lifecycle 全体 |
まとめ
VM の HugePages 設計では、通常 page、HugeTLB、THP を分け、page size、TLB、NUMA placement、ballooning、KSM を同じ memory policy として確認します。大きな page は TLB 効率を改善する可能性がありますが、予約量と配置が新しい制約になります。
host が対応する size、node ごとの pool、live / inactive XML、実 memory map を記録し、必要 pages と host reserve を計算します。その後、性能、起動、再起動、migration、failover を同じ設計条件で検証することが、HugePages を安全に採用するための要点です。

