VM の CPU 性能は、vCPU 数だけでは決まりません。vCPU thread がどのホスト CPU で実行されるか、ゲストメモリがどの NUMA node から割り当てられるか、QEMU の emulator thread や I/O thread がどこで動くかによって、同じ vCPU 数でも待ち時間と揺らぎが変わります。
この記事では KVM / libvirt を前提に、CPU topology の確認、vCPU pinning、emulatorpin、numatune、HugePages、IRQ と I/O thread の配置を対応付けます。pinning は常に性能を上げる設定ではなく、共有資源を専有に近づける代わりに柔軟性を減らす設計です。
書籍
CPU 仮想化支援、メモリ仮想化、割り込み、仮想デバイスなど、VM の実行モデルを低レイヤから理解する参考書籍です。価格や在庫はリンク先で確認してください。
Amazon で見るこのリンクは Amazon アソシエイトリンクです。
vCPU、ホスト CPU、補助 thread を分ける
| 要素 | 実体 | 設計時に決めること |
|---|---|---|
| vCPU | ゲストへ見せる仮想 CPU | 個数、topology、ホスト CPU への affinity |
| ホスト CPU | socket、core、SMT thread | 同じ core の sibling と NUMA node |
| emulator thread | QEMU のデバイス・管理処理 | vCPU と分ける CPU 範囲 |
| I/O thread | 仮想ディスクなどの I/O 処理 | 対象デバイスと CPU affinity |
| IRQ / softirq | NIC やストレージの割り込み処理 | dataplane CPU と競合させるか |
| ゲストメモリ | VM が使うホストメモリ | NUMA node、page size、予約方法 |
libvirt の vcpupin は vCPU ごとの affinity、emulatorpin は QEMU emulator thread の affinity を指定します。vCPU だけを固定しても、補助 thread や IRQ が同じ CPU で競合すると、tail latency が不安定になることがあります。
pinning を使う条件
| 状況 | pinning の判断 | 理由 |
|---|---|---|
| 低遅延、NFV、高 packet rate | 候補になる | スケジューリング待ちと実行場所の変動を抑えたい |
| 性能保証が必要な専有 VM | 候補になる | 他 VM との CPU 競合範囲を限定したい |
| 一般的な業務 VM | 測定して判断する | scheduler の柔軟な共有が有利な場合がある |
| 高い CPU overcommit が前提 | 固定範囲を慎重に決める | 固定先が混雑すると空いている CPU を使えない |
| ライブマイグレーションを頻繁に使う | 移行先も含めて設計する | CPU ID と topology がホストごとに異なる可能性がある |
pinning の目的は平均 CPU 使用率を下げることではありません。run queue、steal、vCPU wait、p95 / p99 latency、packet drop など、改善したい指標を先に決めます。
ホストの CPU topology と NUMA を確認する
CPU 番号だけを見て固定先を決めず、socket、core、SMT sibling、NUMA node の対応を確認します。SMT sibling を別々の専有コアとして数えると、同じ物理 core の実行資源を競合させる可能性があります。
lscpu -e=CPU,NODE,SOCKET,CORE,ONLINE
lscpu
numactl --hardware
virsh nodeinfo
virsh capabilities
lspci -tv
cat /proc/interrupts- 対象 CPU がどの socket、core、NUMA node に属するか
- SMT sibling を同じ VM に渡すか、housekeeping 用に残すか
- NIC、NVMe、HBA がどの NUMA node に近いか
- ホスト OS、libvirt、QEMU、IRQ、監視用に使う CPU を残しているか
- 他 VM と固定 CPU が重複していないか
VM の現在の affinity を確認する
設定 XML と実行中の affinity を分けて確認します。永続設定だけ変更した場合は次回起動まで反映されず、live 設定だけ変更した場合は再起動後に戻る可能性があります。
virsh vcpuinfo vm01
virsh vcpupin vm01
virsh emulatorpin vm01
virsh numatune vm01
virsh domstats vm01 --cpu-total --vcpu --memory
virsh dumpxml vm01virsh vcpupin は vCPU ごとの許可 CPU、virsh emulatorpin は emulator thread、virsh numatune は NUMA memory policy を確認します。実際にどの CPU で実行されたかは、ホストのプロセス・thread 統計や負荷計測も合わせて見ます。
libvirt XML で vCPU と emulator を分ける
次は 2 vCPU の VM を、同じ NUMA node に属するホスト CPU 2 と 3 へ固定し、emulator thread を CPU 0 と 1 へ分ける例です。CPU ID は例なので、実際の topology と予約方針に合わせて変更します。
<vcpu placement='static'>2</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='2'/>
<vcpupin vcpu='1' cpuset='3'/>
<emulatorpin cpuset='0-1'/>
</cputune>
<numatune>
<memory mode='strict' nodeset='0'/>
</numatune>strict は指定 node 以外からのメモリ割り当てを許可しないため、対象 node の空きメモリが不足すると VM を起動できない可能性があります。可用性を優先する場合は、preferred や他の memory policy を含めて判断します。
QEMU では、vcpupin と emulatorpin は emulator 起動後に適用されます。起動の初期段階では、指定外 CPU の使用が統計に現れる可能性があります。
NUMA memory policy と実メモリ配置を確認する
NUMA では、CPU が属する node に近いメモリを使う方が、一般に遅延と帯域の面で有利です。ただし、CPU affinity だけではゲストメモリの node を保証しません。libvirt の numatune とホストの cpuset 制約、空きメモリを合わせて確認します。
numastat
numastat -c
virsh numatune vm01
virsh dommemstat vm01
grep -i huge /proc/meminfoLinux の NUMA 統計では、numa_hit、numa_miss、numa_foreign、local_node、other_node などを確認できます。単一の値だけで判断せず、ワークロード実行中の増加量と CPU affinity を対応付けます。
HugePages は node 配置と予約量を一緒に見る
HugePages は TLB miss を減らす手段ですが、有効にするだけで正しい NUMA node へ配置されるわけではありません。page size、node ごとの予約量、VM のメモリ量、起動順序、他 VM との競合を確認します。
| 確認項目 | 判断 |
|---|---|
| page size | 2 MiB または 1 GiB を CPU とワークロードの要件から選ぶ |
| node ごとの予約 | vCPU を置く node に必要量が確保されているか |
| 起動失敗 | strict policy と空き HugePages の不足を確認する |
| Transparent HugePages | 明示的 HugePages と同じ効果を前提にせず測定する |
| ロックメモリ | DPDK や VFIO を使う場合の制限と権限を確認する |
IRQ、I/O thread、デバイスの NUMA node を確認する
SR-IOV、PCI Passthrough、vhost-user、DPDK では、vCPU とメモリだけでなく、NIC、VF、PMD thread、vhost thread、IRQ の配置が影響します。デバイスが node 1 にあるのに VM の vCPU とメモリを node 0 へ固定すると、I/O が NUMA interconnect をまたぐ可能性があります。
- PCIe デバイスと NUMA node の対応
- IRQ affinity と irqbalance の方針
- QEMU emulator thread と I/O thread の CPU
- vhost、OVS-DPDK、PMD thread の CPU
- VM の vCPU とメモリがデバイスに近い node にあるか
適用前後を同じ条件で比較する
| 段階 | 実施すること |
|---|---|
| 1 | 変更前の vcpupin、emulatorpin、numatune、CPU topology を保存する |
| 2 | 同じワークロードで throughput、p95 / p99、CPU wait、drop を測定する |
| 3 | vCPU、emulator、memory policy を一つずつ変更する |
| 4 | ホストとゲストの両方で再測定する |
| 5 | 性能、可搬性、障害復旧、他 VM への影響を評価する |
| 6 | 改善しない場合は設定を戻し、別のボトルネックを確認する |
よくある失敗
| 失敗 | 問題 | 確認すること |
|---|---|---|
| vCPU だけを固定する | emulator、I/O、IRQ が同じ CPU で競合する | 補助 thread と housekeeping CPU |
| CPU ID を別ホストへそのまま移す | topology と NUMA node が一致しない | 移行先ごとの CPU map |
| 全 CPU を VM へ渡す | ホスト処理の実行場所が不足する | OS、QEMU、IRQ 用の予約 CPU |
| HugePages だけを有効にする | NUMA memory policy が未設定 | node ごとの予約量と numatune |
| live 設定だけ変更する | 再起動後に元へ戻る | live と config の両方 |
| 平均値だけを見る | tail latency と jitter の悪化を見落とす | 分位点、drop、再送、待ち時間 |
まとめ
VM の CPU / NUMA 設計では、vCPU、ホスト CPU、emulator thread、I/O thread、IRQ、ゲストメモリ、PCIe デバイスを同じ topology 上で対応付けます。CPU pinning は実行場所を限定できますが、共有の柔軟性と移行性を減らします。
最初に host topology と現在の affinity を記録し、改善したい指標を測定します。その後で vcpupin、emulatorpin、numatune、HugePages を一つずつ変更し、性能だけでなくホスト余力、他 VM、障害復旧への影響まで再評価します。

