# Lecture 3 - Scheduling

Source: https://wisconsin.twango.dev/sp26-cs537/lectures/lecture-03

# Lecture 3 - Scheduling

**Instructor:** Tej Chajed
**Topics:** Scheduling policies, metrics, FIFO, SJF, STCF, Round Robin, MLFQ, Lottery Scheduling

> [!abstract]- Learning Outcomes
> - Compare different scheduling policies based on several metrics
> - Articulate the challenges from interactive and batch processes
> - Articulate the challenge of scheduling under incomplete info

---

## Recap: Process vs Program

- **Program** is passive — code + data on disk
- **Process** is a running program — stack, registers, program counter

Can have multiple process instances of the same program (e.g., two users both running Firefox — same program, separate processes).

### Process State Transitions

```mermaid
stateDiagram-v2
    New --> Ready : create
    Ready --> Running : schedule
    Running --> Ready : unschedule
    Running --> blocked : I/O, page fault, etc.
    blocked --> Ready : I/O done
    Running --> Terminated : kill
```

### System Calls

- Process invokes `syscall` instruction, which looks up handler in a table and jumps to kernel code
- Kernel can access user memory to fill in user buffer
- `return-from-trap` at end returns control to the process

---

## Scheduling Vocabulary

> **Workload:** A set of jobs, each with an arrival time and run time.

> **Job:** Current execution of a process. Alternates between CPU and I/O, moves between ready and blocked queues. Also called a "task."

> **Scheduler:** Decides which ready job to run.

> **Metric:** A measurement of scheduling quality.

---

## Scheduling Metrics

### Turnaround Time

> **Turnaround time** = *completion_time* - *arrival_time*

> [!example] Example
> Process A arrives at t = 10, finishes t = 30 → turnaround = 20
> Process B arrives at t = 10, finishes t = 50 → turnaround = 40
> Average turnaround = 30

### Response Time

> **Response time** = *first_run_time* - *arrival_time*

Valuable for **interactive processes** — measures how quickly a process first gets the CPU after arriving.

> [!example] Example
> A runs from 0–20, B arrives at 20 and runs 20–30.
> B's turnaround: 20s. B's response: 10s.

### Starvation

> **Starvation:** A process is prevented from making progress because another process holds a resource it needs.

A poor scheduling policy can cause starvation — e.g., if an important process always prevents a less important process from running on the CPU.

---

## Approach

Start with simplifying **assumptions**, pick a **metric**, then design a **scheduling policy**. Progressively relax assumptions to reveal challenges.

### Initial Assumptions

1. Each job runs for the same amount of time
2. All jobs arrive at the same time
3. All jobs only use the CPU (no I/O)
4. Run-time of each job is known

---

## FIFO / FCFS (First Come, First Served)

Run jobs in the order they arrive.

> [!example] Example — Equal-Length Jobs
>
> | Job | Arrival (s) | Run time (s) | Turnaround (s) |
> |-----|-------------|--------------|-----------------|
> | A   | ~0          | 10           | 10              |
> | B   | ~0          | 10           | 20              |
> | C   | ~0          | 10           | 30              |
>
> Average turnaround time = (10 + 20 + 30) / 3 = **20s**

### Problem: Variable-Length Jobs (Convoy Effect)

Relax assumption 1 (jobs have different run times):

> [!example] Example — Variable-Length Jobs
>
> | Job | Arrival (s) | Run time (s) |
> |-----|-------------|--------------|
> | A   | ~0          | 100          |
> | B   | ~0          | 10           |
> | C   | ~0          | 10           |
>
> FIFO schedule: A → B → C
> Average turnaround = (100 + 110 + 120) / 3 = **110s**

Turnaround time suffers when short jobs must wait for long jobs — **head-of-line blocking**.

---

## Shortest Job First (SJF)

Choose the job with the **smallest run time** first.

> [!example] Example — All Arrive at Same Time
>
> | Job | Arrival (s) | Run time (s) | Turnaround (s) |
> |-----|-------------|--------------|-----------------|
> | A   | ~0          | 100          | 120             |
> | B   | ~0          | 10           | 10              |
> | C   | ~0          | 10           | 20              |
>
> SJF schedule: B → C → A
> Average turnaround = (10 + 20 + 120) / 3 = **50s**

### Problem: Late Arrivals

Relax assumption 2 (jobs can arrive at different times). SJF is **non-preemptive** — once a job starts, it runs to completion.

> [!example] Example — Different Arrival Times
>
> | Job | Arrival (s) | Run time (s) |
> |-----|-------------|--------------|
> | A   | ~0          | 100          |
> | B   | 10          | 10           |
> | C   | 10          | 10           |
>
> SJF schedule: A runs first (only job at t=0), then B → C
> Average turnaround = (100 + 100 + 110) / 3 = **103.3s**

---

## Preemptive Scheduling: STCF

Previous schedulers (FIFO, SJF) are **non-preemptive** — they only schedule a new job when the previous job voluntarily relinquishes the CPU.

> **Preemption:** Schedule a different job by taking the CPU away from a *running* job.

> [!note] Lecture Note
> Preemption is a **mechanism**, not a policy.

### Shortest Time-to-Completion First (STCF)

Always schedule the job with the **minimum time remaining**.

> [!example] Example
>
> | Job | Arrival (s) | Run time (s) |
> |-----|-------------|--------------|
> | A   | ~0          | 100          |
> | B   | 10          | 10           |
> | C   | 10          | 10           |
>
> At time 10, B and C arrive. Remaining times: A = 90s, B = 10s, C = 10s.
> STCF preempts A, runs B (10–20), then C (20–30), then A resumes (30–120).
> Average turnaround = (10 + 20 + 120) / 3 = **50s**

---

## Round Robin (RR)

**Key idea:** Switch between jobs periodically (using a **time slice**) to reduce response time.

> [!example] Example — Three Equal Jobs (5s each, all arrive at ~0)
>
> **FCFS:** A (0–5), B (5–10), C (10–15)
> - Average response time = (0 + 5 + 10) / 3 = **5s**
> - Average turnaround time = (5 + 10 + 15) / 3 = **10s**
>
> **Round Robin (1s slice):** A, B, C, A, B, C, A, B, C, ...
> - Average response time = (0 + 1 + 2) / 3 = **1s**
> - Average turnaround time = (13 + 14 + 15) / 3 = **14s**

### Trade-offs

- Round robin **increases turnaround time** but **decreases response time**
- Tuning challenges:
  - What is a good time slice length?
  - What is the overhead of context switching?

> [!note] Lecture Note
> Context switch overhead is ~50μs. The time slice must be long enough to amortize this cost but short enough for good response time.

---

## I/O-Aware Scheduling

Relax assumption 3 (jobs can perform I/O).

### Not I/O Aware

If a job holds the CPU while blocked on disk, the CPU sits idle — wasteful.

### I/O-Aware Approach

Treat each CPU burst of a job as a separate "sub-job." When a job completes I/O, its new CPU burst enters the ready queue.

> [!example] Example
> Job A alternates between 10s CPU bursts and disk I/O. Job B needs 50s of CPU.
> With STCF, each of A's CPU bursts (10s) is shorter than B's remaining time, so A preempts B whenever A completes I/O. Both CPU and disk stay busy.

---

## Multi-Level Feedback Queue (MLFQ)

Relax assumption 4 (run time is **not** known). Must support two job types:

- **Interactive** programs care about **response time**
- **Batch** programs care about **turnaround time**

### Structure

- Multiple levels of round-robin queues
- Each level has **higher priority** than the level below
- Higher-priority jobs can **preempt** lower-priority jobs

### MLFQ Rules

- **Rule 1:** If `priority(A) > priority(B)`, A runs
- **Rule 2:** If `priority(A) == priority(B)`, A & B run in round robin
- **Rule 3:** New processes start at the **top priority**
- **Rule 4:** If a job uses its whole time slice, **demote** it to a lower priority queue

> [!note] Lecture Note
> Rule 4 is the "feedback" — the scheduler uses past behavior to predict future behavior. Longer time slices are given at lower priorities.

### One Long Job

A long-running job starts at Q2 (top), gets demoted to Q1 after using its time slice, then demoted to Q0 where it runs with the longest time slice.

### Interactive Process Joins

When a new interactive process B arrives, it enters at Q2 (top priority) and preempts the long-running job A sitting in Q0.

### Problems with Basic MLFQ

- **Starvation:** If many interactive jobs (B, C) keep arriving at Q2 and alternate between I/O and CPU, the long-running job A in Q0 never gets to run

### Avoiding Starvation: Priority Boost

- **Rule 5:** After some time period S, move **all** jobs in the system to the topmost queue

> [!note] Lecture Note
> Each queue has its own time slice length. The boost periodically resets all priorities, giving long-running jobs a chance to run.

---

## Lottery Scheduling

MLFQ is not fair — shorter burst jobs get more CPU time.

**Goal:** Proportional (fair) share — e.g., 3 users each get 1/3 of CPU, no matter how long they run.

### Approach

- Give processes **lottery tickets**
- At each scheduling decision, draw a random winner — that process runs
- **Higher priority = more tickets** = higher proportion of CPU

### Example

| Job | Tickets |
|-----|---------|
| A   | 10      |
| B   | 20      |
| C   | 30      |

- Lotteries control proportion of CPU — more tickets means higher proportion
- When a job runs is **random** — may win multiple lotteries in a row or lose many

### Implementation

```c
int counter = 0;
int winner = getrandom(0, totaltickets);
node_t *current = head;
while (current) {
    counter += current->tickets;
    if (counter > winner) break;
    current = current->next;
}
// current is the winner
```

> [!example] Example
> Jobs: A(1), B(1), C(100), D(200), E(100) — totaltickets = 402
> - `winner = 50` → counter passes 50 at Job C → **C runs**
> - `winner = 350` → counter passes 350 at Job E → **E runs**
> - `winner = 0` → counter passes 0 at Job A → **A runs**

---

## Summary

**Scheduling Policies:** FIFO, SJF, STCF, RR, MLFQ, Lottery

- Understand **workload characteristics** like arrival, CPU, I/O
- Scope out goals with **metrics** (turnaround time, response time)
- **Trade-offs** based on goals and metrics (RR vs. STCF)
- Past behavior is a good predictor of future behavior (MLFQ)
- Proportional-share scheduling (Lottery) provides fairness

---

## Related

- **Navigation:** [[lecture-02|← Lecture 2]] · [[lecture-04|Lecture 4 →]]
- **Exam:** [[exams/midterm-1/index|Midterm 1]]
- **Course:** [[README|CS 537 Map of Content]]
