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 ファイルで永続的に有効にすることもできます。永続的に有効にする場合は 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 ツールを使用するとパフォーマンスと電力消費効率のバランスを指定することができます。パフォーマンスまたは電力消費効率どちらをどの程度犠牲にするかのオプションを選択すると、機能に対応するプロセッサーを動作させるためこの情報が使用されます。
デフォルトではツールは全プロセッサー上で performance モードで動作します。プロセッサーのサポートを必要とします。CPUID.06H.ECX.bit3 の表示があればサポートされています。実行する場合は root 権限で行ってください。
x86_energy_perf_policykernel-tools パッケージで提供されます。x86_energy_perf_policy の使い方については「x86_energy_perf_policy」を参照するか man ページをご覧ください。
$ man x86_energy_perf_policy

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

taskset ツールは util-linux パッケージで提供されます。Taskset を使用すると実行中プロセスのプロセッサー親和性を読み出して設定したり、指定プロセッサー親和性でプロセスを起動することができるようになります。

重要

taskset はローカルのメモリー割り当てを保証しません。ローカルメモリー割り当てによりパフォーマンスを向上させる必要がある場合は 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 μs もしくは 1 秒です。
/proc/sys/kernel/sched_rt_runtime_us
上記のパラメーターはリアルタイムスレッドの実行に当てられる時間をマイクロ秒で定義します。デフォルト値は 950000 μs もしくは 0.95 秒です。
6.3.6.1.2. SCHED_RR を使ったラウンドロビン方式の優先度スケジューリング
SCHED_RRSCHED_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 を使った通常のスケジュール
Red Hat Enterprise Linux 7 では SCHED_OTHER がデフォルトのスケジューリングポリシーになります。CFS (Completely Fair Scheduler) を使ってこのポリシーでスケジュールされているスレッドすべてに対してプロセッサーへの公平なアクセスを提供します。多量のスレッドやデータの処理が重要な場合にはこのポリシーの方が長い期間で見ると効率的なスケジュールになります。
このポリシーを使用するとスケジューラーは各プロセススレッドの niceness 値に基づいて動的な優先リストを作成します。管理者側でプロセスの niceness 値を変更することはできますがスケジューラーの動的な優先リストを直接変更することはできません。
プロセスの niceness の変更に関する詳細は、『Red Hat Enterprise Linux 7 システム管理者のガイド』を参照してください。

6.3.6.2. CPU の分離

isolcpus 起動パラメーターを使用するとスケジューラーから CPU を切り離すことができます。これによりスケジューラーがその CPU 上にあるユーザー領域のスレッドをスケジュールしないよう保護します。
CPU を分離させた場合は CPU 親和性のシステムコールか numactl コマンドを使ってその CPU に手動でプロセスを割り当てなければなりません。
3 番目の CPU と6 番目から 8 番目の CPU を分離させる場合は以下をカーネルのコマンドラインに追加します。
isolcpus=2,5-7
また CPU の分離は Tuna ツールを使っても行うことができます。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) モードが使用されているかを確認します。
    非物理フラットモード (flat) のみが複数の CPU への割り込みの分散に対応します。このモードは、9 個以上の CPU を持つシステムでは利用できません。
    $ journalctl --dmesg | grep APIC
    コマンド出力で、以下を行います。
    • システムが flat 以外のモードを使用する場合は、Setting APIC routing to physical flat と同様の行を確認できます。
    • このようなメッセージが表示されない場合、システムは 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 を参照してください。

このページには機械翻訳が使用されている場合があります (詳細はこちら)。