Counting

A register kept through a confirmation

Two registers beside a two-ended selection sort's extremes, holding where the run of known order from each one ends, read 53.5 words a question on a sorted array of 1,024 keys, more than reading the record plainly — because 263,167 times a neighbour's question, answered by the previous scan's chains, rewrote a pair at the value it already held, and each rewrite dropped a register. Kept through such a confirmation and dropped only when a pair is forgotten or turns round, the registers read 0.347 words a question on that array, a 154th, and 0.345 on 4,096 keys: the reading stops growing with n. They become the cheapest record at every run length, 49 to 73 times under the next on runs and 5.75 times under the run list's search on the sorted array, and the price of a saved comparison now falls as runs lengthen, from 3.97 word reads at runs of four to 0.35. The dearest saving is now the short run's, and it is still above the 3.1 at which a table of compared pairs breaks even.

A register dropped by what it already knew found why a cache had cost more than the thing it cached. The two-ended selection sort keeps a record of adjacent order, two bits a neighbouring pair, and answers questions from it when the pairs between two positions all point one way. A run end kept in a register put beside each of the sort’s two extremes a register holding where the run of known order starting there ends, so a question from an extreme could be answered without reading the record. On short runs that cut the reading to a forty-fifth. On a sorted array of 1,024 keys it raised it to 53.5 words a question, against 22.1 for reading the record plainly.

The cause was not the one first proposed. The sort’s swaps were blamed, and a sorted array makes no swaps. Of the 264,190 times a register was dropped on that array, 263,167 were a pair rewritten at the value the record already held: on sorted keys the scan’s largest-so-far moves one place at every step, so “is this key larger than the largest so far?” is a question about two neighbours every time, the chains the previous scan left answer it, and the answer is written into the record. The register’s rule dropped it on any write to a pair inside its run, and the next question refilled it by scanning hundreds of pairs.

That essay’s closing section proposed the smaller repair the counts pointed to: drop a register only when a pair under it is forgotten or changes direction, and treat a write at the same value as no write. It predicted that the sorted array’s reading would fall to well under a word a question; that the price of a saved comparison would fall with run length instead of rising, leaving the registers cheapest at every run length and the run list’s search with nothing to win; and named the failure, that the swaps and the pairs learned for the first time might be what kept the registers expensive on runs, in which case only the sorted array would improve.

The rule, and what it still drops on

The sort, the inputs and the charges are those of the two earlier essays: 256, 1,024 and 4,096 keys cut into sorted runs of 4 keys to the whole array, a pass between neighbours first, the previous scan’s chains consulted before the record, and every question charged at the 32-bit words it reads, sixteen pairs to a word. A question answered from a register reads nothing; a refill scans the record from the extreme to the run’s first break.

Under the new rule a register is still dropped in three cases: a swap forgets a pair under it, a comparison learns a pair under it that was unknown, or a comparison or a chain’s answer turns a pair under it round. It is no longer dropped when a pair under it is written at the value it already held. Each register’s answer is still checked against the record at every question it answers, so the rule cannot buy reading with a stale run end: a register kept through a confirmation that had actually become wrong would fail the check at its next use. None does, at any size or run length.

A hundred and fifty-fourth

Kept when a pair is relearned at the value it held, the registers read 0.347 words a question on 1,024 sorted keys where they read 53.54, a 154th, and never more than 0.347 at any run length: under the plain record and under the run list's search everywhereOn 1,024 keys, a pass first, reads a question: the plain record, the run list's probes, the registers, and the registers kept when a comparison relearns a pair at its own value. runs of 4: plain 1.094, list 8.805, registers 0.024, kept 0.022; runs of 8: plain 1.119, list 8.009, registers 0.026, kept 0.022; runs of 16: plain 1.141, list 7.072, registers 0.028, kept 0.021; runs of 32: plain 1.425, list 6.283, registers 0.046, kept 0.025; runs of 64: plain 1.901, list 5.737, registers 0.097, kept 0.029; runs of 128: plain 2.769, list 4.962, registers 0.292, kept 0.038; runs of 256: plain 4.204, list 4.713, registers 1.137, kept 0.058; runs of 512: plain 5.852, list 4.505, registers 5.374, kept 0.093; runs of sorted: plain 22.132, list 1.996, registers 53.541, kept 0.347.keys in each sorted run of the inputreads a question (log scale)0.111048163264128256512sortedregisterstwo bits a pair, plainrun list's searchkept through a relearning1,024 keys, a pass firstlogarithmic scale
Fig. 1 Reads a question on 1,024 keys, a pass first, against run length, on a log scale: the plain record, the run list’s probes, the registers as they were, and the registers kept through a confirmation. On the sorted array: plain 22.13, list 2.00, registers 53.54, kept 0.347. On runs of four: plain 1.09, list 8.81, registers 0.024, kept 0.022.

Kept when a pair is rewritten at the value it held, the registers read 0.347 words a question on 1,024 sorted keys where they read 53.54, a 154th, and never more than 0.347 at any run length — under the plain record and under the run list’s search at every input. The sorted array was the prediction’s input and it moved by more than the prediction asked: well under a word, it said, and the reading fell two orders of magnitude below that word. But the run lengths in between moved too, which is the part the prediction left uncertain. On runs of 256 the registers read 1.14 words a question and now read 0.058; on runs of 512, 5.37 and now 0.093. On runs of 64 the fall is from 0.097 to 0.029, and even on runs of four, where the registers already held almost everywhere, from 0.024 to 0.022.

So the failure the proposal named did not happen. The swaps and the first-time learning still drop registers, but they drop them at a rate that does not grow with the run, and a refill after such a drop is no longer followed at once by another. Keeping registers past a break in their run, the earlier essay’s survivors, adds nothing on top of the new rule: the two together read exactly what the new rule reads alone, at every run length and size.

The reading stops growing with n

Kept through relearning, the registers read 0.357, 0.347, 0.345 words a question on sorted arrays of 256, 1,024, 4,096 keys, where they read 13.54, 53.54, 213.54: the reading stops growing with n on every input, and on short runs it falls as n growsWords read a question, a pass first, with registers kept through relearning (solid) and the registers as before (dashed), at 256, 1,024, 4,096 keys: sorted: kept 0.357, 0.347, 0.345; before 13.539, 53.541, 213.542; runs of 64: kept 0.102, 0.029, 0.008; before 0.425, 0.097, 0.023; runs of 4: kept 0.079, 0.022, 0.005; before 0.090, 0.024, 0.006.keys sortedwords read a question (log scale)0.010.11101002561,0244,096sortedruns of 64runs of 4dashed: registers beforelogarithmic scale
Fig. 2 Words read a question by the registers kept through a confirmation (solid) and the registers as they were (dashed), at 256, 1,024 and 4,096 keys, on a log scale. Sorted: 0.357, 0.347, 0.345, where they read 13.54, 53.54, 213.54. Runs of 64: 0.102, 0.029, 0.008. Runs of four: 0.079, 0.022, 0.005.

Kept through a confirmation, the registers read 0.357, 0.347 and 0.345 words a question on sorted arrays of 256, 1,024 and 4,096 keys, where they read 13.54, 53.54 and 213.54: the reading stops growing with n on every input, and on runs it falls as n grows. On the sorted array the reading is flat because both the refills and the questions grow at the same rate. Each scan now refills its registers six times, whatever the size, and each refill still scans a run as long as before — but the scan asks about as many questions as the run is long, so the refills amortise to a third of a word a question at every size.

On runs the reading falls with n because the refills a scan stay near eleven while the questions a scan grow with the unsorted part. At 4,096 keys in runs of 64 a scan asks about 2,000 questions and refills eleven times, each refill reading a word or two. What it costs to consult two bits a pair began with a record that read about a word a question at every size; with registers that are not dropped by confirmations it reads under a hundredth of a word on 4,096 keys from runs of four to runs of 64, and the reading per question shrinks as the input grows.

A price that falls with the run

Kept through relearning, the registers' price of a saved comparison falls as runs lengthen — on 1,024 keys from 3.97 word reads at runs of four to 0.35 on the sorted array, where it was 53.75 — and the dearest saving is now the short run's, 3.61, 3.97, 3.88 at 256, 1,024, 4,096 keysWord reads by the registers kept through relearning, per comparison the record saves, at each run length and size (solid), and the registers as before (dashed): 256 keys: 4 3.605 (was 4.112), 8 2.210 (was 2.602), 16 1.256 (was 1.672), 32 0.702 (was 1.406), 64 0.479 (was 2.005), 128 0.370 (was 4.460), sorted 0.362 (was 13.747); 1,024 keys: 4 3.966 (was 4.498), 8 2.280 (was 2.693), 16 1.243 (was 1.618), 32 0.741 (was 1.371), 64 0.463 (was 1.576), 128 0.319 (was 2.432), 256 0.251 (was 4.934), 512 0.254 (was 14.703), sorted 0.348 (was 53.749); 4,096 keys: 4 3.877 (was 4.390), 8 2.318 (was 2.724), 16 1.283 (was 1.638), 32 0.767 (was 1.348), 64 0.477 (was 1.455), 128 0.320 (was 1.958), 256 0.241 (was 3.478), 512 0.203 (was 7.091), 1024 0.194 (was 17.353), 2048 0.229 (was 55.384), sorted 0.345 (was 213.750).keys in each sorted run (the last point of each size is its sorted array)word reads a comparison saved (log scale)11010048163264128256512102420484096256 keys1,024 keys4,096 keysdashed: registers beforelogarithmic scale
Fig. 3 Word reads by the registers per comparison the record saves, at each run length and at three sizes (solid), and the registers as they were (dashed), on a log scale; the last point of each size is its sorted array. On 1,024 keys: 3.97 at runs of four, 1.24 at sixteen, 0.46 at 64, 0.25 at 256, 0.35 sorted, where the registers as they were paid 4.50, 1.62, 1.58, 4.93 and 53.75.

Kept through a confirmation, the registers’ price of a saved comparison falls as runs lengthen — on 1,024 keys from 3.97 word reads at runs of four to 0.35 on the sorted array, where it was 53.75 — and the dearest saving is now the short run’s: 3.61, 3.97 and 3.88 at 256, 1,024 and 4,096 keys. That is the second half of the prediction, and it holds. The registers as they were drew a valley: cheapest per saving at runs of sixteen to 64, and dearer on either side, because on long runs every confirmation sent a register back to rescan. With the new rule the valley’s right side is gone. On long runs the record saves most — 263,167 comparisons on the sorted array of 1,024 keys, where a run of all the keys answers nearly every question — and the registers now read least per saving there too.

What is left is the left side of the valley — the side a filter that reads the word it saves found had a floor under it, because on short runs the record is asked many questions for every comparison it saves. On runs of four the record saves 1,438 comparisons on 1,024 keys and the registers read about 5,700 words to save them, nearly four words a saving at every size. The price of remembering an answer found that a table of the pairs a selection sort had already compared pays for itself once a comparison costs more than about three word reads. The registers now clear that line by a factor of nine on sorted keys, and on runs of eight they cost 2.2 to 2.3 a saving, under it. On runs of four they are still over it, at 3.6 to 4.0.

What a scan still refills

What is left to read is a handful of refills a scan: kept through relearning, the registers on 1,024 keys refill 10.63 times a scan on runs of four and 5.99 on the sorted array, against 1.97 drops a scan at a swap and 4.18 at a pair learned or changed on runs of fourOn 1,024 keys, registers kept through relearning, per scan of the sort: refills, and drops at a swap and at a pair learned or changed: runs of 4: 10.63 refills, 1.97 at a swap, 4.18 learned or changed; runs of 8: 10.66 refills, 1.95 at a swap, 4.22 learned or changed; runs of 16: 10.70 refills, 1.96 at a swap, 4.18 learned or changed; runs of 32: 10.76 refills, 1.96 at a swap, 4.23 learned or changed; runs of 64: 10.70 refills, 1.97 at a swap, 4.23 learned or changed; runs of 128: 10.92 refills, 1.98 at a swap, 4.31 learned or changed; runs of 256: 11.05 refills, 1.97 at a swap, 4.31 learned or changed; runs of 512: 11.72 refills, 1.95 at a swap, 4.32 learned or changed; runs of sorted: 5.99 refills, 0.00 at a swap, 2.00 learned or changed.410.63 refills810.66 refills1610.70 refills3210.76 refills6410.70 refills12810.92 refills25611.05 refills51211.72 refillssorted5.99 refillsupper bar: refills · lower: drops at a swap, then at a pair learned or changedrun1,024 keysper scan of the sort
Fig. 4 On 1,024 keys, with registers kept through a confirmation, per scan of the sort: register refills, and drops at a swap and at a pair learned or changed. Runs of four: 10.63 refills, 1.97 drops at a swap, 4.18 learned or changed. Runs of 512: 11.72 refills. The sorted array: 5.99 refills, no drop at a swap, 2.00 learned.

What is left to read is a handful of refills a scan: kept through a confirmation, the registers on 1,024 keys refill 10.63 times a scan on runs of four and 5.99 on the sorted array, against 1.97 drops a scan at a swap and 4.18 at a pair learned or changed on runs of four. A scan has two extremes, each starts the scan with an empty register, and each fills it at its first question: that is two refills a scan before anything is dropped. Each time the scan’s minimum or maximum moves to a new position, the question starts from a position no register describes, and that is a refill too. The swaps add two drops a scan, one at each end, and the pairs a scan learns for the first time add about four.

On the sorted array the refills are exactly six a scan at every size, since nothing is ever swapped and the extremes never move from the ends of the unsorted part. On runs the count sits between ten and twelve and barely moves with run length, which is why the reading per question kept falling as the questions per scan grew. The refills are now set by the sort’s own structure — two extremes, two swaps, a few new pairs a scan — and not by how much of the record the scan happens to confirm.

Nothing left to choose

Kept through relearning, the registers are the cheapest record at every run length on 1,024 keys — 49 to 73 times under the next-cheapest on runs, and 5.75 times under the run list on the sorted array, the least — and the choice the pass's run count used to make has nothing left to chooseOn 1,024 keys, for each run length, how many times fewer reads a question the registers kept through relearning take than the cheaper of the plain record and the run list's search: runs of 4: 50.79 (kept 0.022, plain 1.094, list 8.805); runs of 8: 51.77 (kept 0.022, plain 1.119, list 8.009); runs of 16: 53.28 (kept 0.021, plain 1.141, list 7.072); runs of 32: 57.46 (kept 0.025, plain 1.425, list 6.283); runs of 64: 66.55 (kept 0.029, plain 1.901, list 5.737); runs of 128: 72.34 (kept 0.038, plain 2.769, list 4.962); runs of 256: 72.85 (kept 0.058, plain 4.204, list 4.713); runs of 512: 48.53 (kept 0.093, plain 5.852, list 4.505); runs of sorted: 5.75 (kept 0.347, plain 22.132, list 1.996). Logarithmic scale.450.79× · plain next851.77× · plain next1653.28× · plain next3257.46× · plain next6466.55× · plain next12872.34× · plain next25672.85× · plain next51248.53× · list nextsorted5.75× · list nexttimes cheaper than the next-cheapest recordrun1,024 keyslogarithmic scale
Fig. 5 On 1,024 keys, for each run length, how many times fewer reads a question the registers kept through a confirmation take than the cheaper of the plain record and the run list’s search, on a log scale. Runs of four: 50.8 under the plain record. Runs of 256: 72.9 under the plain record. Runs of 512: 48.5 under the run list. The sorted array: 5.75 under the run list.

Kept through a confirmation, the registers are the cheapest record at every run length on 1,024 keys — 49 to 73 times under the next-cheapest on runs, and 5.75 times under the run list’s search on the sorted array, the least — and the choice the pass’s run count used to make has nothing left to choose. A run end kept in a register ended on a chooser: read the number of runs off the pass between neighbours, and use the registers on short runs and the run list’s binary search on long ones, since each won on its own side. The plain record never won anywhere the registers or the list did not.

With the new rule the run list’s only input left is the sorted array, and there it reads two probes a question against the registers’ third of a word. The list’s search costs a logarithm of the number of runs, which is small when there are few runs; the registers cost a refill amortised over a scan, which is smaller still when the runs are long. Both structures exist to exploit long runs, and the registers now exploit them better, at the cost of two words of memory beside a record the list does not need at all.

What the registers cost to hold

The comparison with the run list is a comparison of reading, and the two structures do not hold the same thing. The record of adjacent order is two bits a pair, 64 words on 1,024 keys and 256 on 4,096, and the registers sit beside it: two of them, each a starting position and a run end, four words in all. A run list holds three words a run, its start, its end and its direction, and on a sorted array it is one run, three words, with no record behind it. A record of stretches, not pairs built the list for that reason, and on the input of one long run it remains the smaller structure by a factor of about twenty.

So on the sorted array the registers buy a factor of 5.75 in reading for a factor of about twenty in memory, and on runs of 512 — two runs on 1,024 keys, six words of list — they buy 48 in reading for a record about eleven times the list’s size. Which trade is right depends on how often the record is asked and on what memory costs, and neither number is measured here. What the measurement does settle is that the reading no longer decides it: the registers read least everywhere, so a choice of record is now a choice about memory alone. The runs too short to drop found that the record keeps everything the run list keeps in a thirty-second of its memory on random keys, so across most inputs the registers and the record are both the smaller structure and the cheaper one to read; only on very long runs does the list win back anything, and what it wins back is space.

Why a confirmation looked like a change

A rule that drops a cache when its source is written is the safe default, and it is what the registers had. It is safe because a write that changes nothing costs one unnecessary refill, and a write that changes something and is missed costs a wrong answer. The trouble was that the cost of the unnecessary refill was never small. A register’s refill reads its run, and a run on a sorted array is most of the array; a write that confirmed a pair at every step of every scan turned a cache meant to read the run once a scan into one that read it at every step.

The repair costs one comparison of two bits, the pair’s old value against its new one, at each write, and nothing else. It needs no change to the record, the sort or the chains. Two swaps a pass, and the stretches hold built the chains whose answers caused the confirmations, and those answers were and remain the right thing to write: they restore pairs that swaps forgot. What changed is that a register now reads the write rather than only noticing it.

What the counts assume

One sort and one seed per size. The inputs are one shuffle each, cut into sorted runs, at three sizes. The refills a scan are close across sizes and run lengths, which is the evidence that they come from the sort’s structure, but they are one sample of each input.

Words as the cost, and comparisons as a separate one. The price of a saved comparison divides reads by comparisons; whether a saving pays depends on what a comparison costs on the machine and the keys, which the table’s break-even of about three word reads stands for and does not measure.

Two registers. The sort has two extremes and keeps two registers. A sort that asked questions from more positions would need more, and the refills from moved extremes would grow with them.

A check that would catch a wrong register. Every answer from a register is compared with the record’s own answer; the rule could only be wrong by keeping a register through a write that did change its run, and that would fail at the register’s next use. That check is why the rule can be stated as safe rather than measured as usually right.

Still open: a record switched off where it cannot pay

The registers have made the record cheap to consult everywhere but on the shortest runs, where it saves too little to pay for any reading at all. On runs of four the record saves 1,438 comparisons on 1,024 keys and the registers read nearly four words for each; on runs of eight they read 2.3, under the three or so at which a table of compared pairs breaks even. The pass between neighbours already counts the runs before the first scan, and the earlier chooser used that count to pick between records. It could use it instead to decide whether to consult any record.

The measurement that follows reads the mean run length off the pass, consults the record through the registers only when it is at least a threshold — swept from two keys to sixteen — and otherwise asks every question as a comparison, and charges reads and comparisons together at exchange rates of one to four word reads a comparison. The prediction is that at three word reads a comparison the cheapest threshold is a mean run of six keys, so that runs of four lose their record and gain about a fifth of their cost back, and runs of eight and up keep it. It could fail on inputs whose runs are uneven: a mean of four can hide a few long runs where the record saves most of what it saves, and switching it off by the mean would give those back. The question is whether one number from the pass is enough to decide whether a record is worth reading, or whether the record’s value on an input lives in the runs a mean cannot see.

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.

Bit vectorComparison countNatural runPredictionSelection sortSpace time trade