専用サーバーにおけるNUMA対応とCPUピンニング
16分で読めます - 2026年6月16日

NUMAトポロジーの確認方法と、Linuxワークロードを適切なコアおよびメモリに割り当てる方法について解説します。numactl、taskset、systemd、BIOS設定、およびワークロード固有の戦略について扱います。
専用サーバーにおけるNUMA対応とCPUピンニング
マルチソケットサーバーでは、プロセスが実行される場所とメモリが配置される場所は別々の問題であり、これらを同期させないことは、パフォーマンスを無駄にする最も簡単な原因の一つとなります。 NUMAの理解とCPUピンニングは、この問題を解決する2つの手段です。本記事では、NUMAの仕組み、Linuxでの確認方法、およびデータベース、AIトレーニング、レイテンシに敏感なサービス向けにワークロードを適切にピンニングする方法について解説します。
マルチソケットサーバーにおけるNUMAの仕組み
NUMA(Non-Uniform Memory Access)ノードとは、専用のメモリコントローラを介してローカルなRAMブロックにバインドされたCPUコアのグループです。2ソケットサーバーでは通常、2つのノードが存在します。 どのコアも任意のアドレスを読み取ることができますが、ローカルアクセスは約80 nsであるのに対し、IntelのUPIやAMDのInfinity Fabricを介したソケット間ホップは約130~150 nsかかります。ソケット数が多い大規模なシステムでは、最悪の場合、ノード間のアクセス時間が250 nsを超えることもあります。
帯域幅も同様の傾向を示します。2ソケットのSapphire Rapidsシステムでは、コアがローカルメモリにアクセスする場合、約600 GB/sの帯域幅を維持できますが、ソケット間リンクの帯域幅はそのほんの一部であるため、そこを通過するトラフィックはすぐにボトルネックとなります。 コア数の多いプロセッサでは、この現象がより細分化されます。インテルのSub-NUMA Clustering(SNC)やAMDのNodes Per Socket(NPS)は、各ソケットを複数のNUMAドメインに分割するため、「2ソケット」のシステムでも、Linuxに対して4つや8つのノードを容易に提示できます。
NUMAを意識しない場合、Linuxスケジューラは、スレッドのワーキングセットが元のノードに残ったまま、ソケット間でスレッドを平然と移動させてしまいます。その後のアクセスはすべてリモートアクセスとなります。目に見える症状は、CPU使用率が高いにもかかわらず実際のスループットが低いことです。これは、コアがメモリを待つことに時間を費やしているためです。I/Oデバイスはこの状況をさらに悪化させます。 GPUやNICは特定のPCIeルートに接続されており、そのルートは1つのNUMAノードに属しています。もしそれらにデータを供給するプロセスが別のソケット上で実行されている場合、すべてのDMA転送はインターコネクトを横断することになります。
Linux での NUMA トポロジーの確認
以下の4つのツールで、必要な機能のほぼすべてを網羅できます:
lscpuソケットとノードの概要を素早く確認するために。numactl --hardwareノードごとのメモリ合計およびノード間の距離行列を確認する場合。numastatプロセスごとのヒット/ミスカウンターを確認するには。lstopo(hwloc より) キャッシュ階層および PCIe デバイスの局所性を確認します。
まずは numactl --hardwareから始めます。これには各ノード、それに属するコアとメモリ、および距離行列が一覧表示されます。値が10の場合はローカル、20以上はリモートです。マルチソケットマシンで単一のノードしか表示されない場合は、BIOSでNode Interleavingが有効になっており、トポロジーが隠されているため、まずそれを修正してください(以下を参照)。
特定のプロセスについては、 numastat -p <PID> は、そのプロセスのメモリが実際にどこに割り当てられているかを詳細に示します。重要なカウンターは4つあります:
numa_hit: 対象ノード上に割り当てられたメモリ。この値は高いほど望ましい。numa_miss: 対象ノードが満杯だったため、割り当てが他の場所に溢れた。numa_foreign: 別のノードがローカルでの割り当てを試みたができなかった場合。メモリ圧迫を示しています。other_node: プロセスが実行されているノード以外のノードに割り当てられたページ。ここでの値が高いのは、不適切なピンニングの典型的な兆候です。
GPUやNICのワークロードについては、 lstopo-no-graphics を実行し、各PCIeデバイスがどのNUMAノードに接続されているかを確認してください。デバイスを駆動するコアが別のノードにある場合、それが最初に修正すべき点です。
CPUピンニングとメモリポリシー
CPU ピンニング(または CPU アフィニティ)は、プロセスを特定のコアにバインドし、スケジューラがそのプロセスを移動できないようにします。しかし、それだけでは不十分です。なぜなら、Linux はデフォルトでファーストタッチ・メモリポリシーを採用しており、ページは最初に書き込みが行われたノードに割り当てられるからです。 スレッドがピン留めされる前に誤ったノードで起動した場合、そのメモリはそこに留まります。配置と割り当ての両方を一緒に制御する必要があります。
一般的なケースに対応するツールは3つあります:
| ツール | 制御対象 | 用途 |
|---|---|---|
taskset | CPUコアのみ | 既存プロセスのクイックな単発バインディング |
numactl | CPUコアとメモリ | 厳密な局所性を伴うワークロードの起動 |
| systemd | CPUコアとメモリ、永続的 | 再起動後も固定が必要なサービス |
numactl 4つのメモリポリシーをサポート:
--membind=N: ノードNにのみ割り当て、満杯の場合は失敗する。--preferred=N: ノードNを優先し、必要に応じて他のノードにフォールバックする。--interleave=all: 帯域幅を均等に分散させるため、ノード間でラウンドロビン方式を採用。--localalloc: 実行中のCPUが配置されているノードに割り当てる。
ワークロードを特定のノードに固定する
まず、ターゲットノードに属するコアを特定します:
numactl --hardware次に、そのノードにコアとメモリの両方をバインドしてアプリケーションを起動します:
numactl --cpunodebind=0 --membind=0 ./your_application既に実行中のプロセスについては、CPUアフィニティを次のように調整します: taskset:
taskset -cp 0-7 <PID>再起動後も設定を維持するには、systemdユニットで設定します:
[Service]
CPUAffinity=0-7
NUMAPolicy=bind
NUMAMask=0再読み込みして再起動します:
sudo systemctl daemon-reload && sudo systemctl restart <service>手動でピン留めを行う際は、配置設定と競合しないよう、カーネルの自動負荷分散機能を無効にします:
sysctl -w kernel.numa_balancing=0これを /etc/sysctl.conf に追加して永続化します。その後、 numastat -p <PID> 数分間の実際のワークロードで検証します。もし other_node がゼロ付近に留まる場合、ピンニングが有効になっています。
ワークロードに応じた戦略の選定
適切なポリシーは、ワークロードにとって低遅延の方が有益か、それとも全ノードにわたる総帯域幅の方が有益かによって異なります。
| ワークロード | ポリシー | 理由 |
|---|---|---|
| データベース(PostgreSQL、MySQL、SQL Server) | --cpunodebind + --membind | 大規模な共有バッファ、レイテンシに敏感なクエリパス |
| インメモリキャッシュ(Redis、Memcached) | シングルノードバインド | すべてがRAMアクセスであり、リモートレイテンシが即座に現れる |
| AI/MLのトレーニングおよび推論 | GPUのNUMAノードへのバインド | PCIeルート間を跨ぐテンソル転送を回避 |
| 分析(Spark、Elasticsearch) | --interleave=all | 大規模なワーキングセットには全ノードにわたる帯域幅が必要 |
| レイテンシに敏感なAPI、トレードオフ | 厳格なピン + IRQ アフィニティ | ピークスループットよりも予測可能性が重要 |
| ネットワーク負荷が高い(RoCEv2、InfiniBand) | NICのNUMAノードへのピン割り当て、IRQ専用のコア確保 | 割り込み処理をローカルに保ち、アプリスレッドの邪魔にならないようにする |
特にGPUワークロードについては、 lstopo を実行してGPUがどのNUMAノードに配置されているかを確認し、その後 numactl --cpunodebind=N --membind=N を使用して、その同じ N でトレーニングまたは推論プロセスを起動します。デフォルトのスケジューラの配置は往々にして不適切であるため、これはマルチソケット GPU サーバーにおいて最も簡単に成果が得られる手法の一つです。
両方のソケットにまたがる HPC および MPI ワークロードについては、すべてをインターリーブするのではなく、 localalloc を使用して各ランクを単一のノードに固定し、すべてをインターリーブしないようにします。各ランクはローカルメモリを取得し、並列処理はランクレベルで行われます。
実用上の注意点として、単一のノードにランクを固定する場合は、そのノードに2~4GBの余裕を残しておいてください。ノードの使用率がほぼ満杯になるとメモリの回収がトリガーされ、削減しようとしていたレイテンシの損失につながります。
確認すべきBIOSおよびカーネルの設定
ツールの出力精度は、ファームウェアが公開するトポロジーの正確さに依存します。確認すべき設定は以下の通りです:
- ノードインターリーブ:無効にしてください。有効にすると、BIOSはすべてのメモリを単一のフラットプールとして提示し、OSからNUMAを完全に隠蔽します。
numactl --hardwareこの場合、マルチソケットマシンでも1ノードとして表示されます。 - Sub-NUMA Clustering(Intel)または Nodes Per Socket(AMD):より細かい局所性を求める場合は、コア数の多いプロセッサで有効にしてください。
lscpu再起動後に確認してください。 vm.zone_reclaim_mode: ほとんどの運用サーバーでは0に設定します。0以外の値に設定すると、リモートに割り当てるのではなくローカルメモリを積極的に回収するため、有用なページキャッシュが追い出される可能性があります。kernel.numa_balancing: 汎用ワークロードではオンにしておき、手動でピン留めを行う場合はオフにしてください。自動バランサーは、ポリシーと矛盾する方法でページやスレッドを移行してしまいます。
BIOS、カーネルパラメータ、IRQアフィニティを制御できるベアメタル環境でNUMAチューニングを行う場合、ハイパーバイザーの抽象化を回避することなく、上記の設定をすべて適用できます。これが、クラウドVMよりも専用ハードウェアの方がこの種の作業が容易である主な理由です。
フルルートアクセスが可能なマルチソケット専用サーバーについては、FDCの専用サーバーをご覧ください。

Linuxサーバーのワークロード最適化のためのチューニング・プロファイル
GPU、データベース、高帯域幅 Linux サーバー用の調整済みプロファイルの選択、適用、カスタマイズ方法について、例と Ansible 導入のヒントを示します。
16分で読めます - 2026年6月9日
Linux OOM Killer Tuning for VPS: 実践ガイド
12分で読めます - 2026年6月8日

ご質問またはカスタムソリューションが必要ですか?
柔軟なオプション
グローバル・リーチ
即時展開
柔軟なオプション
グローバル・リーチ
即時展開