What the libraries do

How many pieces the head can afford

A sorted file of 65,536 keys with a late tenth appended in random order can be sorted by taking the tail a buffer at a time, sorting each piece, and merging each into the head. It beats the buffered merge sort at a 64th of the file and loses at smaller buffers, because every piece's merge crosses the head's top keys again. Merging the pieces with each other first, while a round costs less than the crossings it removes, was predicted to win at every buffer. It wins at none. The first round always costs more than no rounds — 18.97 moves a key against 16.04 at a 1,024th — because a group larger than the buffer crosses the head by rotation, and the cost falls under the merge sort's only when the rounds run all the way, which is the merge sort. The head can afford thirteen pieces and not twenty-six; beyond that the best a tail can do is be one run.

Pieces that fit the buffer sorted a file the merge sort finds awkward. Sixty-five thousand keys arrive sorted, and then a late tenth arrives in random order, drawn from the top fifth of the keys. The buffered stable merge sort — the library’s merge, which works in a buffer when the shorter of two runs fits and by rotation when it does not — spends two thirds of its moves sorting the late tenth among itself, a dozen levels of merging over 6,554 keys. The earlier page took the tail a buffer at a time instead, sorted each piece, and merged each sorted piece into the head from the right. At a buffer of a 64th of the file the pieces won, by removing the final merge’s rotation. At smaller buffers they lost, because each piece’s merge into the head crosses the head’s top fifth again, about 0.15 moves a key of the file a crossing.

Its closing section saw the two buffers as two ends of one choice. A sort could merge the pieces with each other before merging them into the head: four pieces merged pairwise and then into the head cross the head half as often as four pieces would, and pay one more level of merging inside the tail. Both prices are known before they are paid. A level of merging inside the tail costs about two moves a tail key; a crossing costs about the number of head keys above the group’s smallest key, which one binary search in the head finds. The proposal was a rule — merge the pieces among themselves while one more round costs less than the crossings it removes, then merge what is left into the head. It predicted that at a 64th the rule merges the seven pieces once and saves a little more than the pieces already save, and that at a 1,024th it merges the 103 pieces down to four or eight and comes within a fifth of the merge sort. It named the failure: merged groups are larger than the buffer, and their merges into the head rotate again.

The sort, counted by the library’s own merge

The input is the earlier page’s: 65,536 distinct keys, the first nine tenths sorted, the last tenth drawn at random from the top fifth of the keys and appended in random order. The sort finds the head by a scan. It sorts each piece of the tail in place — binary insertion into runs of eight, then merges that fit the buffer, as a library sorts a short array and for the reason where insertion sort actually wins measured — then runs some number of rounds, each merging adjacent groups of the tail in pairs, and finally merges each remaining group into the head. Every merge is the library’s: trimmed by galloping from both ends, the step the skip the split already made added, done in the buffer when the shorter side fits, and split by binary search and rotation until it fits when it does not. Every move is counted by the same counter that counted the merge sort, so the two are on one scale; counting rather than timing is what lets a twentieth of a move a key be a finding, the practice counting instead of timing set out. Sorting the pieces this way costs a little more than the earlier page’s — 2.46 moves a key at a 64th against its 2.41 — and the comparison here is between round counts, all of which share it.

The rule is the proposal’s. Before each round it prices the round at two moves a key of the tail. It prices the crossings the round removes, for each pair of groups, at the head keys above the larger of the two groups’ smallest keys — the later group’s crossing, which the pair would no longer make separately — found by one binary search in the head. It takes the round while the crossings cost more.

Thirteen pieces and not twenty-six

The head can afford 13 pieces and not 26: sent straight in, pieces that fit the buffer beat the merge sort down to a buffer of 1/128 (3.20 moves a key against 3.54), and below it merging them all first is the merge sort to within a twentieth of a move; the proposed rule, which merges part of the way, matches the cheaper only where it takes no roundsMoves a key of a 65,536-key file whose last tenth arrives in random order from the top fifth of the keys, against the buffer. Merge sort, whole file: 1/16 2.04, 1/32 2.41, 1/64 2.93, 1/128 3.54, 1/256 4.39, 1/1024 6.45, 1/4096 8.82. Pieces straight into the head: 1/16 1.98, 1/32 2.16, 1/64 2.46, 1/128 3.20, 1/256 5.00, 1/1024 16.04, 1/4096 58.46. Pieces merged to one first: 1/16 2.10, 1/32 2.47, 1/64 2.96, 1/128 3.55, 1/256 4.35, 1/1024 6.46, 1/4096 8.87. The proposed rule: 1/16 1.98, 1/32 2.16, 1/64 2.75, 1/128 3.55, 1/256 4.68, 1/1024 7.68, 1/4096 11.09. Pieces a buffer: 1/16 2, 1/32 4, 1/64 7, 1/128 13, 1/256 26, 1/1024 103, 1/4096 410. Logarithmic in moves.1248163264buffer, as a share of the filemoves a key of the file (log scale)1/161/321/641/1281/2561/10241/4096pieces straight into the headthe proposed rulepieces merged to one firstmerge sort, whole file65,536 keys, a late tenth from the top fifthmoves counted, sorted checked
Fig. 1 Moves a key of the file against the buffer, from a 16th to a 4,096th. The merge sort: 2.04, 2.41, 2.93, 3.54, 4.39, 6.45, 8.82. Pieces straight into the head: 1.98, 2.16, 2.46, 3.20, 5.00, 16.04, 58.46. Pieces merged to one group first: 2.10, 2.47, 2.96, 3.55, 4.35, 6.46, 8.87. The rule: 1.98, 2.16, 2.75, 3.55, 4.68, 7.68, 11.09.

Sent straight into the head, pieces that fit the buffer beat the merge sort down to a buffer of a 128th — 3.20 moves a key against 3.54, with thirteen pieces — and lose from a 256th, with twenty-six; below that, the cheapest sort is the pieces merged all the way to one group first, which costs the merge sort’s moves to within six hundredths. The rule matches the cheaper end only at the two largest buffers, where it takes no rounds. At a 64th it takes one round and costs 2.75 against the pieces’ 2.46; at a 1,024th it takes five and costs 7.68 against 6.46.

The prediction for a 1,024th was that the rule comes within a fifth of the merge sort, and 7.68 against 6.45 is 19% over, so it holds by a hair. The prediction for a 64th fails: one round makes the pieces worse, not better. And the plate’s larger shape is not one either prediction imagined. There is no buffer at which merging part of the way is the cheapest thing to do. Each buffer has two candidates, no rounds and all of them, and the cheaper of the two changes between a 128th and a 256th.

That the all-rounds sort is the merge sort to within six hundredths is less of a coincidence than it looks. Merging the tail’s sorted pieces all the way to one group is the merge sort’s own schedule applied to the tail alone, and merging that one group into the head is the merge sort’s last merge. The only difference is that the merge sort’s schedule merges the shortest runs first and may merge some of the head’s runs with the tail’s on the way; on a file whose head is one run it has nothing to merge the head with until the end.

The first round always costs more

The first round of merging costs more than none at every buffer: at 1/1024 it raises the cost from 16.04 moves a key to 18.97, and the cost falls under the pieces' own only from the second round, reaching the merge sort's 6.45 at seven; at 1/64 the cheapest is no rounds at allMoves a key against the number of rounds of pairwise merging inside the tail, at three buffers, each beside the merge sort's cost as a faint level. 1/64 (7 pieces, merge sort 2.93): 0 rounds 2.46, 1 rounds 2.75, 2 rounds 2.85, 3 rounds 2.96. 1/256 (26 pieces, merge sort 4.39): 0 rounds 5.00, 1 rounds 5.79, 2 rounds 5.35, 3 rounds 4.68, 4 rounds 4.51, 5 rounds 4.35. 1/1024 (103 pieces, merge sort 6.45): 0 rounds 16.04, 1 rounds 18.97, 2 rounds 15.70, 3 rounds 11.75, 4 rounds 9.16, 5 rounds 7.68, 6 rounds 6.84, 7 rounds 6.46.12481601234567rounds of merging pieces with each other before they crossmoves a key of the file (log scale)buffer 1/1024buffer 1/256buffer 1/64faint levels: the merge sort at each buffermoves a key
Fig. 2 Moves a key against the number of rounds, at buffers of a 64th, a 256th and a 1,024th, each beside the merge sort as a faint level. At a 64th: 2.46, 2.75, 2.85, 2.96 for none to three rounds. At a 256th: 5.00, 5.79, 5.35, 4.68, 4.51, 4.35. At a 1,024th: 16.04, 18.97, 15.70, 11.75, 9.16, 7.68, 6.84, 6.46.

At every buffer the first round of merging inside the tail costs more than none: at a 1,024th it raises the cost from 16.04 moves a key to 18.97, at a 256th from 5.00 to 5.79, at a 64th from 2.46 to 2.75. After the first, each round lowers the cost at the two smaller buffers, and the curve falls below the pieces’ own cost only from the second round at a 1,024th and the third at a 256th, reaching the merge sort’s level when the rounds run out. At a 64th every round costs more than the last.

The shape is a bump, and its cause is the buffer. A piece is exactly one buffer long, so after the first round every group is two buffers long, and a merge of a group into the head no longer has a shorter side that fits the buffer — the head is far longer, and the group is longer than the buffer too. The library’s merge then splits it by binary search and rotates the parts into place, and rotation moves the head’s keys that lie between the split points, sometimes more than once. Before the round, each piece crossed the head by shifting the head keys above its smallest one place along; after it, each group crosses by rotation, which costs more than the shifts of the two pieces it replaced.

The rounds that follow lower the cost because they reduce the number of groups that cross, and every crossing costs rotation. At a 1,024th, after seven rounds there is one group: one crossing, by rotation, for the whole tail. That one rotation is the merge sort’s own rotation, 4.54 moves a key here against its 4.58, which is the earlier page’s finding again from the other side. The pieces won at a 64th by removing the final merge’s rotation; at small buffers the rotation cannot be removed, only made to happen once.

Where the moves go

At a buffer of 1/1024 the head merges are almost the whole cost — 15.39 moves a key with no rounds, falling to 2.19 with 7 — and once a group outgrows the buffer most of a head merge is rotation: 10.38 of 18.31 after one roundMoves a key at a buffer of 1/1024 (64 keys, 103 pieces), split into the pieces' own sorting, the rounds inside the tail and the merges into the head, with the moves made by rotation among the last two. 0 rounds: pieces 0.65, rounds 0.00, head 15.39, of which rotation 0.00; 16.04 in all. 1 rounds: pieces 0.65, rounds 0.15, head 18.17, of which rotation 10.38; 18.97 in all. 2 rounds: pieces 0.65, rounds 0.36, head 14.68, of which rotation 10.78; 15.70 in all. 3 rounds: pieces 0.65, rounds 0.77, head 10.32, of which rotation 8.59; 11.75 in all. 4 rounds: pieces 0.65, rounds 1.29, head 7.21, of which rotation 6.73; 9.16 in all. 5 rounds: pieces 0.65, rounds 1.96, head 5.07, of which rotation 5.55; 7.68 in all. 6 rounds: pieces 0.65, rounds 2.68, head 3.51, of which rotation 4.89; 6.84 in all. 7 rounds: pieces 0.65, rounds 3.62, head 2.19, of which rotation 4.54; 6.46 in all. The merge sort: 6.45, rotation 4.58.0481216200 rounds16.041 rounds18.972 rounds15.703 rounds11.754 rounds9.165 rounds7.686 rounds6.847 rounds6.46pale: pieces · green: rounds · dark: head merges · thin: rotation · line: merge sortmoves a key of the filebuffer 1/1024moves by where they went
Fig. 3 Moves a key at a buffer of a 1,024th (64 keys, 103 pieces) at every number of rounds, split into the pieces’ own sorting, the rounds inside the tail and the merges into the head, with the rotation inside the last two drawn beneath. No rounds: 0.65, 0, 15.39, no rotation. One round: 0.65, 0.15, 18.17, rotation 10.38. Seven: 0.65, 3.62, 2.19, rotation 4.54. The merge sort: 6.45, rotation 4.58.

At a buffer of a 1,024th the merges into the head are almost the whole cost with no rounds — 15.39 moves a key of 16.04 — and once a group outgrows the buffer, most of a head merge is rotation: 10.38 of 18.31 after one round. The pieces’ own sorting is 0.65 at every round count. The first round inside the tail costs 0.15 moves a key of the file, a move and a half for each tail key, near the proposal’s two; the later rounds cost more, 0.94 a key of the file for the seventh, because they merge groups larger than the buffer and rotate too, and all seven together cost 3.62. The proposal priced the first round about right and the crossings as shifts, and with no rounds the second is right as well: 103 pieces each shifting a few thousand head keys is 15.39 moves a key.

After one round the shifts are gone and rotation has replaced them. Fifty-two groups of 128 keys each cross by splitting and rotating, and each rotation moves the head’s keys above the group’s split point as a block, with a buffer of 64 to help. The 10.38 is what fifty-two such crossings cost. The rule did not see it because a binary search in the head says how many head keys a group will pass, not how the library’s merge will pass them, and the answer depends on whether the group fits the buffer — which the group the round has just made, never does.

What the rule expected

The rule's estimate of a round is wrong both ways: at 1/1024 it expects round 1 to save 4.78 moves a key and the round costs 2.93, because groups larger than the buffer cross the head by rotation; and it expects the later rounds to save almost nothing when they save more than the first one lostFor each round of merging inside the tail at a buffer of 1/1024: the rule's estimate of what it saves (the crossings it removes, at the head keys above the later of each pair's smallest keys, less two moves a key of the tail) and what it actually saved, the cost before the round less the cost after. Round 1: expected 4.78, measured -2.93; round 2: expected 2.37, measured 3.27; round 3: expected 1.09, measured 3.95; round 4: expected 0.40, measured 2.59; round 5: expected 0.10, measured 1.48; round 6: expected -0.00, measured 0.84.-2.5002.505123456roundmoves a key the round saveswhat the round savedthe rule's estimatebuffer 1/1024above nought: the round pays
Fig. 4 For each round at a buffer of a 1,024th, the rule’s estimate of what it saves and what it saved. Round 1: expected 4.78 moves a key, measured −2.93. Round 2: 2.37 and 3.27. Round 3: 1.09 and 3.95. Round 4: 0.40 and 2.59. Round 5: 0.10 and 1.48. Round 6: nought and 0.84.

The rule’s estimate of a round is wrong both ways: at a 1,024th it expects the first round to save 4.78 moves a key and the round costs 2.93; it expects the fifth to save a tenth of a move and the round saves 1.48, and the sixth nothing when it saves 0.84. It stops after five rounds, at 7.68, where two more would have reached 6.46. At a 64th it expects the first round to save a tenth of a move a key, takes it, and the round costs 0.28.

Both errors come from the same omission. The rule counts a crossing’s head keys and never asks whether the crossing will rotate. On the first round it believes it is removing shifts, when it is replacing them with rotation. On the later rounds it believes the remaining crossings are cheap shifts of a few head keys each — the estimate falls because the pairs’ smallest keys are high — when each is a rotation whose cost is set by the size of the group as well as the head keys it passes, and removing one removes a large cost. A rule that knew how the merge would cross would see the bump and the long fall after it. Knowing that is knowing the library’s merge, and the earlier pages measured that a rotation-based merge’s cost depends on the split points it chooses, which is not something one binary search can price.

The reach of the late tenth

At a buffer of a 1,024th, the further the late tenth reaches into the file the worse the pieces do — 5.90 moves a key when it comes from the top tenth, 97.19 from anywhere — and the rule ties the merge sort from a reach of two fifths on only by merging all the wayMoves a key at a buffer of a 1,024th as the late tenth is drawn from the top 10%, 20%, 40%, 70%, 100% of the keys. 10%: merge sort 4.31, pieces straight in 5.90, the rule 5.90 after 0 rounds; 20%: merge sort 6.45, pieces straight in 16.04, the rule 7.68 after 5 rounds; 40%: merge sort 8.61, pieces straight in 36.34, the rule 8.58 after 7 rounds; 70%: merge sort 11.80, pieces straight in 66.64, the rule 11.77 after 7 rounds; 100%: merge sort 15.05, pieces straight in 97.19, the rule 14.95 after 7 rounds.1248163264the late tenth is drawn from the top share of the keysmoves a key (log scale)10%20%40%70%100%pieces straight inmerge sort, whole filethe proposed rulebuffer 1/1,024moves a key
Fig. 5 Moves a key at a buffer of a 1,024th as the late tenth is drawn from the top 10%, 20%, 40%, 70% and 100% of the keys. The merge sort: 4.31, 6.45, 8.61, 11.80, 15.05. Pieces straight in: 5.90, 16.04, 36.34, 66.64, 97.19. The rule: 5.90, 7.68, 8.58, 11.77, 14.95, after 0, 5, 7, 7 and 7 rounds.

At a buffer of a 1,024th, the further the late tenth reaches into the file the worse the pieces do — 5.90 moves a key when it is drawn from the top tenth, 97.19 when it is drawn from anywhere — and the rule ties the merge sort from a reach of two fifths on only because it then merges all the way. At a reach of a tenth it takes no rounds and pays 5.90 against the merge sort’s 4.31, the same mistake from the opposite side: there the pieces’ crossings are short, the rule sees them cheap, and they are, but 103 of them still cost more than one merge sort.

The reach sets how many head keys every crossing passes. A tail drawn from the top tenth crosses a tenth of the head at most; one drawn from anywhere crosses all of it, on average half. With 103 pieces each crossing half the head, the pieces cost nearly a hundred moves a key; the merge sort crosses once and costs fifteen. A reach of a fifth, the earlier pages’ input, is near the point where the two kinds of cost meet, which is why that page’s best buffer was a narrow win.

What the head can afford

The question the proposal asked was how many pieces the head can afford, and the measurement answers it directly: at a reach of a fifth, thirteen, and not twenty-six. Below a buffer of a 128th there are too many pieces for each to cross separately, and there is no way to cross a few times rather than many without crossing by rotation. Merging the pieces into larger groups trades each piece’s cheap crossing for a group’s expensive one, and the trade pays only when it is taken all the way, to one group and one crossing, which is the merge sort.

The buffer a schedule cannot bend found that a merge schedule chosen to fit a buffer still meets merges that do not fit it, and pays rotation for them. The pieces are the same lesson at the tail’s scale: a schedule can arrange for many small merges that fit or one large one that does not, and the many fit only while there are few enough of them. A merge sort with a small buffer is cheap for the reason the pieces are dear — it crosses the head once — and the moves no stable sort can skip measured how far above the floor that single crossing still is, fifteen times at a 64th.

A simpler rule shows how close the boundary sits to the estimates. Comparing only the two ends — every piece crossing at its estimated shifts, against every round at two moves a tail key and one crossing of the whole tail, also priced as shifts — picks the cheaper end at every buffer but one. At a 128th it chooses to merge everything and pays 3.55 where the pieces cost 3.20, and at a 64th it chooses the pieces by an estimated margin of 54 moves in 52,000, where the true margin is 32,000. It is right there by luck. It prices the one crossing that rotates as if it shifted, and that crossing is most of the merge sort’s cost.

So the choice for a late tail is binary and the boundary is computable in advance from two numbers the sort can know: the number of pieces, which is the tail’s length over the buffer, and the head keys each piece will pass, which one binary search a piece finds. Below the boundary, send the pieces straight in. Above it, sort the tail as a whole. The rule that tried to find an interior point found the worst of both.

What this file leaves out

One input shape. The head is one run and the tail is random within its reach, a shape a run is a property of the input would describe by its two runs’ lengths and nothing else. A tail that arrives partly sorted, or a head that is several runs, would change both costs, and the merge sort’s schedule would have more to merge before the end.

One way to sort a piece. Each piece is sorted by insertion into runs of eight and merging, a library’s method for short arrays. The earlier page’s alternative, the buffered merge sort on the piece alone, costs a twentieth of a move a key less at a 64th and would move every curve here down by that much without changing their shape.

Moves only. Every cost here is moves, the quantity a buffer-limited merge spends where the floor a merge cannot reach counted comparisons. The binary searches and the galloping trims cost comparisons, which are counted but not reported, and are a small fraction of the moves at every setting.

Still open: a boundary priced in advance

The boundary between the two ends is a comparison the sort can make before it moves anything. The pieces’ cost is about each piece’s size plus the head keys above its smallest, summed — the rule’s own crossing estimate, which is right when nothing rotates. The merge sort’s cost on the tail is about two moves a tail key a level of its schedule, plus one crossing of the whole tail by rotation, and the rotation’s cost is the part the rule had no way to price.

The measurement that follows prices that rotation from the head’s length, the tail’s length and the buffer — measuring the library merge’s rotation cost for one long run crossing a longer one at every buffer — and gives the sort a rule that sends the pieces straight in when their summed crossings cost less than the merge sort’s estimate, and sorts the tail as a whole otherwise. The prediction is that it chooses correctly at every buffer and every reach drawn here, including the 128th where the ends rule, which priced the single crossing as shifts, chose wrongly, and so costs the cheaper end everywhere, 3.20 at a 128th and 6.45 at a 1,024th. It could fail where the two ends are close: at a 64th they differ by half a move a key and the estimate’s error may be larger than that. The question is whether the rotation a merge will do can be priced without doing it, or whether the merge’s cost at the boundary is a measurement a sort cannot afford to take.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

The objects this essay names

Each one links to every other essay that touches it.

Auxiliary spaceBinary searchData movementMerge sortPredictionRotationStable sort