Red Hat Training

A Red Hat training course is available for Red Hat Enterprise Linux

6.3. 推奨設定

Red Hat Enterprise Linux ではシステムの設定に役立つツールをいくつか提供しています。このセクションではこうしたツールを簡単に説明し、Red Hat Enterprise Linux 7 でそのツールを使用してプロセッサー関連の問題を解決する例を紹介します。

6.3.1. カーネルティックタイムの設定

Red Hat Enterprise Linux 7 はデフォルトではティックレスカーネルを使用します。このカーネルは節電のためアイドル状態の CPU には割り込みを行わせないようにして新しいプロセッサーがディープスリープ状態に入れるようにします。
Red Hat Enterprise Linux 7 では動的ティクレスのオプションも用意されています (デフォルトでは無効)。高性能な計算やリアルタイムの計算など待ち時間に厳しい制約のある作業に便利です。
特定のコアで動的なティックレス動作を有効にするには、nohz_full パラメーターを使用して、カーネルコマンドラインでそれらのコアを指定します。16 コアシステムなら nohz_full=1-15 と指定すると動的ティックレス動作が 1 から 15 までのコアで有効になり時間管理はすべて未指定のコアに移動されます (コア 0)。この動作は、システムの起動時に一時的に有効にするか、/etc/default/grub ファイルの GRUB_CMDLINE_LINUX オプションを使用して永続的に有効にできます。永続的な動作を得るには、grub2-mkconfig -o /boot/grub2/grub.cfg コマンドを実行して設定を保存します。
動的ティックレス動作を有効にする場合は手作業による管理が必要になります。
  • システムが起動したら手作業で rcu スレッドを待ち時間に制約のないコアに移動します。この場合コア 0 に移動しています。
    # for i in `pgrep rcu[^c]` ; do taskset -pc 0 $i ; done
  • カーネルコマンドラインで isolcpus パラメーターを使用して、特定のコアをユーザー空間タスクから分離します。
  • オプションでカーネルの write-back bdi-flush スレッドの CPU 親和性を housekeeping コアに設定することができます。
    echo 1 > /sys/bus/workqueue/devices/writeback/cpumask
動的ティックレス設定が正しく動作しているか次のコマンドを実行して確認します。stress には 1 秒は CPU で実行しているプログラムを入力します。
# perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
stress の代替のひとつに、while :; do d=1; done などを実行するスクリプトが考えられます。
デフォルトのカーネルタイマー設定は、ビジーな CPU の 1000 ティックを示しています。
# perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
1000 irq_vectors:local_timer_entry
動的ティックレスカーネルを設定すると、代わりに 1 ティックが表示されるはずです。
# perf stat -C 1 -e irq_vectors:local_timer_entry taskset -c 1 stress -t 1 -c 1
1 irq_vectors:local_timer_entry

6.3.2. ハードウェアパフォーマンスポリシーの設定 (x86_energy_perf_policy)

x86_energy_perf_policy ツールを使用すると、管理者はパフォーマンスとエネルギー効率の相対的な重要度を定義できます。この情報は、パフォーマンスと電力消費効率の間でトレードオフするオプションを選択すると、この機能をサポートするプロセッサーに影響を与えるために使用できます。
デフォルトでは、パフォーマンス モードのすべてのプロセッサーで動作します。CPUID.06H.ECX.bit3 の有無によって示され、root 権限で実行する必要があります。
x86_energy_perf_policy は、kernel-tools パッケージで提供されます。x86_energy_perf_policy の使用方法は、「x86_energy_perf_policy」 または man ページを参照してください。
$ man x86_energy_perf_policy

6.3.3. taskset でプロセスの親和性を設定

taskset ツールは、util-linux パッケージで提供されます。taskset を使用すると、管理者は実行中のプロセスのプロセッサーアフィニティーを取得して設定したり、指定のプロセッサーアフィニティーでプロセスを起動したりできます。
重要
taskset はローカルメモリー割り当てを保証しません。ローカルメモリー割り当てによる追加のパフォーマンス上の利点が必要な場合、Red Hat は taskset の代わりに numactl を使用することを推奨します。
taskset の詳細は、「taskset」 または man ページを参照してください。
$ man taskset

6.3.4. numactl で NUMA 親和性を管理

管理者は numactl を使用して、指定したスケジューリングまたはメモリー配置ポリシーでプロセスを実行できます。numactl は、共有メモリーセグメントまたはファイルに永続ポリシーを設定し、プロセスのプロセッサーアフィニティーとメモリーアフィニティーを設定することもできます。
NUMA トポロジーのシステムではプロセッサーのメモリーへのアクセス速度はプロセッサーとメモリーバンク間の距離が離れるほど低下していきます。したがってパフォーマンス重視のアプリケーションの場合はメモリー割り当てをできるだけ近距離のメモリーバンクから行うよう設定することが重要となります。メモリーと CPU は同じ NUMA ノードのものを使用するのが最適です。
パフォーマンスに敏感するマルチスレッドアプリケーションは、特定のプロセッサーではなく特定の NUMA ノードで実行されるように設定することで、メリットが得られます。これが適切なかどうかは、システムやアプリケーションの要件によって異なります。複数のアプリケーションスレッドが同じキャッシュされたデータにアクセスする場合、同じプロセッサーでこれらのスレッドを実行するように設定することが適切な場合があります。ただし、異なるデータにアクセスし、キャッシュする複数のスレッドが同じプロセッサーで実行される場合、各スレッドは、以前のスレッドによってアクセスされたキャッシュデータをエビクトする可能性があります。つまり、それぞれのスレッドがキャッシュをミスするため、メモリーからデータを取り出すため実行時間を浪費してキャッシュの入れ替えを行います。「perf」 に記載されている perf ツールを使用して、過剰な数のキャッシュミスの有無を確認できます。
numactl は プロセッサーとメモリーアフィニティーの管理に役立つ多くのオプションを提供します。詳細については、「numastat」 または man ページをご覧ください。
$ man numactl
注記
numactl パッケージには libnuma ライブラリーが含まれています。このライブラリーは、カーネルがサポートする NUMA ポリシーにシンプルなプログラミングインターフェイスを提供し、numactl アプリケーションよりもきめ細かい調整に使用できます。詳細は man ページをご覧ください。
$ man numa

6.3.5. numad を使用した NUMA 親和性の自動管理

numad は自動 NUMA アフィニティー管理デーモンです。NUMA リソースの割り当てと管理を動的に改善するために、システム内の NUMA トポロジーとリソースの使用状況を監視します。
numad は、さまざまなジョブ管理システムでクエリーできるプレプレースメントアドバイスサービスも提供し、それらのプロセスに対する CPU およびメモリーリソースの初期バインディングのサポートを提供します。numad が実行可能ファイルとして実行しているのかサービスとして実行しているのかに関わらずこのプレプレースメントアドバイスを利用することができます。
numad の使い方については、「numad」 または man ページを参照してください。
$ man numad

6.3.6. スケジューリングポリシーの調整

Linux スケジューラーではスレッドの実行場所と実行期間を決定する数種類のスケジューリングポリシーを実装しています。大きく分けると通常ポリシーとリアルタイムポリシーの 2 種類のカテゴリーに分けられます。通常スレッドは、通常の優先度のタスクに使用されます。リアルタイムポリシーは、中断なしで完了する必要のある時間的制約のあるタスクに使用されます。
リアルタイムスレッドは、タイムスライスの対象ではありません。つまり、ブロックされる、終了する、自発的に停止する、より優先度の高いスレッドが取って代わるなどが起こるまで実行されます。最も優先度の低いリアルタイムスレッドは、通常のポリシーを持つスレッドの前にスケジュールされます。

6.3.6.1. スケジューリングポリシー

6.3.6.1.1. SCHED_FIFO を使用した静的優先度のスケジューリング
SCHED_FIFO (静的優先度スケジューリングとも呼ばれる)は、各スレッドに固定の優先度を定義するリアルタイムポリシーです。このポリシーを使用するとイベントの応答時間を改善し待ち時間を低減させることができるため、長期間は実行しない時間的制約のあるタスク対しての使用が推奨されます。
SCHED_FIFO を使用すると、スケジューラーは SCHED_FIFO スレッドの一覧を優先度順にスキャンし、実行準備ができているスレッドで最も優先度の高いスレッドをスケジュールします。SCHED_FIFO スレッドの優先度レベルは 1 から 99 までの任意の整数にすることができます。この場合、99 は最も高い優先度として処理されます。Red Hat では最初は低い順位で使用を開始し、待ち時間に関する問題が見つかった場合にのみ徐々に優先度を上げていく方法を推奨しています。
警告
リアルタイムスレッドはタイムスライスに依存しないため、Red Hat では優先度 99 の設定は推奨していません。この優先度を使用するとプロセスは移行スレッドや監視スレッドと同じ優先レベルにすることになってしまいます。このスレッドが演算ループに陥りブロックされるとスレッドの実行は不可能になります。単一のプロセッサーを持つシステムは、この状況では最終的にハングします。
管理者は SCHED_FIFO 帯域幅を制限して、リアルタイムアプリケーションのプログラマーがプロセッサーを独占するリアルタイムタスクを開始しないようにすることができます。
/proc/sys/kernel/sched_rt_period_us
上記のパラメーターはプロセッサー帯域幅の 100% とみなされるべき時間をマイクロ秒で定義します。デフォルト値は 1000000 DHEs (1 秒)です。
/proc/sys/kernel/sched_rt_runtime_us
上記のパラメーターはリアルタイムスレッドの実行に当てられる時間をマイクロ秒で定義します。デフォルト値は 950000 DHEs または 0.95 秒です。
6.3.6.1.2. SCHED_RR を使用したラウンドロビン方式の優先度スケジューリング
SCHED_RR は、SCHED_FIFO のラウンドロビン版です。このポリシーは、複数のスレッドを同じ優先レベルで実行する必要がある場合に役に立ちます。
SCHED_FIFO と同様に、SCHED_RR は各スレッドに固定の優先度を定義するリアルタイムポリシーです。スケジューラーは、すべての SCHED_RR スレッドの一覧を優先度順にスキャンし、実行準備ができているスレッドで最も優先度の高いスレッドをスケジュールします。ただし、SCHED_FIFO とは異なり、同じ優先度を持つスレッドは、特定のタイムスライス内でラウンドロビン方式にスケジュールされます。
このタイムスライスの値は、sched_rr_timeslice_ms カーネルパラメーター(/proc/sys/kernel/sched_rr_timeslice_ms)を使用してミリ秒単位で設定できます。最も小さい値は 1 ミリ秒になります。
6.3.6.1.3. SCHED_OTHER を使用した通常のスケジュール
SCHED_OTHER は、Red Hat Enterprise Linux 7 のデフォルトのスケジューリングポリシーです。このポリシーは Completely Fair Scheduler (CFS) を使用して、このポリシーでスケジュールされているすべてのスレッドへの公平プロセッサーアクセスを許可します。多量のスレッドやデータの処理が重要な場合にはこのポリシーの方が長い期間で見ると効率的なスケジュールになります。
このポリシーが使用されると、スケジューラーは各プロセススレッドの niceness 値に基づいて動的な優先順位リストを作成します。管理者側でプロセスの niceness 値を変更することはできますがスケジューラーの動的な優先リストを直接変更することはできません。
プロセスの niceness の変更に関する詳細は、Red Hat Enterprise Linux 7 システム管理者のガイドを参照してください。

6.3.6.2. CPU の分離

isolcpus ブートパラメーターを使用して、スケジューラーから 1 つ以上の CPU を分離できます。これによりスケジューラーがその CPU 上にあるユーザー領域のスレッドをスケジュールしないよう保護します。
CPU を分離させた場合は CPU 親和性のシステムコールか numactl コマンドを使用してその CPU に手動でプロセスを割り当てなければなりません。
3 番目の CPU と 6 番目から 8 番目の CPU を分離させる場合は以下をカーネルのコマンドラインに追加します。
isolcpus=2,5-7
Tuna ツールを使用して CPU を分離することもできます。Tuna は、起動時にだけでなく、いつでも CPU を分離できます。ただし、この方法は isolcpus パラメーターとは大きく異なるため、現在 isolcpus に関連するパフォーマンスの向上は実現しません。ツールの詳細については、「Tuna を使用した CPU、スレッド、割り込みの親和性の設定」 を参照してください。

6.3.7. AMD64 および Intel 64 での割り込み親和性の設定

割り込み要求には、割り込み要求を処理するプロセッサーを定義する関連するアフィニティープロパティー smp_affinity があります。アプリケーションのパフォーマンスを向上させるには、割り込みの親和性とプロセスの親和性を同じプロセッサーまたは同じコアにあるプロセッサーに割り当てます。これにより、指定された割り込みとアプリケーションスレッドがキャッシュラインを共有できるようになります。
重要
本セクションでは、AMD64 および Intel 64 のアーキテクチャーのみを説明します。割り込み親和性の設定は、他のアーキテクチャーとは大きく異なります。

手順6.1 割り込みの自動分散

  • BIOS が NUMA トポロジーをエクスポートする場合、irqbalance サービスは、サービスを要求するハードウェアに対してローカルとなるノードで割り込み要求を自動的に処理できます。
    irqbalance の設定に関する詳細は、「irqbalance」 を参照してください。

手順6.2 割り込みの手動分散

  1. 設定する割り込み要求に対応するデバイスを確認します。
    Red Hat Enterprise Linux 7.5 より、システムが最適な割り込み親和性を特定のデバイスおよびそれらのドライバーに自動的に設定するようになりました。親和性を手動で設定する必要はありません。これは以下のデバイスを対象とします。
    • be2iscsi ドライバーを使用するデバイス
    • NVMe PCI デバイス。
  2. プラットフォームのハードウェア仕様を見つけます。システムのチップセットが割り込みの分散に対応しているかどうかを確認します。
    • その場合には、以下の手順に従って割り込み配信を設定できます。
      また、チップセットが割り込みの分散に使用するアルゴリズムを確認してください。BIOS によっては割り込み配信を設定するオプションがあります。
    • そうでない場合、チップセットは常にすべての割り込みを 1 つの静的な CPU にルーティングします。使用される CPU を設定することはできません。
  3. システムで、どの APIC (Advanced thumbnailer Interrupt Controller) モードが使用されているかを確認します。
    物理以外のフラットモード(フラット)のみが、複数の CPU への割り込みの分散をサポートします。このモードは、CPU が最大 8 のシステムでのみ利用できます。
    $ journalctl --dmesg | grep APIC
    コマンド出力で、以下を行います。
    • システムが flat 以外のモードを使用している場合は、APIC ルーティングの物理フラットへの設定 と同様の行が表示されます。
    • このようなメッセージが表示されない場合は、システムが flat モードを使用します。
    システムが x2apic モードを使用している場合は、ブートローダー設定のカーネルコマンドラインに nox2apic オプションを追加して無効にできます。
  4. smp_affinity マスクを計算します。
    smp_affinity 値は、システム内のすべてのプロセッサーを表す 16 進数ビットマスクとして保存されます。各ビットは異なる CPU を設定します。最も大きなビットは CPU 0 です。
    マスクのデフォルト値は f です。つまり、割り込み要求はシステム内のどのプロセッサーでも処理できます。この値を 1 に設定すると、プロセッサー 0 のみが割り込みを処理できます。

    手順6.3 マスクの計算

    1. バイナリーでは、割り込みを処理する CPU に 1 の値を使用します。
      たとえば、CPU 0 および CPU 7 で割り込みを処理するには、0000000010000001 をバイナリーコードとして使用します。

      表6.1 CPU のバイナリービット

      CPU1514131211109876543210
      バイナリー0000000010000001
    2. バイナリーコードを 16 進数に変換します。
      たとえば、Python を使用してバイナリーコードを変換するには、次のコマンドを実行します。
      >>> hex(int('0000000010000001', 2))
      
      '0x81'
      
    プロセッサーが 32 個を超えるシステムでは、32 ビットグループごとに smp_affinity 値を区切る必要があります。たとえば、64 プロセッサーシステムの最初の 32 プロセッサーのみが割り込み要求に対応するようにするには、0xffffffff,00000000 を使用します
  5. smp_affinity マスクを設定します。
    特定の割り込み要求の割り込み親和性の値は、関連付けられた /proc/irq/irq_number/smp_affinity ファイルに保存されます。
    計算されたマスクを関連ファイルに書き込みます。
    # echo mask > /proc/irq/irq_number/smp_affinity

関連情報

  • 割り込みステアリングをサポートするシステムでは、割り込み要求の smp_affinity プロパティーを変更するとハードウェアが設定され、カーネルからの介入なしに特定のプロセッサーで割り込みを処理する決定がハードウェアレベルで行われます。
    割り込みステアリングについては、9章ネットワーク を参照してください。

6.3.8. Tuna を使用した CPU、スレッド、割り込みの親和性の設定

Tuna は実行中のプロセスを調整するためのツールで、CPU、スレッド、および割り込みアフィニティーを制御できます。また、制御できるエンティティーのタイプごとに多くのアクションも提供します。Tuna の詳細は、4章Tuna を参照してください。