หัวข้อ 16 · 16 นาที
Max-Min fairness และ Multi-level Feedback Queue
นึกภาพก่อน
โปรแกรมพิมพ์งาน: รอคนกดคีย์ → ทำงานเสี้ยววินาที → รออีก ใช้ CPU น้อยมากแต่ต้องตอบ ทันที ที่กดคีย์ โปรแกรมแปลงวิดีโอ: ใช้ CPU เต็มที่ตลอด ช้าไปนิดหน่อยไม่มีใครรู้สึก
ถ้าใช้ Round Robin ทั้งสองได้ quantum เท่ากัน โปรแกรมพิมพ์งานใช้ไปนิดเดียวแล้วไปรอ I/O พอกลับมาต้อง ต่อแถวใหม่ หลังโปรแกรมแปลงวิดีโอทุกตัว ผลคืองานที่ต้องการน้อยที่สุดกลับได้รับบริการแย่ที่สุด
Workload แบบผสม
| ชนิด | ลักษณะ |
|---|---|
| I/O-bound | ต้องการ CPU เพียงเล็กน้อยแล้วไปรอ I/O ทำซ้ำไปเรื่อย ๆ |
| Compute-bound | ใช้ CPU ได้มากเท่าที่ได้รับ |
ภาพในสไลด์ (p.15): task แบบ I/O-bound หนึ่งตัวกับ compute-bound สองตัวภายใต้ Round Robin — task I/O-bound ทำงานสั้น ๆ ออกไปทำ I/O แล้วกลับมาต้องรอ compute-bound ทั้งสองใช้ quantum เต็มก่อน จึงได้ใช้ CPU น้อยกว่าส่วนแบ่งของตัวเองและ I/O device ก็ว่างรอ นี่คือเหตุที่สไลด์สรุปว่า task ที่ผสม processor กับ I/O "ทำได้ไม่ดีภายใต้ Round Robin"
Max-Min fairness
แนวทาง: maximize ส่วนแบ่งขั้นต่ำ ที่ task ได้รับ
- ถ้ามี task ใดต้องการ น้อยกว่าส่วนแบ่งที่เท่ากัน ให้จัดตัวที่ต้องการน้อยที่สุดก่อน (ให้เท่าที่ขอ)
- แบ่งเวลาที่เหลือด้วย max-min ต่อ (ทำซ้ำกับ task ที่เหลือ)
- ถ้า task ที่เหลือทุกตัวต้องการ อย่างน้อยเท่าส่วนแบ่งที่เท่ากัน ให้แบ่งเท่า ๆ กัน
ตัวอย่างไล่ทีละขั้น
โจทย์: มี task สี่ตัว ต้องการ CPU 10%, 20%, 50% และ 80% ของเวลา (รวม 160% เกินที่มี) แบ่งแบบ max-min
| รอบ | เหลือ | task ที่เหลือ | ส่วนแบ่งเท่ากัน | ตัดสิน |
|---|---|---|---|---|
| 1 | 100% | 4 ตัว | 100 / 4 = 25% | task 10% ต้องการน้อยกว่า 25% → ได้ 10% |
| 2 | 90% | 3 ตัว | 90 / 3 = 30% | task 20% ต้องการน้อยกว่า 30% → ได้ 20% |
| 3 | 70% | 2 ตัว | 70 / 2 = 35% | ทั้งสองต้องการอย่างน้อย 35% → ได้คนละ 35% |
ผล: 10%, 20%, 35%, 35% (รวม 100%)
- task ที่ขอน้อยได้ ครบตามที่ขอ จึงตอบสนองเร็ว
- ส่วนที่ task เล็กไม่ใช้ถูกนำไปเพิ่มให้ task ใหญ่ (จาก 25% เป็น 35%) ไม่มีอะไรเสียเปล่า
- ถ้าแบ่งเท่ากันเฉย ๆ คนละ 25% task แรกจะใช้ไม่หมด 15% และ task ที่สองใช้ไม่หมด 5%
สไลด์สรุป: Max-Min fairness ปรับปรุง response time ของ task แบบ I/O-bound และทั้ง Round Robin กับ Max-Min ไม่เกิด starvation
Multi-level Feedback Queue (MFQ)
เป้าหมาย
- Responsiveness — ตอบสนองเร็ว
- Low overhead
- Starvation freedom — ไม่มี task ใดถูกทิ้ง
- รองรับ task ที่ priority สูง/ต่ำ
- Fairness (ระหว่าง task ที่ priority เท่ากัน)
ไม่ได้ดีที่สุดสักข้อ! แต่สมดุล ใช้ใน Linux (และน่าจะใน Windows, MacOS)
กฎ
- เป็น ชุดของ Round Robin queue แต่ละ queue มี priority ของตัวเอง
- queue ที่ priority สูงมี time slice สั้น queue ที่ priority ต่ำมี time slice ยาว
- scheduler เลือก thread แรกใน queue ที่ priority สูงสุด
- task เริ่มที่ queue ที่ priority สูงสุด
- ถ้า ใช้ time slice หมด task ลดลงหนึ่งระดับ
| Priority | Time slice | ใครอยู่ |
|---|---|---|
| 1 (สูงสุด) | 10 ms | task ใหม่ และ task แบบ I/O-bound |
| 2 | 20 ms | |
| 3 | 40 ms | |
| 4 (ต่ำสุด) | 80 ms | task แบบ compute-bound |
ทำไมกฎเหล่านี้ได้ผล
MFQ เดาชนิดของ task จากพฤติกรรม โดยไม่ต้องให้ใครบอก:
- task ที่ทำสั้น ๆ แล้วไปรอ I/O ไม่เคยใช้ time slice หมด จึงอยู่ queue บนตลอด และถูกเลือกก่อนเสมอ → responsiveness
- task ที่ใช้ CPU ยาว ๆ ใช้ time slice หมดซ้ำแล้วซ้ำเล่า จึงไหลลงล่าง แต่ได้ time slice ยาวขึ้น จึงถูกสลับน้อยครั้ง → low overhead
- task สั้นทำเสร็จตั้งแต่ queue บน ๆ ก่อน task ยาว ผลจึงคล้าย SJF โดย ไม่ต้องรู้ความยาวล่วงหน้า — สไลด์เรียก MFQ ว่า "approximation of optimal"
ตัวอย่างไล่ทีละขั้น
โจทย์: task A แบบ compute-bound ต้องการ CPU 200 ms อยู่ตัวเดียวในระบบ time slice 10 / 20 / 40 / 80 ms
| ช่วง (ms) | queue | ใช้ไป | เหลือ | ผล |
|---|---|---|---|---|
| 0–10 | priority 1 | 10 | 190 | ใช้ slice หมด → ลงระดับ 2 |
| 10–30 | priority 2 | 20 | 170 | ใช้ slice หมด → ลงระดับ 3 |
| 30–70 | priority 3 | 40 | 130 | ใช้ slice หมด → ลงระดับ 4 |
| 70–150 | priority 4 | 80 | 50 | ใช้ slice หมด → อยู่ระดับ 4 (ต่ำสุดแล้ว) |
| 150–200 | priority 4 | 50 | 0 | เสร็จ |
A ใช้เวลา 10 + 20 + 40 = 70 ms แรกในการไหลลงถึงระดับล่างสุด หลังจากนั้นถูกสลับออกทุก 80 ms แทนที่จะเป็นทุก 10 ms
รูปแบบ: ชื่อ เวลาCPUรวม [เวลาCPUก่อนทำ I/O] — A 200 คืองาน compute-bound, C 15 5 คืองานที่ทำ I/O ทุก 5 ms
จาก priority สูงไปต่ำ 2–4 ระดับ
เวลา = 0 ms
- priority 1
slice 10 msA - priority 2
slice 20 msว่าง - priority 3
slice 40 msว่าง - priority 4
slice 80 msว่าง
| งาน | ชนิด | เหลือ | priority | เสร็จ |
|---|---|---|---|---|
| A | CPU | 200 | 1 | – |
คำอธิบายทีละขั้น
ทุกงานเริ่มที่ priority สูงสุด
ใน simulation ลอง preset "I/O + CPU 2 งาน": งาน C ที่ทำ I/O ทุก 5 ms ค้างอยู่ priority 1 ตลอด และเมื่อกลับจาก I/O ก็แทรก (preempt) งาน A, B ที่อยู่ระดับล่างได้ทันที
สรุป uniprocessor scheduling (ตามสไลด์)
- FIFO ง่ายและ overhead น้อยที่สุด
- ถ้า task มีขนาดต่างกัน FIFO อาจมี average response time แย่มาก
- ถ้า task มีขนาดเท่ากัน FIFO optimal ในแง่ average response time
- พิจารณาเฉพาะ processor: SJF optimal ในแง่ average response time
- SJF pessimal ในแง่ variance ของ response time
- ถ้า task มีขนาดต่างกัน Round Robin ใกล้เคียง SJF
- ถ้า task มีขนาดเท่ากัน Round Robin มี average response time แย่มาก
- task ที่ผสม processor กับ I/O ได้ประโยชน์จาก SJF และทำได้ไม่ดีภายใต้ Round Robin
- Max-Min fairness ปรับปรุง response time ของ task แบบ I/O-bound ได้
- Round Robin และ Max-Min fairness ไม่เกิด starvation
- MFQ ได้สมดุลระหว่าง responsiveness, low overhead และ fairness ด้วยการปรับการจัด task เข้า priority queue
จุดที่มักพลาด
1. คิดว่า queue ที่ priority สูงได้ time slice ยาว
กลับกัน: priority สูง = time slice สั้น
2. คิดว่า task ใหม่เริ่มที่ queue ล่าง
เริ่มที่ priority สูงสุด แล้วค่อยลดลงถ้าใช้ slice หมด
3. คิดว่า max-min แบ่งเท่ากันทุก task
แบ่งเท่ากันเฉพาะ task ที่ต้องการอย่างน้อยเท่าส่วนแบ่ง task ที่ขอน้อยกว่าได้เท่าที่ขอ
4. ลืมคำนวณส่วนแบ่งใหม่ทุกรอบใน max-min
หลังจัด task เล็กแล้ว ต้องหารส่วนที่ เหลือ ด้วยจำนวน task ที่ เหลือ
5. คิดว่า MFQ ดีที่สุดทุกเป้าหมาย
สไลด์ระบุชัดว่า "Not perfect at any of them"
6. คิดว่า I/O-bound task ถูกลดระดับ
มันไม่ใช้ slice หมด จึงไม่ถูกลด
ที่มา: 06-scheduling_v5 (1).pdf หน้า 14–22