度算法:從操作系統(tǒng)原理到任務(wù)隊(duì)列的工程實(shí)踐)
1. 從“先來先到”到“誰短誰先”為什么我們需要SJF在操作系統(tǒng)、任務(wù)調(diào)度乃至我們?nèi)粘L幚砉ぷ鞯膱鼍袄镆粋€(gè)核心問題始終存在當(dāng)一堆任務(wù)進(jìn)程、作業(yè)、待辦事項(xiàng)同時(shí)擺在你面前時(shí)你按什么順序來處理它們最樸素、最直覺的想法就是“先來先服務(wù)”FCFS誰先到誰就先被處理。這很公平對吧但如果你是一個(gè)系統(tǒng)管理員或者一個(gè)項(xiàng)目團(tuán)隊(duì)的負(fù)責(zé)人你很快就會發(fā)現(xiàn)這種“公平”有時(shí)會帶來災(zāi)難性的效率低下。想象一下你面前有五個(gè)任務(wù)一個(gè)需要運(yùn)行8小時(shí)的復(fù)雜計(jì)算任務(wù)A和四個(gè)都只需要5分鐘就能完成的簡單報(bào)告生成任務(wù)B, C, D, E。如果按照FCFS任務(wù)A先到那么它就會獨(dú)占資源8小時(shí)后面那四個(gè)可憐的小任務(wù)就得干等8小時(shí)。對于整個(gè)系統(tǒng)來說平均每個(gè)任務(wù)要等待的時(shí)間長得驚人系統(tǒng)的響應(yīng)性用戶感覺到的速度也差到極點(diǎn)。這就是FCFS算法著名的“護(hù)航效應(yīng)”Convoy Effect一個(gè)長任務(wù)阻塞了后面所有短任務(wù)。SJFShortest Job First最短作業(yè)優(yōu)先調(diào)度算法就是為了解決這個(gè)痛點(diǎn)而生的。它的核心思想直白而有力總是優(yōu)先調(diào)度預(yù)計(jì)運(yùn)行時(shí)間最短的那個(gè)任務(wù)。在上面的例子里SJF會毫不猶豫地先處理B、C、D、E這四個(gè)短任務(wù)最后再處理A這個(gè)長任務(wù)。直覺上這能顯著減少平均等待時(shí)間讓系統(tǒng)整體“感覺”更快。我第一次在線上服務(wù)部署中深刻體會到SJF的威力是在處理一個(gè)異步任務(wù)隊(duì)列時(shí)。當(dāng)時(shí)我們的用戶上傳圖片后后臺需要生成多種尺寸的縮略圖短任務(wù)和進(jìn)行復(fù)雜的內(nèi)容識別分析長任務(wù)。初期使用FCFS隊(duì)列經(jīng)常有用戶抱怨“生成個(gè)縮略圖怎么要等好幾分鐘”一查日志發(fā)現(xiàn)前面排了一個(gè)分析視頻的長任務(wù)。后來切換到基于SJF思想的優(yōu)先級隊(duì)列短小的縮略圖任務(wù)被優(yōu)先處理用戶端的響應(yīng)速度立刻有了質(zhì)的提升而長任務(wù)在后臺慢慢跑對用戶體驗(yàn)幾乎沒有影響。這讓我意識到在資源有限的世界里“公平”有時(shí)不如“高效”來得實(shí)在。2. SJF算法的兩種面孔非搶占式與搶占式SJF算法并非鐵板一塊根據(jù)任務(wù)執(zhí)行過程中是否允許被更高優(yōu)先級的任務(wù)即新來的、更短的任務(wù)打斷它可以分為兩種主要變體非搶占式SJF和搶占式SJF。理解這兩者的區(qū)別是應(yīng)用SJF的關(guān)鍵。2.1 非搶占式SJF一諾千金非搶占式SJF有時(shí)也叫作最短進(jìn)程優(yōu)先SPN, Shortest Process Next。它的規(guī)則很簡單一旦一個(gè)任務(wù)開始執(zhí)行它就會一直運(yùn)行到完成期間不會被任何新來的、更短的任務(wù)打斷。工作流程如下當(dāng)CPU空閑時(shí)從就緒隊(duì)列中選擇預(yù)計(jì)運(yùn)行時(shí)間最短的那個(gè)任務(wù)將CPU分配給它。該任務(wù)開始執(zhí)行并持續(xù)占用CPU直到它主動結(jié)束完成或等待I/O。在該任務(wù)執(zhí)行期間即使有運(yùn)行時(shí)間更短的新任務(wù)到達(dá)也不會中斷當(dāng)前任務(wù)。新任務(wù)進(jìn)入就緒隊(duì)列排隊(duì)。當(dāng)前任務(wù)結(jié)束后CPU再次空閑算法重復(fù)步驟1從當(dāng)前就緒隊(duì)列包含等待的和新到達(dá)的中再次選擇最短的任務(wù)。用一個(gè)簡單的例子來說明假設(shè)有三個(gè)任務(wù)幾乎同時(shí)到達(dá)時(shí)間0它們的運(yùn)行時(shí)間Burst Time分別是P1: 6個(gè)單位時(shí)間P2: 8個(gè)單位時(shí)間P3: 7個(gè)單位時(shí)間按照非搶占式SJF在0時(shí)刻就緒隊(duì)列中有P1(6), P2(8), P3(7)。最短的是P1(6)所以先執(zhí)行P1。P1從0運(yùn)行到6結(jié)束。在時(shí)刻6隊(duì)列中剩下P2(8)和P3(7)最短的是P3(7)執(zhí)行P3。P3從6運(yùn)行到13結(jié)束。最后執(zhí)行P2從13運(yùn)行到21。計(jì)算關(guān)鍵指標(biāo)周轉(zhuǎn)時(shí)間 完成時(shí)間 - 到達(dá)時(shí)間P1: 6 - 0 6P3: 13 - 0 13P2: 21 - 0 21平均周轉(zhuǎn)時(shí)間 (6 13 21) / 3 ≈ 13.33帶權(quán)周轉(zhuǎn)時(shí)間 周轉(zhuǎn)時(shí)間 / 運(yùn)行時(shí)間 衡量公平性越小越好P1: 6 / 6 1P3: 13 / 7 ≈ 1.86P2: 21 / 8 2.625可以看到短任務(wù)P1得到了極快的響應(yīng)而最長的P2則需要等待很長時(shí)間。非搶占式SJF的優(yōu)點(diǎn)在于實(shí)現(xiàn)簡單上下文切換開銷小。但其缺點(diǎn)也很明顯如果一個(gè)長任務(wù)剛開始執(zhí)行緊接著就來了一個(gè)非常短的任務(wù)這個(gè)短任務(wù)也不得不等待長任務(wù)執(zhí)行完這在一定程度上損失了SJF“極致響應(yīng)短任務(wù)”的優(yōu)勢。2.2 搶占式SJF能者隨時(shí)上為了彌補(bǔ)非搶占式SJF的上述缺陷搶占式SJF應(yīng)運(yùn)而生它更廣為人知的名字是最短剩余時(shí)間優(yōu)先SRTF, Shortest Remaining Time First。它的規(guī)則更具動態(tài)性在任何時(shí)刻CPU總是分配給當(dāng)前剩余運(yùn)行時(shí)間最短的那個(gè)任務(wù)。如果一個(gè)新任務(wù)到達(dá)其運(yùn)行時(shí)間比當(dāng)前正在執(zhí)行的任務(wù)的剩余時(shí)間還要短那么當(dāng)前任務(wù)會被立即剝奪CPU新任務(wù)開始執(zhí)行。工作流程如下初始狀態(tài)與選擇同非搶占式。當(dāng)一個(gè)新任務(wù)到達(dá)時(shí)系統(tǒng)會比較這個(gè)新任務(wù)的總運(yùn)行時(shí)間與當(dāng)前正在執(zhí)行任務(wù)的剩余運(yùn)行時(shí)間。如果新任務(wù)的運(yùn)行時(shí)間 當(dāng)前任務(wù)的剩余時(shí)間則發(fā)生搶占當(dāng)前任務(wù)被掛起放回就緒隊(duì)列CPU分配給新任務(wù)。如果沒有發(fā)生搶占或者當(dāng)前任務(wù)結(jié)束則算法重新從就緒隊(duì)列包含被掛起的任務(wù)中選擇剩余時(shí)間最短的任務(wù)執(zhí)行。讓我們修改上面的例子加入搶占假設(shè)任務(wù)到達(dá)時(shí)間不同P1: 到達(dá)時(shí)間0 運(yùn)行時(shí)間6P2: 到達(dá)時(shí)間1 運(yùn)行時(shí)間8P3: 到達(dá)時(shí)間2 運(yùn)行時(shí)間7調(diào)度過程推演時(shí)刻0只有P1到達(dá)執(zhí)行P1。時(shí)刻1P2到達(dá)。比較P1剩余時(shí)間5 P2運(yùn)行時(shí)間8。5 8不搶占P1繼續(xù)。時(shí)刻2P3到達(dá)。比較P1剩余時(shí)間4 P3運(yùn)行時(shí)間7。4 7不搶占P1繼續(xù)。時(shí)刻6P1完成。此時(shí)就緒隊(duì)列有P2(剩余8)和P3(剩余7)。最短的是P3執(zhí)行P3。時(shí)刻13P3完成。執(zhí)行P2。時(shí)刻21P2完成。這個(gè)例子中沒有發(fā)生搶占。我們再構(gòu)造一個(gè)會發(fā)生搶占的場景P1: 到達(dá)時(shí)間0 運(yùn)行時(shí)間8P2: 到達(dá)時(shí)間1 運(yùn)行時(shí)間4時(shí)刻0執(zhí)行P1。時(shí)刻1P2到達(dá)。比較P1剩余時(shí)間7 P2運(yùn)行時(shí)間4。4 7發(fā)生搶占P1被掛起P2開始執(zhí)行。時(shí)刻5P2運(yùn)行時(shí)間4完成。就緒隊(duì)列中只有被掛起的P1剩余時(shí)間7繼續(xù)執(zhí)行P1。時(shí)刻12P1完成。計(jì)算關(guān)鍵指標(biāo)搶占式例子P2: 完成時(shí)間5 周轉(zhuǎn)時(shí)間5-14P1: 完成時(shí)間12周轉(zhuǎn)時(shí)間12-012平均周轉(zhuǎn)時(shí)間 (4 12) / 2 8如果使用非搶占式順序?qū)⑹荘1先執(zhí)行完0-8再執(zhí)行P28-12。平均周轉(zhuǎn)時(shí)間 [(8-0)(12-1)]/2 (811)/2 9.5。可見在這個(gè)場景下?lián)屨际絊JFSRTF進(jìn)一步降低了平均周轉(zhuǎn)時(shí)間。注意搶占雖然優(yōu)化了平均指標(biāo)但帶來了顯著的開銷。每次搶占都意味著一次上下文切換需要保存當(dāng)前任務(wù)的狀態(tài)寄存器、程序計(jì)數(shù)器等并加載新任務(wù)的狀態(tài)。如果任務(wù)非常短小且頻繁到達(dá)上下文切換的開銷可能抵消甚至超過調(diào)度優(yōu)化帶來的收益。在實(shí)際系統(tǒng)中需要仔細(xì)權(quán)衡。3. SJF的理想與現(xiàn)實(shí)核心優(yōu)勢與致命挑戰(zhàn)SJF算法在理論上非常優(yōu)美尤其是在優(yōu)化平均等待時(shí)間和周轉(zhuǎn)時(shí)間方面它被證明是最優(yōu)的。這里的“最優(yōu)”指的是在給定一組任務(wù)及其運(yùn)行時(shí)間的前提下SJF能給出最小的平均等待時(shí)間。這是它最吸引人的理論光環(huán)。其核心優(yōu)勢可以總結(jié)為極高的短任務(wù)響應(yīng)速度短任務(wù)無需在長任務(wù)后苦苦等待極大地改善了交互式系統(tǒng)的用戶體驗(yàn)。這對于Web服務(wù)器、數(shù)據(jù)庫查詢響應(yīng)、交互式命令行工具等場景至關(guān)重要。最優(yōu)的平均性能最小化平均等待時(shí)間和平均周轉(zhuǎn)時(shí)間從系統(tǒng)整體吞吐量的角度來看資源利用率更高。避免護(hù)航效應(yīng)從根本上解決了FCFS中一個(gè)長任務(wù)阻塞一堆短任務(wù)的問題。然而當(dāng)我們將這個(gè)理想的算法搬到現(xiàn)實(shí)的計(jì)算世界中時(shí)會遇到幾個(gè)幾乎無法回避的致命挑戰(zhàn)這也限制了“純”SJF在通用操作系統(tǒng)中的直接應(yīng)用。3.1 挑戰(zhàn)一如何預(yù)知未來——運(yùn)行時(shí)間的預(yù)測這是SJF算法面臨的最大、最根本的挑戰(zhàn)。算法的前提是我們必須事先知道每個(gè)任務(wù)的確切運(yùn)行時(shí)間CPU Burst Time。但在真實(shí)的操作系統(tǒng)中任務(wù)在未來需要運(yùn)行多久在它結(jié)束之前操作系統(tǒng)是不知道的。這就迫使我們只能進(jìn)行預(yù)測。常見的預(yù)測方法基于過去的行為來估計(jì)未來類似于時(shí)間序列預(yù)測指數(shù)平均法這是最常用的方法。用上一個(gè)實(shí)際運(yùn)行時(shí)間T_n和上一個(gè)預(yù)測值τ_n來共同決定下一個(gè)預(yù)測值τ_{n1}。公式τ_{n1} α * T_n (1 - α) * τ_n其中α0 ≤ α ≤ 1是平滑因子。α越接近1表示更重視最近一次的實(shí)際表現(xiàn)α越接近0表示更依賴歷史預(yù)測。例如設(shè)置α0.5上一個(gè)預(yù)測τ_n10ms上一個(gè)實(shí)際運(yùn)行T_n6ms則下一個(gè)預(yù)測τ_{n1}0.56 0.510 8ms。其他啟發(fā)式方法比如取最近幾次運(yùn)行時(shí)間的移動平均、考慮任務(wù)類型I/O密集型任務(wù)通常CPU區(qū)間短等。預(yù)測永遠(yuǎn)是不準(zhǔn)的。一個(gè)典型的“誤傷”場景是一個(gè)長時(shí)間運(yùn)行的批處理任務(wù)如視頻轉(zhuǎn)碼初期可能因?yàn)轭A(yù)測算法將其誤判為短任務(wù)而獲得調(diào)度但它實(shí)際運(yùn)行起來后才發(fā)現(xiàn)是個(gè)“巨無霸”。在非搶占式SJF下它就會霸占CPU很久在搶占式下雖然可能被后續(xù)短任務(wù)搶占但初期的誤判已經(jīng)影響了調(diào)度決策。3.2 挑戰(zhàn)二饑餓——長任務(wù)的永恒夢魘這是SJF算法尤其是搶占式SRTF一個(gè)非常嚴(yán)重的副作用。如果一個(gè)系統(tǒng)持續(xù)有短任務(wù)到達(dá)那么長任務(wù)可能永遠(yuǎn)得不到執(zhí)行永遠(yuǎn)在就緒隊(duì)列中等待。這種現(xiàn)象稱為“饑餓”Starvation??紤]一個(gè)極端例子一個(gè)長任務(wù)L需要1小時(shí)在等待。之后每秒鐘都來一個(gè)超短任務(wù)S需要0.1秒。在SRTF調(diào)度下CPU會一直執(zhí)行這些源源不斷的短任務(wù)S因?yàn)樗鼈兊氖S鄷r(shí)間0.1秒永遠(yuǎn)比L的剩余時(shí)間1小時(shí)短。任務(wù)L將無限期等待。解決饑餓需要引入額外的機(jī)制這已經(jīng)超出了純SJF的范疇。例如老化Aging隨著任務(wù)等待時(shí)間的增加逐步提高它的優(yōu)先級或虛擬地減少它的“預(yù)測運(yùn)行時(shí)間”。等待了足夠久之后一個(gè)長任務(wù)可能被認(rèn)為“足夠短”而獲得調(diào)度。多級反饋隊(duì)列MLFQ這是現(xiàn)代操作系統(tǒng)如Linux的CFS調(diào)度器思想基礎(chǔ)實(shí)際采用的、更復(fù)雜的調(diào)度策略它融合了SJF、優(yōu)先級、時(shí)間片輪轉(zhuǎn)等多種思想能在響應(yīng)速度和公平性之間取得更好的平衡。3.3 挑戰(zhàn)三實(shí)現(xiàn)開銷與公平性權(quán)衡實(shí)現(xiàn)復(fù)雜度無論是非搶占還是搶占式都需要維護(hù)一個(gè)按運(yùn)行時(shí)間或剩余時(shí)間排序的優(yōu)先隊(duì)列。每次有新任務(wù)到達(dá)或任務(wù)完成都可能需要調(diào)整隊(duì)列順序。雖然使用最小堆等數(shù)據(jù)結(jié)構(gòu)可以將插入/刪除復(fù)雜度保持在O(log n)但這仍然比FCFS的簡單FIFO隊(duì)列要復(fù)雜。公平性缺失SJF本質(zhì)上是不公平的。它明確地“歧視”長任務(wù)。在某些對任務(wù)公平性有嚴(yán)格要求的場景如某些公平分配計(jì)算資源的集群純SJF是不可接受的。4. 超越理論SJF思想在真實(shí)世界的應(yīng)用與變體盡管純SJF在通用操作系統(tǒng)中難以直接作為主調(diào)度器但其“短任務(wù)優(yōu)先”的核心思想?yún)s滲透在計(jì)算機(jī)科學(xué)的各個(gè)角落并以各種變體和混合策略的形式發(fā)揮著巨大作用。4.1 操作系統(tǒng)的調(diào)度策略融合沒有主流操作系統(tǒng)會傻傻地問進(jìn)程“你要運(yùn)行多久”但它們會巧妙地利用SJF的思想。Linux CFS完全公平調(diào)度器它的核心是維護(hù)一個(gè)按“虛擬運(yùn)行時(shí)間vruntime”排序的紅黑樹。vruntime增長慢的進(jìn)程可以理解為短任務(wù)或I/O密集型任務(wù)會被優(yōu)先調(diào)度。這本質(zhì)上是一種動態(tài)的、公平包裝下的“短任務(wù)優(yōu)先”傾向。I/O密集型進(jìn)程在醒來時(shí)vruntime很小能很快獲得CPU這正是SJF精神的體現(xiàn)。交互式進(jìn)程優(yōu)先許多系統(tǒng)調(diào)度器會隱含地區(qū)分“交互式進(jìn)程”如桌面UI、文本編輯器和“批處理進(jìn)程”如編譯器、科學(xué)計(jì)算。交互式進(jìn)程通常CPU區(qū)間短等待用戶輸入會被賦予更高的動態(tài)優(yōu)先級這暗合了SJF的原則。4.2 I/O設(shè)備調(diào)度磁盤臂調(diào)度算法這是SJF思想最經(jīng)典、最直接的應(yīng)用領(lǐng)域之一。磁盤的尋道時(shí)間磁頭移動到目標(biāo)磁道的時(shí)間是主要開銷。最短尋道時(shí)間優(yōu)先SSTF這是SJF在磁盤調(diào)度上的直接映射。它總是選擇當(dāng)前磁頭位置最近的那個(gè)請求進(jìn)行服務(wù)。這能顯著減少平均尋道時(shí)間提高磁盤I/O吞吐量。SSTF同樣面臨饑餓問題如果不斷有新的請求到達(dá)在磁頭當(dāng)前位置附近那么遠(yuǎn)處磁道的請求可能永遠(yuǎn)得不到服務(wù)。因此實(shí)踐中更常用的是**電梯算法SCAN, LOOK**或其變體它們在類似SSTF的效率和平移掃描的公平性之間做了折衷。4.3 網(wǎng)絡(luò)數(shù)據(jù)包調(diào)度在網(wǎng)絡(luò)路由器和交換機(jī)的隊(duì)列管理中SJF思想也有應(yīng)用。例如處理短包優(yōu)先可以降低平均包延遲。但同樣需要防止長包如大數(shù)據(jù)傳輸?shù)酿囸I。4.4 異步任務(wù)隊(duì)列與作業(yè)調(diào)度系統(tǒng)這是我個(gè)人實(shí)踐中最常接觸到SJF思想的地方例如使用Celery、RabbitMQ等消息隊(duì)列處理后臺任務(wù)。優(yōu)先級隊(duì)列我們可以根據(jù)任務(wù)的預(yù)估執(zhí)行時(shí)間或類型來設(shè)置優(yōu)先級。短任務(wù)如發(fā)送歡迎郵件、清理臨時(shí)文件設(shè)置為高優(yōu)先級長任務(wù)如生成月度報(bào)表、訓(xùn)練機(jī)器學(xué)習(xí)模型設(shè)置為低優(yōu)先級。工作進(jìn)程Worker從高優(yōu)先級隊(duì)列開始消費(fèi)。動態(tài)優(yōu)先級調(diào)整更高級的用法是結(jié)合“老化”機(jī)制。一個(gè)在低優(yōu)先級隊(duì)列等待太久的任務(wù)可以自動提升其優(yōu)先級防止饑餓。一個(gè)基于Redis和Pythonheapq的簡易SJF任務(wù)隊(duì)列示例import heapq import time import threading import redis import json class SJFTaskQueue: def __init__(self, queue_namesjf_queue): self.redis_client redis.Redis(hostlocalhost, port6379, db0) self.queue_key queue_name # 使用一個(gè)本地最小堆來維護(hù)“預(yù)測運(yùn)行時(shí)間”最短的任務(wù)ID self.heap [] self.lock threading.Lock() def _push_to_heap(self, task_id, predicted_time): 將任務(wù)ID和預(yù)測時(shí)間推入最小堆 heapq.heappush(self.heap, (predicted_time, task_id)) def add_task(self, task_data, predicted_time): 添加一個(gè)新任務(wù)。 task_data: 任務(wù)的具體數(shù)據(jù)字典 predicted_time: 預(yù)測的運(yùn)行時(shí)間秒 task_id ftask_{int(time.time()*1000)}_{hash(str(task_data))%10000} # 1. 將任務(wù)詳情存入Redis Hash task_info { id: task_id, data: json.dumps(task_data), predicted: predicted_time, status: pending } self.redis_client.hset(ftask:{task_id}, mappingtask_info) # 2. 將任務(wù)ID和預(yù)測時(shí)間推入本地優(yōu)先堆 with self.lock: self._push_to_heap(task_id, predicted_time) # 也可以將堆頂元素ID存入一個(gè)Redis鍵供多個(gè)Worker協(xié)調(diào)使用 self.redis_client.set(f{self.queue_key}:next, self.heap[0][1] if self.heap else ) print(f任務(wù) {task_id} 已添加預(yù)測時(shí)間 {predicted_time}s) return task_id def get_next_task(self): 獲取下一個(gè)要執(zhí)行的任務(wù)預(yù)測時(shí)間最短的 with self.lock: if not self.heap: return None predicted_time, task_id heapq.heappop(self.heap) # 從堆中彈出 # 更新Redis中的“下一個(gè)任務(wù)”指示器 next_id self.heap[0][1] if self.heap else self.redis_client.set(f{self.queue_key}:next, next_id) # 從Redis中獲取任務(wù)詳情 task_info self.redis_client.hgetall(ftask:{task_id}) if not task_info: return None # 任務(wù)可能已被其他worker取走或刪除 task_info {k.decode(): v.decode() for k, v in task_info.items()} task_info[data] json.loads(task_info[data]) return task_info def mark_task_done(self, task_id, actual_time): 標(biāo)記任務(wù)完成并可用于更新預(yù)測模型 with self.lock: # 這里可以加入指數(shù)平均法更新預(yù)測的邏輯 # 例如讀取舊的預(yù)測值結(jié)合actual_time計(jì)算新預(yù)測值更新該任務(wù)后續(xù)的預(yù)測 pass self.redis_client.hset(ftask:{task_id}, status, done) print(f任務(wù) {task_id} 完成實(shí)際用時(shí) {actual_time}s) # 模擬使用 queue SJFTaskQueue() queue.add_task({type: generate_thumbnail, url: pic.jpg}, predicted_time0.5) queue.add_task({type: send_email, to: userexample.com}, predicted_time0.2) queue.add_task({type: train_model, dataset: large}, predicted_time3600) # Worker線程會調(diào)用 get_next_task()它將返回預(yù)測時(shí)間為0.2的發(fā)送郵件任務(wù)。這個(gè)示例展示了SJF思想在分布式任務(wù)調(diào)度中的一個(gè)簡單實(shí)現(xiàn)雛形。關(guān)鍵在于維護(hù)一個(gè)按預(yù)測時(shí)間排序的優(yōu)先隊(duì)列。在實(shí)際生產(chǎn)環(huán)境中你需要考慮分布式鎖、持久化、預(yù)測模型更新以及更復(fù)雜的協(xié)調(diào)機(jī)制。5. 實(shí)戰(zhàn)中的抉擇何時(shí)考慮使用SJF策略經(jīng)過上面的分析我們可以總結(jié)出SJF及其思想變體的適用場景和決策要點(diǎn)適合使用的場景批處理系統(tǒng)任務(wù)運(yùn)行時(shí)間可以相對準(zhǔn)確地預(yù)估例如運(yùn)行標(biāo)準(zhǔn)化的數(shù)據(jù)分析腳本。SJF能最大化系統(tǒng)吞吐量。交互式系統(tǒng)的前/后端明確區(qū)分短時(shí)交互請求API調(diào)用、頁面渲染和長時(shí)批處理任務(wù)。使用優(yōu)先級隊(duì)列將短請求優(yōu)先處理。I/O調(diào)度如磁盤SSTF算法在已知請求位置的情況下能有效優(yōu)化性能。已知任務(wù)長度的特定領(lǐng)域在某些科學(xué)計(jì)算或工程仿真中任務(wù)規(guī)模是預(yù)先可知的集群調(diào)度器可以采用類似SJF的策略。需要謹(jǐn)慎或避免的場景通用分時(shí)操作系統(tǒng)作為唯一調(diào)度策略因?yàn)闊o法準(zhǔn)確預(yù)測進(jìn)程運(yùn)行時(shí)間且存在饑餓問題。對任務(wù)公平性有嚴(yán)格要求的場景例如所有用戶付費(fèi)相同的云計(jì)算環(huán)境需要保證每個(gè)任務(wù)都有進(jìn)展。任務(wù)運(yùn)行時(shí)間波動極大、不可預(yù)測的場景錯(cuò)誤的預(yù)測會導(dǎo)致調(diào)度性能甚至不如簡單的輪轉(zhuǎn)法。決策 checklist[ ]能否預(yù)測是否有可靠的方法歷史數(shù)據(jù)、任務(wù)類型標(biāo)簽來估計(jì)任務(wù)長度[ ]能否容忍饑餓長任務(wù)延遲完成是否可接受是否有“老化”等補(bǔ)償機(jī)制[ ]開銷是否值得實(shí)現(xiàn)和維護(hù)優(yōu)先隊(duì)列、預(yù)測模型的復(fù)雜度是否被帶來的性能提升所覆蓋[ ]是否需要混合策略是否可以將SJF作為更高層次調(diào)度器的一部分如多級隊(duì)列中的高優(yōu)先級隊(duì)列在我經(jīng)歷的系統(tǒng)優(yōu)化案例里引入SJF思想很少是“一刀切”的替換更多是“打補(bǔ)丁”式的優(yōu)化。例如在一個(gè)FCFS的郵件發(fā)送隊(duì)列中我們發(fā)現(xiàn)驗(yàn)證郵件、通知郵件等短小任務(wù)被大型郵件列表發(fā)送任務(wù)阻塞。解決方案不是重寫整個(gè)調(diào)度器而是簡單地增加了一個(gè)高優(yōu)先級的快速隊(duì)列短任務(wù)投遞到這個(gè)隊(duì)列。這就是SJF思想最樸素也最有效的應(yīng)用識別出系統(tǒng)中的“短任務(wù)”并給它們開一條綠色通道。這種混合方案既獲得了SJF響應(yīng)快的優(yōu)點(diǎn)又避免了純SJF的復(fù)雜性和潛在風(fēng)險(xiǎn)。理解一個(gè)算法的精髓遠(yuǎn)比死板地實(shí)現(xiàn)它更重要。