- カーネルのCPUスケジューラは、システムスループットと応答時間の間のトレードオフを実現する複数のプリエンプションモードを提供している。
- 2023年9月、スケジューリングに関する議論の中で「遅延プリエンプション(lazy preemption)」という概念が提案され、これはカーネルのスケジューリングを単純化しつつ、より良い結果をもたらす可能性がある。
- この概念はしばらく静かだったが、Peter Zijlstraのパッチシリーズによって再び登場した。
現在のカーネルのプリエンプションモード
- PREEMPT_NONE: 実行中のタスクがタイムスライスを使い切ったときにのみプリエンプションが許可される。
- PREEMPT_VOLUNTARY: 必要に応じて、カーネル内でプリエンプション可能な地点を多数追加する。
- PREEMPT_FULL: スピンロックがかかっている場合を除き、ほぼすべての地点でプリエンプションが可能。
- PREEMPT_RT: ほとんどの他の要素よりもプリエンプションを優先し、スピンロックのコードの大半もプリエンプション可能にする。
遅延プリエンプションの導入
- 遅延プリエンプションのパッチは、新しいフラグ
TIF_NEED_RESCHED_LAZY を追加し、即時ではなく将来のどこかの時点でリスケジュールが必要であることを示す。
- 遅延プリエンプションモード(PREEMPT_LAZY)では、大半のイベントがこの新しいフラグを設定し、カーネルからユーザー空間へ戻る際に、2つのフラグのいずれかが設定されていればスケジューラが呼び出される。
- この変更の結果、遅延プリエンプションモードではカーネル内の大半のイベントが現在のタスクをプリエンプトしなくなる。
cond_resched() の削除
- この作業の最終目標は、非リアルタイムモードとして PREEMPT_LAZY と PREEMPT_FULL の2つだけを残すことにある。
- 遅延モードは PREEMPT_NONE と PREEMPT_VOLUNTARY の中間に位置し、この2つのモードを置き換える。
- 現時点では cond_resched() 呼び出しが残っており、PREEMPT_NONE および PREEMPT_VOLUNTARY モードが存在する限り必要とされる。
GN⁺の要約
- 遅延プリエンプションは、カーネルのスケジューリングを単純化し、予測可能なレイテンシを提供することに貢献する可能性がある。
- この作業は、カーネルのサイズ削減とコードの簡素化に役立つ可能性がある。
- 遅延プリエンプションは PREEMPT_VOLUNTARY に近いスループットを提供するが、さらに多くのテストと最適化が必要である。
- 類似した機能を持つ別のプロジェクトとして、FreeBSD の ULE スケジューラがある。
1件のコメント
Hacker Newsのコメント
遅延プリエンプション作業の最終的な成果は、カーネルをより小さく単純にしつつ、予測可能なレイテンシを提供することにある。これは、コード全体にスケジューラ関連の呼び出しをばらまく必要がない、より良い解決策に見える。ただし、これを実現するには時間がかかるだろう。
高いレベルのプリエンプションは、システムがイベントにより素早く反応できるようにする。マウスの移動や原子炉の「差し迫った崩壊」信号のようなイベントへの素早い反応は、より満足感がある。しかし、高いレベルのプリエンプションはシステム全体のスループットに影響する可能性がある。CPU集約型のタスクが多いワークロードでは、できるだけ妨げられない方が有利だ。より頻繁なプリエンプションは、より高いロック競合を引き起こす可能性がある。だからこそ複数のモードが存在し、最適なプリエンプションモードはワークロードに依存するのだろう。
現在のカーネルには、あるタスクが別のタスクのためにプリエンプトされうるタイミングを制御する4つのモードがある。
リンクされたスレッドでは、パッチに関連する数値を見つけられない。この変更の実際の可能性を示せる予備的なベンチマークは行われていたはずだ。
スケジューラが残りのカーネルコードとどれほど密結合しているのか気になる。
プリエンプションがイベントに応じて適応できればよいが、すべてのイベントについてこれを管理するのはシステムの安定性を損なう可能性がある。これは、Tomba Finderのようなツールを使ってリードを獲得することに似ている。