29.6. VOD のチューニング

29.6.1. VDO のチューニングの概要

データベースやその他複雑なソフトウェアのチューニングと同様、VDO のチューニングには、壮大なシステム制限の妥協が関与するため、いくらか実験が必要となります。VDO のチューニングに利用できるプライマリコントロールは、さまざまなタイプの作業、これらのスレッドの GPU 親和性の設定、キャッシュ設定に割り当てられたスレッドの数です。

29.6.2. VDO アーキテクチャの背景

VDO カーネルドライバーはマルチスレッドで、複数の同時 I/O リクエストにわたり処理コストを焼却することでパフォーマンスを向上します。1 つのスレッドに I/O リクエストを開始から終了まで処理させるのではなく、さまざまなステージの作業を 1 つ以上のスレッドまたはスレッドグループに委任し、I/O リクエストがパイプラインを通り抜ける際に、間にメッセージを受け渡します。これにより、1 つのスレッドは、I/O 操作が処理されるたびにロックとロック解除を行わずにグローバルデータ構造にすべてのアクセスを直列することができます。VDO ドライバーを適切にチューニングすると、要求された処理ステージをスレッドが完了するたびに、同じ処理に待ち行列に入れられた別のリクエストが存在するようになります。これらのスレッドを絶えず使うと、コンテキストの切り替えやスケジュール設定のオーバーヘッドを低減させることができ、パフォーマンスを向上できます。個別のスレッドは、基礎となるストレージシステムへの I/O オペレーションのキューの追加や、UDS へのメッセージなど、ブロックできるオペレーティングシステムの部分にも使用されます。
VDO が使用するさまざまなワーカースレッドタイプ:
ローカルゾーンスレッド
logical スレッド (文字列 kvdo:logQ を含むプロセス名) は、VDO デバイスのユーザーに示される論理ブロック数 (LBN) と基礎となるストレージシステムの物理ブロック数 (PBN) 間におけるマッピングを維持します。これらは、2 つの I/O 操作をロックし、同じブロックへの書き込みが同時に処理されないようにしています。論理ゾーンスレッドは、読み込みと書き込みの両方の処理の際にアクティブになります。
LBN は小分けされます (ブロックマップページには、3 MB を超える LBN が含まれます)。分けられた LBN は、スレッド間において分けられた zones へとグループ化されます。
ある不運なアクセスパターンにより、時折、何らかのスレッドに作業が集してしまうことがありますが、処理はスレッドに妥当に分配されます。たとえば、指定のブロックマップページ内の LBN に頻繁なアクセスがあると、論理スレッドの 1 つが、すべての操作を処理してしまいます。
論理ゾーンスレッドの数は、vdo コマンドに --vdoLogicalThreads=thread count オプションを指定することで制御できます。
物理ゾーンスレッド
Physical、または kvdo:physQ スレッドは、データブロックの割り当てを管理し、リファレンスカウントを維持します。これらは、書き込み操作中にアクティブになります。
LBN のように、PBN は、スラブ というチャンクに分けられます。これは、さらにゾーンへと分けられ、処理負荷を分配するワーカースレッドへと割り当てられます。
物理ゾーンスレッドの数は、vdo コマンドに --vdoPhysicalThreads=thread count オプションを指定することで制御できます。
I/O 発信スレッド
kvdo:bioQ スレッドは、VDO からストレージシステムにブロック I/O (bio) 操作を送信します。これらは、その他の VDO スレッドによってキューに追加された I/O リクエストを取得し、基礎となるデバイスドライバーに受け渡します。このスレッドは、デバイスと通信し、デバイスに関連したデータ構造を更新します。または、処理するデバイスドライバーのカーネルスレッドのリクエストをセットアップします。I/O リクエストの送信は、基礎となるデバイスのリクエストキューがフルであればブロックできます。そのため、この作業は、処理の遅延を避けるために、専用のスレッドによって行われます。
これらのスレッドが頻繁に ps または top ユーティリティによって D 状態に示されている場合、VDO は頻繁に、I/O リクエストでストレージシステムを頻繁に使っている状態です。これは、ストレージシステムが並列に複数のリクエストをサービスできる場合は、あるいは、リクエスト処理がパイプライン型であれば良い状態です。一部の SSD は可能です。スレッド CPU 使用率が、これらの期間で非常に低い場合は、I/O 発信スレッドの数を減らせる可能性があります。
CPU 使用率とメモリ競合は、VDO 下のデバイスドライバーに依存します。より多くのスレッドが追加され、デバイスドライバーの CPU、メモリ、ロック競合をチェックすると、I/O リクエストごとの CPU 使用率が増大します。
I/O 発信スレッドの数は、vdo コマンドに --vdoBioThreads=thread count オプションを指定することで制御できます。
CPU 処理スレッド
kvdo:cpuQ スレッドは、その他のスレッドタイプに関連したデータ構造への排他的なアクセスをブロックしたり、これを必要としないハッシュ値の計算やデータブロックの圧縮など CPU 集約的な作業を実行するために存在します。
CPU 処理スレッドの数は、vdo コマンドに --vdoCpuThreads=thread count オプションを指定することで制御できます。
I/O 承認スレッド
kvdo:ackQ スレッドは、VDO 上に来るすべてに対するコールバックを発行し (たとえば、カーネルページキャッシュ、またはダイレクト I/O を行うアプリケーションプログラムスレッド)、I/O リクエストの完了を報告します。
承認処理スレッドの数は、vdo コマンドに --vdoAckThreads=thread count オプションを指定することで制御できます。
拡張不可 VDO カーネルスレッド:
重複排除スレッド
kvdo:dedupeQ は、待ち行列に入れられた I/O リクエストを取得して UDS に通信します。サーバーがリクエストを十分に速く処理できない場合や、カーネルメモリがその他のシステムアクティビティによって制約されている場合、ソケットバッファーが埋まる可能性があるため、この作業は別のスレッドで行われます。つまり、スレッドがブロックする必要があれば、その他の VDO プロスは続行できます。また、長い遅延 (数秒) 後には、I/O リクエストをスキップするためのタイムアウトメカニズムもあります。
ジャーナルスレッド
kvdo:journalQ スレッドは、リカバリージャーナルを更新し、書き込み用のジャーナルブロックのスケジュール設定を行います。VDO デバイスは、1 つのジャーナルのみを使用するため、この作業はスレッドにわたって分配できません。
パッカースレッド
圧縮が有効化されている場合に書き込みパスでアクティブな kvdo:packerQ スレッドは、kvdo:cpuQ スレッドで圧縮されたデータブロックを収集し、容量の無駄を最小限に抑えます。存在するパッカーデータ構造は 1 つのみなため、VDO デバイスごとに 1 パッカースレッドが存在します。

29.6.3. チューニングする値

29.6.3.1. CPU/メモリー

29.6.3.1.1. 論理、物理、CPU、承認スレッドカウント
論理、物理、CPU、そして I/O 承認作業は、複数のスレッドにわたり分散できます。これらの数は、VDO デバイスが再起動すれば、初期設定または後で指定できます。
1 つのコア、または 1 つのスレッドは、指定した時間において無限の作業を行うことができます。1 つのスレッドがあれば、すべてのデータブロックのハッシュ値を計算できます。たとえば、毎秒処理できるデータブロックの数にハードリミットを課すことになります。複数のスレッド (およびコア) で作業を分割すると、ボトルネックを緩和できます。
スレッドまたはコアの使用率が 100% につれ、より多くの作業項目が処理のキューに追加される傾向があります。これにより、CPU のアイドルサイクルが短くなることがありますが、個別の I/O リクエストの待ち行列入れの遅延やレイテンシーは通常増加します。一部のキュー理論モデルによると、70% または 80% の使用率レベルにタスセルと、通常の処理時間よりも数倍長い角の遅延が発生することがあります。そのため、このような処理は、50% 以上の使用率でスレッドまたはコアに対してさらに作業を分配するのに役立ちます。
逆の場合では、スレッドまたは CPU には非常に軽い負荷がかかっています (頻繁にスリープ状態)。このようなものに作業を割り当てることは、さらに多くのコストが発生することになりかねません。(別のスレッドの復帰を試行するスレッドは、スケジューラーのデータ構造でグローバルロックを取得する必要があります。また、作業を別のコアに転送するためにプロセッサー間の中断を送信することもあります)。VDO スレッドで設定されるコアが多いほど、作業がスレッド間で移動したり、スレッドがコア間で移動したりするため、一定のデータがキャッシュされなくなります。つまり、あまりにも多くの作業を分配すると、パフォーマンスの低下につながる可能性があります。
I/O リクエストごとの論理、物理、CPU スレッドが実行する作業は、ワークロードのタイプに基づいて変わります。そのため、サービスすることが期待される別のタイプのワークロードでシステムをテストする必要があります。
重複排除に成功した同期モードの書き込み操作は、新しいデータの書き込みに比べ、さらなる I/O 操作 (以前に格納したデータブロックの読み込み)、一部の CPU サイクル (新しいデータブロックを比較してマッチするかどうかを検証)、ジャーナル更新 (以前格納したデータブロックの PBN に LBN を再マッピング) を必然的に伴います。非同期モードで重複排除が検出されると、データの書き込み操作は、読み込みを犠牲にして会費されます。また、上記で説明した操作を比較します。書き込みごとのジャーナル更新は 1 つのみです。これは、重複排除の検出のありなしに関係ありません。
圧縮が有効化されると、圧縮可能データの読み込みと書き込みには、CPU スレッドによる、さらに多くの処理が必要になります。
すべてのゼロバイト (ゼロブロック) を含むブロックは、一般的に発生するため特別に扱われます。ブロックマップのデータなどには、特殊なエントリーが使われます。ゼロブロックはストレージデバイスに書き込まれたり、ストレージデバイスから読み込まれたりしません。つまり、オールゼロブロックは、誤解を招くような結果を生み出す可能性があります。ゼロブロックまたは初期化していないブロック上の書き込みを行うテストにも、少なからず同じ事が言えます。これは、物理スレッドによって行われる参照カウント更新が、ゼロまたは初期化されていないブロックに対して必要とされていないためです。
I/O 操作の承認は、行われている作業タイプや、操作されているデータによって大幅に影響されない唯一のタスクです。これは、1 つのコールバックが I/O 操作ごとに発行されるためです。
29.6.3.1.2. CPU アフィニティと NUMA
NUMA ノード境界にわたるメモリーへのアクセスは、ローカルノード上のメモリーにアクセスするよりも時間がかかります。ノード上のコア間における最終レベルのキャッシュを共有する Intel プロセッサーでは、ノード間のキャッシュ競合は、ノード内のキャッシュ競合よりも大きな問題となります。
top などのツールは、作業を行う CPU サイクルや、引き留められているサイクルの区別を行うことはできません。これらのツールは、実際の作業としてキャッシュ競合と低速メモリーを解釈します。結果として、ノード間のスレッドを移動すると、毎秒ごとに実行する操作の数が増えると同時に、スレッドの見かけの CPU 使用率が低減したように見えることがあります。
VDO のカーネルスレッドの多くは、1 つのみのスレッドによってアクセスされるデータ構造を維持しますが、I/O リクエストについてのメッセージを頻繁に交換します。VDO スレッドが複数のノードで実行されている場合や、スレッドが、スケジューラーによって、あるノードから別のノードに割り当てられている場合は、競合率が高くなることがあります。その他の VDO 関連の作業 (VDO への I/O 送信や、ストレージデバイスの中断処理など) を実行できる場合は、競合が低減できる可能性があります。1 つのノードに、すべての VDO 関連の作業を実行できるほどのサイクル外ない場合、その他のノードに移動するためにスレッドを選択したときにはメモリーの競合を考慮する必要があります。
実用的であれば、taskset ユーティリティで、1 つのノード上で VDO スレッドを収集します。その他の VDO 関連の作業が、同じノード上で実行できれば、さらに競合を低減させることができます。その場合、処理需要に合わせるための CPU パワーが欠如すると、その他ノードに移動するノードを選ぶときにメモリー競合を考慮しなければなりません。たとえば、ストレージデバイスのドライバーに、維持しなければならないデータ構造が多い場合、デバイスの中断処理と VDO の I/O 送信 (デバイスのドライバーコードを呼び出す bio スレッド) を別のノードに移動するのに役立ちます。I/O 承認 (承認スレッド) と高レベルの I/O 送信スレッド (ダイレクト I/O を行うユーザーモードスレッド、またはペアリングされたカーネルページのキャッシュ flush スレッド) も良い対策と言えます。
29.6.3.1.3. 周波数の調整
電力消費が問題でなければ、/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor ファイル (存在する場合) に文字列 performance を書き込むことで、結果がより良いものになります。これらの sysfs ノードが存在しない場合、Linux またはシステムの BIOS では、CPU 周波数管理の設定にさらに優れたオプションを利用できます。
パフォーマンス測定は、ワークロードに基づいて周波数が動的に変化する CPU によってさらに複雑化します。これは、タスクの切り替えやキャッシュ競合がない場合であっても、CPU が行っているその他の作業により、作業の特定の部分を実現するために必要時間が異なるためです。

29.6.3.2. キャッシュ

29.6.3.2.1. ブロックマップキャッシュ
VDO は、効率を向上するために多くのブロックマップページをキャッシュします。このキャッシュサイズはデフォルトで 128 MB ですが、vdo コマンドに --blockMapCacheSize=megabytes オプションを指定することで増大させることができます。より大きなキャッシュを使用すると、ランダムアクセスワークロードに対するメリットを大いに得ることができます。
29.6.3.2.2. 読み込みキャッシュ
第 2 のキャッシュは、VDO の重複排除アドバイスを検証するためにストレージシステムからデータブロックの読み込みをキャッシュするのに使用できます。同じようなデータブロックが短期間で見られた場合は、必要な I/O 操作数が減ります。
また、読み込みキャッシュは、圧縮ユーザーデータを含むストレージブロックを保持します。複数の圧縮ブロックが短期間で書き込まれる場合、圧縮バージョンが同じストレージシステムブロックに配置されることがあります。同じように、短期間に読み込まれれば、キャッシングにより、ストレージシステムからさらなる読み込みのニーズを避けることができます。
vdo command's --readCache={enabled | disabled} オプションは、読み込みキャッシュが使用されるかどうかを制御します。有効化すると、キャッシュの最低サイズは 8 MB になりますが、これは --readCacheSize=megabytes オプションで増大します。読み込みキャッシュを管理することで、多少のオーバーヘッドを受けます。そのため、ストレージシステムが十分に高速であればパフォーマンスは上昇しません。読み込みキャッシュはデフォルトで無効化されています。

29.6.3.3. ストレージシステム I/O

29.6.3.3.1. Bio スレッド
RAID 構成の一般的なハードドライブでは、I/O 操作の送信に必要となるのは 1 つまたは 2 つの bio スレッドです。ストレージデバイスドライバーが、1 つまたは 2 つのスレッドが非常にアクティブで、ストレージデバイスが頻繁にアイドル状態というような、相当な量の作業 (データ構造の更新や、デバイスとの通信) を行うために I/O 送信スレッドを必要とする場合、bio スレッドカウントを、相殺のために増大させることができます。ただし、ドライバー実装によっては、スレッドカウントを上昇させすぎると、競合のキャッシュやスピンロックに発展することがあります。デバイスアクセス時間がすべての NUMA ノードにわたって統一されていない場合は、ストレージデバイスコントローラーに対してノード「closest」上で bio スレッドを実行するのに役立ちます。
29.6.3.3.2. IRQ 処理
デバイスドライバーが割り込みハンドラーで重要な作業を行い、スレッド型の IRQ ハンドラーを使用しない場合は、スケジューラーによる最高のパフォーマンスの発揮が妨げられることがあります。ハードウェア割り込みをサービスするのに費やした CPU 時間は、ある意味では通常の VDO (またはその他の) カーネルスレッドのように見えることがあります。たとえば、ハードウェア IRQ 処理がコアサイクルの 30% を必要とした場合、同じコア上のビジーなカーネルスレッドは残りの 70% のみを使用できます。ただし、そのスレッドに列を成す作業がコアサイクルの 80% を求める場合、スレッドは追いつくことはなく、スケジューラーは、あまりビジーでないコアに切り替える代わりに、そのコアで妨げられた状態で実行するように、そのスレッドを放置します。
このようなデバイスドライバーを高負荷の VDO 下で使用すると、ハードウェア割り込みのサービスに多くのサイクルが必要となります (top ディスプレイのヘッダーにおける %hi インジケーター)。その場合、特定のコアに対して IRQ 処理を割り当て、これらのコアで実行しないように VDO カーネルスレッドの CPU アフィニティを調整するとよいかもしれません。

29.6.3.4. 最大破棄セクター

VDO デバイスに対する DISCARD (TRIM) 操作の最大許容サイズは、システムの使用率に基づいて /sys/kvdo/max_discard_sectors から調整できます。デフォルトは 8 セクター (1 つの 4 KB ブロック) です。VDO が依然として 1 度に 1 ブロック、ループ状態で処理を行いますが、より大きなサイズを指定することができます。これは、1 つの破棄されたブロックのメタデータ更新がジャーナルに書き込まれ、次のブロックで開始する前にディスクにフラッシュされるようになります。
ローカルファイルシステムとして VDO ボリュームを使用する場合、Red Hat テストでは、小さなはきサイズが最もよく昨日することがわかっています。Linux カーネルの一般的なブロックデバイスコードが、大型の破棄リクエストを複数の小さなリクエストに分割し、並列して送信するためです。デバイスの I/O アクティビティが低い場合は、VDO は大きなリクエストよりも、より多くの小さなリクエストを同時かつ素早く処理できます。
VDO デバイスが SCSI ターゲットとして使用される場合は、イニシエーターやターゲットソフトウェアにより、考慮すべき要素が生まれます。ターゲット SCSI ソフトウエアが SCST である場合、最大破棄サイズを読み込み、イニシエーターにリレーします。(Red Hat は、LIO SCSI ターゲットコードとともに VDO 設定の調整を試行していません。)
Linux SCSI イニシエーターコードにより、1 度に 1 つの破棄操作が可能になるため、最大サイズを超えた破棄リクエストは、複数のより小さな破棄へと分割され、1 度に 1つずつターゲットシステム (および VDO) に送信されます。そのため、多くの破棄操作を順番に処理する VDO 処理加え、2 つのシステム間いおける往復の通信時間により、さらなるレイテンシーが生まれます。
より大きな最大破棄サイズを設定すると、この通信のオーバーヘッドを低減させることができます。ただし、より大きなリクエストは、すべて VDO に渡され、1 度に 1 つの 4 KB ブロックが処理されます。ブロックごとの通信遅延はありませんが、より大きなブロックに対する処理時間の増大により、SCSI イニシエーターソフトウェアがタイムアウトすることがあります。
SCSI ターゲット使用率については、Red Hat は、典型的な破棄時間をイニシエーターのタイムアウト設定内に維持しつつ、最大破棄サイズを比較的大きめに設定することを推奨しています。たとえば、数秒ごとの余分な往復コストが、パフォーマンスに大幅に影響を与えず、30 または 60 秒のタイムアウトの SCSI イニシエーターはタイムアウトするべきではありません。

29.6.4. ボトルネックの識別

VDO パフォーマンスに影響を与えるものには複数の主要要素があり、最も影響のあるものを識別するためのツールもあります。
topps で見られるような 70% を超えるスレッドや CPU 使用率は一般的に、1 つのスレッドまたは 1 つの CPU に、あまりにも多くの作業が集中していることを意味します。ただし、一部のケースでは、VDO スレッドが CPU で実行されるようにスケジュールさ設定されていても、実際に何も作業が行われていないことを意味する場合もあります。このようなシナリオは、角のハードウェア割り込みハンドラー処理、コアまたは NUMEA ノード間のメモリ競合、またはスピンロックの競合によって発生することがあります。
システムパフォーマンスの検証に top ユーティリティを使用する場合、Red Hat では top -H を実行してすべてのプロセススレッドを個別に表示してから、1 f j キーを入力して Enter/リターンキーを押します。次に top コマンドにより、個別の CPU コアのロードが表示され、最後に実行した書くプロセスまたはスレッド上の CPU を識別します。これにより、以下の情報がわかります。
  • コアに %id (アイドル) と %wa (I/O 待機) 値がある場合、ある種の作業でビジー状態が維持されます。
  • コアの %hi 値が非常に低い場合、そのコアは通常の処理作業を行っています。これは、カーネルスケジューラーによって負荷分散が行われています。そのセットにさらにコアを追加すると、NUMA 競合を取り込まない限り、負荷を低減させることができます。
  • コアの %hi が数パーセント以上で、そのコアに 1 つのスレッドのみが割り当てられ、%id%wa がゼロである場合、コアはコミットされ過ぎており、スケジューラーは状況に対処していません。この場合、カーネルスレッドやデバイス中断処理は、個別のコアで維持するように割り当てられます。
perf ユーティリティは、多くのパフォーマンスカウンターを検証できます。Red Hat では、スレッドやプロセッサーが行っている作業の検証には、第一歩として perf top サブコマンドの使用をおすすめします。たとえば、bioQ スレッドが多くのサイクルを消費して、スピンロックを取得しようとしている場合、VDO 下のデバイスドライバーに競合が多く発生することがあります。また、bioQ スレッドの数を低減せることで、状況を緩和できる可能性があります。たとえば、bioQ スレッドやデバイス中断ハンドラーが各種ノードで動作している場合、CPU の使用率が高いと (スピンロックの取得など)、NUMA ノード間に競合が見られることがあります。プロセッサーが対応していれば、stalled-cycles-backendcache-missesnode-load-misses などのカウンターを参照してみてください。
sar ユーティリティは、複数のシステム統計についてのレポートを定期的に出力できます。sar -d 1 コマンドは、1 病後とのブロックデバイスの使用率レベル (最低でも 1 つの処理中の操作がある時間の割合) やキューの長さ (待機中の I/O リクエストの数) を報告します。ただし、すべてのブロックデバイスドライバーがこのような情報を報告できるわけではありません。よって、sar の便利性は、使用中のデバイスドライバーによって変わります。