4KBメモリページの「隠れた税金」
ほとんどのLinuxディストリビューションは、今でもデフォルトのメモリページサイズを4KBとして出荷しています。サーバーのRAMが4GBだった2005年当時は理にかなっていましたが、現代のハードウェアにとってはパフォーマンスを低下させる要因となります。64GBのPostgreSQLインスタンスを実行したり、300億パラメータのAIモデルをロードしたりする場合、この古い設定がCPUのボトルネックになっている可能性があります。すべてのメモリ操作には仮想アドレスから物理アドレスへの変換が必要であり、CPUはこの変換をTranslation Lookaside Buffer(TLB)にキャッシュします。
計算してみましょう。標準的な4KBページを使用した32GBのワークロードでは、正確に8,388,608個のエントリが生成されます。TLBは非常に小さいものです。エントリが溢れると「TLBミス」が発生し、CPUはメインメモリ内のページテーブルを手動で「ウォーク(走査)」せざるを得なくなります。これにより、ミス1回につき50〜100ナノ秒のペナルティが発生します。データ負荷の高いアプリケーションでは、これらのわずかな遅延が積み重なり、ほとんどの管理者が気づかないほどの巨大なパフォーマンスの低下を招きます。
私の本番環境のUbuntuノードでは、ページサイズを大きくすることで、メモリ管理に費やされるサイクルが大幅に削減されました。4GB程度の小さなインスタンスであっても、カーネルによるメモリ追跡を効率化することで、CPUはアドレス変換ではなく、実際のロジックの処理により多くの時間を割けるようになります。
Static vs. Transparent Huge Pages:戦略の選択
TLBのボトルネックを回避するために、Linuxには2つのオプションがあります。Static Huge Pages(静的Huge Pages)とTransparent Huge Pages(THP:透過的Huge Pages)です。誤った選択をすると、逆にパフォーマンスが悪化することもあります。
Static Huge Pages(プロフェッショナルな選択)
Static Huge Pagesは起動時に事前割り当てされます。これらは永続的でRAM内に固定(ピン留め)され、ディスクにスワップされることはありません。4KBではなく2MB of ページを使用することで、TLBが追跡する必要のあるエントリ数を512分の1に減らすことができます。これにより、予測可能で決定論的なパフォーマンスの底上げが実現します。唯一の欠点は、このRAMがHuge Pages専用に予約されるため、事前にメモリ需要を計算しておく必要があることです。
Transparent Huge Pages(「手軽な」ボタン)
THPはバックグラウンドで4KBページを2MBブロックにマージすることで、このプロセスを自動化しようとします。一見するとメリットしかないように思えますが、実装には欠点があることが多いです。バックグラウンドデーモンの khugepaged は、突然のCPUスパイクやメモリ断片化を引き起こす可能性があります。OracleやMongoDBなどのほとんどのデータベースエキスパートは、高負荷時の予測不可能な100ミリ秒以上のレイテンシスパイクを避けるために、THPを無効にすることを推奨しています。
本番環境のケーススタディ:スループットが14%向上
本番環境でこれらの調整を6ヶ月間運用した結果、データは明白です。私の主なワークロードは、巨大なNumPyテンソルを処理するPythonベースのAIサービスでした。最適化前、システムは約7%の時間を「システム」モード(カーネルのオーバーヘッド)に費やしていました。データセットを2MBのHuge Pagesに移行したところ、そのオーバーヘッドは1.5%まで減少しました。その結果、AIロジックを一行も変更することなく、モデル推論の全体的なスループットが14%向上しました。
ステップ・バイ・ステップ:Huge Pagesの有効化
実際にやってみましょう。以下の例ではUbuntuを使用していますが、コマンドはほとんどの現代的なカーネルで動作します。
1. 現在の使用状況を確認する
まず、システムが現在ラージページを使用しているかどうかを確認します。
grep -i huge /proc/meminfo
HugePages_Total が0の場合、CPUは何百万もの小さな4KBチャンクを管理するために、必要以上に負荷がかかっています。
2. 必要な要件を計算する
データベースに4GBのRAMを割り当てたい場合、ページサイズが2048KB(2MB)であれば、2,048ページが必要になります。私は通常、アライメントのオーバーヘッドを考慮して5%のバッファを追加します。
3. 即時テスト(非永続的)
再起動せずに、すぐに割り当てをテストします:
sudo sysctl -w vm.nr_hugepages=2048
再び /proc/meminfo を確認します。カウントが要求した数より少ない場合は、メモリが断片化しすぎている可能性があります。これは稼働時間が長いシステムでよく見られる問題であり、そのため起動時の割り当てが推奨される標準的な方法となります。
4. 設定を永続化する
クラッシュや再起動後も設定を維持するために、sysctlファイルに設定を追加します:
echo "vm.nr_hugepages=2048" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
ミッションクリティカルなサーバーでは、/etc/default/grub の GRUB_CMDLINE_LINUX_DEFAULT に hugepages=2048 を追加し、sudo update-grub を実行することをお勧めします。これにより、メモリが断片化する前にカーネルが強制的にメモリを予約します。
Transparent Huge Pages(THP)の無効化
PostgreSQL、Redis、またはSAP HANAを実行している場合は、”khugepaged” がデータベースのスレッドをストールさせるのを防ぐために、THPを無効にする必要があります。
現在のステータスを確認します:
cat /sys/kernel/mm/transparent_hugepage/enabled
[always] が括弧で囲まれている場合は、以下のコマンドを実行して停止させます:
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/defrag
アプリケーションの設定
ページを割り当てるのは戦いの半分に過ぎません。ソフトウェアに対して、それらを使用するように指示する必要があります。
PostgreSQL
postgresql.conf を編集し、huge_pages = on に設定します。これは安全機能です。約束したHuge Pagesが見つからない場合、データベースは起動に失敗します。これにより、低速な4KBページへサイレントにフォールバックするのを防ぎます。
Python (AI/Data Science)
カスタムツールの場合は、mmap フラグを使用して、カーネルから直接巨大なメモリブロックを要求します:
import mmap
# Huge Pagesを使用して2MBを割り当てる
size = 2 * 1024 * 1024
buf = mmap.mmap(-1, size, flags=mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS | mmap.MAP_HUGETLB)
最終的な判断
Huge Pagesは不適切なコードに対する魔法の解決策ではありませんが、高性能なデータベースやAIにとっては、利用可能な最も効果的な低レベルのチューニング手法です。TLBの負荷を軽減することで、これまで基本的な管理作業に浪費されていたCPUサイクルを取り戻すことができます。サーバーで16GB以上のRAMを扱っている場合は、1時間かけてこれを実装してみてください。ベンチマーク結果がその価値を証明してくれるはずです。

