Z mapy ve [L5.4] McNaughton víš, že $P \,|\, \mathrm{pmtn}, \mathrm{prec} \,|\, C_{max}$ je NP-hard — preempce sama už nestačí, jakmile graf precedencí [L5.2] svazuje, co smí běžet kdy. Dnešní lekce je doložená zkoušková klasika (termín 1. 6. 2026; stejný typ úlohy padl 2. 6. 2021 i 9. 7. 2021, pokaždé jiná konkrétní instance): Muntz&Coffmanův úrovňový algoritmus. U zkoušky se po tobě chce přesně to, co říká zadání: levely při inicializaci, množina volných úloh $\mathcal{Z}$ v každé iteraci a Ganttův diagram.
Zdržením úlohy $T_j$ zdržíš celý řetěz jejích následníků. Nejhorší takový řetěz je nejdelší cesta z $T_j$ do koncové úlohy, měřená součtem $p_i$ (včetně $p_j$) — tomu se říká level úlohy. Je to dolní mez na čas, který od startu $T_j$ ještě uplyne do konce rozvrhu. Stejná myšlenka „kritické cesty“ jako nejdelší cesty v [L5.2] grafu temporálních omezení — tam dávala earliest starty, tady dává priority.
“Principle:
For $P2 \,|\,pmtn, prec\,|\, C_{\max}$ and $P \,|\,pmtn, forest\,|\, C_{\max}$, the algorithm is exact. For $P \,|\,pmtn, prec\,|\, C_{\max}$ approximation alg. with factor $r_{MC} = 2 - \frac{2}{R}$. Time complexity is $\mathcal{O}(n^2)$.
“Input: $R$, the number of parallel identical resources, $n$, the number of preemptive tasks, proc. times $(p_1, p_2, \ldots, p_n)$ and DAG. Output: $n$-elements vectors $s^1, \ldots, s^k, \ldots s^K$ and $z^1, \ldots, z^k, \ldots z^K$ where $s_i^k$ is the start time of the $k$-th part of task $T_i$ and $z_i^k$ is corresponding resource ID.”
Levely počítáš odzadu (od koncových úloh): koncová úloha má $L_i = p_i$, jinak $$L_i = p_i + \max\{L_j : T_i \to T_j\}.$$ Důležité pro běh algoritmu: jakmile úloha kus práce odvede, její aktuální level klesá — je to vždy zbývající $p_i$ plus nejdelší cesta dál, tedy $L_i$ minus odvedená práce.
Celou lekci si odpracujeme na téhle mini-instanci (vlastní, přiznáno — zkouškové instance tě čekají v T01 a T02), $R = 2$:
$L_5 = p_5 = 1$ (koncová). $L_4 = p_4 + L_5 = 2 + 1 = 3$. $L_1 = p_1 + L_5 = 3 + 1 = 4$ (jediný následník $T_1$ je $T_5$). $L_2 = p_2 + L_4 = 2 + 3 = 5$ a stejně $L_3 = 2 + 3 = 5$. Tedy $L = (4, 5, 5, 3, 1)$ — nejvíc „hoří“ $T_2$ a $T_3$, protože za nimi stojí řetěz $T_4 \to T_5$.
V každém okamžiku $t$ algoritmus sestaví $\mathcal{Z}$ = volné úlohy (nedokončené, jejichž všichni předchůdci jsou hotovi) a rozdává procesory od nejvyššího aktuálního levelu dolů. Háček nastane, když má víc úloh stejný level, než zbývá procesorů.
Chyták — žádné dvě. Ať vybereš kterékoli, třetí zaostane, její level zůstane vyšší a za chvíli bys ji stejně musel prohodit zpět: vznikla by smršť preempcí. Muntz&Coffman to řeší elegantně: všechny tři poběží „současně“ a každá dostane část kapacity $\beta = h/|\mathcal{S}| = 2/3$ — jejich levely pak klesají stejně rychle a žádná nezaostává. Že na reálných procesorech nemůžou běžet tři úlohy na dvou strojích, vyřeší až závěrečný McNaughton — hlavní smyčka počítá s „kapalinovou“ fikcí.
compute the level of all tasks ; t := 0; h := R; // h free res.
while unfinished tasks exist do
construct Z; // tasks that are free at time t
while h > 0 and |Z| > 0 do // free resources, free tasks
construct S; // tasks in Z with the highest level
if |S| > h then // more tasks than resources
assign part of capacity β := h/|S| to tasks in S; h := 0;
else
assign one resource to each task in S; β := 1; h := h - |S|;
end
Z := Z \ S;
end
compute δ; // see explanation below
decrease proc.times and levels by (δ)·β; // finished part
t := t + δ; h := R;
end
Use McNaughton to re-schedule parts with more tasks on less res.;
“$t + \delta$ is time when (1) EITHER one task is finished (2) OR a current level of an assigned task becomes lower than a level of an unassigned ready task (3) OR a task executed at faster rate $\beta$ starts to have current level below the current level of a task executed at slower rate $\beta$.”
Mezi událostmi je rozhodnutí (kdo běží a jakou rychlostí) stabilní: levely klesají lineárně rychlostmi $\beta$ a pořadí podle levelu se nemění. Přehodnotit ho musíš přesně tehdy, když se změní vstup rozhodnutí: (1) úloha skončí (změní se $\mathcal{Z}$ — a může uvolnit následníky), (2) běžící úloha klesne na level čekající volné úlohy (čekající si od teď zaslouží podíl — kdyby běžící klesla pod ni, dostávala by „horší“ úloha víc kapacity), (3) rychleji obsluhovaná úloha dožene pomaleji obsluhovanou (od teď musí sdílet stejné $\beta$, jinak by se pořadí přetočilo). Ve všech třech případech se $\delta$ spočítá z lineárních rovnic „kdy se dvě hladiny protnou / kdy zbývající práce dojde na nulu“ — bereš minimum přes všechny kandidáty.
Přesně takhle běží i příklad ze slidu 53 (8 úloh, $R = 2$, $p = (3,4,3,5,3,1,2,0)$, $level = (8,9,8,5,5,1,2,0)$) — první dvě iterace cituji, protože krásně ukazují případy (3) a (1):
(Slide pokračuje dalšími iteracemi a grafem „levels–time“; přepis obrázku je dál nečitelný, proto si celý běh poctivě projdeme na vlastní instanci níže.)
Proklikej běh na instanci z obrázku výše ($R = 2$, $L = (4,5,5,3,1)$). Sleduj, jak se v každé iteraci znovu staví $\mathcal{Z}$ a $\mathcal{S}$, kdy se sdílí kapacita a která ze tří událostí určí $\delta$:
Neselhal — pro $R = 2$ je Muntz&Coffman exaktní (slide 51), takže $5{,}5$ je optimum a chyba je v mezi: je platná, ale tady nedosažitelná. Silnější argument: $T_5$ smí začít, až jsou hotové $T_1$ i $T_4$ — a před $T_5$ tedy musí být odvedena veškerá práce $T_1, T_2, T_3, T_4$, tj. $9$ jednotek. Dva stroje ji odvedou nejdřív za $4{,}5$, takže $C_{max} \ge 4{,}5 + p_5 = 5{,}5$. Mimochodem: s preempcí je zlomkové $C_{max}$ normální — nenech se u zkoušky vyděsit polovinami a třetinami.
Závěrečný krok („Use McNaughton to re-schedule…“) je doslova volání [L5.4] uvnitř každého sdíleného úseku: úsek $[1;\,2{,}5]$ je okno délky $1{,}5$ se třemi kusy práce po $1$ — McNaughtonovo $C^*_{max} = \max\{1, \tfrac{3}{2}\} = 1{,}5$ přesně vyplní okno a wrap-around zaručí, že se kusy téže úlohy nepřekrývají. Sdílené $\beta$ je jen účetní fikce; skutečný rozvrh střídá úlohy po kusech.
“Solve the following instance $P2 \mid pmtn, prec \mid C_{\max}$ by Muntz&Coffman's Level Algorithm. Record a list of levels at initialization and $\mathcal{Z}$, set of free tasks, for each iteration of the main loop. Precedence relations are given by the following graph. Processing time is behind the slash of each task. Draw the Gantt chart.”
Deset úloh, dva procesory, preempce povolena, DAG precedencí (u uzlu $T_i/p_i$). Chce se: ① levely při inicializaci, ② pro každou iteraci hlavní smyčky množina $\mathcal{Z}$ (a prakticky i $\mathcal{S}$, $\beta$, $\delta$ — bez nich iterace nezdůvodníš), ③ Ganttův diagram. Pozn. k přepisu zdroje (přiznáno): jiný přepis téže stránky uvádí navíc hranu $T_2 \to T_3$; řešíme verzi z banky (bez ní) — s hranou $T_2 \to T_3$ by vyšlo $L_2 = 4 + 9 = 13$, což odporuje tabulce levelů v doloženém řešení banky ($L_2 = 10$).
Nejdřív levely odzadu: koncové $T_8, T_9, T_{10}$ mají $L = p$; pak $T_6, T_7$, pak $T_4$, $T_3$, $T_5$ a nakonec zdroje $T_1, T_2$. Pak veď tabulku iterací: v každém řádku $t$, $\mathcal{Z}$ s aktuálními levely, rozdělení procesorů a $\delta$ s důvodem (1)/(2)/(3). Pozor hned na $t = 0$: tři volné úlohy s levely $11, 10, 9$ — kdy nastane událost (2)? A na konci ($t = 11$): levely $2, 2, 1$ — nejdřív běží dvojka, pak sdílí trojice. Nakonec sdílené úseky $[3,6]$, $[6,9]$, $[12;\,13{,}5]$ rozbal McNaughtonem.
① Levely při inicializaci (odzadu; banka doslova: “A terminal task … has level equal to its processing time. For any other task, the level is the processing time of the task plus the largest level among its immediate successors: $L_i = p_i + \max\{L_j : T_i \to T_j\}$.”):
| $T_i$ | $T_1$ | $T_2$ | $T_3$ | $T_4$ | $T_5$ | $T_6$ | $T_7$ | $T_8$ | $T_9$ | $T_{10}$ |
|---|---|---|---|---|---|---|---|---|---|---|
| $p_i$ | 2 | 4 | 5 | 2 | 6 | 2 | 1 | 1 | 2 | 2 |
| $L_i$ | 11 | 10 | 9 | 6 | 9 | 4 | 3 | 1 | 2 | 2 |
Např. $L_6 = 2 + \max\{1, 2\} = 4$, $L_4 = 2 + \max\{4, 3\} = 6$, $L_1 = 2 + \max\{9, 6\} = 11$.
② Iterace hlavní smyčky (v $\mathcal{Z}$ uvádím aktuální levely; $\beta$ po skupinách $\mathcal{S}$):
| # | $t$ | $\mathcal{Z}$ (aktuální levely) | $\mathcal{S}$ a $\beta$ | $\delta$ | událost |
|---|---|---|---|---|---|
| 1 | 0 | $T_1(11),\,T_2(10),\,T_5(9)$ | $\{T_1\}\,\beta{=}1$; $\{T_2\}\,\beta{=}1$; $T_5$ čeká | 1 | (2) level $T_2$ klesl na $9$ = level $T_5$ |
| 2 | 1 | $T_1(10),\,T_2(9),\,T_5(9)$ | $\{T_1\}\,\beta{=}1$; $\{T_2, T_5\}\,\beta{=}\tfrac12$ | 1 | (1) $T_1$ hotova v $t=2$ → uvolní $T_3$ |
| 3 | 2 | $T_3(9),\,T_2(8{,}5),\,T_5(8{,}5)$ | $\{T_3\}\,\beta{=}1$; $\{T_2, T_5\}\,\beta{=}\tfrac12$ | 1 | (3) rychlejší $T_3$ dohnala $T_2, T_5$ |
| 4 | 3 | $T_2(8),\,T_3(8),\,T_5(8)$ | $\{T_2, T_3, T_5\}\,\beta{=}\tfrac23$ | 3 | (1) $T_2$ hotova v $t=6$ → uvolní $T_4$ |
| 5 | 6 | $T_3(6),\,T_4(6),\,T_5(6)$ | $\{T_3, T_4, T_5\}\,\beta{=}\tfrac23$ | 3 | (1) $T_3$ i $T_4$ hotovy v $t=9$ → uvolní $T_6$ |
| 6 | 9 | $T_5(4),\,T_6(4)$ | $\{T_5, T_6\}\,\beta{=}1$ | 1 | (1) $T_5$ hotova v $t=10$ → uvolní $T_7$ |
| 7 | 10 | $T_6(3),\,T_7(3)$ | $\{T_6, T_7\}\,\beta{=}1$ | 1 | (1) $T_6$ i $T_7$ hotovy v $t=11$ → uvolní $T_8, T_9, T_{10}$ |
| 8 | 11 | $T_9(2),\,T_{10}(2),\,T_8(1)$ | $\{T_9, T_{10}\}\,\beta{=}1$; $T_8$ čeká | 1 | (2) level $T_9, T_{10}$ klesl na $1$ = level $T_8$ |
| 9 | 12 | $T_8(1),\,T_9(1),\,T_{10}(1)$ | $\{T_8, T_9, T_{10}\}\,\beta{=}\tfrac23$ | 1,5 | (1) vše hotovo: $C_{max} = 13{,}5$ |
③ Gantt — sdílené úseky ($[1,3]$ na $P^2$, $[3,6]$, $[6,9]$, $[12;\,13{,}5]$) rozbalí McNaughton (např. $[3,6]$: tři úlohy po $2$ v okně $3$ → $C^* = \max\{2, \tfrac{6}{2}\} = 3$, wrap-around). Proklikej oba pohledy:
Optimalita: $\sum_i p_i = 27$, takže $C_{max} \ge \tfrac{27}{2} = 13{,}5$ — rozvrh meze dosahuje (žádné zahálení), a pro $R = 2$ je M&C beztak exaktní. Optimum je $C_{max} = 13{,}5$.
Pozor (přiznaná odchylka od kolujícího řešení): řešení v bance úloh jede zjednodušenou variantu — statické levely, bez sdílení kapacity — a dostává $C_{max} = 14$ s argumentem $C_{max} \ge \lceil 27/2 \rceil = 14$. Zaokrouhlení nahoru ale při povolené preempci neplatí ($C_{max}$ smí být zlomkové) a běh věrný pseudokódu slidu 52 (výše) končí v $13{,}5$. U zkoušky se drž slidů: sdílení $\beta = h/|\mathcal{S}|$ a tři případy $\delta$ jsou přesně to, co zadání slovy „Muntz&Coffman's Level Algorithm“ myslí.
Tohle je doložená zkoušková úloha (02.06.2021, znovu jako téma 1. 6. 2026). Vyřeš ji celou NA PAPÍR bez koukání do řešení: tabulka levelů, tabulka iterací ($t$, $\mathcal{Z}$ s aktuálními levely, $\mathcal{S}$, $\beta$, $\delta$ + případ), McNaughtonovo rozbalení sdílených úseků a finální Gantt s $C_{max}$. Pošli mi fotku — zkontroluju ti ji a vytknu chyby (nejčastější: statické levely, zapomenuté sdílení $\beta$ a úloha vytažená do $\mathcal{Z}$ dřív, než doběhli všichni předchůdci).
“Solve the following instance of $P2 \,|\,pmtn, prec\,|\, C_{max}$ problem by Muntz&Coffman's Level Algorithm. Record a list of levels at initialization and $\mathcal{Z}$, set of free tasks, for each iteration of the main loop. Precedence relations are given by the following graph. Processing time is behind the slash of each task label. Draw the Gantt chart.”
Stejné zadání jako T01, jiná instance: devět úloh, dva procesory. Tahle instance je „hodnější“ (skoro pořád $\beta = 1$), ale má dvě vlastní pasti: dvakrát se zahálí, přestože práce zbývá (předchůdci nejsou hotovi), a na konci jedna trojice sdílí — tentokrát jen jeden volný procesor pro dvě úlohy ($\beta = \tfrac12$).
Levely odzadu ($T_7, T_8, T_9$ jsou koncové). Hned po dokončení $T_1$ v $t = 2$ se zeptej: je $T_3$ volná? (Má dva předchůdce.) A v $t = 7$ pečlivě: tři volné úlohy s levely $2, 1, 1$ a dva procesory — kdo dostane celý procesor a kdo půlku?
① Levely: $L_7 = 1$, $L_8 = 2$, $L_9 = 2$; $L_5 = 2 + \max\{1,2\} = 4$, $L_6 = 1 + \max\{2,2\} = 3$; $L_3 = 1 + 4 = 5$, $L_4 = 2 + \max\{4,3\} = 6$; $L_1 = 2 + 5 = 7$, $L_2 = 3 + \max\{5,6\} = 9$. Tedy $L = (7, 9, 5, 6, 4, 3, 1, 2, 2)$.
② Iterace:
| # | $t$ | $\mathcal{Z}$ (aktuální levely) | $\mathcal{S}$ a $\beta$ | $\delta$ | událost |
|---|---|---|---|---|---|
| 1 | 0 | $T_2(9),\,T_1(7)$ | $\{T_2\}\,\beta{=}1$; $\{T_1\}\,\beta{=}1$ | 2 | (1) $T_1$ hotova v $t=2$ |
| 2 | 2 | $T_2(7)$ | $\{T_2\}\,\beta{=}1$ — $P^2$ zahálí ($T_3$ čeká na $T_2$!) | 1 | (1) $T_2$ hotova v $t=3$ → uvolní $T_3, T_4$ |
| 3 | 3 | $T_4(6),\,T_3(5)$ | $\{T_4\}\,\beta{=}1$; $\{T_3\}\,\beta{=}1$ | 1 | (1) $T_3$ hotova v $t=4$ |
| 4 | 4 | $T_4(5)$ | $\{T_4\}\,\beta{=}1$ — $P^2$ zahálí ($T_5$ čeká na $T_4$) | 1 | (1) $T_4$ hotova v $t=5$ → uvolní $T_5, T_6$ |
| 5 | 5 | $T_5(4),\,T_6(3)$ | $\{T_5\}\,\beta{=}1$; $\{T_6\}\,\beta{=}1$ | 1 | (1) $T_6$ hotova v $t=6$ → uvolní $T_9$ |
| 6 | 6 | $T_5(3),\,T_9(2)$ | $\{T_5\}\,\beta{=}1$; $\{T_9\}\,\beta{=}1$ | 1 | (1) $T_5$ hotova v $t=7$ → uvolní $T_7, T_8$ |
| 7 | 7 | $T_8(2),\,T_7(1),\,T_9(1)$ | $\{T_8\}\,\beta{=}1$; $\{T_7, T_9\}\,\beta{=}\tfrac12$ | 2 | (1) vše hotovo: $C_{max} = 9$ |
③ Gantt — jediný sdílený úsek $[7,9]$ na $P^2$ rozbalí McNaughton (okno $2$, kusy $1 + 1$):
Optimalita: tady rozhoduje druhá mez — kritická cesta: $\max_i L_i = L_2 = 9$ (řetěz $T_2 \to T_4 \to T_5 \to T_8$, $3+2+2+2 = 9$), zatímco $\tfrac{1}{R}\sum p_i = \tfrac{16}{2} = 8$. Rozvrh končí v $C_{max} = 9 = L_2$ → optimální; ta dvě zahálení ($[2,3]$, $[4,5]$ na $P^2$) jsou vynucená precedencemi, ne chyba.