Show Table of Contents
9.3. libvirt の NUMA チューニング
通常 NUMA システム上の最高のパフォーマンスは、単一 NUMA ノードのリソース量にゲストのサイズを制限することによって達成できます。複数の NUMA ノード間でリソースを不必要に分割することを避けてください。
numastat ツールを使用して、プロセスおよびオペレーティングシステムの NUMA ノードごとのメモリー統計を表示します。
以下の例では、
numastat ツールは、複数の NUMA ノード間のメモリー配置が最適ではない 4 つの仮想マシンを示しています。
# numastat -c qemu-kvm
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Node 4 Node 5 Node 6 Node 7 Total
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
51722 (qemu-kvm) 68 16 357 6936 2 3 147 598 8128
51747 (qemu-kvm) 245 11 5 18 5172 2532 1 92 8076
53736 (qemu-kvm) 62 432 1661 506 4851 136 22 445 8116
53773 (qemu-kvm) 1393 3 1 2 12 0 0 6702 8114
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
Total 1769 463 2024 7462 10037 2672 169 7837 32434
numad を実行して、ゲストの CPU とメモリーリソースを自動的に調整します。
次に
numastat -c qemu-kvm を再び実行して実行中の numad の結果を表示します。以下の出力は、リソースが調整されていることを示しています。
# numastat -c qemu-kvm
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Node 2 Node 3 Node 4 Node 5 Node 6 Node 7 Total
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
51747 (qemu-kvm) 0 0 7 0 8072 0 1 0 8080
53736 (qemu-kvm) 0 0 7 0 0 0 8113 0 8120
53773 (qemu-kvm) 0 0 7 0 0 0 1 8110 8118
59065 (qemu-kvm) 0 0 8050 0 0 0 0 0 8051
--------------- ------ ------ ------ ------ ------ ------ ------ ------ -----
Total 0 0 8072 0 8072 0 8114 8110 32368
注記
-c を指定して numastat を実行することにより、簡易出力が表示されます。-m オプションを追加することにより、システム全体でのメモリー情報のノード別の内訳が出力に追加されます。詳細は、numastat の man ページを参照してください。
9.3.1. ホスト NUMA ノードごとのメモリー監視
nodestats.py スクリプトを使用して、ホスト上の各 NUMA ノードの合計メモリーと空きメモリーを報告できます。また、このスクリプトは実行中の各ドメインの特定のホストノードに対して厳密にバインドされているメモリーの量をレポートします。以下は例になります。
# /usr/share/doc/libvirt-python-2.0.0/examples/nodestats.py
NUMA stats
NUMA nodes: 0 1 2 3
MemTotal: 3950 3967 3937 3943
MemFree: 66 56 42 41
Domain 'rhel7-0':
Overall memory: 1536 MiB
Domain 'rhel7-1':
Overall memory: 2048 MiB
Domain 'rhel6':
Overall memory: 1024 MiB nodes 0-1
Node 0: 1024 MiB nodes 0-1
Domain 'rhel7-2':
Overall memory: 4096 MiB nodes 0-3
Node 0: 1024 MiB nodes 0
Node 1: 1024 MiB nodes 1
Node 2: 1024 MiB nodes 2
Node 3: 1024 MiB nodes 3
この例は、それぞれに約 4GB の合計 RAM (
MemTotal) が含まれる 4 つのホスト NUMA ノードを示しています。ほぼすべてのメモリーが各ドメインで消費されています (MemFree)。4 つのドメイン (仮想マシン) が実行されています。ドメイン 'rhel7-0' には、特定のホスト NUMA ノードにピニングされていない 1.5GB メモリーがあります。ドメイン 'rhel7-2' には 4GB メモリーがあり、4 つの NUMA ノードはホストノードに 1:1 でピニングされています。
ホスト NUMA ノードの統計値を出力するには、ご使用の環境用の
nodestats.py スクリプトを作成します。サンプルスクリプトは、/usr/share/doc/libvirt-python-version/examples/nodestats.py の libvirt-python パッケージファイルにあります。rpl -ql libvirt-python コマンドを使用して、スクリプトへの実際のパスを表示することができます。
9.3.2. NUMA の vCPU ピニング
vCPU ピニングは、ベアメタルシステム上でのタスクを固定する場合と同様の利点を提供します。vCPU はホストオペレーティングシステム上でユーザー領域タスクとして実行されるため、ピニングによりキャッシュ効率が増大します。この 1 つの例となるのは、すべての vCPU スレッドが同じ物理ソケット上で実行されるため、それらが L3 キャッシュドメインを共有する環境です。
注記
Red Hat Enterprise Linux バージョン 7.0 から 7.2 では、アクティブな vCPU のみをピニングできます。ただし、Red Hat Enterprise Linux 7.3 では、非アクティブな vCPU のピニングも実行できます。
vCPU ピニングを
numatune と組み合わせることにより、NUMA ミスが回避されます。NUMA ミスによるパフォーマンスへの影響は大きく、通常はパフォーマンスが 10% 以上低下します。vCPU ピニングと numatune は一緒に設定する必要があります。
仮想マシンがストレージまたはネットワーク I/O タスクを実行している場合、I/O アダプターに物理的に接続されている同じ物理ソケットにすべての vCPU とメモリーを固定すると効果的です。
注記
lstopo ツールを使用して NUMA トポロジーを仮想化することができます。さらに、このツールは vCPU が同じ物理ソケットのコアにバインドされていることを検証する際にも役立ちます。lstopo についての詳細は、以下のナレッジベースの記事を参照してください。https://access.redhat.com/site/solutions/62879
重要
ピニングは、物理コアより多くの vCPU がある場合には複雑化の原因になります。
以下の XML 設定例では、ドメインプロセスが物理 CPU 0 から 7 に固定されています。vCPU スレッドはそれぞれ独自の cpuset に固定されています。たとえば、vCPU0 は物理 CPU0 に、vCPU1 は物理 CPU 1 に固定されています。
<vcpu cpuset='0-7'>8</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='2'/>
<vcpupin vcpu='3' cpuset='3'/>
<vcpupin vcpu='4' cpuset='4'/>
<vcpupin vcpu='5' cpuset='5'/>
<vcpupin vcpu='6' cpuset='6'/>
<vcpupin vcpu='7' cpuset='7'/>
</cputune>
vcpu と vcpupin タグ間には直接的な関係があります。vcpupin オプションを指定しないと、値は自動的に確定され、親となる vcpu タグオプションから継承されます。以下の設定では vcpu 5 の
<vcpupin> がないことを示しています。したがって、vCPU5 は親タグの <vcpu> で指定しているように物理 CPU の 0 から 7 に固定される可能性があります。
<vcpu cpuset='0-7'>8</vcpu>
<cputune>
<vcpupin vcpu='0' cpuset='0'/>
<vcpupin vcpu='1' cpuset='1'/>
<vcpupin vcpu='2' cpuset='2'/>
<vcpupin vcpu='3' cpuset='3'/>
<vcpupin vcpu='4' cpuset='4'/>
<vcpupin vcpu='6' cpuset='6'/>
<vcpupin vcpu='7' cpuset='7'/>
</cputune>重要
最適なパフォーマンスを確実に実現するには、
<vcpupin>、<numatune>、および <emulatorpin> を共に設定する必要があります。<numatune> タグの詳細は、「ドメインプロセス」を参照してください。<emulatorpin> タグの詳細は、「emulatorpin の使用」を参照してください。
9.3.3. ドメインプロセス
Red Hat Enterprise Linux で規定されている通り、libvirt ではドメインプロセスのメモリーバインディングのポリシー設定に libnuma を使用します。これらのポリシーのノードセットは、static (ドメイン XML に指定) か、または auto (numad のクエリーで設定) のいずれかに設定することができます。
<numatune> タグ内にこれらを設定する方法は、以下の XML 設定例を参照してください。
<numatune>
<memory mode='strict' placement='auto'/>
</numatune>
<numatune>
<memory mode='strict' nodeset='0,2-3'/>
</numatune>
libvirt は、sched_setaffinity(2) を使ってドメインプロセスの CPU バインディングポリシーを設定します。cpuset オプションは static (ドメイン XML で指定) または auto (numad のクエリーで設定) にすることができます。
<vcpu> タグ内にこれらを設定する方法については、以下の XML 設定例を参照してください。
<vcpu placement='auto'>8</vcpu>
<vcpu placement='static' cpuset='0-10,ˆ5'>8</vcpu>
<vcpu> および <numatune> に使用する配置モード間には暗黙的な継承ルールがあります。
<numatune>の配置モードは<vcpu>と同じ配置モードにデフォルト設定されます。または、<nodeset>を指定した場合はstatic に設定されます。- 同様に、
<vcpu>の配置モードでは<numatune>と同じ配置モードにデフォルト設定されます。または、<cpuset>を指定した場合は static に設定されます。
つまり、ドメインプロセスのメモリーチューニングと CPU チューニングは別々に指定し、定義することができますが、同時に相互の配置モードに依存するように設定することもできます。
さらに、起動時にすべての vCPU を固定せずに選択した数の vCPU を起動できるように numad を使用してシステムを設定することもできます。
たとえば、32 の vCPU が設定されたシステムで 8 つの vCPU のみを有効にするには、以下のように XML を設定します。
<vcpu placement='auto' current='8'>32</vcpu>
注記
vcpu および numatune に関する詳細は、http://libvirt.org/formatdomain.html#elementsCPUAllocation および http://libvirt.org/formatdomain.html#elementsNUMATuning を参照してください。
9.3.4. ドメインの vCPU スレッド
ドメインプロセスのチューニングのほかにも、libvirt では XML 設定内の vcpu の各スレッドにピニングポリシーの設定を許可します。
<cputune> タグ内に各 vcpu スレッドのピニングポリシーを設定します。
<cputune>
<vcpupin vcpu="0" cpuset="1-4,ˆ2"/>
<vcpupin vcpu="1" cpuset="0,1"/>
<vcpupin vcpu="2" cpuset="2,3"/>
<vcpupin vcpu="3" cpuset="0,4"/>
</cputune>
このタグでは、libvirt は cgroup または sched_setaffinity(2) のいずれかを使って vcpu スレッドを指定の cpuset に固定しています。
注記
<cputune> の詳細は、以下の URL を参照してください: http://libvirt.org/formatdomain.html#elementsCPUTuning
さらに、単一の NUMA ノードよりも多くの vCPU を持つ仮想マシンをセットアップする必要がある場合、ゲストがホスト上で NUMA トポロジーを検知できるようにホストを設定します。これにより、CPU、メモリーおよび NUMA ノードの 1:1 のマッピングが可能になります。たとえば、これは 4 つの vCPU と 6 GB メモリーを持つゲストと、以下の NUMA 設定を持つホストに適用できます。
4 available nodes (0-3) Node 0: CPUs 0 4, size 4000 MiB Node 1: CPUs 1 5, size 3999 MiB Node 2: CPUs 2 6, size 4001 MiB Node 3: CPUs 0 4, size 4005 MiB
このシナリオでは、以下のドメイン XML 設定を使用します。
<cputune> <vcpupin vcpu="0" cpuset="1"/> <vcpupin vcpu="1" cpuset="5"/> <vcpupin vcpu="2" cpuset="2"/> <vcpupin vcpu="3" cpuset="6"/> </cputune> <numatune> <memory mode="strict" nodeset="1-2"/> </numatune> <cpu> <numa> <cell id="0" cpus="0-1" memory="3" unit="GiB"/> <cell id="1" cpus="2-3" memory="3" unit="GiB"/> </numa> </cpu>
9.3.5. emulatorpin の使用
ドメインプロセスのピニングポリシーを調整する別の方法として、
<cputune> 内で <emulatorpin> タグを使用する方法があります。
<emulatorpin> タグは、エミュレーター (vCPU を含まないドメインのサブセット) が固定されるホスト物理 CPU を指定します。<emulatorpin> タグは、エミュレーターのスレッドプロセスに正確な親和性 (アフィニティー) を設定する方法を提供します。結果として vhost スレッドは物理 CPU およびメモリーの同じサブセットで実行されるため、キャッシュの局所性 (ローカリティー) によるメリットがあります。たとえば、以下のようになります。
<cputune>
<emulatorpin cpuset="1-3"/>
</cputune>注記
Red Hat Enterprise Linux 7 では、NUMA の自動負荷分散はデフォルトで有効にされています。NUMA の自動負荷分散は、vhost-net エミュレータースレッドと vCPU タスクとの連携がより正確に行われるため、
<emulatorpin> を手動で調整する必要性を軽減します。NUMA の自動負荷分散についての詳細は、「NUMA の自動負荷分散」を参照してください。
9.3.6. virsh による vCPU ピニングのチューニング
重要
以下に示すのは説明を目的としたコマンド例です。実際には、使用する環境に適した値を入力する必要があります。
以下の
virsh コマンドの例では、ID が 1 となる vcpu スレッド rhel7 を物理 CPU 2 に固定します。
% virsh vcpupin rhel7 1 2
さらに、
virsh コマンドで 現在の vcpu ピニング設定を取得することもできます。以下は例になります。
% virsh vcpupin rhel7
9.3.7. virsh を使用したドメインプロセスの CPU ピニングのチューニング
重要
以下に示すのは説明を目的としたコマンド例です。実際には、使用する環境に適した値を入力する必要があります。
emulatorpin オプションでは、各ドメインプロセスに関連付けられたスレッドに CPU アフィニティーの設定を適用します。詳細のピニングを実行するには、virsh vcpupin (前述) と virsh emulatorpin の両方を各ゲストに使用する必要があります。例を示します。
% virsh emulatorpin rhel7 3-4
9.3.8. virsh を使用したドメインプロセスのメモリーポリシーのチューニング
ドメインプロセスのメモリーには動的なチューニングを実行できます。以下のコマンド例を参照してください。
% virsh numatune rhel7 --nodeset 0-10
これらのコマンドの詳細な使用例については、
virsh の man ページを参照してください。
9.3.9. ゲスト NUMA トポロジー
ゲスト NUMA トポロジーは、ゲスト仮想マシンの XML の
<cpu> タグ内にある <numa> タグを使って指定できます。以下の例を参照し、値を適宜置き換えてください。
<cpu>
...
<numa>
<cell cpus='0-3' memory='512000'/>
<cell cpus='4-7' memory='512000'/>
</numa>
...
</cpu>
それぞれの
<cell> 要素は NUMA セルまたは NUMA ノードを指定します。cpus は、ノードの一部である CPU または CPU の範囲を指定します。memory はノードメモリーをキビバイト単位で指定します (1024 バイトのブロック)。それぞれのセルまたはノードには、0 から始まる昇順で cellid または nodeid が割り当てられます。
重要
ゲスト仮想マシンの NUMA トポロジーを、CPU ソケット、コアおよびスレッドの設定されたトポロジーで変更する場合、単一ソケットに属するコアとスレッドが同じ NUMA ノードに割り当てられていることを確認します。同じソケットのスレッドまたはコアが異なる NUMA ノードに割り当てられている場合は、ゲストは起動に失敗する可能性があります。
9.3.10. ホストの Huge Page の複数のゲスト NUMA ノードへの割り当て
Red Hat Enterprise Linux 7.1 以降では、ホストからの huge page は複数のゲスト NUMA ノードに割り当てることができます。これにより、ゲストがホストによって割り当てられた huge page を引き続き使用できる一方でゲスト NUMA ノードを必要に応じてホストの NUMA ノードに移行できるため、メモリーのパフォーマンスを最適化できます。
ゲスト NUMA ノードトポロジーを設定した後 (詳細は、「ゲスト NUMA トポロジー」を参照)、ゲスト XML の
<memoryBacking> 要素に huge page サイズおよびゲスト NUMA ノードセットを指定します。page size および unit はホストからの huge page のサイズを参照します。nodeset は huge page が割り当てられるゲスト NUMA ノード (またはいくつかのノード) を指定します。
以下の例では、ゲスト NUMA ノード 0 - 5 (NUMA ノード 4 を除く) は 1 GB の huge page を使用し、ゲスト NUMA ノード 4 は、ホスト上のゲスト NUMA ノードの位置にかかわらず 2 MB の huge page を使用します。ゲストに 1 GB の huge page を使用するには、ホストを 1 GB の huge page を有効にした状態でまず起動しておく必要があります。1 GB の huge page を有効にする方法は、「Huge Page および Transparent Huge Page (THP)」を参照してください。
<memoryBacking>
<hugepages/>
<page size="1" unit="G" nodeset="0-3,5"/>
<page size="2" unit="M" nodeset="4"/>
</hugepages>
</memoryBacking>
これにより、一部のゲスト NUMA ノードを単一のホスト NUMA ノードにマージするものの、huge page の複数の異なるサイズを継続して使用するのが便利な場合に huge page の制御を強化できます。たとえば、ゲスト NUMA ノード 4 および 5 がホストの NUMA ノード 1 に移行する場合でも、それらがいずれも異なるサイズの huge page を引き続き使用する場合などが考えられます。
注記
strict メモリーノードを使用する場合、NUMA ノードに利用可能な huge page が十分にない場合はゲストが起動に失敗します。<numatune> 内の strict メモリーモードオプションの設定例については、「ドメインプロセス」を参照してください。
9.3.11. PCI デバイスの NUMA ノードの局所性 (ローカリティー)
新規の仮想マシンを起動する際、ホスト NUMA トポロジーと PCI デバイスの NUMA ノードとの関連生の両方について理解しておくことは大切です。PCI パススルーが要求される場合、ゲストがメモリーパフォーマンスの最適化に向けて正しい NUMA ノードに固定されるようにするためです。
たとえば、ゲストが NUMA ノード 0-1 に固定するが、その PCI デバイスのいずれかがノード 2 に関連付けられる場合、ノード間のデータ送信にはいくらかの時間がかかります。
Red Hat Enterprise Linux 7.1 以降では、libvirt はゲスト XML で PCI デバイスについての NUMA ノードの局所性 (ローカリティー) をレポートし、管理アプリケーションでの適切なパフォーマンス関連の決定ができるようにします。
この情報は、
/sys/devices/pci*/*/numa_node の sysfs ファイルに表示されます。これらの設定を検証する 1 つの方法は、lstopo ツールを使用して sysfs データをレポートする方法です。
# lstopo-no-graphics
Machine (126GB)
NUMANode L#0 (P#0 63GB)
Socket L#0 + L3 L#0 (20MB)
L2 L#0 (256KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0 + PU L#0 (P#0)
L2 L#1 (256KB) + L1d L#1 (32KB) + L1i L#1 (32KB) + Core L#1 + PU L#1 (P#2)
L2 L#2 (256KB) + L1d L#2 (32KB) + L1i L#2 (32KB) + Core L#2 + PU L#2 (P#4)
L2 L#3 (256KB) + L1d L#3 (32KB) + L1i L#3 (32KB) + Core L#3 + PU L#3 (P#6)
L2 L#4 (256KB) + L1d L#4 (32KB) + L1i L#4 (32KB) + Core L#4 + PU L#4 (P#8)
L2 L#5 (256KB) + L1d L#5 (32KB) + L1i L#5 (32KB) + Core L#5 + PU L#5 (P#10)
L2 L#6 (256KB) + L1d L#6 (32KB) + L1i L#6 (32KB) + Core L#6 + PU L#6 (P#12)
L2 L#7 (256KB) + L1d L#7 (32KB) + L1i L#7 (32KB) + Core L#7 + PU L#7 (P#14)
HostBridge L#0
PCIBridge
PCI 8086:1521
Net L#0 "em1"
PCI 8086:1521
Net L#1 "em2"
PCI 8086:1521
Net L#2 "em3"
PCI 8086:1521
Net L#3 "em4"
PCIBridge
PCI 1000:005b
Block L#4 "sda"
Block L#5 "sdb"
Block L#6 "sdc"
Block L#7 "sdd"
PCIBridge
PCI 8086:154d
Net L#8 "p3p1"
PCI 8086:154d
Net L#9 "p3p2"
PCIBridge
PCIBridge
PCIBridge
PCIBridge
PCI 102b:0534
GPU L#10 "card0"
GPU L#11 "controlD64"
PCI 8086:1d02
NUMANode L#1 (P#1 63GB)
Socket L#1 + L3 L#1 (20MB)
L2 L#8 (256KB) + L1d L#8 (32KB) + L1i L#8 (32KB) + Core L#8 + PU L#8 (P#1)
L2 L#9 (256KB) + L1d L#9 (32KB) + L1i L#9 (32KB) + Core L#9 + PU L#9 (P#3)
L2 L#10 (256KB) + L1d L#10 (32KB) + L1i L#10 (32KB) + Core L#10 + PU L#10 (P#5)
L2 L#11 (256KB) + L1d L#11 (32KB) + L1i L#11 (32KB) + Core L#11 + PU L#11 (P#7)
L2 L#12 (256KB) + L1d L#12 (32KB) + L1i L#12 (32KB) + Core L#12 + PU L#12 (P#9)
L2 L#13 (256KB) + L1d L#13 (32KB) + L1i L#13 (32KB) + Core L#13 + PU L#13 (P#11)
L2 L#14 (256KB) + L1d L#14 (32KB) + L1i L#14 (32KB) + Core L#14 + PU L#14 (P#13)
L2 L#15 (256KB) + L1d L#15 (32KB) + L1i L#15 (32KB) + Core L#15 + PU L#15 (P#15)
HostBridge L#8
PCIBridge
PCI 1924:0903
Net L#12 "p1p1"
PCI 1924:0903
Net L#13 "p1p2"
PCIBridge
PCI 15b3:1003
Net L#14 "ib0"
Net L#15 "ib1"
OpenFabrics L#16 "mlx4_0"
この出力は以下の点を示しています。
- NIC
em*およびディスクsd*は NUMA ノード 0 およびコア 0、2、4、6、8、10、12、14 に接続されます。 - NIC
p1*およびib*は NUMA ノード 1 およびコア 1、3、5、7、9、11、13、15 に接続されます。

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.