Counting

A record the size of a pass's minima

Selection sort's table of every pair it has compared is 256 KB at 1,024 keys, and indexed by the ordered pair it lives almost entirely in the rows of the current minima. Keep only those rows — a ring of r rows, the least recently used given up — and 12 rows, 3 KB, catch 99% of the comparisons the whole table skips, with no misses beyond the first touch of each row. The ring beats the cache of 1,024 pairs on both axes at once. The rows it needs grow with the logarithm of the input, a few more than the minima one pass passes through, and which row it gives up matters as much as how many it has.

The questions a sort asks twice found that selection sort asks most of its questions more than once. On 256 keys it makes 32,640 comparisons, and 19,561 of them have answers it already holds. The price of remembering an answer gave it a table of every pair it had compared, two bits a pair, which caught all of those repeats. The record that forgets on purpose gave it a direct-mapped cache of 1,024 pairs instead, which caught nine tenths of them in an eighth of the space. Then the record measured where it would run replayed every access through a cache of 64-byte lines. At 1,024 keys through 32 KB, the table as specified missed 477,824 times to save 258,290 comparisons. The same table, indexed by the ordered pair — each question recorded once, in the row of its smaller key — missed 3,079 times.

The ordered table works because selection sort’s questions in a pass share an operand. Each pass scans the unsorted keys comparing each one to the current minimum, and the smaller of the two keys in a question is almost always that minimum. So almost every question the sort records, and almost every repeat it catches, lives in the row of whichever key is the minimum at that moment.

That page’s closing section drew the conclusion: throw the rest of the table away. Keep a ring of rr rows, one for each recent minimum, and ask what share of the table’s skips each rr catches, what it costs in misses, and whether a few rows beat the cache of pairs on both axes. It predicted that about ln⁡n\ln n rows would catch most of the repeats, since a pass revisits roughly the minima the previous pass passed through.

The ring beats the cache on both axes, and by a wide margin. The number of rows it needs does grow with the logarithm, and is a few rows more than the prediction. The rule for choosing which row to give up turns out to matter as much as the count.

A dozen rows do the table’s work

The ring holds rr rows of the ordered table. A question about keys lo<hilo < hi is looked up in lolo’s row if lolo has one. If the pair was asked before, the comparison is skipped and its answer read from the row. A question that has to be asked is recorded in lolo’s row. If lolo has none, it takes the ring’s least recently used row, and every word of that row is cleared. The clearing is charged as writes. Each row is one key’s row of the table, 1,024 bits at 1,024 keys, stored as the table stores it: 32 pairs to an 8-byte word, 256 bytes a row.

At 1,024 keys a ring of 12 rows — 3 KB — catches 99% of the comparisons the whole 256 KB table skips; 8 rows catch 77%, and 4 catch 13%: the curve is a step, a little past the number of minima one pass passes throughFor selection sort on random keys, the share of the ordered table's skipped comparisons caught by a ring of r rows, least recently used row reused, against r. 256 keys (the table skips 15,835): 1 rows 0.0%, 2 rows 3.2%, 3 rows 17.4%, 4 rows 41.8%, 6 rows 74.0%, 8 rows 91.0%, 12 rows 98.6%, 16 rows 98.7%, 24 rows 98.9%, 32 rows 98.9%, 64 rows 99.5%. 1,024 keys (the table skips 258,290): 1 rows 0.0%, 2 rows 0.5%, 3 rows 3.7%, 4 rows 12.7%, 6 rows 42.6%, 8 rows 76.7%, 12 rows 99.1%, 16 rows 99.4%, 24 rows 99.5%, 32 rows 99.5%, 64 rows 99.6%. 2,048 keys (the table skips 1,039,848): 1 rows 0.0%, 2 rows 0.3%, 3 rows 2.4%, 4 rows 6.9%, 6 rows 30.3%, 8 rows 59.7%, 12 rows 93.7%, 16 rows 99.2%, 24 rows 99.7%, 32 rows 99.7%, 64 rows 99.7%. Ticks on the axis mark the expected number of left-to-right minima in a random sequence of that length, the harmonic number: 6.1 at 256, 7.5 at 1,024, 8.2 at 2,048. The horizontal axis is logarithmic.1248163264rows in the ringshare of the table's skipped comparisons0%25%50%75%100%256 keys1,024 keys2,048 keysselection sort, random keysticks: expected minima a pass
Fig. 1 The share of the ordered table’s skipped comparisons that a ring of r rows catches, against r, for selection sort on 256, 1,024 and 2,048 random keys. At 1,024 keys: 4 rows catch 12.7%, 8 rows 76.7%, 12 rows 99.1%, 16 rows 99.4%. At 256 keys, 8 rows catch 91.0%; at 2,048 keys, 16 rows catch 99.2%. Ticks on the axis mark the expected number of minima in one pass: 6.1, 7.5 and 8.2.

At 1,024 keys a ring of 12 rows — 3 KB — catches 99.1% of the comparisons the 256 KB table skips. Eight rows catch three quarters, four catch an eighth, and one row catches nothing at all. The curve is a step, not a slope, and the step sits just above the number of keys that are the minimum at some point during one pass.

That number is known exactly for random keys. As a pass scans the unsorted part, the current minimum changes each time a key smaller than every key before it appears — a left-to-right minimum of the sequence scanned. In a random sequence of length mm the expected number of those is the harmonic number Hm=1+12+⋯+1mH_m = 1 + \frac{1}{2} + \cdots + \frac{1}{m}, about ln⁡m+0.58\ln m + 0.58. At 1,024 keys that is 7.5 for the first pass, and fewer for later passes, which scan less.

A single row catches nothing, and the reason is in how a repeat happens. A question is asked again in a later pass, not the same one: within a pass each key is compared once. For the repeat to be caught, the row of the key that was the minimum then must still be held now, and the ring must also have held a row for every minimum in between. With one row, every new minimum clears the only row there is.

Why a pass’s minima recur

The rows are worth keeping because the minima of one pass are mostly the minima of the next.

Selection sort's first 16 passes over 48 random keys, marking each key that was the current minimum during the pass: 29 of the 54 minima in passes after the first were minima in the pass before as wellEach row is one pass of selection sort on 48 random keys; a mark at a key's value means the key was the pass's current minimum at some point, so the ring would give it a row. Filled marks were also minima in the pass before; open marks are new. Pass 1: 36, 33, 5, 3, 0; Pass 2: 40, 33, 5, 3, 2, 1; Pass 3: 33, 5, 3, 2; Pass 4: 5, 3; Pass 5: 23, 14, 5, 4; Pass 6: 28, 14, 5; Pass 7: 46, 37, 14, 9, 7, 6; Pass 8: 37, 14, 9, 7; Pass 9: 44, 14, 9, 8; Pass 10: 14, 9; Pass 11: 28, 14, 12, 11, 10; Pass 12: 14, 12, 11; Pass 13: 16, 12; Pass 14: 26, 16, 13; Pass 15: 16, 14; Pass 16: 30, 19, 16, 15.012243647pass 1pass 2pass 3pass 4pass 5pass 6pass 7pass 8pass 9pass 10pass 11pass 12pass 13pass 14pass 15pass 16key value48 random keysfilled: a minimum in the pass before too
Fig. 2 Selection sort’s first 16 passes over 48 random keys. In each pass a mark at a key’s value means that key was the current minimum at some point, so the ring would give it a row. Filled marks were also minima in the pass before; open marks are new. Of the 54 minima in passes after the first, 29 were minima in the pass before.

A pass ends by swapping its minimum to the front. The next pass scans the same keys, minus that one and minus one displaced key, in almost the same order. So its left-to-right minima are almost the same chain: the previous chain with its last element removed, and perhaps a new key that the swap moved into a position where it now comes first. On 48 keys, 29 of the 54 minima of passes two to sixteen were minima of the pass before. The new ones come at the two ends of the chain: at its start, where the key displaced by the swap can land, and at its end, where the pass now continues past the removed minimum to the smallest keys left.

At full size the pattern holds and can be counted. Over all 1,023 passes on 1,024 keys, a pass’s chain of minima averages 6.5 keys and is at most 14; nine passes in ten have nine or fewer. Of the minima in each pass after the first, 70% were minima in the pass before. Every one of the 1,024 keys is a minimum at some point, since each ends up as a pass’s final minimum, but most are a minimum only briefly: the median key is in the chain for 4 passes, and the longest-lived for 61. Those long-lived keys are the ones a row is most worth keeping for, since each pass they survive asks their questions again.

Every question in the next pass that involves one of the recurring minima is a question the previous pass already asked: the same minimum against the same key, for every key the two passes both scanned while that minimum was current. That is where the repeats come from, and why they live in so few rows. The whole table holds a row for every key. The only rows the sort ever reads again are those of keys that were a minimum in two consecutive passes, and at any moment there are about HnH_n of those.

Against the cache of pairs, and against the table

The earlier pages compared records by what they skip and what they cost. The ring can be placed on the same axes.

At 1,024 keys through 32 KB: a ring of 12 rows skips 255,881 comparisons for 176 misses; the cache of 1,024 pairs skips 155,420 for 192; the ordered table 258,290 for 3,079 — the ring skips nearly as many as the table and misses fewer times than the cacheComparisons skipped against cache misses through 512 lines of 64 bytes, for selection sort on 1,024 random keys. A ring of r rows: 2 rows (512 B), 1,393 skipped, 136 misses; 3 rows (768 B), 9,683 skipped, 140 misses; 4 rows (1 KB), 32,736 skipped, 144 misses; 6 rows (2 KB), 109,937 skipped, 152 misses; 8 rows (2 KB), 198,186 skipped, 160 misses; 12 rows (3 KB), 255,881 skipped, 176 misses; 16 rows (4 KB), 256,809 skipped, 192 misses; 24 rows (6 KB), 256,872 skipped, 224 misses; 32 rows (8 KB), 256,984 skipped, 256 misses; 64 rows (16 KB), 257,220 skipped, 384 misses. The cache of 1,024 pairs (4 KB): 155,420 skipped, 192 misses. The ordered table (256 KB): 258,290 skipped, 3,079 misses. With no record the sort misses 128 times. Both axes are logarithmic.1001,00010³10⁴10⁵cache misses through 32 KBcomparisons skippeda ring of rowsa cache of 1,024 pairsthe ordered table1,024 keys, 512 linesrings of 2 to 64 rows along the line
Fig. 3 Comparisons skipped against cache misses through 32 KB at 1,024 keys. Rings of 2 to 64 rows: 2 rows skip 1,393 for 136 misses; 8 rows 198,186 for 160; 12 rows 255,881 for 176; 64 rows 257,220 for 384. The cache of 1,024 pairs, 4 KB: 155,420 skipped for 192 misses. The ordered table, 256 KB: 258,290 for 3,079. With no record the sort misses 128 times.

A ring of 12 rows skips 255,881 comparisons for 176 misses. The cache of 1,024 pairs skips 155,420 for 192. The ring is ahead on both axes: 65% more comparisons skipped, 16 fewer misses, in three quarters of the bytes. It skips 99.1% of what the ordered table skips and misses 5.7% as often.

The ring’s misses are all first touches. The array is 128 lines and each row is four, so twelve rows add 48 lines, and 128 + 48 = 176 is exactly the measured count. After the first pass nothing the sort touches is ever evicted from a 32 KB cache. The ring’s row clears are also cheap: 1,663 of them at 12 rows, 53,216 word writes over the whole sort, against 255,881 comparisons skipped.

The cache of pairs loses because it forgets the wrong things. It holds the 1,024 most recently recorded pairs, whatever rows they are in, so a pass that records more than 1,024 questions pushes out the start of the previous pass before the next pass can ask them again. At 1,024 keys an early pass records about a thousand questions. The ring forgets by row, and the rows it forgets are the ones whose owners stopped being minima. That is the structure of selection sort’s repeats, which a hash of recent pairs cannot see.

The prediction said the ring would miss “no more than the cache of pairs does”. It misses less, because a cache of pairs scatters its entries across 4 KB by hash while a ring’s lookups walk along a few rows in key order.

Both small records sit at their compulsory misses, and that is the regime the count is not the time calls the easy one: once a record fits, its misses stop depending on how it is used. The table sits in the other regime at 1,024 keys, where its 256 KB cannot stay resident in 32 KB and every pass reloads the rows it needs. The ring gets the table’s catch at the small records’ cost because it is the part of the table that selection sort actually touches — its working set, in the sense the record measured where it would run used for the lines the ordered table fetched. The ordered table’s 3,079 misses were bounded there by the first touches of the minima’s rows, four lines each. The ring keeps those same rows and refuses to hold any others, so it never has to fetch one twice.

How many rows, as the input grows

The rows a ring needs grow with the logarithm of the input, as the minima a pass passes through do: 8 at 128 keys, 8 at 256 keys, 8 at 512 keys, 10 at 1,024 keys, 12 at 2,048 keys catch nine tenths of the table's skips, against 5.4 to 8.2 minima a passFor selection sort on random keys of each size: the fewest rows an LRU ring needs to catch 90% of the comparisons the ordered table skips — 128 keys 8, 256 keys 8, 512 keys 8, 1,024 keys 10, 2,048 keys 12 — and 99%: 128 keys 38, 256 keys 35, 512 keys 13, 1,024 keys 12, 2,048 keys 16; beside the expected number of left-to-right minima in a random sequence of that length, 5.4, 6.1, 6.8, 7.5, 8.2. The horizontal axis is logarithmic.1,000keys sortedrows0481216rows for 90% of the skipsminima a pass, expectedselection sort, random keysleast recently used row reused
Fig. 4 The fewest rows a ring needs to catch 90% of the ordered table’s skipped comparisons: 8 at 128, 256 and 512 keys, 10 at 1,024 and 12 at 2,048. The expected number of minima in one pass of that length: 5.4, 6.1, 6.8, 7.5 and 8.2. For 99% of the skips: 38 rows at 128 keys, 35 at 256, 13 at 512, 12 at 1,024 and 16 at 2,048.

The rows needed for nine tenths of the skips track the expected minima a pass, two to four rows above them. From 128 keys to 2,048 they go from 8 to 12 while the harmonic number goes from 5.4 to 8.2. So the prediction’s scaling is right and its constant is a little low. The ring must hold every minimum of the current pass plus the ones from the previous pass that have not yet recurred, and a pass’s chain of minima varies around its mean: sometimes it has twelve links, not seven.

The requirement for 99% behaves differently at small sizes: 38 rows at 128 keys and 35 at 256, against 12 at 1,024. At 128 keys the table skips only 3,674 comparisons, and the last 1% is about 37 repeats of questions whose minimum was current a long time before. Those are rare, scattered across old rows, and worth nothing. At larger sizes the repeats are overwhelmingly the recurring-chain kind, and the 99% line sits a few rows above the 90% line. Either way the ring is a few kilobytes. At 2,048 keys 16 rows are 8 KB against the ordered table’s 1 MB.

That makes the record’s size grow as nlog⁡nn \log n bits — ln⁡n\ln n rows of nn bits — where the table’s grows as n2n^2 and the cache of pairs is whatever size was chosen. Selection sort makes n(n−1)/2n(n-1)/2 comparisons whatever its input, a count that has no distribution in the sense the sort whose count has no distribution measured for a sorting network; this record removes half of them in memory of the order of a single row of the table times its logarithm.

Which row to give up

The ring gives up its least recently used row. The obvious simpler rule gives up rows in turn, the oldest-allocated first, and it does noticeably worse.

Which row a ring gives up matters as much as how many it has: at 1,024 keys and 12 rows, reusing the least recently used row catches 99% of the table's skips, reusing rows in turn 89%For selection sort on 1,024 random keys, the share of the ordered table's skipped comparisons caught by a ring of r rows under two reuse rules. Least recently used row reused: 1 rows 0.0%, 2 rows 0.5%, 3 rows 3.7%, 4 rows 12.7%, 6 rows 42.6%, 8 rows 76.7%, 12 rows 99.1%, 16 rows 99.4%, 24 rows 99.5%, 32 rows 99.5%, 64 rows 99.6%. Rows reused in turn: 1 rows 0.0%, 2 rows 0.5%, 3 rows 2.8%, 4 rows 9.8%, 6 rows 34.2%, 8 rows 63.3%, 12 rows 88.7%, 16 rows 93.6%, 24 rows 97.4%, 32 rows 97.9%, 64 rows 99.1%. The horizontal axis is logarithmic.1248163264rows in the ringshare of the table's skipped comparisons0%25%50%75%100%least recently used rowreusedrows reused in turnselection sort, 1,024 keystwo ways of choosing the row to clear
Fig. 5 The share of the ordered table’s skipped comparisons caught at 1,024 keys, against rows, for two rules for choosing the row to clear. Least recently used: 12.7% at 4 rows, 76.7% at 8, 99.1% at 12, 99.5% at 32. In turn: 9.8%, 63.3%, 88.7% and 97.9%.

At 12 rows the least-recently-used rule catches 99.1% of the table’s skips and the in-turn rule 88.7%. The in-turn rule needs 32 rows to reach 98%. The difference is the chain’s long-lived members. A key that stays in the chain of minima for many passes is looked up in every one of them and allocated only once. Under the in-turn rule its row is still given up when the ring comes round to it, however recently it was used, and the next pass has to ask all its questions again. The least-recently-used rule keeps it as long as it keeps recurring.

The two rules can be seen in the access pattern itself, the kind of picture where an algorithm looks draws for an array. Plotted as row against time, the ring’s lookups would form a few long horizontal streaks — the long-lived minima, read in pass after pass — and many short ones, keys that were a minimum for a pass or two. The in-turn rule cuts every streak at a fixed period whatever its length; the least-recently-used rule cuts only the streaks that have ended. Selection sort’s repeats are the long streaks, so the rule that preserves them is the one that catches them.

This is the same lesson the record that forgets on purpose found from the other direction. What decides a record for a sort’s repeats is not how long it waits before a question comes back but which questions come back, and a record whose eviction follows that structure needs far less memory than one that does not. The cache of pairs evicts by hash, the in-turn ring by age, and the least-recently-used ring by use. On this sort, use is the structure.

What was measured and what was not

Selection sort on random keys. The ring works because selection sort has a current minimum and its chain recurs from pass to pass. On sorted input the chain is one key a pass and a single row catches everything; on reversed input the chain is the whole pass and the ring needs as many rows as the pass is long. Neither was measured. Bubble sort, whose smaller operand also tends to stay put within a pass, should behave like selection sort, and insertion and merge sort, which never repeat a question, have nothing for any record to catch.

A fully associative LRU cache. Every miss count is through an exact least-recently-used cache of stated capacity, the model the digit a pass chooses for itself used too. A list and a block of memory found a gap of more than three to one in misses between two layouts of the same traversal, and the ring’s margin over the cache of pairs is a gap of that kind: the same questions, recorded in a different arrangement. Real caches are set-associative, and a ring of rows at regular addresses could conflict in a few sets. At 12 rows of four lines each it is unlikely to matter; it was not checked.

Rows of two bits a pair. The ring’s rows are stored exactly as the table stores its rows, so that the three records are compared at the same layout. A row needs one bit a pair to say the pair was asked, and the answer is implied by the ordered layout. Halving the rows would halve the ring’s bytes and change none of its catches.

Word operations were not charged. The earlier pages priced records in word operations as well as misses, and the count is not the time is the reminder that neither count is a running time. The ring’s lookup is one word read and its record one word write, the same as the ordered table’s, and its clears add 53,216 writes over a sort that skips 255,881 comparisons.

Still open: the chain of minima as the record itself

The ring stores rows for the chain’s keys, and each row is the whole of a key’s answers against every other key. But the questions a pass asks again are more specific than that. A recurring minimum is compared, in the next pass, against the same keys the previous pass compared it against — the keys scanned while it was current — and those keys lie in one contiguous stretch of the scan. A row records them as scattered bits. A record could instead store, for each link of the chain, only the stretch of positions it covered, and answer every question inside that stretch without a bit per pair.

That is a record of HnH_n intervals rather than HnH_n rows: a few dozen words at any size. The measurement that follows gives selection sort such a record — the chain of the previous pass, each minimum with the range of positions over which it was current — and has the next pass answer a comparison from it whenever the key under the scan lies in its minimum’s recorded range. It asks what share of the table’s skips that catches and at what size of input it stops working. The prediction is that it catches nearly as much as the ring. It should fail where a key displaced by the pass’s swap breaks a stretch in two, and the question is how often that happens and whether the ring’s rows or a list of intervals is the right record for a structure that is, in the end, just a chain of a few keys.

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.

CacheComparison countLocalityMeasured countMemoisationMemory layoutPredictionSelection sortSpace time tradeWorking set