The index that replaces the text

A price that cannot see the array

A growing directory's document array, refitted when the share of characters behind its escape passes twice what the escape's code word assumes and held until the characters have grown by a quarter, loses 0.098 bits a symbol for 4.52 final arrays of reading. A hold set by price instead — refit when the bits wasted since the last refit reach a constant times the characters a refit reads — was predicted to do at least as well, refitting early where refits are cheap. It does worse: about 0.119 at the same reading. The array's size multiplies both sides of the rule, so the price never sees it; what the rule watches is the loss summed over versions, and that refits late in the small, fast-growing early history where the fixed hold refitted often. The price also counts only the refits' reading, while waiting is paid in rebuilds of the escape, so the total reading stops falling at about 4.3 arrays however high the price goes.

Refitting when the escape is wrong found a good clock for a growing document array. The array names, for every character of a collection, the document it belongs to, and the array is the length distribution showed that its best code is a Huffman code over the documents’ lengths. A collection that grows needs a way to name documents added after that code was fitted, and the Huffman code carries one: an escape, a code word meaning “a document added since the fit”, followed by a small code over the added documents. The escape’s code word is ℓ\ell bits long and so stands for a share 2−ℓ2^{-\ell} of the characters. When the share actually named through it passes twice that, the escape’s code word is wrong by a bit. A clock that refits at that moment, held back until the collection’s characters have grown by a quarter since the last refit, replayed through a source directory’s history from twenty files to 222 over 47 versions, lost 0.098 bits a symbol against the Huffman code refitted at every version, and read 4.52 final arrays’ worth of characters doing it. Every schedule that refitted when the files doubled did worse on both counts.

The hold was the part that made the clock work, and it was a fixed number: a quarter of the characters. That essay’s closing section noticed what was arbitrary about it. Early in the history a refit reads a small array and a quarter’s growth is a version or two; late, a refit reads an array twenty times the size and a quarter’s growth takes many versions. A hold could instead be set by price. What a refit costs is the characters it reads, which is the array’s current size. What it removes is the loss: the bits a symbol the stale code wastes, times the characters coded with it. A clock could refit when the waste accumulated since the last refit, in bits, passes the characters a refit would read, times a constant. The proposal predicted it would land at least as well as the best fixed hold, at about 0.08 bits a symbol for 4.5 arrays, because it would refit early, when refits are cheap, and late only once the waste had grown large. It named one way to fail, the late history, where waste accumulates slowly and the rule might wait while a few large files sat behind a code word fitted before they arrived.

One constant, in bits per character read

The history, the Huffman code and the charges are the earlier pages’. At each version the array is coded by the Huffman code fitted at the last refit, with every document added since named through the escape and then through a Huffman subtree over the added documents’ current lengths. The loss at a version is its bits a symbol beyond the Huffman code refitted at that version — the Huffman code with no escape, fitted to every document’s current length, which is the comparison a saving quoted without its collection showed has to be made against the best code for the same collection rather than against the plain array. The reading is every character read by a refit and by every rebuild of the escape’s subtree, counted in final arrays: 6,912,669 characters is one.

The priced clock refits at the first version. At every later version it computes the loss the Huffman code in force would have there, which needs only the documents’ lengths: a store holds those as metadata, and fitting a Huffman code over 222 numbers reads nothing of the array. It adds that loss times the version’s characters to a running total, the waste, and if the waste has reached cc times the version’s characters it refits instead, and the waste starts again from nought. The constant cc is in bits wasted per character a refit reads. It was swept from 0.02 to 3.2, doubling each time from 0.05. There is no escape share, no factor and no hold: one constant, and the rule’s whole content is that the refit and the loss are measured against each other. The escape is weighted at every refit as one average document, the weight under which the escape clock did best.

Worse than the hold it was meant to replace

Refitting when the bits wasted since the last refit reach a constant times the characters a refit reads draws one line from 0.026 bits a symbol for 11.76 final arrays to 0.338 for 4.31, and at the held clock's 4.52 arrays it loses about 0.119 against the held clock's 0.098: the price is worse than the fixed hold it was meant to replaceEach schedule's mean loss against the Huffman code refitted at every version, against the bytes its refits and escape rebuilds read, in final arrays; the escape weighted as one document throughout. The priced clock at constants of 0.05: 0.026 for 11.76 (28 refits), 0.1: 0.033 for 8.01 (20 refits), 0.2: 0.045 for 6.57 (17 refits), 0.4: 0.082 for 5.19 (12 refits), 0.8: 0.119 for 4.51 (9 refits), 1.6: 0.207 for 4.92 (8 refits), 3.2: 0.338 for 4.31 (6 refits) bits wasted per character a refit reads. The escape clock held to a quarter's growth 0.098 for 4.52 (11 refits); with no hold 0.042 for 10.59. Doubling the files, growth seen with half at first, 0.356 for 5.65; doubling with the weight read from the future 0.183 for 5.65. Interpolated between its points at 0.4 and 0.8, the priced line at 4.52 arrays loses 0.119.00.1000.2000.3000.40002.5057.5010bytes read by refits and rebuilds, in final arraysmean loss, bits a symbolc 0.05c 0.2c 0.8c 3.2doubling, best a store can computedoubling, growth to comeclock, held to 1.25×priced clocklower and left is better47 versions, 20 to 222 files
Fig. 1 Mean loss against the characters read by refits and rebuilds, in final arrays. The priced clock at constants from 0.05 to 3.2 runs from 0.026 bits a symbol for 11.76 arrays to 0.338 for 4.31; at 0.4 it loses 0.082 for 5.19, at 0.8 0.119 for 4.51. The escape clock held to a quarter’s growth loses 0.098 for 4.52; the best doubling schedule a store can compute 0.356 for 5.65, and the doubling schedule weighted by the growth still to come 0.183.

Priced, the clock draws a single line from 0.026 bits a symbol for 11.76 final arrays read to 0.338 for 4.31, and at the 4.52 arrays the held escape clock reads, it loses about 0.119 where the held clock loses 0.098. The two points that straddle the held clock’s reading are the prices 0.4, which loses 0.082 for 5.19 arrays, and 0.8, which loses 0.119 for 4.51. The held clock lies under the line between them by about a fiftieth of a bit a symbol. The price does beat every doubling schedule, as the held clock did, and at its cheap end it reaches losses the held clock never offered: 0.033 for 8.01 arrays at a constant of 0.1, a third of the held clock’s loss for not quite twice its reading. But the question was whether the price could replace the hold at the hold’s own budget, and at that budget it cannot.

The prediction was wrong on its main claim and wrong about why it would be right. It expected the price to refit early, where the array is small and a refit cheap, and to save its refits for when the waste had grown. The plates below show it doing the opposite in the early history and doing about what the hold did late. The late history, which the proposal named as the risk, is where the price does least badly.

The size cancels

Divided by the array a refit would read, the waste the price watches is almost exactly the bits a symbol lost at each version since the last refit, summed — within 0.061 at every version — because the array's size multiplies both sides of the rule; at a price of 0.8 the clock refits 9 times, whenever about 0.8 bits a symbol of loss has been summedAt a price of 0.8 bits per character a refit reads, with the escape weighted as one document: at every version the waste carried since the last refit divided by that version's characters, against the threshold of 0.8; and the plain sum of the per-version losses since the last refit. Refits at 0, 3, 7, 11, 15, 22, 27, 37, 44. v0 0.000 (refit), v1 0.092, v2 0.366, v3 0.000 (refit), v4 0.102, v5 0.203, v6 0.739, v7 0.000 (refit), v8 0.043, v9 0.140, v10 0.444, v11 0.000 (refit), v12 0.092, v13 0.364, v14 0.648, v15 0.000 (refit), v16 0.027, v17 0.054, v18 0.081, v19 0.108, v20 0.270, v21 0.624, v22 0.000 (refit), v23 0.023, v24 0.136, v25 0.403, v26 0.678, v27 0.000 (refit), v28 0.012, v29 0.023, v30 0.035, v31 0.046, v32 0.058, v33 0.246, v34 0.440, v35 0.610, v36 0.789, v37 0.000 (refit), v38 0.020, v39 0.039, v40 0.058, v41 0.096, v42 0.196, v43 0.425, v44 0.000 (refit), v45 0.125, v46 0.371.00.5001010203040versionbits wasted since the last refit ÷ nprice 0.8losses summedwaste ÷ nvertical lines: refitsescape as one document
Fig. 2 At a price of 0.8, at every version, the waste carried since the last refit divided by that version’s characters, against the threshold; and the plain sum of the per-version losses since the last refit. The two lines agree within 0.061 at every version. Refits at versions 0, 3, 7, 11, 15, 22, 27, 37 and 44.

Divided by the characters a refit would read, the waste the price watches is almost exactly the loss at each version since the last refit, summed — within 0.061 of it at every version — because the array’s size multiplies both sides of the rule. The waste at a version is its loss in bits a symbol times its characters, nn. The cost of a refit there is cc times the same nn. Between consecutive versions nn changes by a few per cent, so the rule “waste at least c nc\,n” is very nearly “the losses since the last refit sum to at least cc”. The array’s size, which was the whole reason to price the refit, is on both sides of the comparison and drops out of it.

That is not a quirk of this history. A version’s waste is the stale code’s bits over the whole array, and the array is the same array the refit would read; any version-by-version accounting that charges waste at the size of the thing being coded and the refit at the size of the thing being read divides through by that size. What remains is a rule about time, counted in versions: refit once the stale code has lost cc bits a symbol, summed over the versions it has served. At a price of 0.8, that means refitting after a loss of about 0.4 for two versions, or 0.1 for eight, or 0.03 for nearly thirty. The plate shows that rhythm. From version 27 the loss is a hundredth of a bit a symbol a version and the waste creeps for five versions; from version 33 the loss is a fifth of a bit and the waste reaches the threshold in four.

So the proposal’s picture of a rule that is cheap to satisfy early and expensive late did not survive the arithmetic. A refit is cheap early, but the loss it removes is also small in bits, because there are fewer characters to waste it on, and the ratio of the two is the loss a symbol — which is no smaller early than late. If anything it is larger, and that is where the price loses to the hold.

Where each schedule refits

The price does not refit early where refits are cheap: in the first fifteen versions, where the array is small, the held clock refits 7 times and the price of 0.8 4; over the whole history the price of 0.4 refits 12 times and 0.8 9, spaced by the loss summed rather than by the size of what a refit readsThe versions at which each schedule refits, of 47: doubling the files: 0, 10, 22, 44 (2 before version 15); clock, held to 1.25×: 0, 2, 3, 6, 9, 11, 13, 21, 25, 37, 43 (7 before version 15); price 0.4: 0, 3, 6, 9, 11, 14, 17, 22, 25, 34, 39, 44 (6 before version 15); price 0.8: 0, 3, 7, 11, 15, 22, 27, 37, 44 (4 before version 15); price 3.2: 0, 5, 11, 19, 26, 43 (3 before version 15).doubling the filesclock, held to 1.25×price 0.4price 0.8price 3.2version 15v0v10v20v30v40v46versiondots: refits47 versions
Fig. 3 The versions at which each schedule refits. Doubling the files: 0, 10, 22 and 44. The escape clock held to a quarter: 11 refits, 7 before version 15. The price of 0.4: 12 refits, 6 before version 15. The price of 0.8: 9 refits, 4 before version 15. The price of 3.2: 6 refits, 3 before version 15.

The price does not refit early where refits are cheap: in the first fifteen versions, where twenty files become fifty, the held clock refits seven times and the price of 0.8 four, at versions 0, 3, 7 and 11. Over the whole history the two have about the same number of refits, eleven and nine, and the price spends the difference in the early stretch. The price of 0.4 refits twelve times and comes closer early, six, which is why its loss is lower; it pays for that with 5.19 arrays of reading rather than 4.52.

The early versions are the ones where the collection doubles fastest. Between versions 2 and 3 the characters grow by 43%, between 5 and 6 by 26%, between 9 and 10 by 12%. A code fitted at one of them is wrong at the next by a large fraction of a bit, because nearly a third of the characters are in documents it has never seen — the early shape and late bytes the shape arrived early and the bytes late first measured — and the escape names all of them through one code word sized for one document. The held clock refits there because the share behind the escape crosses twice its code word’s share in a version or two, and its hold, a quarter’s growth, is met in the same version or two. The price lets the loss run: at version 6 the price of 0.8 is carrying a waste of 0.74 bits a symbol, summed over three versions, and refits only at version 7.

The gap is all in the first stretch

The price loses its whole gap in the first fifteen versions: there the held clock loses 0.073 bits a symbol and the price of 0.8 0.168, while from version 30 they lose 0.098 and 0.098; the price of 0.4 is worse than the held clock early, 0.105, and better late, 0.080Mean loss, bits a symbol beyond a Huffman code refitted at each version, over three stretches of the 47 versions: clock, held to 1.25×: versions 0–14 0.073, versions 15–29 0.124, versions 30–46 0.098; price 0.4: versions 0–14 0.105, versions 15–29 0.060, versions 30–46 0.080; price 0.8: versions 0–14 0.168, versions 15–29 0.095, versions 30–46 0.098.versions 0–140.073 clock, held to 1.25×0.105 price 0.40.168 price 0.8versions 15–290.124 clock, held to 1.25×0.060 price 0.40.095 price 0.8versions 30–460.098 clock, held to 1.25×0.080 price 0.40.098 price 0.8mean loss in the stretch, bits a symbolescape as one documentthree stretches
Fig. 4 Mean loss over three stretches of the history. Versions 0–14: the held clock 0.073, the price of 0.4 0.105, the price of 0.8 0.168. Versions 15–29: 0.124, 0.060 and 0.095. Versions 30–46: 0.098, 0.080 and 0.098.

The price loses its whole gap to the hold in the first fifteen versions: there the held clock loses 0.073 bits a symbol and the price of 0.8 loses 0.168, while from version 30 to the end the two lose 0.098 each. The middle stretch goes the other way. From version 15 to 29 the held clock loses 0.124, its worst stretch, because the hold kept it from refitting at versions 15, 16 and 20, where the escape clock without a hold would have refitted; the price loses 0.095 there and the price of 0.4 0.060. The price of 0.4 is worse than the hold early and better in the other two stretches.

So the late history, which the proposal feared, is a tie, and the middle is a win for the price. The early history, which the proposal expected to be the price’s strength, is where it fails, and it fails because its unit of time is the version. A version is whatever the history happened to commit, and early in a project’s life a version can add a third of the characters, while late it can add almost nothing — versions 18 and 19 here change nothing at all. A rule that sums losses per version counts a version that changed nothing as a version of waste, and counts a version that grew the collection by 43% the same as any other. The hold counts growth instead, which is the thing that actually makes the Huffman code wrong, and in a history whose growth per version varies thirtyfold that difference decides the early stretch.

What the price leaves out

A higher price reads less in refits and more in rebuilding the escape: from 16.26 final arrays of refits and 0.01 of rebuilds at a price of 0.02 to 2.11 and 2.20 at 3.2, so the total stops falling — 4.51 arrays at 0.8, 4.92 at 1.6 — and never reaches under 4.31The bytes each schedule reads, in final arrays, split into whole-array refits and rebuilds of the escape's subtree at the versions between: price 0.02: 16.26 in 39 refits and 0.01 in rebuilds; price 0.05: 11.72 in 28 refits and 0.04 in rebuilds; price 0.1: 7.78 in 20 refits and 0.24 in rebuilds; price 0.2: 6.20 in 17 refits and 0.37 in rebuilds; price 0.4: 4.50 in 12 refits and 0.69 in rebuilds; price 0.8: 3.40 in 9 refits and 1.11 in rebuilds; price 1.6: 3.29 in 8 refits and 1.63 in rebuilds; price 3.2: 2.11 in 6 refits and 2.20 in rebuilds; clock, held to 1.25×: 3.52 in 11 refits and 1.00 in rebuilds. The price counts only the first part against the loss.price 0.0216.26 · 39 refitsprice 0.0511.76 · 28 refitsprice 0.18.01 · 20 refitsprice 0.26.57 · 17 refitsprice 0.45.19 · 12 refitsprice 0.84.51 · 9 refitsprice 1.64.92 · 8 refitsprice 3.24.31 · 6 refitsclock, held to 1.25×4.52 · 11 refitsdark: whole-array refits · light: escape rebuildsbytes read, in final arraysescape as one document47 versions
Fig. 5 The characters each schedule reads, in final arrays, split into whole-array refits and rebuilds of the escape’s subtree. At a price of 0.02: 16.26 in 39 refits, 0.01 in rebuilds. At 0.8: 3.40 and 1.11. At 1.6: 3.29 and 1.63. At 3.2: 2.11 and 2.20. The held clock: 3.52 in 11 refits and 1.00 in rebuilds.

A higher price reads less in refits and more in rebuilding the escape, so the total reading stops falling: 4.51 final arrays at a price of 0.8, 4.92 at 1.6, and never under 4.31 however high the price goes. At every version without a refit the escape’s subtree is rebuilt over the documents added since the last one. The longer a code is kept, the larger that subtree grows and the more versions it is rebuilt at, and at a price of 3.2 the rebuilds read as many characters as the refits do. The price of 1.6 reads more in total than the price of 0.8, though it refits one time fewer.

That is the second thing the proposal’s accounting left out, and it is the one where the reading goes had already measured for the doubling schedule: most of a rare-refitting schedule’s reading is not refitting at all but the price of an escape left to grow. The rule compares the waste with the cost of one refit, and is silent about the reading that not refitting incurs. Delaying a refit is not free in characters read; it moves them from the refit column to the rebuild column. A price that left them out was pricing half the exchange it meant to price, and the plate shows the price of the other half: below about 4.3 arrays, no constant buys anything.

The escape clock’s hold, by comparison, reads 3.52 arrays in refits and 1.00 in rebuilds, almost the split of the price of 0.8. Neither rule was told about rebuilds; the hold lands near the bottom of the total by accident, because a quarter’s growth is short enough that the escape’s subtree never covers much of the collection.

A loss no refit removes

Weighted as half the coding space, the escape costs about a bit a symbol that no refit removes, so the price counts it as waste and refits 30 times in 47 versions for a mean loss of 0.768, reading 16.56 final arrays; weighted as one document it refits 9 times for 0.119At a price of 0.8, and beside the escape clock held to a quarter's growth, under each escape weight: escape as one document: price 0.119 for 4.51 with 9 refits; held clock 0.098 for 4.52 with 11 refits. escape as half the coding space: price 0.768 for 16.56 with 30 refits; held clock 0.384 for 17.04 with 1 refits. escape growth seen, half at first: price 0.178 for 6.59 with 10 refits; held clock 0.384 for 17.04 with 1 refits. The loss is measured against a Huffman code refitted with no escape, so an escape that takes coding space is loss whatever the schedule.escape as one document0.119 · 9 refits0.098 · 11escape as half the coding space0.768 · 30 refits0.384 · 1escape growth seen, half at first0.178 · 10 refits0.384 · 1upper bar: price 0.8 · lower bar: clock held to 1.25×mean loss, bits a symbolprice 0.8mean loss
Fig. 6 At a price of 0.8, beside the escape clock held to a quarter, under three escape weights. As one document: the price 0.119 with 9 refits, the held clock 0.098 with 11. As half the coding space: the price 0.768 with 30 refits for 16.56 arrays, the held clock 0.384 with one. Growth seen, half at first: the price 0.178 with 10 refits, the held clock 0.384 with one.

Weighted as half the coding space, the escape costs about a bit a symbol that no refit removes, and the price counts it as waste anyway: it refits 30 times in 47 versions, reads 16.56 final arrays, and loses 0.768 bits a symbol, twice what the held clock loses under the same weight. The loss at a version is measured against the Huffman code refitted with no escape at all. A code that gives the escape half the coding space makes every other document’s code word one bit longer — and a code word is at least one bit is why the escape’s own cannot be made shorter in return — so just after a refit it is a bit a symbol worse than the reference, with nothing yet behind its escape. A refit does not change that; it installs a new code with the same half-space escape and the same bit of loss. The rule sees a loss, sums it, finds it over the threshold within a version, and refits, and the refit buys nothing.

The held clock fails the other way under that weight, as the earlier essay found: the escape’s code word is one bit, nothing behind it can ever pass twice a half, and the clock never fires after the first version. Each rule is blind to something. The clock watches the escape and cannot see a code word that is too short to be wrong. The price watches the loss and cannot tell a loss a refit removes from a loss the Huffman code builds in. Under the weight read from the growth since the last refit, which starts at a half and then follows the history, the price does better than the clock, 0.178 to 0.384 — but only because the clock never moves at all.

Two units the rule did not make comparable

The proposal asked whether a refit can be priced in the same units as the loss it removes. The answer the plates give is that this rule did price them in the same units, characters, and that pricing them in the same units was the mistake. The waste is bits over the array; the refit is the array read once; the ratio of the two is a loss a symbol, and a loss a symbol is not something a refit’s cost can ever be weighed against, because it has no size. The rule turned the question “is this refit worth its reading?” into “has the Huffman code been wrong for long enough?”, and answered the second with a clock that counts versions.

Choosing a growth factor is the classical case where the right unit is growth rather than time: a dynamic array that resizes by a constant factor pays a constant amortised cost per insertion, whatever the insertions’ timing, because the copies are charged against the elements that caused them. The escape clock’s hold is an amortisation of that kind: a refit, which costs the array, is allowed once the characters have grown by a quarter of the array, so each refit is paid for by the characters that arrived since the last. The price is not. It charges a refit to the versions that elapsed, and a version is not a cause of anything.

The day a filter cannot grow rebuilt a filter when its own count said its rate had passed a threshold, a clock read off the structure. Its threshold was a rate, a quantity without a size, and it worked because a filter’s damage is a rate. The price here put a rate, bits a symbol, on one side and a size on the other, and multiplied both by the version’s characters to make them look alike.

What one directory cannot settle

One history. Every number is one source directory replayed version by version, the history a corpus that was not generated chose because nobody designed it. A history whose versions arrive at even growth would make the version a fair unit of time, and on it the price and the hold should agree. That was not run, and it is the obvious control.

The version as the unit of waste. The waste counts each version once, as though every committed state were coded and stored for the same length of time. A store that holds versions for different spans, or answers queries at a rate that depends on its size, would charge waste differently, and the size would then no longer cancel. That would be a different store, and was not measured.

A coarse sweep. Eight constants, doubling. The frontier between 0.4 and 0.8 is a straight line through two points, and the held clock’s lead of about 0.021 at its own reading is read off that line, not off a price that reads exactly 4.52 arrays.

The escape weighted as one document. The essay’s main comparison uses the weight under which both clocks do best. Under the other weights both rules fail for the reasons given, and neither is a fair test of pricing.

Still open: a price with the waiting in it

The price fails for two separable reasons, and each points at a repair. The size cancels because waste and refit are charged in the same characters; and the rule leaves out the reading that not refitting incurs, the rebuilds of the escape’s subtree, which at a high price are half of everything read. Both are fixed by charging the choice as it actually is. At each version the store can compare two things it knows: the characters it reads if it refits now, the whole array; and the characters it reads if it waits, the escape’s subtree rebuilt over everything added since the last refit, which is the reading that waiting buys. The loss counts as well, at an exchange rate, but only the part a refit removes: the Huffman code’s bits beyond the Huffman code a refit would install, escape included, so that an escape weighted as half the coding space is not counted as waste.

The measurement that follows refits when the rebuild reading accumulated since the last refit plus the removable waste times a constant passes the characters a refit reads, with the constant swept as here, and charges the same three weights. The prediction is that with the waiting priced, the frontier passes under the held clock, to about 0.09 bits a symbol for 4.3 arrays, because a code is kept only while the reading it saves is larger than the reading it costs, and that the half-space escape stops refitting at every version. It could fail early, again: the rebuild reading is small while the escape is small, and in the first stretch, where the characters grow by a third a version, a rule paced by the escape’s subtree may still wait too long. The question is whether the right unit for a refit’s timing is the characters it saves rereading, or the growth that made it necessary — and whether, priced honestly, those turn out to be the same thing.

What this makes readable

Essays that name this one as a prerequisite.

Named alongside this one

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

What links here

Every essay whose body links to this one.

The objects this essay names

Each one links to every other essay that touches it.

AmortisationDocument arrayDocument collectionEntropyHuffman codingIndex sizePrediction