Start-time Fair Queueing: Pay-after fair scheduling
The problem we target
- Indivisible and non-preemptible: once a request is dispatched, it can neither be split nor paused to let others go first.
- Large cost variance: costs span several orders of magnitude, from short completions to very long generations, and any formal upper bound shifts with the model and the workload.
- Bursty user activity: it is common for a user to send a large burst of requests and then go silent for a long time.
1. Notation and assumptions
1.1. Users and requests
1.2. Cost and the sequential model
1.3. Mapping to packet terminology
| Original paper's term | This article's term | Concrete entity in LLM serving |
|---|---|---|
| Flow | User | Fairness entity: an organization, an API key, etc. |
| Packet | Request | A single inference call |
| Packet length \(l\) | Cost \(c(r)\) | Processing work estimated from input length and estimated output length (§7.4) |
| Weight \(r_f\) | Weight \(w_i\) | Contract or priority (this article's analysis uses equal weights; §4.4) |
| Transmission on the link | Dispatch to the server fleet | The load balancer sending out a request |
| Busy period | Period during which waiting requests exist | One contiguous stretch of congestion (§2.5) |
2. The algorithm
2.1. The original definition: tags and virtual time
2.2. Folding into one variable per user
2.3. Selection and charging
- Select the user with the smallest meter, \(i^{*} = \mathop{\mathrm{arg\,min}}_{i \in A} S_i\) (ties broken by user ID).
- Advance the virtual time: \(v \leftarrow S_{i^{*}}\). Since the selected meter is the minimum among waiting users, this corresponds to the original paper's "start tag of the packet in service" (§5.3).
- Dispatch the head request \(r\) of \(i^{*}\)'s waiting queue.
- Charge: \(S_{i^{*}} \leftarrow S_{i^{*}} + c(r)\). The value after charging is the finish tag of the request just dispatched, i.e. the start tag of the next one.
2.4. Joining and returning
2.5. Global idle: resetting the competition
2.6. The algorithm as a whole
- When a request arrives, append it to the user's FIFO waiting queue. If the queue was empty, first rejoin with \(S_i \leftarrow \max(S_i, v)\) (a new user starts at \(S_i = v\)).
- While there is spare processing capacity, repeat the selection, dispatch, and charge of §2.3, one request at a time.
- When no waiting user remains, reset the competition with \(v \leftarrow \max(v, \max_i S_i)\) (§2.5).
- Completions, failures, and cancellations never modify the meters.
2.7. A small numerical example
| Dispatch | \(v\) | Selected | Charge | \(S_A\) | \(S_B\) |
|---|---|---|---|---|---|
| Initial | 0 | — | — | 0 | 0 |
| 1 | 0 | A (tie) | +3 | 3 | 0 |
| 2 | 0 | B | +1 | 3 | 1 |
| 3 | 1 | B | +1 | 3 | 2 |
| 4 | 2 | B | +1 | 3 | 3 |
| 5 | 3 | A (tie) | +3 | 6 | 3 |
| 6 | 3 | B | +1 | 6 | 4 |
| 7 | 4 | B | +1 | 6 | 5 |
| 8 | 5 | B | +1 | 6 | 6 |
3. Fairness properties
3.1. The band invariant of the meters
- The virtual time \(v\) is monotonically non-decreasing. It never moves backwards.
- For every user, \(S_i \le v + c_{\max}\).
- For every waiting user, \(S_i \ge v\).
- Selection and charge: the new \(v\) is the minimum meter among waiting users, so by property 3 it is at least the old \(v\) (property 1). The selected user's meter immediately after charging is \(v + c(r) \le v + c_{\max}\) (property 2). Every other waiting user's meter is at least the minimum, i.e. at least the new \(v\) (property 3).
- Joining and returning: the rejoined meter is \(\max(S_i, v)\); by the induction hypothesis \(S_i \le v + c_{\max}\), so it remains at most \(v + c_{\max}\) after rejoining (property 2), and at least \(v\) (property 3).
- Global idle: the new \(v\) is \(\max(v, \max_i S_i)\), which is at least the old \(v\) (property 1), and by property 2 at most the old \(v + c_{\max}\). Since \(v\) only increases, property 2 is preserved, and property 3 holds vacuously because no user is waiting.
- Departure: changes nothing.
3.2. Cost difference between continuously backlogged users
3.3. No gain from repeatedly leaving and rejoining
3.4. Why the global-idle reset is safe
3.5. The lower bound on granularity
4. Comparison with DRR
4.1. How DRR works and SFQ's pay-after accounting
4.2. No quantum parameter
4.3. Returning from idle
4.4. Weighting
4.5. Comparison summary
| Aspect | DRR | SFQ |
|---|---|---|
| Per-user state | Deficit counter (credit balance) | Meter \(S_i\) (the next request's start tag) |
| Global state | Current position in the circular list | Virtual time \(v\) |
| Selection | Traverse the circular list | argmin over waiting users |
| Cost handling | Distribute quanta in advance, consume on send (pay-before) | Charge at dispatch (pay-after) |
| Cost information required | Exact cost (packet length in the original paper) | Any value fixed at dispatch (an estimate suffices) |
| Parameters | Quantum \(Q_i\) | None |
| Fairness granularity | On the order of \(Q + L_{\max}\) | \(c_{\max}\) (interval cost difference \(\le 2c_{\max}\)) |
| Cost per decision | Amortized \(O(1)\) (when \(Q \ge L_{\max}\)) | \(O(T)\) scan (\(O(\log T)\) with an ordered structure) |
| Intended scale | Thousands to tens of thousands of flows in a router | Tens to hundreds of users per scheduler |
| Return from idle | Reset counter to 0, rejoin at list tail (worst case one round's wait) | Rejoin with \(\max(S_i, v)\) (immediately a candidate if no residual) |
| Global idle | No state (each flow already reset on departure) | Advance \(v\) to the maximum finish tag, resetting the competition |
| Weighting | \(Q_i \propto w_i\) | Charge \(c / w_i\) |
5. Design decisions tailored to LLM serving
5.1. Fix the estimated cost and never change it afterward
- Simplicity of the invariant: the one-line rule "a meter is charged exactly once, at dispatch" becomes a collection of state-dependent branches as soon as corrections are allowed: preventing duplicate corrections, handling failures in progress, and so on.
- Stability of ordering: output lengths vary widely, and post-hoc corrections would perturb the selection order with noise from past observations. Restricting the quantities used for ordering to values fixed at dispatch makes simulations reproducible and debugging straightforward.
- Where calibration belongs: systematic deviations from reality can be absorbed by calibrating the estimation formula against long-run measurements, without settling accounts on individual requests (§7.4). Calibration and the fairness mechanism can evolve independently.
5.2. Decide only the order
5.3. Reinterpreting a single link as a server fleet
6. Position among related methods
6.1. The lineage of fair queueing and stride scheduling
6.2. Relation to fairness research in LLM serving
6.3. Comparison summary
- SFQ (the method presented in this article)[1]
- Heavy requests: as long as the user's meter is minimal, even a request with cost near \(c_{\max}\) is dispatched immediately, and its full cost is charged exactly once at that moment. The user is then not selected again until the other meters catch up. This is pay-after—"let it through first, then make the user wait"—and over any interval, the cost difference between continuously backlogged users remains at most \(2c_{\max}\) (§3.2).
- On return: under the rejoining rule \(\max(S_i, v)\), users cannot bank priority while idle, and any meter residual above the baseline carries over. A user with no residual immediately joins the group with the smallest meter and becomes eligible for the next dispatch (§2.4). The competition resets only when no waiting users remain (§2.5).
- Deficit Round Robin[2]
- Heavy requests: a quantum \(Q_i\) accumulates as credit on each visit, and a heavy request is not transmitted until the balance reaches its length. This is pay-before—"make a large job wait before sending"—and payment is completed by deducting the request's length at send time, with no penalty afterward.
- On return: when the queue empties, the deficit counter is reset to 0, and the flow rejoins at the tail of the circular list. Unused credit is forfeited, and the flow waits up to one full round for its turn.
- Difference from SFQ: the direction of payment is reversed. DRR "saves up and waits before sending"; SFQ "sends first and waits afterward." This distinction determines whether a quantum parameter is needed, whether selection uses amortized \(O(1)\) round-robin traversal or argmin, and whether a heavy request waits before or after dispatch (§4).
- GPS[4]
- Heavy requests: GPS is a fluid model that assumes infinitely divisible work. Even a heavy job is processed incrementally and simultaneously with all backlogged flows at weight-proportional rates. There is no notion of an order in which whole jobs are dispatched.
- On return: from the instant of return, the flow receives its weight-proportional share of capacity. Capacity unused while idle is not carried forward, so banking cannot arise in principle.
- Difference from SFQ: the divisibility assumption is the exact opposite of SFQ, which dispatches indivisible, non-preemptible requests whole. GPS is not an implementation target; it functions as the idealized reference that discrete schemes like WFQ and SFQ aim to approximate.
- WFQ[3]
- Heavy requests: WFQ dispatches the request with the smallest virtual completion time (finish tag) under GPS. A heavy request has a later finish tag, so it yields to lighter requests before being dispatched whole. Payment is built into the ordering itself: "defer what would finish late."
- On return: an arriving request's tag is computed from the larger of the user's previous finish tag and the current virtual time, so no banking accrues while idle.
- Difference from SFQ: WFQ orders by completion (finish), whereas SFQ orders by start. WFQ needs the request's cost (packet length in the original paper) at selection time and must simulate GPS to obtain virtual time. SFQ needs neither; these two simplifications make it a good fit for LLM serving, where costs are only estimates (§5.1, §6.1).
- Stride scheduling[5]
- Heavy requests: stride scheduling selects the client with the smallest pass value, adding on each selection a fixed stride inversely proportional to the client's tickets. Each allocation is a fixed quantum, and "heavy work" is divided into preemptible CPU-time slices, so the accounting never jumps by the cost of one large job.
- On return: a joining or returning client's pass is aligned to the global reference value, preventing banking during dormancy.
- Difference from SFQ: whether the amount added each time is fixed (derived from tickets) or the cost of each job. SFQ's single variable per user can be seen as "stride scheduling whose stride is each request's cost" (§6.1). The difference between divisible CPU time and indivisible requests corresponds exactly to making the stride variable.
- Linux CFS[6]
- Heavy requests: CFS runs the task with the smallest vruntime. Execution is preemptible, and the CPU time actually used (normalized by weight) is charged continuously to vruntime—a measurement-based form of pay-after. Heavy work is divided into time slices and interleaved with other tasks, with charges accumulating incrementally.
- On return: using min_vruntime as the reference point, a waking task's vruntime is adjusted so it is not retroactively over-favored (the treatment of sleep wake-ups involves implementation details).
- Difference from SFQ: charging is measured and continuous, whereas this article's SFQ charges the estimated cost in one lump at dispatch and never corrects it (§5.1). The difference between a preemptible CPU and a request that cannot be stopped once dispatched shows up directly in the timing of charging.
- VTC (Virtual Token Counter)[7]
- Heavy requests: at each continuous-batching step inside the inference engine, VTC prioritizes the client whose weighted cumulative counter of processed input and output tokens is smallest. A heavy request's counter grows as it is processed, gradually yielding to other clients at token granularity.
- On return: the counter of a client returning from idle is raised to the current level, preventing banking during dormancy. This plays the same role as SFQ's rejoining rule.
- Difference from SFQ: the insertion point and responsibility differ. VTC replaces the engine's scheduler and equalizes "the token volume actually processed," while the upstream SFQ equalizes only "the dispatch order" in front of an unmodified server fleet (§6.2). Charging also differs: incremental addition of measured tokens versus a lump-sum charge of the estimated cost at dispatch.
7. Implementation notes
7.1. Data structures and complexity
7.2. Maintaining numerical stability
7.3. Determinism
total_cmp in Rust) is the safe choice.
7.4. Estimating the cost
max_tokens) directly, one can estimate output length by multiplying that cap by the historical ratio "total actual output length ÷ total output cap." This ratio needs only infrequent updates from long-term aggregates and can be maintained per model or per user.
7.5. Properties worth testing
8. Design limitations
- Fairness applies to charged cost: if a user's actual resource consumption systematically differs from the charged cost, measured fairness is distorted by that discrepancy. The remedy is to calibrate the estimation formula, not to correct individual charges (§5.1, §7.4).
- Fairness of ordering, not of execution: only the dispatch order is controlled; post-dispatch GPU time, KV-cache occupancy, and preemption inside the inference engine are not touched.
- The original delay and throughput guarantees do not carry over: those guarantees presuppose a single link and exact packet lengths, so our reinterpretation (§5.3) carries only the fairness of cost allocation (§3). No deadlines or maximum waiting times for individual requests are guaranteed.
- Within a user, FIFO is fixed: there is no intra-user prioritization or per-request-type control.
- A single scheduler is assumed: the meters and virtual time are state held by one process. If the load balancer is replicated, fairness across replicas is outside the scope of this design.
- Selection costs \(O(T)\) or \(O(\log T)\): this is unsuitable for processing tens of thousands of flows at line rate, where \(O(1)\) designs in the DRR family are a better fit (§4.2).
9. Summary
- There are only three rules: select the user with the smallest meter and charge the cost exactly once at dispatch; rejoin with \(\max(S_i, v)\) when a user joins or returns; and when no waiting users remain, advance virtual time to the maximum finish tag. These are the rules of the original 1996 SFQ, and once a charge is fixed, it is never corrected or refunded.
- The guarantee is fairness over the charged estimated cost, limited to the dispatch order at a single scheduler. Over any interval, the cost difference between continuously backlogged users is at most \(2c_{\max}\). Because requests are indivisible, this granularity is of the same order as the fundamental lower bound.
- The decisive difference from DRR is pay-after accounting: instead of distributing transmission credit in advance, SFQ dispatches a request first and then records its cost on the meter. This removes the need to tune a quantum, gives single-request granularity, and allows prompt return from idle. The trade-off is \(O(T)\) selection, or \(O(\log T)\) with an ordered structure, which makes the design unsuitable for router-scale flow counts.
- Selection depends only on the meters, never on the cost of the request under consideration; virtual time is derived directly from the scheduler's operation; and fairness is defined independently of processing speed. These three properties allow SFQ to be applied with only a change in interpretation to LLM-serving load balancers, where costs are estimates and execution is parallel.
10. References
- [1] Pawan Goyal, Harrick M. Vin, Haichen Cheng, "Start-time Fair Queueing: A Scheduling Algorithm for Integrated Services Packet Switching Networks," SIGCOMM, 157―168, 1996. (Start-time Fair Queueing; the subject of this article)
- [2] M. Shreedhar, George Varghese, "Efficient Fair Queuing Using Deficit Round-Robin," IEEE/ACM Transactions on Networking 4(3), 375―385, 1996. (Deficit Round Robin)
- [3] Alan Demers, Srinivasan Keshav, Scott Shenker, "Analysis and Simulation of a Fair Queueing Algorithm," SIGCOMM, 1―12, 1989. (Fair Queueing / WFQ)
- [4] Abhay K. Parekh, Robert G. Gallager, "A Generalized Processor Sharing Approach to Flow Control in Integrated Services Networks: The Single-Node Case," IEEE/ACM Transactions on Networking 1(3), 344―357, 1993. (Generalized Processor Sharing)
- [5] Carl A. Waldspurger, William E. Weihl, "Stride Scheduling: Deterministic Proportional-Share Resource Management," Technical Memorandum MIT/LCS/TM-528, MIT Laboratory for Computer Science, 1995. (Stride scheduling)
- [6] Ingo Molnar, "CFS Scheduler," The Linux Kernel documentation, 2007. (Linux CFS design document)
- [7] Ying Sheng, Shiyi Cao, Dacheng Li, Banghua Zhu, Zhuohan Li, Danyang Zhuo, Joseph E. Gonzalez, Ion Stoica, "Fairness in Serving Large Language Models," OSDI, 2024. (VTC: token-level fairness inside the inference engine)
- [8] Shiyi Cao, Yichuan Wang, Ziming Mao, Pin-Lun Hsu, Liangsheng Yin, Tian Xia, Dacheng Li, Shu Liu, Yineng Zhang, Yang Zhou, Ying Sheng, Joseph Gonzalez, Ion Stoica, "Locality-aware Fair Scheduling in LLM Serving," arXiv preprint arXiv:2501.14312, 2025. (DLPM: combining prefix locality with fairness)
- [9] Zhixiang Wei, James Yen, Jingyi Chen, Ziyang Zhang, Zhibai Huang, Chen Chen, Xingzi Yu, Yicheng Gu, Chenggang Wu, Yun Wang, Mingyuan Xia, Jie Wu, Hao Wang, Zhengwei Qi, "Equinox: Holistic Fair Scheduling in Serving Large Language Models," arXiv preprint arXiv:2508.16646, 2025. (Equinox: integrated treatment of fairness and operational efficiency)
Last Modified: July 12, 2026