リクエストのデータが準備され、処理スレッドもウェイクアップされたにもかかわらず、応答がなかなか返ってこない。このような場合、スケジューラのいくつかの判断基準をたどって追跡する必要があります。どこで実行するか、どのくらいの CPU 時間を割り当てられるか、この瞬間に誰が順番か。
観察される現象 参照箇所
スレッドはウェイクアップされたが、すぐに実行されない 第 1 節:実行資格と再スケジューリング
nice 値が異なる場合、CPU 時間はどのように分配されるか? 第 2~3 節:重みと EEVDF のシミュレーション
ホストには空きリソースがあるのに、コンテナがキュー待ちしている 第 5 節:グループ重みとクォータ
マルチコア間で負荷が偏っているが、マイグレーションが必ずしも高速になるとは限らない 第 6~7 節:配置と観察
本稿では、通常の公平スケジューリングを主に扱います。Linux は 6.6 から EEVDF を導入しました。数値モデルはアルゴリズムの理解のために用いられ、実装の詳細はローカル Linux 7.2 を対照とします。
1. 実行キューは「誰が今、実行する機会があるか」に答える
Linux は主に CPU ごとに実行キューを管理しています。現在実行中のタスクも、実行待ちのタスクも TASK_RUNNING 状態になり得ますが、I/O や条件待ちでスリープしているタスクは、優先度が高くても CPU を消費し続けることはありません。ウェイクアップパスではタスク状態が更新され、適切な CPU が選択されてキューに挿入されます。新しいタスクが現在のタスクをプリエンプトするに値する場合、再スケジューリング要求が発生します。
再スケジューリング要求は、任意の命令ポイントでの強制ジャンプではありません。カーネルは対応するスケジューリング可能なポイントで要求をチェックする必要があります。割り込み、プリエンプションの無効化、ロック制約、およびカーネルプリエンプションモデルは、切り替えのタイミングに影響を与えます。スケジューラが一度実行しても、依然として現在のタスクが選択される可能性があるため、「schedule() を実行する」と「prev != next のコンテキストスイッチが発生する」は同義ではありません。
CPU ごとのキューはグローバルな競合を減らしますが、スケジューリングが完全にローカル操作であるわけではありません。遠隔タスクのウェイクアップ、負荷のプル、タスクのマイグレーションには、複数の CPU とキューロックの調整が必要です。CPU アフィニティ、cpuset、有効なスケジューリングポリシーは候補範囲をまず限定し、負荷分散は許可された範囲内で割り当てを改善するだけで、均等に見せるために隔離設定を迂回することはできません。
2. 公平な計測:実時間を仮想サービスに変換
まず、A と B のみの理想モデルを見てみましょう。両者は継続的に実行可能で、重みが 2:1 です。1 つの CPU の 9 ms の時間を共有します。重みに基づいて分配すると、A は 6 ms、B は 3 ms を得ます。
実際の時間は異なるが、正規化されたサービス進行度は同じ
A:重み 2
6 ms
÷ 重み
3
B:重み 1
3 ms
÷ 重み
3
実際の CPU 時間
仮想増分
単一 CPU、2 つのタスクが継続的に実行可能、理想的なシェア;基準重みを 1 に正規化。総実時間は 9 ms。
両者の実際の時間は異なりますが、各自の重みで割ると、どちらも 3 つの仮想単位分進みます。重みが大きいタスクの仮想クロックはゆっくり進み、同じ進行度に追いつくにはより長い実行時間が必要です。
一般的な表現は以下の通りです。
Δ v i = Δ t i w i w 0
ここで w 0 は基準重みです。
例えば、nice 0 の基礎重みは 1024、nice 5 は 335 です。これら 2 つのみが同じ公平スケジューリング階層、同じ CPU 上で継続的に競合する場合、前者の理想的なシェアは 1024 / (1024 + 335) = 75.35% 程度になります。
この比率は競合集合の変化に応じて変わります。相手がブロックされると、前者はより多くのアイドル時間を使用できます。新しいタスクや cgroup 階層が追加されると、シェアを再計算する必要があります。nice は相対的な重みを表しており、固定された CPU パーセンテージではありません。
以下では、重みを幅として、実際の時間を面積として描画します。割り当てと重みを調整すると、高さによって誰がより多くの正規化サービスを得たかが示されます。
図を準備しています 再読み込み
同じ仮想進行度を得るために、どのくらいの CPU 時間が必要か? 仮想実行増分は、実時間に基準重みを掛け、タスクの重みで割ったものです。図では、幅が重み、高さが正規化サービス、面積が実際の CPU 時間を表します。
{"id":"scheduler-virtual-service","title":"同じ仮想進行度を得るために、どのくらいの CPU 時間が必要か?","summary":"仮想実行増分は、実時間に基準重みを掛け、タスクの重みで割ったものです。図では、幅が重み、高さが正規化サービス、面積が実際の CPU 時間を表します。","height":1000,"html":"<p class=\"eyebrow\" data-i18n=\"eyebrow\"></p><h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"controls\"><div class=\"control\"><label for=\"weight\" data-i18n=\"weight\"></label><output id=\"weight-value\" for=\"weight\"></output><input id=\"weight\" type=\"range\" min=\"1\" max=\"4\" step=\"1\" value=\"2\"></div><div class=\"control\"><label for=\"service\" data-i18n=\"service\"></label><output id=\"service-value\" for=\"service\"></output><input id=\"service\" type=\"range\" min=\"0\" max=\"9\" step=\"0.05\" value=\"6\"></div></div><div class=\"service-plot\" role=\"img\" data-i18n-label=\"chart\"><span class=\"axis-top\">Δv = 9</span><span class=\"axis-bottom\">0</span><div class=\"service-areas\"><div id=\"area-a\" class=\"service-area\"><b>A</b></div><div id=\"area-b\" class=\"service-area other\"><b>B</b></div><i id=\"level-a\"></i><i id=\"level-b\"></i></div></div><p class=\"caption\" data-i18n=\"geometry\"></p><div class=\"metrics\"><div><span data-i18n=\"clockA\"></span><strong id=\"virtual-a\"></strong></div><div><span data-i18n=\"clockB\"></span><strong id=\"virtual-b\"></strong></div></div><div class=\"formula\"><div id=\"formula-a\"></div><div id=\"formula-b\"></div></div><div class=\"actions\"><button id=\"balance\" data-i18n=\"balance\"></button></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"equal\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"aheadA\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"aheadB\"></p></div><details class=\"scope\"><summary data-i18n=\"scope\"></summary><p class=\"note\" data-i18n=\"note\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.note,.caption{font-size:12px;color:var(--muted);margin-top:14px}.controls{display:grid;gap:12px;margin:18px 0}.control{display:grid;grid-template-columns:1fr auto;gap:6px 12px;align-items:center;font-size:13px}.control input{grid-column:1/3;width:100%;margin:0}.control output{color:var(--accent);font-variant-numeric:tabular-nums}.presets{display:flex;flex-wrap:wrap;gap:4px}.presets button{flex:1 1 auto;min-width:min-content}.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}.actions button{flex:1;min-width:0;min-height:42px;font-size:13px}.copy{display:grid;margin-top:16px;font-size:14px}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.metrics{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:16px 0}.metrics>div{min-width:0;border-left:2px solid var(--accent);padding-left:12px}.metrics span{display:block;color:var(--muted);font-size:12px;min-height:2.8em}.metrics strong{display:block;font-size:22px;font-weight:500;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.formula{font-size:13px;overflow-wrap:anywhere;border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-variant-numeric:tabular-nums}.formula div+div{margin-top:6px}.two{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:18px}.card{min-width:0;border:1px solid var(--rule);border-radius:9px;padding:14px}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card code{font-size:12px;overflow-wrap:anywhere}.card .value{font-size:24px;font-weight:500;font-variant-numeric:tabular-nums}.card small{display:block;color:var(--muted);font-size:12px}.card button{width:100%;min-width:0;font-size:13px;min-height:42px}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{accent-color:var(--accent);width:16px;height:16px;flex-shrink:0}.pill{display:inline-block;padding:3px 8px;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:12px}.plot{position:relative;margin:18px 0}.plot svg{width:100%;height:auto;overflow:visible}.plot text{font-size:12px;fill:var(--muted)}.plot .ink{fill:var(--ink)}.plot .blue{fill:var(--accent);stroke:var(--accent)}.plot .gold{fill:var(--second);stroke:var(--second)}.plot .area{fill:var(--accent-soft);stroke:var(--accent);transition:all .25s}.plot .other{fill:var(--second-soft);stroke:var(--second)}.plot line,.plot path,.plot rect,.plot circle{transition:all .25s}.legend{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:12px;color:var(--muted);margin-top:10px}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:5px}.legend .gold:before{background:var(--second)}details{margin-top:14px;font-size:12px;color:var(--muted)}summary{cursor:pointer;padding:8px 0}button:disabled{cursor:default}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.card button{font-size:12px}.metrics{gap:10px}.metrics strong{font-size:20px}.actions button{font-size:12px}.plot text{font-size:13px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n\n.service-plot{height:230px;position:relative;padding:25px 0 25px 28px}.service-areas{height:180px;position:relative;display:flex;gap:16px;align-items:flex-end;border-bottom:1px solid var(--rule)}.service-area{position:relative;background:var(--accent-soft);border:0;box-shadow:inset 0 0 0 1px var(--accent);transition:width .25s,height .25s;min-height:0}.service-area.other{background:var(--second-soft);box-shadow:inset 0 0 0 1px var(--second)}.service-area b{position:absolute;bottom:-26px;left:50%;transform:translateX(-50%);font-size:13px;font-weight:600}.axis-top,.axis-bottom{position:absolute;left:0;font-size:11px;color:var(--muted)}.axis-top{top:0}.axis-bottom{bottom:19px}.service-areas>i{position:absolute;left:0;right:0;border-top:1px dashed var(--accent);pointer-events:none;transition:bottom .25s}#level-b{border-color:var(--second)}","js":"const $=s=>document.querySelector(s),num=(n,d=2)=>viz.number(n,d);\nfunction draw(){const w=+$('#weight').value,t=+$('#service').value,b=9-t,va=t/w,vb=b;$('#weight-value').textContent=w+' : 1';$('#service-value').textContent=num(t,2)+' / 9 ms';$('#area-a').style.width='calc((100% - 16px) * '+w/5+')';$('#area-b').style.width='calc((100% - 16px) / 5)';$('#area-a').style.height=va*20+'px';$('#area-b').style.height=vb*20+'px';$('#level-a').style.bottom=va*20+'px';$('#level-b').style.bottom=vb*20+'px';$('#virtual-a').textContent=num(va);$('#virtual-b').textContent=num(vb);$('#formula-a').textContent='ΔvA = '+num(t)+' / '+w+' = '+num(va);$('#formula-b').textContent='ΔvB = '+num(b)+' / 1 = '+num(vb);$('#explanation').textContent=viz.t(Math.abs(va-vb)<1e-8?'equal':va>vb?'aheadA':'aheadB');}\nfor(const id of ['weight','service'])$('#'+id).addEventListener('input',draw);$('#balance').addEventListener('click',()=>{$('#service').value=9*(+$('#weight').value)/(+$('#weight').value+1);draw();});draw();\n","audio":false,"strings":{"aheadA":"A が高い:各自の重みに対して、A はより多くのサービスを得ています。実時間を増やすと面積ブロックが高くなり、重みを増やすと、同じ時間に対応する高さは下がります。","aheadB":"B が高い:各自の重みに対して、B はより多くのサービスを得ています。時間の分配を調整するか、A の重みを変更して、高さがどのように近づいていくか観察します。","balance":"重みに基づいてこの 9 ms を分配","chart":"幅は重み、高さは仮想サービス、面積は実際の CPU 時間","clockA":"A の仮想サービス Δv","clockB":"B の仮想サービス Δv","equal":"同じ高さは、同じ正規化サービスを表します。重みが大きいほどブロックは広くなり、同じ高さに達するにはより多くの実時間が必要です。","eyebrow":"01 · 公平な計測","geometry":"幅は固定単位で重みを表します。高さは Δv、面積は実際の実行時間です。同じ高さであることが、正規化サービスが同じであることを意味します。","heading":"同じ仮想進行度を得るために、どのくらいの CPU 時間が必要か?","intro":"合計 9 ms を割り当てます。A の重みと実時間を調整し、2 つの面積ブロックの高さを比較します。B の重みは固定で 1 です。","note":"単一 CPU、2 つのタスクが継続的に実行可能、基準重みを 1 に正規化。図は計測式を説明するものであり、実際のスケジューリングの選択をシミュレートするものではありません。nice、グループ階層、ウェイクアップは実際の競合を変更します。","scope":"モデルの範囲と規約","service":"A に割り当てられた実時間","weight":"重み A : B"}}
古典的な CFS は、vruntime を用いて「正規化サービスを得た量が最も少ないタスク」に近づき、遅れているタスクを優先して補填します。これにより長期シェアを説明できますが、短いリクエストがどれだけ速く応答を得るべきかを十分に表現しているわけではありません。EEVDF はこれに基づき、実行資格 と合格したリクエストの順序 を分離します。
3. EEVDF:資格の閾値と仮想デッドラインの役割分担
ステップ 1:lag を用いて資格を判断する
固定された実行可能集合の簡易モデルにおいて、タスクの仮想実行時間を vᵢ、重みを wᵢ、システム仮想時間をその加重平均 V とします。タスクのサービス差額は以下のように書けます。
V = ∑ i w i ∑ i w i v i , lag i = w i ( V − v i )
lag が 0 でも資格があります。正の lag は、公平な基準に対してサービスが不足していることを示し、選択に参加する資格があります。負の lag は、すでに先行していることを示し、一時的に短いリクエストだけで他のタスクをプリエンプトすべきではありません。
ステップ 2:合格したリクエスト間で期限を比較する
現在のリクエストが実際の実行量 rᵢ を希望する場合、その仮想デッドラインは以下の式で理解できます。
d i = v i , start + r i w i w 0
この式は、新しいリクエストが期限を確立するときの関係を記述しており、このとき vᵢ はそのリクエストの開始仮想位置です。リクエストの実行中に vruntime は引き続き増加しますが、すでに確定された deadline はそれに伴って後方にシフトしてはいけません。新しいリクエストのみが期限を再設定します。以下のスナップショットは、各タスクのリクエスト起点の状態を選択し、直接比較できるようにしています。
合格したエンティティの中では、仮想デッドラインが早い方を優先して選択します。ここでいう deadline は仮想サービス座標 であり、壁時計時刻でも、SCHED_DEADLINE のリアルタイム完了保証でもありません。
シミュレーション:なぜ最後に B が選ばれるか
3 つの等重みタスクを考えます。基準重みを 1 に正規化します。
タスク v リクエスト量 r d = v + r V − v 選択資格
A 98 10 108 2 あり
B 99 5 104 1 あり
C 103 0.25 103.25 -3 なし
このとき V = (98 + 99 + 103) / 3 = 100 です。vruntime のみが最小であれば A が選ばれます。deadline のみが最小であれば C が選ばれます。EEVDF はこのスナップショットで B を選択します。C の短いリクエストは、すでに多くのサービスを得ているという事実を相殺できません。一方、B は A よりも期限が切れている合格したリクエストであり、優先的な応答を得ます。これが、2 つの判断を分離する必要がある理由です。
EEVDF の選択:まず資格、次に仮想デッドラインの比較
A
vruntime = 98
仮想 deadline = 108
資格あり、lag > 0
B
vruntime = 99
仮想 deadline = 104
資格あり、選択される
C
vruntime = 103
仮想 deadline = 103.25
サービス先行、一時的に参加せず
等重みスナップショット V = 100:C は deadline が最も早いものの、資格を満たさない
A は B よりも実行が不足している。B の合格リクエストの方が期限が早く切れるため、B が選択される
これは固定タスク集合の教育用スナップショットです。実際の実装では、グループ階層、スリープの会計処理、および現在のリクエスト保護も処理します。
C のサービス位置を変更すると資格に影響します。B のリクエスト量を変更すると期限に影響しますが、すでに得られたサービスは変わりません。パラメータの調整は毎回新しいリクエストスナップショットとなります。
図を準備しています 再読み込み
期限が最も早いのに、なぜまだ順番が回ってこないのか? EEVDF はまず lag ≥ 0 で資格をフィルタリングし、次に仮想デッドラインを比較します。デフォルトのスナップショットでは C の期限が最も早いものの不合格であり、合格集合の中で B が勝利します。
{"id":"scheduler-eevdf","title":"期限が最も早いのに、なぜまだ順番が回ってこないのか?","summary":"EEVDF はまず lag ≥ 0 で資格をフィルタリングし、次に仮想デッドラインを比較します。デフォルトのスナップショットでは C の期限が最も早いものの不合格であり、合格集合の中で B が勝利します。","height":1000,"html":"<p class=\"eyebrow\" data-i18n=\"eyebrow\"></p><h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"controls\"><div class=\"control\"><label for=\"position\" data-i18n=\"position\"></label><output id=\"position-value\" for=\"position\"></output><input id=\"position\" type=\"range\" min=\"94\" max=\"106\" step=\"0.25\" value=\"103\"></div><div class=\"control\"><label for=\"request\" data-i18n=\"request\"></label><output id=\"request-value\" for=\"request\"></output><input id=\"request\" type=\"range\" min=\"0.25\" max=\"12\" step=\"0.25\" value=\"5\"></div></div><div class=\"eevdf-chart\"><div id=\"system-v\"><span id=\"system-label\"></span></div><div class=\"request-row\" id=\"task-A\"><header><strong>A</strong><span id=\"status-A\"></span></header><div class=\"request-rail\"><i id=\"request-A\"></i><b class=\"v-point\" id=\"v-A\"></b><b class=\"d-point\" id=\"d-A\"></b></div><div class=\"values\" id=\"values-A\"></div></div><div class=\"request-row\" id=\"task-B\"><header><strong>B</strong><span id=\"status-B\"></span></header><div class=\"request-rail\"><i id=\"request-B\"></i><b class=\"v-point\" id=\"v-B\"></b><b class=\"d-point\" id=\"d-B\"></b></div><div class=\"values\" id=\"values-B\"></div></div><div class=\"request-row\" id=\"task-C\"><header><strong>C</strong><span id=\"status-C\"></span></header><div class=\"request-rail\"><i id=\"request-C\"></i><b class=\"v-point\" id=\"v-C\"></b><b class=\"d-point\" id=\"d-C\"></b></div><div class=\"values\" id=\"values-C\"></div></div><div class=\"scale\"><span>94</span><span>118</span></div></div><p class=\"caption\" data-i18n=\"markers\"></p><div class=\"formula\" id=\"formula\"></div><div class=\"winner\"><span data-i18n=\"winner\"></span><strong id=\"winner\"></strong></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"whyB\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"whyA\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"whyC\"></p></div><details class=\"scope\"><summary data-i18n=\"scope\"></summary><p class=\"note\" data-i18n=\"note\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.note,.caption{font-size:12px;color:var(--muted);margin-top:14px}.controls{display:grid;gap:12px;margin:18px 0}.control{display:grid;grid-template-columns:1fr auto;gap:6px 12px;align-items:center;font-size:13px}.control input{grid-column:1/3;width:100%;margin:0}.control output{color:var(--accent);font-variant-numeric:tabular-nums}.presets{display:flex;flex-wrap:wrap;gap:4px}.presets button{flex:1 1 auto;min-width:min-content}.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}.actions button{flex:1;min-width:0;min-height:42px;font-size:13px}.copy{display:grid;margin-top:16px;font-size:14px}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.metrics{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:16px 0}.metrics>div{min-width:0;border-left:2px solid var(--accent);padding-left:12px}.metrics span{display:block;color:var(--muted);font-size:12px;min-height:2.8em}.metrics strong{display:block;font-size:22px;font-weight:500;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.formula{font-size:13px;overflow-wrap:anywhere;border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-variant-numeric:tabular-nums}.formula div+div{margin-top:6px}.two{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:18px}.card{min-width:0;border:1px solid var(--rule);border-radius:9px;padding:14px}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card code{font-size:12px;overflow-wrap:anywhere}.card .value{font-size:24px;font-weight:500;font-variant-numeric:tabular-nums}.card small{display:block;color:var(--muted);font-size:12px}.card button{width:100%;min-width:0;font-size:13px;min-height:42px}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{accent-color:var(--accent);width:16px;height:16px;flex-shrink:0}.pill{display:inline-block;padding:3px 8px;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:12px}.plot{position:relative;margin:18px 0}.plot svg{width:100%;height:auto;overflow:visible}.plot text{font-size:12px;fill:var(--muted)}.plot .ink{fill:var(--ink)}.plot .blue{fill:var(--accent);stroke:var(--accent)}.plot .gold{fill:var(--second);stroke:var(--second)}.plot .area{fill:var(--accent-soft);stroke:var(--accent);transition:all .25s}.plot .other{fill:var(--second-soft);stroke:var(--second)}.plot line,.plot path,.plot rect,.plot circle{transition:all .25s}.legend{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:12px;color:var(--muted);margin-top:10px}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:5px}.legend .gold:before{background:var(--second)}details{margin-top:14px;font-size:12px;color:var(--muted)}summary{cursor:pointer;padding:8px 0}button:disabled{cursor:default}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.card button{font-size:12px}.metrics{gap:10px}.metrics strong{font-size:20px}.actions button{font-size:12px}.plot text{font-size:13px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n\n.eevdf-chart{position:relative;padding-top:30px;margin-top:20px}.request-row{position:relative;padding:10px 0}.request-row header{display:flex;justify-content:space-between;gap:10px;font-size:12px}.request-row header span{color:var(--muted)}.request-rail{height:22px;position:relative;margin:8px 0;border-bottom:1px solid var(--rule)}.request-rail>i{position:absolute;height:6px;bottom:0;transform:translateY(50%);background:var(--accent);transition:left .25s,width .25s}.v-point{position:absolute;width:10px;height:10px;border-radius:50%;bottom:0;transform:translate(-50%,50%)}.d-point{position:absolute;width:3px;height:18px;bottom:0;transform:translate(-50%,50%)}.v-point,.d-point{background:var(--accent);transition:left .25s}.values{font-size:12px;font-variant-numeric:tabular-nums;color:var(--muted);margin-top:12px;min-height:3.4em}.scale{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}#system-v{position:absolute;top:25px;bottom:25px;border-left:1px dashed var(--accent);pointer-events:none;transition:left .25s}#system-label{position:absolute;top:-25px;transform:translateX(-50%);white-space:nowrap;font-size:12px;color:var(--accent);background:var(--paper)}.winner{display:flex;justify-content:space-between;align-items:center;padding:14px;margin-top:16px;background:var(--accent-soft);border-radius:8px}.winner strong{font-size:26px;color:var(--accent)}","js":"const $=s=>document.querySelector(s),f=n=>viz.number(n,2),x=n=>(n-94)/24*100;\nfunction draw(){const v=[98,99,+$('#position').value],r=[10,+$('#request').value,.25],V=v.reduce((a,b)=>a+b)/3,d=v.map((n,i)=>n+r[i]),eligible=v.map(n=>n<=V+1e-9);let winner=-1;for(let i=0;i<3;i++)if(eligible[i]&&(winner<0||d[i]<d[winner]))winner=i;$('#position-value').textContent=f(v[2]);$('#request-value').textContent=f(r[1]);$('#system-v').style.left=x(V)+'%';$('#system-label').textContent='V = '+f(V);for(let i=0;i<3;i++){const k='ABC'[i],color=winner===i?'var(--second)':eligible[i]?'var(--accent)':'var(--muted)';$('#request-'+k).style.left=x(v[i])+'%';$('#request-'+k).style.width=(d[i]-v[i])/24*100+'%';$('#request-'+k).style.background=color;$('#request-'+k).style.opacity=eligible[i]?'1':'.25';$('#v-'+k).style.left=x(v[i])+'%';$('#v-'+k).style.background=color;$('#d-'+k).style.left=x(d[i])+'%';$('#d-'+k).style.background=color;$('#values-'+k).textContent='v '+f(v[i])+' → d '+f(d[i])+' · lag '+f(V-v[i]);$('#status-'+k).textContent=viz.t(eligible[i]?'eligible':'ineligible');$('#status-'+k).style.color=color;}$('#formula').textContent='V = ('+v.map(f).join(' + ')+') / 3 = '+f(V);$('#winner').textContent='ABC'[winner];$('#explanation').textContent=viz.t('why'+'ABC'[winner]);}\n$('#position').addEventListener('input',draw);$('#request').addEventListener('input',draw);draw();\n","audio":false,"strings":{"eligible":"資格あり","eyebrow":"02 · EEVDF","heading":"期限が最も早いのに、なぜまだ順番が回ってこないのか?","ineligible":"一時的に不合格","intro":"C のサービス位置をドラッグするか、B のリクエストを短くします。資格の閾値 V と仮想デッドラインがそれぞれ選択にどのように影響するかを観察します。","markers":"丸は v、短い縦線は d、中間の線分はリクエスト長さを表します。破線はシステム仮想時間 V です。金色は選択されたタスク、灰色は不合格を表します。","note":"3 つの等重みタスクの新しいリクエストスナップショット、基準重みは 1。A の v=98、r=10、B の v=99、C の r=0.25。パラメータ調整のたびにスナップショットを再構築しており、実行時に既存の deadline を継続的に再計算するものではありません。同期限の場合はアルファベット順の選択がデモ上の規約です。","position":"C の仮想実行位置 vC","request":"B のリクエスト量 rB","scope":"モデルの範囲と規約","whyA":"A は資格があり、その期限は他の合格リクエストより遅くありません。サービスを得すぎたタスクは、期限が早いからといって資格の閾値を越えることはできません。","whyB":"B は合格集合の中で期限が最も早いです。リクエストを短くすると期限は変わりますが、タスクがすでに先行しているサービス差額を直接消去するわけではありません。","whyC":"C は現在 V の左側または線上にあり、資格を備えています。その短いリクエストも期限が最も早いため、選択されます。","winner":"本スナップショットで選択"}}
この小さなモデルを実行して結果を確認できます。Linux のウェイクアップ、階層、マイグレーションロジックをシミュレートするものではありません。
from fractions import Fraction as F
tasks = {
" A" : { " w" : F ( 1 ) , " v" : F ( 98 ) , " r" : F ( 10 ) } ,
" B" : { " w" : F ( 1 ) , " v" : F ( 99 ) , " r" : F ( 5 ) } ,
" C" : { " w" : F ( 1 ) , " v" : F ( 103 ) , " r" : F ( 1 , 4 ) } ,
}
V = sum ( t [ " w" ] * t [ " v" ] for t in tasks . values ( ) ) / sum (
t [ " w" ] for t in tasks . values ( )
)
eligible = { }
for name , t in tasks . items ( ) :
lag = t [ " w" ] * ( V - t [ " v" ] )
deadline = t [ " v" ] + t [ " r" ] / t [ " w" ]
print ( name , " lag=" , lag , " deadline=" , deadline )
if lag >= 0 :
eligible [ name ] = deadline
assert V == 100
assert min ( eligible , key = eligible . get ) == " B"
教育用モデルからカーネル実装へ
リクエスト量を短くすることは応答順序に影響しますが、タスクの長期重みを自動的に増やすわけではありません。実際のカ kernel は、タスクが「先行するまで実行して、短時間スリープし、ウェイクアップ後に再び信用を得る」という方法で公平性を破壊するのを防がなければなりません。スリープ中の lag の保存、減衰、および遅延出隊の会計処理は、この制約を維持する手段です。遅延出隊中の会計エンティティは、イベントを待っているタスクがユーザーコードを実行できることを意味しません。
実際の実装では検索コストの問題も解決する必要があります。ローカルの kernel/sched/fair.c に関連する木構造は deadline で整理されており、サブツリー vruntime 情報を用いて合格エンティティの検索を剪枝します。pick_eevdf() は現在のリクエスト保護も処理します。
階層化されたグループエンティティ、入隊/出隊の修正、および整数精度により、V の維持は固定集合の平均値よりも複雑になります。ソースコードを読む際は、まず「資格と期限」という 2 つの判断を確認し、その後これらの実装の詳細を追跡することをお勧めします。EEVDF ドキュメント 、CFS デザイン
4. スケジューリングポリシーとスケジューリングクラス:3 種類の deadline を混同しない
通常、タスクは SCHED_OTHER を使用します。カーネルでは SCHED_NORMAL と呼ばれることも多いです。公平クラス内部では EEVDF が採用されており、アプリケーションが SCHED_EEVDF に変更して初めて有効になるような一般的なポリシーは存在しません。リアルタイムポリシーと拡張スケジューリングは別の選択肢です。
ポリシーまたはメカニズム 割り当てルール 重要な境界
SCHED_OTHER / BATCH 公平シェア;BATCH はバッチ処理の傾向を表す 通常の優先度と nice 重みは、より高いスケジューリングクラスを上書きできない
SCHED_IDLE 公平クラス内の極低重みポリシー CPU にアイドルタスクを実行させる場合とは異なる
SCHED_FIFO 同優先度で FIFO;ブロック、明示的な譲渡、またはプリエンプトされるまで実行可能 通常の RR タイムスライスがないため、システム帯域幅の制約を受けないわけではない
SCHED_RR リアルタイム優先度 + 同優先度のラウンドロビン タイムスライスによる回転が、アプリケーションの完了期限を保証するものではない
SCHED_DEADLINE EDF 選択と CBS 実行予算管理 パラメータ、准入制御、実行負荷が実行可能性を共同で決定する
sched_ext 対応する BPF スケジューラを有効化してロードした後、拡張ポリシーが関連タスクを管理する 通常タスクを乗っ取るかどうかはモードに依存する。エラーが発生するとフォールバックがトリガーされる
通常、deadline クラスはリアルタイムクラスより先に、リアルタイムクラスは公平クラスより先に位置します。カーネルには stop などの内部クラスもあり、sched_ext を有効化する際は実行モードとクラス順序を合わせて読む必要があり、3 行の表をすべてと捉えてはいけません。
対照:スケジューリング期限とビジネスタイムアウト
runtime=2 ms、deadline=10 ms、period=10 ms を例に挙げます。タスクは、各周期で最大 2 ms の CPU サービスが必要であり、対応する期限を要求します。パラメータは runtime ≤ deadline ≤ period などの制約を満たす必要があり、准入チェックも通過する必要があります。
これは、アプリケーションが 10 ms ごとに自動的に関数を呼び出すことを意味するものではありません。アプリケーションは依然としてワークを解放し、実行予算を制御し、ブロックを分析して、期限が実現可能かどうかを議論する必要があります。
名前に「期限」が含まれる 実際には何を制約するか
EEVDF 仮想 deadline 公平サービス座標内でのリクエストの順序
SCHED_DEADLINE の deadline リアルタイムタスクのスケジューリング期限
RPC タイムアウト キュー待ち、実行、ネットワークなどが共同で構成するエンドツーエンドの待機時間
これら 3 種類の期限は異なる階層に属しており、相互に代替できません。SCHED_DEADLINE 、sched_ext
5. cgroup:重みが競合シェアを決定し、クォータが利用可能な上限を決定する
グループ重み:まずグループに分配し、次にタスクに分配する
公平スケジューリングは階層化できます。ルート配下に A、B の 2 つのグループがあり、重みが 2:1 で、A 内に 2 つの等重みで継続的に実行可能なタスク、B 内に 1 つのタスクがあるとします。1 つの CPU のみ、クォータがない場合を考慮すると、A はまず約 2/3 を獲得し、グループ内でそれぞれ 1/2 に分配されるため、3 つのタスクはそれぞれ約 1/3 になります。A にタスクが 1 つ追加されると、A は依然として約 2/3 を占め、内部ではそれぞれ約 2/9 になります。B の唯一のタスクは依然として約 1/3 です。グループ隔離により、「スレッドをいくつか作成する」ことが別のグループのシェアを直接侵害する必要はありません。
クォータ:マルチコアで予算を共有して消費する
cpu.weight は相対的なシェアを示すものであり、リソースの予約や厳格な上限ではありません。cpu.max は、周期内でのグループの総 CPU 時間を制限します。例えば 50000 100000 は、100 ms の周期で最大 50 ms の CPU 時間を使用できることを意味します。2 つのスレッドが 2 つの CPU 上で同時に実行する場合、合計 50 ms の予算を約 25 ms で使い果たし、その後補充を待ってスロットルされます。「各 CPU が 50 ms 使用可能」と理解してはいけません。
同じクォータでも、2 つの CPU では予算をより早く使い果たせる
cpu.max = 50000 100000:各周期の総予算 50 ms、周期 100 ms
CPU 0
実行 25 ms
予算枯渇、補充待ち
CPU 1
実行 25 ms
予算枯渇、補充待ち
0
25 ms
100 ms
バーストなし、周期開始時に予算が完全に満たされている、2 つのスレッドが継続的に並列実行すると簡略化。実際には配信粒度などの影響を受ける。
並列数を調整し、壁時計時間を周期の境界までドラッグします。各 CPU の実行帯は、同じ予算から時間を差し引きます。
図を準備しています 再読み込み
CPU が多いほど、なぜ早くスロットルされるのか? 複数の CPU が同じグループ予算を消費します。50 ms の CPU 時間が 2 つの CPU で並列に消費されるため、約 25 ms の壁時計時間で使い果たされ、その後周期の補充を待ちます。
{"id":"scheduler-quota","title":"CPU が多いほど、なぜ早くスロットルされるのか?","summary":"複数の CPU が同じグループ予算を消費します。50 ms の CPU 時間が 2 つの CPU で並列に消費されるため、約 25 ms の壁時計時間で使い果たされ、その後周期の補充を待ちます。","height":1000,"html":"<p class=\"eyebrow\" data-i18n=\"eyebrow\"></p><h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"controls\"><div class=\"control\"><label for=\"quota\" data-i18n=\"quota\"></label><output id=\"quota-value\" for=\"quota\"></output><input id=\"quota\" type=\"range\" min=\"10\" max=\"200\" step=\"5\" value=\"50\"></div><div class=\"control\"><label for=\"cpus\" data-i18n=\"cpus\"></label><output id=\"cpus-value\" for=\"cpus\"></output><input id=\"cpus\" type=\"range\" min=\"1\" max=\"4\" step=\"1\" value=\"2\"></div><div class=\"control\"><label for=\"time\" data-i18n=\"time\"></label><output id=\"time-value\" for=\"time\"></output><input id=\"time\" type=\"range\" min=\"0\" max=\"200\" step=\"1\" value=\"25\"></div></div><div class=\"budget\"><div><span data-i18n=\"remaining\"></span><strong id=\"remaining\"></strong></div><div class=\"budget-track\"><i id=\"budget-fill\"></i></div><p id=\"period\"></p></div><div class=\"timeline\"><div id=\"probe\"></div><div class=\"lane\" id=\"lane-0\"><code>CPU 0</code><div class=\"lane-track\"><i class=\"run first\"></i><i class=\"run second\"></i><span class=\"inactive\" data-i18n=\"inactive\"></span></div></div><div class=\"lane\" id=\"lane-1\"><code>CPU 1</code><div class=\"lane-track\"><i class=\"run first\"></i><i class=\"run second\"></i><span class=\"inactive\" data-i18n=\"inactive\"></span></div></div><div class=\"lane\" id=\"lane-2\"><code>CPU 2</code><div class=\"lane-track\"><i class=\"run first\"></i><i class=\"run second\"></i><span class=\"inactive\" data-i18n=\"inactive\"></span></div></div><div class=\"lane\" id=\"lane-3\"><code>CPU 3</code><div class=\"lane-track\"><i class=\"run first\"></i><i class=\"run second\"></i><span class=\"inactive\" data-i18n=\"inactive\"></span></div></div><div class=\"ticks\"><span>0</span><span>100</span><span>200 ms</span></div></div><div class=\"legend\"><span data-i18n=\"running\"></span><span class=\"gold\" data-i18n=\"throttled\"></span></div><div class=\"formula\" id=\"formula\"></div><div class=\"actions\"><button id=\"play\" aria-pressed=\"false\"><span id=\"play-label\"></span><span class=\"reserve\" aria-hidden=\"true\" data-i18n=\"play\"></span><span class=\"reserve\" aria-hidden=\"true\" data-i18n=\"pause\"></span></button><button id=\"reset\" data-i18n=\"reset\"></button></div><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"available\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"exhausted\"></p></div><details class=\"scope\"><summary data-i18n=\"scope\"></summary><p class=\"note\" data-i18n=\"note\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.note,.caption{font-size:12px;color:var(--muted);margin-top:14px}.controls{display:grid;gap:12px;margin:18px 0}.control{display:grid;grid-template-columns:1fr auto;gap:6px 12px;align-items:center;font-size:13px}.control input{grid-column:1/3;width:100%;margin:0}.control output{color:var(--accent);font-variant-numeric:tabular-nums}.presets{display:flex;flex-wrap:wrap;gap:4px}.presets button{flex:1 1 auto;min-width:min-content}.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}.actions button{flex:1;min-width:0;min-height:42px;font-size:13px}.copy{display:grid;margin-top:16px;font-size:14px}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.metrics{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:16px 0}.metrics>div{min-width:0;border-left:2px solid var(--accent);padding-left:12px}.metrics span{display:block;color:var(--muted);font-size:12px;min-height:2.8em}.metrics strong{display:block;font-size:22px;font-weight:500;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.formula{font-size:13px;overflow-wrap:anywhere;border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-variant-numeric:tabular-nums}.formula div+div{margin-top:6px}.two{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:18px}.card{min-width:0;border:1px solid var(--rule);border-radius:9px;padding:14px}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card code{font-size:12px;overflow-wrap:anywhere}.card .value{font-size:24px;font-weight:500;font-variant-numeric:tabular-nums}.card small{display:block;color:var(--muted);font-size:12px}.card button{width:100%;min-width:0;font-size:13px;min-height:42px}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{accent-color:var(--accent);width:16px;height:16px;flex-shrink:0}.pill{display:inline-block;padding:3px 8px;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:12px}.plot{position:relative;margin:18px 0}.plot svg{width:100%;height:auto;overflow:visible}.plot text{font-size:12px;fill:var(--muted)}.plot .ink{fill:var(--ink)}.plot .blue{fill:var(--accent);stroke:var(--accent)}.plot .gold{fill:var(--second);stroke:var(--second)}.plot .area{fill:var(--accent-soft);stroke:var(--accent);transition:all .25s}.plot .other{fill:var(--second-soft);stroke:var(--second)}.plot line,.plot path,.plot rect,.plot circle{transition:all .25s}.legend{display:flex;flex-wrap:wrap;gap:6px 16px;font-size:12px;color:var(--muted);margin-top:10px}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:5px}.legend .gold:before{background:var(--second)}details{margin-top:14px;font-size:12px;color:var(--muted)}summary{cursor:pointer;padding:8px 0}button:disabled{cursor:default}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.card button{font-size:12px}.metrics{gap:10px}.metrics strong{font-size:20px}.actions button{font-size:12px}.plot text{font-size:13px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n\n.budget{padding:14px;background:var(--accent-soft);border-radius:8px}.budget>div:first-child{display:flex;justify-content:space-between;gap:12px;font-size:13px}.budget strong{font-size:22px}.budget p{margin:8px 0 0;font-size:12px;color:var(--muted)}.budget-track{height:10px;background:var(--rule);border-radius:5px;overflow:hidden;margin-top:10px}.budget-track i{display:block;height:100%;background:var(--accent);transition:width .2s}.timeline{position:relative;margin:20px 0 0;padding-top:5px}.lane{display:grid;grid-template-columns:52px 1fr;gap:8px;align-items:center;margin:10px 0;font-size:12px}.lane-track{height:26px;position:relative;background:var(--second-soft);border-radius:4px;overflow:hidden}.run{position:absolute;top:0;height:100%;background:var(--accent);transition:width .25s}.run.first{left:0}.run.second{left:50%}.lane.off .lane-track{background:var(--surface)}.lane.off .run{visibility:hidden}.inactive{display:none;font-size:11px;text-align:center;color:var(--muted);line-height:26px}.lane.off .inactive{display:block}#probe{position:absolute;top:0;bottom:28px;width:2px;background:var(--ink);z-index:1;pointer-events:none}.ticks{margin-left:60px;display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}#play{display:grid}#play>span{grid-area:1/1}#quota-value{width:8.4em;white-space:nowrap;text-align:right}#time-value{width:5em;text-align:right}.formula{min-height:calc(3.2em + 13px)}.budget strong{white-space:nowrap}@media(max-width:480px){.budget>div:first-child{display:grid;grid-template-columns:1fr;gap:6px}.budget strong{justify-self:end}}","js":"const $=s=>document.querySelector(s);function playing(on){$('#play').setAttribute('aria-pressed',String(on));$('#play-label').textContent=viz.t(on?'pause':'play');}\nfunction draw(){const q=+$('#quota').value,n=+$('#cpus').value,t=+$('#time').value,local=t%100,used=Math.min(q,n*local),remaining=q-used,run=Math.min(100,q/n),blocked=q<n*100&&local>=q/n;$('#quota-value').textContent=q+' ms / 100 ms';$('#cpus-value').textContent=n;$('#time-value').textContent=t+' ms';$('#remaining').textContent=viz.number(remaining,1)+' ms';$('#budget-fill').style.width=100*remaining/q+'%';$('#period').textContent=viz.t('period')+' '+(Math.floor(t/100)+1)+' · '+local+' / 100 ms';for(let i=0;i<4;i++){const e=$('#lane-'+i);e.classList.toggle('off',i>=n);e.querySelectorAll('.run').forEach(b=>b.style.width=run/2+'%');}$('#probe').style.left='calc(60px + (100% - 60px) * '+t/200+')';$('#formula').textContent=''+q+' ms / '+n+' CPU = '+viz.number(q/n,1)+' ms';$('#explanation').textContent=viz.t(blocked?'exhausted':'available');}\nconst player=viz.playback({delay:100,canAdvance:()=>+$('#time').value<200,advance(){$('#time').value=Math.min(200,+$('#time').value+2);draw();},onChange:playing});for(const id of ['quota','cpus','time'])$('#'+id).addEventListener('input',()=>{player.pause();draw();});$('#play').addEventListener('click',()=>{if(+$('#time').value===200){$('#time').value=0;draw();}player.toggle();});$('#reset').addEventListener('click',()=>{player.pause();$('#time').value=0;draw();});playing(false);draw();\n","audio":false,"strings":{"available":"この時刻にはまだ予算があり、参加している CPU は引き続き実行できます。100 ms の境界で新しい周期が開きます。図の各青帯は、同じ予算から課金されます。","cpus":"同時に実行される CPU 数 N","exhausted":"予算はすでに枯渇しており、マシンに他のアイドル CPU があったとしても、このグループは補充を待つ必要があります。並列数を増やすと、同じ CPU 時間をより早く使い果たします。","eyebrow":"03 · 共有クォータ","heading":"CPU が多いほど、なぜ早くスロットルされるのか?","inactive":"参加していない","intro":"複数の CPU が同時に 1 つの予算から実行時間を差し引きます。クォータと並列数を調整し、時間をドラッグして、予算がいつ使い果たされ、いつ補充されるかを確認します。","note":"固定周期 P=100 ms、タスクは常に CPU が必要、競合なし、祖先の制限なし、バーストクォータなし。図は連続的な予算で計算されており、実際の帯域スライシングや少量の繰り越しは省略されています。Q/N が 1 つの周期を超える場合、この周期は使い果たされません。200 ms は第 3 周期の起点です。","pause":"一時停止","period":"周期","play":"再生","quota":"各周期の CPU 時間予算 Q","remaining":"現在の周期の残り予算","reset":"起点に戻る","running":"実行中","scope":"モデルの範囲と規約","throttled":"予算枯渇後のスロットル期間","time":"壁時計時間"}}
冒頭の遅いリクエストに戻りましょう。マシン全体に空き CPU があったとしても、この cgroup の現在の周期の予算はすでに使い果たされている可能性があります。供給に余力があっても、そのグループが使用できるクレジットがあるとは限りません。
調査時には、cpu.stat のスロットル回数/時間、cpu.max、およびリクエスト遅延を同じ時間ウィンドウで比較します。スロットルの増分はトラブルシューティングの手がかりを提供しますが、具体的な遅いリクエストについては、アプリケーションのロック、I/O、および上位層のキューと組み合わせて分析する必要があります。cgroup v2 CPU コントローラ
6. マルチコア配置:均等であることが必ずしも最速ではない
2 つの CPU がそれぞれ 1 つのタスクを実行している場合、1 つが混雑して 1 つがアイドルであるよりも合理的に見えますが、マイグレーションにはコストがかかります。タスクが CPU 0 でキャッシュを温めた直後に CPU 1 にマイグレートすると、局所性が失われます。データが主に別の NUMA ノードにある場合、メモリアクセスが遠くなる可能性もあります。SMT の兄弟スレッドは実行リソースを共有するため、機械的に 2 つの完全に独立したコアとして扱うことはできません。そのため、スケジューラはアイドル容量、キャッシュ、トポロジ、およびエネルギー消費の間でトレードオフを行います。
PELT などの歴史的な信号は、タスクの直近の実行/実行可能行動を推定するのに役立ちます。利用率、実行可能負荷、および加重負荷は同じ数値ではありません。タスクが CPU を非常に長く待機している場合、それはキュー待ちの圧力を示す可能性がありますが、実際に実行された割合のみが、消費された計算力を直接反映します。ある CPU が 100% であるという短いサンプリングは、マイグレーションが必ずしも利益をもたらすとは予測できず、それがシステム全体のボトルネックであることも保証しません。
ウェイクアップ配置、周期的な均衡化、およびアイドル CPU のプル作業は、異なる状況で発生します。アフィニティと cpuset は候補集合を縮小し、異種 CPU の能力とエネルギー消費ポリシーは選択をさらに変化させます。したがって、調査する際は、まずタスクが実際にどの CPU を使用できるかを確認し、負荷が不均等かどうかを説明してから、1 つの CPU にバインドされたタスクについては、他の CPU の数が増加してもキュー待ちが自動的に解消されるわけではないことを理解する必要があります。
7. 再現可能な読み取り専用観察
以下では、現在の Python プロセスのポリシー、nice、アフィニティ、および一部のスケジューリング統計のみをクエリし、ポリシーやシステム構成を変更することはありません。/proc/self/sched のフィールドはカーネル構成の影響を受けるため、固定された列数形式として解析すべきではありません。
import os
from pathlib import Path
print ( " policy:" , os . sched_getscheduler ( 0 ) )
print ( " nice:" , os . getpriority ( os . PRIO_PROCESS , 0 ) )
print ( " allowed CPUs:" , sorted ( os . sched_getaffinity ( 0 ) ) )
try :
for line in Path ( " /proc/self/sched" ) . read_text ( ) . splitlines ( ) :
if any ( key in line for key in (
" se.sum_exec_runtime" , " nr_switches" , " nr_voluntary_switches" ,
" nr_involuntary_switches" , " se.load.weight" , " policy" , " prio"
) ) :
print ( line )
except OSError as exc :
print ( " sched statistics unavailable:" , exc )
1 回の遅延を分解するには、イベントの発生、sched_wakeup、およびタスクが実際に sched_switch の next 位置に現れる時間をそれぞれ観測する必要があります。ウェイクアップから実行までの間隔は、実行キューの待機および関連するスケジューリング遅延を反映します。実行時間には、アプリケーション自体の作業時間も含まれます。これらは、レジスタとアドレス空間の切り替えの純粋なコストとは等しくありません。次の記事コンテキストスイッチ では、具体的な切り替えを通じてこれらの境界を説明します。