30.6. VDO のチューニング
30.6.1. VDO チューニングの概要
データベースやその他の複雑なソフトウェアのチューニングと同様に、VDO のチューニングには、多数のシステム制約間のトレードオフが含まれ、ある程度の実験が必要です。VDO の調整に使用できる主な制御は、さまざまな種類の作業に割り当てられたスレッドの数、それらのスレッドの CPU アフィニティー設定、およびキャッシュ設定です。
30.6.2. VDO アーキテクチャーの背景
VDO カーネルドライバーはマルチスレッド化されており、複数の同時 I/O 要求間で処理コストを償却することでパフォーマンスを向上させます。1 つのスレッドで I/O 要求を最初から最後まで処理するのではなく、作業のさまざまな段階を 1 つ以上のスレッドまたはスレッドのグループに委任し、I/O 要求がパイプラインを通過するときにメッセージがそれらの間で渡されます。このようにして、1 つのスレッドで、I/O 操作が処理されるたびにグローバルデータ構造をロックおよびアンロックすることなく、グローバルデータ構造へのすべてのアクセスをシリアル化できます。VDO ドライバーのチューニングが適切に行われていないと、スレッドが要求された処理段階を完了するたびに、通常は別の要求が同じ処理のためにキューに追加されます。これらのスレッドをビジー状態に保つと、コンテキストの切り替えとスケジューリングのオーバーヘッドが削減され、パフォーマンスが向上します。基盤となるストレージシステムに I/O 操作をキューに入れることや UDS へのメッセージなど、ブロックできるオペレーティングシステムの部分にも個別のスレッドが使用されます。
VDO で使用されるさまざまなワーカースレッドタイプは次のとおりです。
- 論理ゾーンスレッド
- 文字列
kvdo:logQ
を含むプロセス名を持つ 論理 スレッドは、VDO デバイスのユーザーに提示される論理ブロック番号(LBN)と、基盤となるストレージシステムの物理ブロック番号(PBN)との間のマッピングを維持します。また、同じブロックに書き込もうとする 2 つの I/O 操作が同時に処理されないように、ロックを実装します。論理ゾーンスレッドは、読み取り操作と書き込み操作の両方でアクティブになります。LBN はチャンクに分割され (ブロックマップページ は 3MB 以上の LBN を含む)、このチャンクはスレッド間で分割されるゾーンにグループ化されます。処理はスレッド間でかなり均等に分散させる必要がありますが、一部の不運なアクセスパターンでは、作業が 1 つのスレッドまたは別のスレッドに集中する場合があります。たとえば、特定のブロックマップページ内の LBN に頻繁にアクセスすると、論理スレッドの 1 つがそれらのすべての操作を処理します。論理ゾーンスレッドの数は、vdo コマンドの--vdoLogicalThreads=thread count
オプションを使用して制御できます。 - 物理ゾーンスレッド
- 物理、または
kvdo:physQ
のスレッドは、データブロックの割り当てを管理し、参照カウントを維持します。これらは書き込み操作時にアクティブになります。LBN と同様、PBN は スラブ と呼ばれるチャンクに分割され、さらにゾーンに分割され、処理負荷を分散するワーカースレッドに割り当てられます。物理ゾーンスレッドの数は、vdo コマンドの--vdoPhysicalThreads=thread count
オプションを使用して制御できます。 - I/O 送信スレッド
kvdo:bioQ
スレッドは、VDO からストレージシステムにブロック I/O (bio)操作を送信します。これらは、他の VDO スレッドによってキューに入れられた I/O 要求を受け取り、それらを基盤となるデバイスドライバーに渡します。このようなスレッドは、デバイスに関連付けられたデータ構造と通信し、データ構造を更新したり、デバイスドライバーのカーネルスレッドを処理するように要求を設定したりできます。基礎となるデバイスのリクエストキューが満杯になると、I/O リクエストの送信がブロックされる可能性があるため、この作業は専用のスレッドで行われ、処理の遅延を回避します。ps
ユーティリティーまたはトップ
ユーティリティーによってこれらのスレッドが頻繁にD
状態に表示される場合、VDO は頻繁にストレージシステムを I/O 要求でビジー状態に保ちます。これは、一部の SSD のように、ストレージシステムが複数のリクエストを並行して処理できる場合、またはリクエスト処理がパイプライン化されている場合に一般的に適しています。この期間にスレッドの CPU 使用率が非常に低い場合は、I/O 送信スレッドの数を減らすことができます。CPU 使用率およびメモリー競合は、VDO の下にあるデバイスドライバーにより異なります。スレッドが追加されるにつれて I/O 要求あたりの CPU 使用率が増加する場合は、それらのデバイスドライバーの 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 要求の完了を報告します。CPU 時間の要件およびメモリーの競合は、この他のカーネルレベルのコードに依存します。確認応答スレッドの数は、vdo コマンドの--vdoAckThreads=thread count
オプションを使用して制御できます。- スケーラビリティーのない VDO カーネルスレッド:
- 重複排除スレッド
kvdo:dedupeQ
スレッドは、キューに入れられた I/O 要求を受け取り、UDS に接続します。サーバーが要求を十分に迅速に処理できない場合、またはカーネルメモリーが他のシステムアクティビティーによって制約されている場合、ソケットバッファーがいっぱいになる可能性があることから、この作業は別のスレッドによって実行され、スレッドがブロックされた場合でも、他の VDO 処理を続行できます。また、長い遅延 (数秒) 後に I/O 要求をスキップするタイムアウトメカニズムもあります。- ジャーナルスレッド
kvdo:journalQ
スレッドはリカバリージャーナルを更新し、書き込み用にジャーナルブロックをスケジュールします。VDO デバイスは 1 つのジャーナルのみを使用するため、この作業はスレッド間では分割できません。- パッカースレッド
- 圧縮が有効な場合に書き込みパスでアクティブな
kvdo:packerQ
スレッドは、無駄なスペースを最小限に抑えるためにkvdo:cpuQ
スレッドによって圧縮されたデータブロックを収集します。VDO デバイスごとに 1 つのパッカーデータ構造があり、これにより 1 つのパッカースレッドがあります。
30.6.3. 調整する値
30.6.3.1. CPU/メモリー
30.6.3.1.1. 論理、物理、cpu、ack スレッド数
論理、物理、CPU、および I/O の確認作業は、複数のスレッドに分散できます。その数は、初期の設定中、または VDO デバイスが再起動された場合は後で指定できます。
1 つのコア、つまり 1 つのスレッドは、指定された時間内に限られた量の作業を実行できます。たとえば、1 つのスレッドですべてのデータブロックのハッシュ値を計算すると、1 秒あたりに処理できるデータブロックの数にハード制限が課されます。複数のスレッド (およびコア) にわたって作業を分割すると、ボトルネックが緩和されます。
スレッドまたはコアの使用率が 100% に近づくにつれて、より多くのワークアイテムが処理のキューに追加される傾向があります。これにより、CPU のアイドルサイクルが減る可能性がありますが、個々の I/O 要求のキューイング遅延および待ち時間は通常増加します。一部のキュー理論モデルでは、使用率が 70% または 80% を超えると過剰な遅延が発生し、通常の処理時間よりも数倍長くなる可能性があります。したがって、それらのスレッドまたはコアが常にビジーであるとは限らない場合でも、使用率が 50% 以上のスレッドまたはコアの作業をさらに分散すると役立つ場合があります。
逆の場合、スレッドまたは CPU の負荷が非常に軽い (したがって、非常に頻繁にスリープ状態になっている) 場合、そのための作業を提供すると、追加のコストが発生する可能性が高くなります。(別のスレッドをウェイクアップしようとするスレッドは、スケジューラーのデータ構造に対するグローバルロックを取得する必要があり、プロセッサー間割り込みを送信して別のコアに作業を転送する可能性があります)。VDO スレッドを実行するように設定されているコアが増えると、作業がスレッド間で移動したり、スレッドがコア間で移動したりするときに、特定のデータがキャッシュされる可能性が低くなります。そのため、作業の分散が多すぎるとパフォーマンスが低下する可能性があります。
I/O 要求ごとに、論理スレッド、物理スレッド、および CPU スレッドが実行する作業はワークロードのタイプにより異なるため、システムでは、サービスを受ける予定のさまざまなタイプのワークロードでテストする必要があります。
重複排除に成功した同期モードでの書き込み操作では、新しいデータの書き込みと比較して、余分な I/O 操作 (前に保存されたデータブロックの読み込み)、いくつかの CPU サイクル (新しいデータブロックと比較して一致するか確認)、ジャーナル更新 (LBN を前に保存されたデータブロックの PBN に再マップ) が発生します。非同期モードで重複が検出された場合、上記の読み取りおよび比較操作を犠牲にして、データ書き込み操作が回避されます。重複が検出されたかどうかに関係なく、書き込みごとに発生できるジャーナル更新は 1 つだけです。
圧縮が有効になっていると、圧縮可能なデータの読み取りと書き込みに、CPU スレッドによる処理がさらに必要になります。
すべてゼロバイトを含むブロック (ゼロブロック) は、一般的に発生するため、特別に扱われます。ブロックマップでは、このようなデータを表すのに特殊なエントリーが使用され、ゼロのブロックはストレージデバイスに書き込まれたり、読み取られたりしません。したがって、すべてゼロのブロックを書き込みまたは読み取るテストでは、誤解を招く結果が生じる可能性があります。物理スレッドによる参照カウントの更新はゼロブロックまたは初期化されていないブロックには必要ないため、ゼロブロックまたは初期化されていないブロック (VDO デバイスの作成以降に書き込まれなかったブロック) を上書きするテストについても、程度は低くなりますが、同じことが当てはまります。
I/O 操作の承認は、I/O 操作ごとにコールバックが 1 つ発行されるため、実行中の作業のタイプや操作されるデータに大きく影響されない唯一のタスクです。
30.6.3.1.2. CPU アフィニティーおよび NUMA
NUMA ノードの境界を越えたメモリーのアクセスは、ローカルノードのメモリーのアクセスよりも時間がかかります。Intel プロセッサーがノードのコア間で最終レベルのキャッシュを共有すると、ノード間のキャッシュ競合は、ノード内のキャッシュ競合よりもはるかに大きな問題になります。
top などのツールは、作業を行う CPU サイクルと、停止したサイクルを区別できません。このようなツールは、キャッシュの競合と、低速なメモリーアクセスを実際の動作として解釈します。その結果、ノード間でスレッドを移動すると、スレッドの見かけの CPU 使用率が低下し、1 秒あたりに実行される操作の数が増えるように見える場合があります。
VDO のカーネルスレッドの多くは、1 つのスレッドのみがアクセスするデータ構造を維持しますが、I/O 要求自体に関するメッセージを頻繁に交換します。VDO スレッドが複数のノードで実行されている場合、またはスケジューラーがあるノードから別のノードにスレッドを再割り当てしている場合は、競合が高くなることがあります。VDO スレッドと同じノードで、他の VDO 関連の作業 (VDO への I/O 送信、ストレージデバイスの割り込み処理など) を実行できる場合は、競合をさらに減らすことができます。1 つのノードにすべての VDO 関連の作業を実行するための十分なサイクルがない場合は、他のノードに移動するスレッドを選択するときにメモリーの競合を考慮する必要があります。
実用的な場合は、
taskset
ユーティリティーを使用して 1 つのノードで VDO スレッドを収集します。他の VDO 関連の作業も同じノードで実行できる場合は、競合がさらに減少する可能性があります。この場合、処理に対する要求に対応するために、あるノードで CPU パワーが足りないと、別のノードに移動するスレッドを選択する際に、メモリーの競合を考慮する必要があります。たとえば、ストレージデバイスのドライバーが保持するデータ構造が多数ある場合は、デバイスの割り込み処理と VDO の I/O 送信 (デバイスのドライバーコードを呼び出す bio スレッド) の両方を別のノードに移動させると便利です。I/O 確認スレッド(ack スレッド)と上位レベルの I/O 送信スレッド(ダイレクト I/O を実行するユーザーモードスレッド、またはカーネルのページキャッシュ フラッシュ
スレッド)をペアにしておくこともお勧めします。
30.6.3.1.3. 周波数のスロットル調整
消費電力が問題でない場合は、
/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
ファイルに文字列の performance
を書き込むと、より良い結果が生じる可能性があります。これらの sysfs
ノードが存在しない場合は、Linux またはシステムの BIOS により、CPU 周波数管理を設定するための他のオプションが提供される場合があります。
特定の作業を実行するために必要な時間は、タスクの切り替えやキャッシュの競合がなくても、CPU が実行している他の作業によって異なる可能性があるため、パフォーマンスの測定は、ワークロードに基づいて頻度を動的に変化させる CPU によってさらに複雑になります。
30.6.3.2. キャッシュ
30.6.3.2.1. ブロックマップキャッシュ
VDO は、効率化のために多数のブロックマップページをキャッシュします。キャッシュサイズのデフォルトは 128 MB ですが、vdo コマンドの
--blockMapCacheSize=megabytes
オプションを使用して増やすことができます。より大きなキャッシュを使用すると、ランダムアクセスワークロードに大きなメリットがもたらされる可能性があります。
30.6.3.2.2. 読み取りキャッシュ
2 番目のキャッシュは、VDO の重複排除のアドバイスを検証するために、ストレージシステムから読み取られたデータブロックをキャッシュするために使用できます。短期間で同様のデータブロックが検出されると、必要な I/O 操作の数が減る可能性があります。
読み取りキャッシュには、圧縮されたユーザーデータを含むストレージブロックも含まれます。短期間に複数の圧縮可能ブロックが書き込まれた場合、その圧縮バージョンは、同じストレージシステムブロック内に存在する可能性があります。同様に、短時間で読み取りを行うと、キャッシュにより、ストレージシステムからの読み取りを追加で行う必要がなくなります。
vdo コマンドの
--readCache={enabled | disabled}
オプションは、読み取りキャッシュを使用するかどうかを制御します。これを有効にした場合、キャッシュの最小サイズは 8 MB ですが、
--readCacheSize=megabytes
オプションで増やすことができます。読み取りキャッシュの管理には若干のオーバーヘッドが発生するため、ストレージシステムが十分に高速であれば、パフォーマンスが向上しない可能性があります。読み取りキャッシュはデフォルトで無効になっています。
30.6.3.3. ストレージシステム I/O
30.6.3.3.1. Bio スレッド
RAID 設定の汎用ハードドライブでは、I/O 操作を送信するには 1 つまたは 2 つの bio スレッドで十分な場合があります。ストレージデバイスドライバーが、I/O 送信スレッドに著しく多くの作業 (ドライバーのデータ構造の更新やデバイスとの通信) を要求し、1 つか 2 つのスレッドが非常に忙しく、ストレージデバイスがしばしばアイドルである場合、bio スレッド数を増やして補うことができます。ただし、ドライバーの実装によっては、スレッド数を増やしすぎると、キャッシュまたはスピンロックの競合が発生する可能性があります。デバイスのアクセスタイミングが NUMA ノード全体で均一でない場合は、ストレージデバイスコントローラーに最も近いノードで bio スレッドを実行すると便利な場合があります。
30.6.3.3.2. IRQ の処理
デバイスドライバーが割り込みハンドラーで重要な作業を行い、スレッド化された IRQ ハンドラーを使用しない場合、スケジューラーが最高のパフォーマンスを提供できなくなる可能性があります。ハードウェアの割り込みの処理に費やされる CPU 時間は、通常の VDO (または他の) カーネルスレッドの実行と似ている場合があります。たとえば、ハードウェア IRQ 処理にコアのサイクルの 30% が必要な場合、同じコア上のビジーなカーネルスレッドは残りの 70% しか使用できませんでした。ただし、そのスレッド用にキューに入れられた作業が、コアのサイクルの 80% を要求していた場合、スレッドが追いつくことはなく、スケジューラーは、スレッドをビジーでないコアに切り替える代わりに、そのスレッドをそのコアで実行するのを妨げたままにする可能性があります。
VDO ワークロードでそのようなデバイスドライバーを使用する場合は、ハードウェア割り込みに多くのサイクルが必要になる場合があります( トップ ディスプレイのヘッダーの
%hi
インジケーター)。その場合は、IRQ 処理を特定のコアに割り当て、VDO カーネルスレッドの CPU アフィニティーを調整して、そのコアで実行しないようにすると役立つ場合があります。
30.6.3.4. 最大破棄セクター
VDO デバイスに許可される DISCARD (TRIM) 操作の最大サイズは、システムの使用方法に基づいて、
/sys/kvdo/max_discard_sectors
で調整できます。デフォルトは 8 セクター (つまり 4KB ブロック 1 つ) です。より大きなサイズを指定することもできますが、VDO はループで一度に 1 ブロックずつ処理し、破棄された 1 つのブロックのメタデータの更新がジャーナルに書き込まれ、次のブロックで開始する前にディスクにフラッシュされるようにします。
VDO ボリュームをローカルファイルシステムとして使用すると、Linux カーネルの一般的なブロックデバイスコードが大量の破棄要求を複数の小規模な破棄要求に分割し、並行に送信するため、Red Hat テストでは、小規模な破棄サイズが最適であることがわかりました。デバイスの I/O アクティビティーが少ない場合、VDO は、多数の小規模な要求を同時に処理し、1 つの大規模な要求よりもはるかに迅速に処理できます。
VDO デバイスを SCSI ターゲットとして使用する場合は、イニシエーターとターゲットソフトウェアは考慮すべき追加要素を導入します。ターゲットの SCSI ソフトウェアが SCST の場合は、最大破棄サイズを読み込み、イニシエーターに中継します。(Red Hat では、LIO SCSI ターゲットコードと併用して VDO 設定のチューニングを行っていません。)
Linux SCSI イニシエーターコードでは一度に 1 つの廃棄操作しか許可されないため、最大サイズを超える破棄要求は、複数の小さな破棄に分割され、一度に 1 つずつターゲットシステム (および VDO) に送信されます。したがって、VDO が多数の小さな破棄操作をシリアルで処理することに加えて、2 つのシステム間のラウンドトリップ通信時間により、さらなる遅延が追加されます。
最大廃棄サイズを大きく設定することで、この通信オーバーヘッドを削減することができますが、この大きなリクエストは全体が VDO に渡され、一度に 4KB のブロックが処理されます。ブロックごとの通信遅延はありませんが、大きなブロックに処理時間を追加すると、SCSI イニシエーターソフトウェアがタイムアウトになる場合があります。
SCSI ターゲットの使用方法として、Red Hat では、イニシエーターのタイムアウト設定内で通常の廃棄時間を適切に保持しながら、最大廃棄サイズを適度に大きく設定することを推奨しています。たとえば、数秒ごとの追加のラウンドトリップコストはパフォーマンスに大きな影響を与えないはずであり、タイムアウトが 30 秒または 60 秒の SCSI イニシエータはタイムアウトにならないはずです。
30.6.4. ボトルネックの特定
VDO のパフォーマンスに影響を与えるいくつかの重要な要素があり、最も影響力のあるものを特定するために利用できる多くのツールがあります。
top
や ps
などのユーティリティーで見られるように、スレッドまたは CPU の使用率が 70% を超える場合は、通常、1 つのスレッドまたは 1 つの CPU に多くの作業が集中していることを意味します。ただし、場合によっては、VDO スレッドが CPU で実行されるようにスケジュールされていても、実際には作業が行われなかったことを意味する場合があります。このシナリオは、過度のハードウェア割り込みハンドラー処理、コアまたは NUMA ノード間のメモリー競合、またはスピンロックの競合で発生する可能性があります。
top
ユーティリティーを使用してシステムパフォーマンスを調べる場合、Red Hat は top -H を実行してすべてのプロセススレッドを個別に表示し、1 f j
キーを入力し、その後に Enter/Return キーを入力することをお勧めします。top コマンドは、個々の CPU コアの負荷を表示し、各プロセスまたはスレッドが最後に実行された CPU を識別します。この情報により、以下の洞察が得られます。
- コアに
%id
(idle)と%wa
(waiting-for-I/O)の値が少ない場合、ある種の作業でビジー状態が保たれています。 - コアの
%hi
値が非常に低い場合、そのコアは通常の処理作業を行い、カーネルスケジューラーによって負荷分散されています。そのセットにコアを追加すると、NUMA の競合が発生しない限り、負荷が軽減される可能性があります。 - コアの
%hi
が数パーセントを超え、そのコアに割り当てられているスレッドは 1 つだけで、%id
と%wa
がゼロの場合、コアはオーバーコミットされ、スケジューラーは状況に対処していません。この場合は、カーネルスレッドやデバイスの割り込み処理を別のコアに保持するために再割り当てする必要があります。
perf
ユーティリティーは、多くの CPU のパフォーマンスカウンターを調べることができます。Red Hat は、perf top サブコマンドを開始点として使用して、スレッドまたはプロセッサーが実行する作業を調べることをお勧めします。たとえば、bioQ
スレッドがスピンロックの取得を試行する多くのサイクルを費やしている場合、VDO 未満のデバイスドライバーで競合が多すぎる可能性があり、bioQ
スレッドの数を減らすと状況が軽減される可能性があります。CPU 使用率が高い場合(スピンロックの取得など)は、たとえば bioQ
スレッドとデバイス割り込みハンドラーが異なるノードで実行されている場合など、NUMA ノード間の競合を示している可能性があります。プロセッサーがそれらをサポートしている場合、stalled-cycles-backend
、cache-misses
、および node-load-misses
などのカウンターが重要になる可能性があります。
sar
ユーティリティーは、複数のシステム統計に関する定期的なレポートを提供できます。sar -d 1 コマンドは、ブロックデバイス使用率レベル(少なくとも 1 つの I/O 操作が進行中である割合)とキューの長さ(I/O 要求の待機数)を 1 秒に 1 回報告します。ただし、すべてのブロックデバイスドライバーがそのような情報を報告できるわけではないため、sar
の有用性は、使用中のデバイスドライバーに依存する可能性があります。