SYS Systems and big data Algorithm

Scheduling

Also known as: schedule · job queue · shortest first · SPT · Smith's rule · Moore–Hodgson · SRPT · shortest remaining · list scheduling · LPT · Graham's anomalies · makespan

In what order to do the work, when the order decides how long people wait, how many are late, and when the last piece finishes. On the real judging times of every problem in this catalogue: “shortest first” cuts the wait 1.7 times on one core and turns out to be the worst order on sixty-four, preemption helps everyone but the longest, and an extra core can slow everything down.

Advanced 25 minutes scheduling greedy heap approximation operating systems load balancing in production classic

Before the catalogue is deployed, the reference solution of each problem is rerun on all its tests: 206 problems, 1776 runs, 81 seconds of core time. Let each problem be a submission whose author is waiting for a verdict, and let all 206 arrive at once. There is one core. In what order should they be judged?

In alphabetical order the mean wait for a verdict is 42.4 seconds, shortest first 24.3, longest first 57.3. The work is the same, the core is busy without a break and finishes at the same moment, yet the mean wait of the best and worst orders differs 2.4 times. This is the scheduling problem: the work is given, and the order decides who waits and how long.

The idea

A schedule is the order in which tasks get the core, and when there are several cores, also which core. What counts as a good schedule depends on the goal: a small mean wait, few late tasks, an early end of all the work. Different goals have different best rules, and almost all of them are greedy: sort the tasks, or each time take the best by a simple criterion. They are proved by exchange, as in the article on greedy algorithms, and when the best schedule is too hard to find, the greedy rule is compared with a lower bound.

The article goes from simple to complex: one core and all tasks at once; deadlines; tasks arriving one at a time; many cores; dependencies between tasks. How an operating system shares a core between programs without knowing how long each will run is told in the course chapter. Here the durations are known, and the question is a different one: which order is best, and why.

One core: shortest first

The time to a verdict runs from a submission’s arrival to the end of its judging; in the course chapter it is called the turnaround time. When everything arrives at time 0, it is simply the moment the submission finishes.

If all tasks are ready from the start, the sum of times to verdict on one core is smallest when the tasks go in increasing order of duration.

Let the tasks go in an order with durations p1, p2, …, pn. The task in place k finishes at time p1 + … + pk. Add up these moments and count how many times each duration enters the sum: pk enters the finishing moment of task k itself and of all tasks after it, that is, n − k + 1 times. The sum equals Σ (n − k + 1)·pk: the duration in first place is counted n times, in last place once.

If somewhere a long task stands before a short one, in places k < l with pk > pl, swap them. The sum changes by (n − k + 1)(pl − pk) + (n − l + 1)(pk − pl) = −(l − k)(pk − pl) < 0, that is, it decreases. So in the best order (and one exists, since there are finitely many orders) there is no such pair: the tasks go in increasing order of duration.

The scene shows the same without formulas. The staircase at the bottom shows how many submissions are still waiting, and the area under it equals the sum of times to verdict: each submission adds a strip of height 1 from zero to its end. Shortest first brings the staircase down fastest, longest first keeps it up longest.

Submissions are not always equal: for one verdict a whole group is waiting, for another one person. If a task has a weight w, the price of a second of its waiting, then what must be reduced is Σ w·C, where C is the finishing moment. The rule was found by Wayne Smith in 1956: in increasing order of p/w, that is, first whatever carries more weight per second of work.

The sum Σ w·C on one core is smallest when the tasks go in increasing order of p/w.

Take two neighbouring tasks i and j; everything before them finishes at time t. If i goes first, their contribution is wi(t + pi) + wj(t + pi + pj); if j, then wj(t + pj) + wi(t + pj + pi). The other tasks finish at the same moments. The second minus the first equals wipj − wjpi, so putting i first is no worse when pi/wi ≤ pj/wj. Take a best order. While it has neighbours against the rule, swap them: the sum does not grow, and the number of pairs against the rule decreases, as in bubble sort. In the end we get the order by p/w with the same smallest sum.

With w = 1 this is “shortest first” again. In code the fractions are better compared without division, pi·wj < pj·wi, so there are no rounding errors.

Deadlines: how many will be late

In the article on greedy algorithms a print shop has eight orders with deadlines, and Jackson’s rule (in increasing order of deadlines) makes the worst lateness smallest: three hours. But often something else matters: not how late, but how many orders are late, because a late order is of no use to the customer any more. By this measure the deadline order is bad: in it five orders out of eight are late.

The Moore–Hodgson algorithm goes through the orders by deadline and takes them on. As soon as the next order is late, the longest of those taken is thrown out; it is done at the very end, already late. In the print shop the posters are thrown out, and only they are late. Trying all 256 sets of orders in the listing confirms that eight cannot be done on time but seven can. The price: the posters are 16 hours late. The rule reduces the number of late orders and does not look at how late they are.

The Moore–Hodgson algorithm completes the largest possible number of orders on time.

First note: a set of orders can be done on time only if the deadline order manages it. That is Jackson’s rule: if the worst lateness in deadline order is above zero, it is above zero in any other order as well.

We prove the theorem by induction on the number of orders. If nobody is late in deadline order, the algorithm takes everything, and nothing is better. Otherwise number the orders by deadline; let k be the first late one, and m the longest among orders 1, …, k. The algorithm throws it out first. Let us show there is a best set without m. Take any best set S of orders done on time. Orders 1, …, k cannot all be in it: together they do not fit before deadline k. Let r ≤ k be an order not in S. If m is not in S either, there is nothing to prove. Otherwise replace m in S by r and check that everything fits in deadline order.

An order with number j < k finishes no later than p1 + … + pj, and that is no later than its deadline: before k nobody was late in the overall order. Order k, if it is in the set, finishes no later than p1 + … + pk − pm ≤ p1 + … + pk−1, because pm ≥ pk; and that is no later than deadline k − 1, let alone deadline k. The orders after k are the same as in S, and before them the no longer r stands in place of m: they finish no later than in S. We have a set of the same size, done on time and without m.

So the best answer for all orders equals the best answer for the orders without m. And the algorithm, having thrown out m, proceeds exactly as it would on the orders without m: there nobody before k is late, and k fits by what was just proved. By the induction hypothesis the algorithm finds the best answer on the orders without m, and so on all orders.

The orders taken are conveniently kept in a heap with the longest on top; one removal is always enough, because without the new order everything fitted, and the removed one is no shorter than the new one. This is the article’s first problem.

Submissions arriving one at a time

On a real server submissions do not arrive all at once. Let the same 206 problems arrive one at a time at random moments, so that the core is busy 90% of the time, and compare four rules. “First come” is an ordinary queue. “Round robin” gives each waiting submission 10 milliseconds in turn, as operating systems share a core. “Shortest next”: on becoming free, the core takes the shortest of the waiting submissions and runs it to the end. SRPT (shortest remaining processing time): the submission with the least work left always runs, and a new submission can preempt the current one.

rulemeanmedianthe longest submission, 2.6 s, waits
first come1.9 s1.6 s4.5 s
round robin, 10 ms each2.1 s1.5 s15.5 s
shortest next1.2 s0.7 s12.6 s
SRPT1.0 s0.4 s18.1 s

SRPT halves the mean time to verdict compared with the queue and quarters the median. The scene shows why. All rules have the same amount of work queued, since the core is always busy with something, but under SRPT that work lies in a few big submissions, and under the other rules in many small ones, and each small one is someone’s wait. On the chart at the bottom the SRPT line never rises above the others. Linus Schrage proved in 1968 that this holds for any stream: at any moment SRPT has no more unfinished submissions than any other rule.

If submissions can be preempted and resumed without loss, SRPT gives the smallest sum of times to verdict for any arrival moments.

Treat time as an integer: work proceeds in steps of a millisecond, and at each step the core works on one submission or stands idle. Take any schedule and find the first step at which it acts differently from SRPT. If at that step the core stands idle although someone is waiting, give the step to the waiting submission: it finishes no later, and the others do not change. If instead the core works on submission j while the arrived submission i has the least work left, collect all the steps from this one on that the schedule gives to i or j, and hand them out anew: the first ri to submission i, where ri is its remainder, the rest to j. Both submissions have already arrived, so this is allowed.

The last of the collected steps is still last, and now j finishes on it; before, one of the two finished on it. And i finishes no later than the first of the two finished before: by that moment all the steps of the finished submission had passed, and there are at least ri of them, because its remainder is no smaller. The sum of the two finishing moments did not grow, the other submissions are untouched, and the schedule now coincides with SRPT for one step longer. Repeating this, we turn any schedule into SRPT, and the sum never grows on the way.

Decisions about what to run are needed only at moments of arrival and completion: between them the submission with the smallest remainder stays smallest, since its remainder only decreases. So SRPT is a heap of remainders and a pass over events; this is the article’s second problem.

The longest pay the price. The longest submission, 2.6 seconds of work, waits 18.1 seconds under SRPT: every new shorter submission preempts it. If short ones come in an unbroken stream, a long one may never get its turn. But it is fairer to compare not with the queue but with round robin. That is meant to be fair, and yet the longest submission waits 15.5 seconds under it, almost as long, and its mean is worse even than a plain queue’s: under round robin each submission is stretched over the time of everyone sharing the core with it. In 2003 Mor Harchol-Balter and co-authors built SRPT into a web server’s sending of responses, where the remainder is how many bytes of the file have not yet been sent. The mean response time dropped noticeably, and large files lost almost nothing compared with fair sharing of the link.

An operating system does not know durations, and SRPT is not directly available to it. The multilevel queue from the course chapter approximates it: a program that holds the core for long sinks into low-priority queues and stops delaying the short ones.

Many cores: when the last one finishes

Now the server has m cores, and the goal is different: run all 1776 tests as early as possible, because the catalogue is deployed when the last one finishes. The simplest schedule is list scheduling: the tests stand in a list, and a core that becomes free takes the next one. Any pool of workers with a shared queue works this way. It is impossible to finish earlier than max(all the work / m, the longest test): the cores cannot do the work faster than by splitting it evenly, and the longest test cannot be split.

coresbound, salphabeticalshortest firstlongest first
810.1510.2810.7110.16
322.542.903.252.54
641.271.762.061.27

“Shortest first”, the best order for waiting on one core, is the worst here: the longest tests are left to the end, when there is nothing to even them out with, and on 64 cores everything finishes 62% later than the bound. “Longest first”, LPT (longest processing time), is almost at the bound, and on 32 cores exactly on it, which means that there this schedule is the best possible.

List scheduling, with the list in any order, finishes no later than (2 − 1/m)·OPT, where OPT is the time of the best schedule.

Let test j of duration pj, started at time s, finish last. Before time s all cores were busy, otherwise a free core would have taken j earlier. So by time s at least m·s work was done, all of it the work of other tests: m·s ≤ P − pj, where P is all the work. The end of the schedule is s + pj ≤ (P − pj)/m + pj = P/m + (1 − 1/m)·pj. Each of the two terms is at most the lower bound: P/m ≤ OPT and pj ≤ OPT. In total at most (2 − 1/m)·OPT.

The bound is tight. If the list starts with m(m − 1) one-second tests and ends with an m-second test, the short ones occupy all cores for exactly m − 1 seconds, and the long one starts only after that: everything finishes after 2m − 1 seconds. The best schedule finishes after m: the long test goes on one core at once, the short ones on the rest.

List scheduling with the longest tests first finishes no later than 3/2·OPT.

Let test j finish last. The tests after j in the list are no longer than it and do not affect its end; drop them, and OPT does not grow. Now j is last in the list and the shortest. If there are at most m tests, each runs on its own core from zero, and everything finishes with the longest test, that is, no later than OPT. Otherwise in any schedule two of the m + 1 longest tests land on one core, so OPT ≥ 2pm+1 ≥ 2pj. By the argument of Graham’s theorem the end is at most P/m + pj ≤ OPT + OPT/2.

In 1969 Graham brought the bound for “longest first” down to 4/3 − 1/(3m), and it too is tight. Finding the best schedule itself is hard: already for two cores it is the partition problem, splitting numbers into two piles with equal sums, and that is NP-hard, like the problems in the course chapter on hard problems. For small integer durations dynamic programming solves it, like the knapsack, but that does not help for thousands of tests on dozens of cores. So in practice “longest first” is taken and checked against the bound: if the schedule meets it, it is the best, and there is nothing more to look for.

If a test can be interrupted and finished on another core, the bound is always achievable. Robert McNaughton proposed a wrap-around rule in 1959: lay the tests in a row on the first core up to the mark max(P/m, the longest test), and carry what does not fit to the start of the next core. No test ends up on two cores at once: its pieces, the end of one core and the start of the next, do not overlap in time, because the test is no longer than the mark.

Graham’s anomalies

Add dependencies: a task cannot start until those it needs are finished, as in a project build where linking waits for compilation. List scheduling stays the same: a free core takes the first task in the list whose prerequisites are all done. And here it behaves strangely. Ronald Graham gave an example of nine tasks: on three cores everything is done after 12. Add a fourth core: after 15. Make every task one unit shorter: after 13. Remove two dependencies: after 16.

The cause is the same everywhere. The longest task, the ninth, is last in the list and gets a core only when nothing earlier is ready. On three cores tasks 5–8 wait for the fourth, and at time 3, when the first task’s core frees up, nobody stands in the ninth’s way. With a fourth core the fourth task starts at once, tasks 5–8 are ready by time 2 and take the cores before the ninth, and it starts only at time 6. Any of the three changes (an extra core, shorter tasks, removed dependencies) changes who is ready first, and the greedy rule gives the core to the wrong task.

So it is all about the order of the list. Put first the tasks with the longest chain of work behind them: the list by critical path, 1, 9, 4, 5, 6, 7, 8, 2, 3. With it everything is done after 12 on three and on four cores, after 10 with the shortened tasks, after 12 without the two dependencies: in this example the anomalies disappear. That is no guarantee (Graham’s limit below holds for any list), but it shows where the oddities come from.

Graham also proved a limit on these oddities: if a schedule on m cores finished after T, then after any changes (different durations, fewer dependencies, a different list, m′ cores) it finishes no later than (1 + (m − 1)/m′)·T. For four cores instead of three that is 1.5·12 = 18, and the example gives 15.

Code

The listing repeats the article on the same data: one core and three orders; the print shop with a brute-force check; a stream of submissions and four rules; many cores against the bound; Graham’s nine tasks with the list by number and by critical path.

What it costs

All the rules of the article are cheap. “Shortest first”, Smith’s rule and deadline order are a sort, O(n log n). Moore–Hodgson is a sort and a heap of the orders taken: each order is pushed once and popped at most once, also O(n log n). SRPT is a heap of remainders, and decisions are needed only at 2n moments of arrival and completion, so the whole stream costs O(n log n). List scheduling on m cores is a heap of the moments when the cores become free, O(n log m); “longest first” adds a sort.

Only the best is expensive. The best schedule on several cores is NP-hard, and with dependencies it is no easier. The number of late orders also becomes hard if lateness has different prices: already with a common deadline, choosing the orders to complete is exactly the knapsack. So in practice scheduling is greedy and compared with a lower bound, and on real data the gap is usually small, as for “longest first” above.

Where it works

Judging and tests. Solution-judging servers and continuous integration systems spread tests over cores and machines. CircleCI, for example, splits tests between parallel machines by the timings of past runs: this is the problem from the section on cores, evening out the ends.

Web servers. Sending by shortest remainder: the response with the fewest bytes left goes first. In the experiments of Harchol-Balter and co-authors this noticeably reduced the mean response time and hardly affected large files.

Operating systems. The system does not know programs’ durations and approximates “shortest first” with a multilevel queue: whoever holds the core for long sinks lower.

Builds. make -j and similar build tools start tasks whose dependencies are ready as cores free up. In essence this is list scheduling with dependencies, and Graham’s anomalies apply to it directly.

History

Scheduling theory grew out of problems about machines and orders in the 1950s. James Jackson proved the deadline rule in 1955, Wayne Smith the p/w ratio rule in 1956, Robert McNaughton the wrap-around schedule for several machines in 1959. In 1966 Ronald Graham estimated how many times list scheduling can lose to the best and described its anomalies, and in 1969 analysed “longest first”. In 1968 came J. Michael Moore’s paper on the smallest number of late jobs, which also gave Hodgson’s simplified version, and Linus Schrage’s proof that SRPT is optimal. A short proof of the Moore–Hodgson algorithm, close to the one above, was published in 2021 by Joseph Cheriyan, R. Ravi and Martin Skutella.

Problems

The first is how many orders to complete by their deadlines: Moore–Hodgson with a heap. The second is the smallest sum of turnaround times when submissions can be preempted: SRPT over events, with a stream of all the catalogue’s tests among the tests.

How many by the deadline The shortest remaining

Data: The time of the catalog's reference solutions on each test (measured in the sandbox, 17.10.2026)