Paper
Part IErrors, Information and Channels

Compression in Practice

November 14, 202650 min readbeginner

Arithmetic coding and ANS spend fractions of a bit, LZ77, LZW and the Burrows–Wheeler transform learn a source while reading it, and lossy coding trades exactness for rate, ending inside memory systems.

The previous chapter, Source Coding and Prefix Codes, ended with three weaknesses of Huffman's code. Each letter costs a whole number of bits, so a binary source with p=0.01p = 0.01, whose entropy is h(0.01)=0.0808h(0.01) = 0.0808 bits, pays 1 bit per letter, 12.4 times its entropy. The probabilities must be known before coding starts. And a code for single letters cannot use the way one letter makes the next more predictable. The same chapter showed that compressed data is fragile, since one flipped bit moved every codeword boundary after it.

Arithmetic coding, range coding and asymmetric numeral systems answer the first weakness by spending fractions of a bit. Adaptive models and the dictionary coders LZ77, LZ78 and LZW answer the other two by learning the source while reading it, and the Burrows–Wheeler transform with move-to-front turns the memory in text into skewed letter counts. A survey of lossy coding follows, and the chapter ends inside a computer's memory system, where compression and fragility meet.

Every number in the chapter is recomputed by ch06_compression_in_practice.py in the python/ folder of the companion repository ecc-book-lab, and the charts are drawn by ch06_charts.py in the same folder.

01.Coding with Intervals

A source emits the letters a, b and c independently, with probabilities 0.7, 0.2 and 0.1. Its entropy is H(X)=1.1568H(X) = 1.1568 bits, and Huffman's code, a 0, b 10 and c 11, averages 1.3 bits per letter. The message aabaaa has probability 0.75×0.2=0.0336140.7^5 \times 0.2 = 0.033614, so it carries log⁡2(1/0.033614)=4.8948\log_2(1/0.033614) = 4.8948 bits of information, yet Huffman's code spends 7 bits on it. The waste is rounding, since each a carries log⁡2(1/0.7)=0.5146\log_2(1/0.7) = 0.5146 bits and is charged a whole bit.

Arithmetic coding never charges a letter on its own. It turns the whole message into one interval inside [0,1)[0, 1). Each letter owns a slice as wide as its probability, a the slice [0,0.7)[0, 0.7), b the slice [0.7,0.9)[0.7, 0.9) and c the slice [0.9,1)[0.9, 1). The first a keeps [0,0.7)[0, 0.7). That interval is split in the same proportions, the second a keeps its first 70 percent, [0,0.49)[0, 0.49), and the b keeps the part from 70 to 90 percent of its width, [0.343,0.441)[0.343, 0.441). Table 1 carries the steps to the end of the message, and Figure 1 draws them. To write a step as a formula, fix the order a, b, c and let the cumulative probability F(s)F(s) be the total probability of the letters before ss, so that F(a)=0F(a) = 0, F(b)=0.7F(b) = 0.7, F(c)=0.9F(c) = 0.9 and letter ss owns [F(s),F(s)+p(s))[F(s), F(s) + p(s)).

The widths multiply, so the final width wn=p(x1) p(x2)⋯p(xn)w_n = p(x_1)\, p(x_2) \cdots p(x_n) is the probability of the message, 0.033614 for aabaaa.

Table 1. The intervals of arithmetic coding for the message aabaaa, with , and

Step kkLetter xkx_kSlice [F(xk),F(xk)+p(xk))[F(x_k), F(x_k) + p(x_k))Interval [uk,uk+wk)[u_k, u_k + w_k)Width wkw_k
1a[0,0.7)[0, 0.7)[0,0.7)[0, 0.7)0.7
2a[0,0.7)[0, 0.7)[0,0.49)[0, 0.49)0.49
3b[0.7,0.9)[0.7, 0.9)[0.343,0.441)[0.343, 0.441)0.098
4a[0,0.7)[0, 0.7)[0.343,0.4116)[0.343, 0.4116)0.0686
5a[0,0.7)[0, 0.7)[0.343,0.39102)[0.343, 0.39102)0.04802
6a[0,0.7)[0, 0.7)[0.343,0.376614)[0.343, 0.376614)0.033614

Source: reproduced by ch06_compression_in_practice.py.

Arithmetic coding of the message aabaaa from a source with probabilities a 0.7, b 0.2 and c 0.1. Six rows each split the current interval into slices of 70, 20 and 10 percent for a, b and c, and the slice of the letter read is outlined and becomes the next row. The intervals shrink from 0 to 1, to 0 to 0.7, 0 to 0.49, 0.343 to 0.441, 0.343 to 0.4116, 0.343 to 0.39102 and finally 0.343 to 0.376614. In the final interval the tag 010110 is marked, which covers 0.34375 to 0.359375 and lies wholly inside it. interval slices a 0.7, b 0.2, c 0.1 letter [0, 1) a b c read a [0, 0.7) a b c read a [0, 0.49) a b c read b [0.343, 0.441) a b c read a [0.343, 0.4116) a b c read a [0.343, 0.39102) a b c read a [0.343, 0.376614) tag final tag 010110 covers [0.34375, 0.359375)
Figure 1. Arithmetic coding of aabaaa. Each row is the current interval split in the proportions 0.7, 0.2 and 0.1, and the slice of the next letter becomes the next row. The tag 010110 names a dyadic interval inside the final interval .

The bits c1c2⋯clc_1 c_2 \cdots c_l name the fraction v/2lv/2^l, where vv is the integer they spell, and every longer string that begins with them names a number in [v 2−l,(v+1) 2−l)[v\,2^{-l}, (v+1)\,2^{-l}). This is the dyadic interval of the string, the object the previous chapter's section The Kraft Inequality laid end to end. The encoder sends a string whose dyadic interval lies inside the message's interval, so the decoder can identify the message whatever bits follow.

For aabaaa, l=⌈4.8948⌉+1=6l = \lceil 4.8948 \rceil + 1 = 6 and v=⌈0.343×64⌉=⌈21.952⌉=22v = \lceil 0.343 \times 64 \rceil = \lceil 21.952 \rceil = 22, which is 010110 in six bits. Its dyadic interval [0.34375,0.359375)[0.34375, 0.359375) lies inside [0.343,0.376614)[0.343, 0.376614). The tag costs 6 bits, one fewer than Huffman's code and 1.1052 bits more than the information in the message.

Proof. Since v≥u 2lv \ge u\, 2^l, the dyadic interval starts at or above uu. Since v<u 2l+1v < u\, 2^l + 1, it ends below u+2⋅2−l=u+2−(l−1)u + 2 \cdot 2^{-l} = u + 2^{-(l-1)}, and l−1=⌈log⁡2(1/w)⌉l - 1 = \lceil \log_2(1/w) \rceil gives 2−(l−1)≤w2^{-(l-1)} \le w. ■\blacksquare

The decoder reads the tag as z=0.34375z = 0.34375, which lies in a's slice [0,0.7)[0, 0.7), so the first letter is a. It undoes that narrowing by stretching the slice back over [0,1)[0, 1), replacing zz by (z−F(s))/p(s)(z - F(s))/p(s), and repeats, as Table 2 shows. It must also be told when to stop, by a length sent first or by a letter reserved to end a message.

Table 2. Decoding the tag 010110, read as , back into aabaaa

StepValue zzSlice containing zzLetterNext value (z−F(s))/p(s)(z - F(s))/p(s)
10.34375[0,0.7)[0, 0.7)a0.491071
20.491071[0,0.7)[0, 0.7)a0.701531
30.701531[0.7,0.9)[0.7, 0.9)b0.007653
40.007653[0,0.7)[0, 0.7)a0.010933
50.010933[0,0.7)[0, 0.7)a0.015618
60.015618[0,0.7)[0, 0.7)a

Source: reproduced by ch06_compression_in_practice.py.

Proof. For n=0n = 0 the empty message owns [0,1)[0, 1). Within the interval [u,u+w)[u, u + w) of a message of n−1n - 1 letters, the slice of ss runs from u+w F(s)u + w\, F(s) to u+w (F(s)+p(s))u + w\, (F(s) + p(s)), and F(s)+p(s)F(s) + p(s) is FF of the next letter, so each slice ends where the next begins and the last ends at u+wu + w. The slices tile each parent, and by induction the parents tile [0,1)[0, 1). ■\blacksquare

So exactly one slice of a first letter contains zz, the right one, and stretching it carries the message's interval onto the interval of its remaining letters, where the argument repeats.

Proof. A tag that prefixed another would have a dyadic interval containing the other's. But each dyadic interval lies inside its own message's interval, and those intervals are disjoint. ■\blacksquare

02.Why Arithmetic Coding Reaches the Entropy

A tag has length ⌈log⁡2(1/P(x))⌉+1\lceil \log_2(1/P(x)) \rceil + 1, less than log⁡2(1/P(x))+2\log_2(1/P(x)) + 2, and averaging this over all messages gives the main result.

Proof. The upper bound is Ln<∑xP(x)log⁡2(1/P(x))+2L_n < \sum_x P(x) \log_2(1/P(x)) + 2, and with independent letters log⁡2(1/P(x))\log_2(1/P(x)) is a sum of nn terms log⁡2(1/p(xk))\log_2(1/p(x_k)), each with expectation HH. The lower bound holds because the tags are a prefix code for blocks whose entropy is nHnH, and the previous chapter's section The Entropy Lower Bound shows that no prefix code averages less. ■\blacksquare

Huffman's code on blocks of nn letters guarantees H+1/nH + 1/n, but it needs a table of mnm^n codewords, where arithmetic coding computes only the interval of the message sent. Table 3 compares the two for a binary source with p=0.1p = 0.1, whose entropy is h(0.1)=0.4690h(0.1) = 0.4690 bits, and Figure 2 plots them.

Table 3. Expected bits per letter for a binary source with , whose entropy is bits. Huffman's code on blocks needs a table of codewords

Letters per message nnArithmetic codingBound h(0.1)+2/nh(0.1) + 2/nHuffman's code on blocks
12.32.46901
21.31.46900.6450
40.80090.96900.4926
80.67510.71900.4758
1000.48410.4890table of 21002^{100} entries
10000.47050.4710table of 210002^{1000} entries

Source: computed exactly over all messages, reproduced by ch06_compression_in_practice.py.

For short messages the overhead dominates. At n=1n = 1 the letter 0 gets a 2-bit tag and the letter 1 a 5-bit tag, for 0.9×2+0.1×5=2.30.9 \times 2 + 0.1 \times 5 = 2.3 bits per letter. The rate is not even monotone, 0.7114 at n=6n = 6 and 0.7286 at n=7n = 7, because the rounding in the tag length falls differently for each nn. At n=1000n = 1000 it is 0.4705, within 0.0015 of the entropy, with no table at all. The source with p=0.01p = 0.01 fares the same, at 0.0823 bits per letter for n=1000n = 1000 against its entropy of 0.0808 and its bound of 0.0828.

Expected bits per source letter against message length n from 2 to 1000 on a log scale, for a binary source with probabilities 0.9 and 0.1. The arithmetic code costs 1.3 bits per letter at n = 2, 0.6751 at n = 8, 0.4841 at n = 100 and 0.4705 at n = 1000, always below the dashed bound h + 2/n. Squares mark Huffman codes on blocks of 1 to 8 letters, falling from 1 to 0.4758. A dashed line marks the entropy 0.4690. message length n in letters (log scale) bits per source letter 0.4 0.6 0.8 1.0 1.2 1.4 1 10 100 1000 arithmetic code, expected upper bound h(0.1) + 2/n Huffman code on blocks of n entropy h(0.1) = 0.4690
Figure 2. Expected bits per letter of arithmetic coding for a binary source with , beside the bound and Huffman's code on blocks of up to 8 letters. Data: computed by ch06_charts.py in the companion repository.

03.Range Coding and Adaptive Models

As described so far, the encoder holds uku_k and wkw_k exactly, and their digits multiply with every letter. A practical coder uses numbers of fixed size, because the leading bits of the tag settle long before the message ends. After aa the interval [0,0.49)[0, 0.49) lies inside [0,1/2)[0, 1/2), where every number begins with the bit 0, so the encoder emits 0 and doubles the interval to [0,0.98)[0, 0.98). The b narrows this to [0.686,0.882)[0.686, 0.882), inside [1/2,1)[1/2, 1), so it emits 1 and maps xx to 2x−12x - 1, leaving [0.372,0.764)[0.372, 0.764). The bits 01 begin the tag 010110.

The next three letters leave intervals that straddle 1/21/2, down to [0.372,0.506456)[0.372, 0.506456), and settle nothing. An interval inside [1/4,3/4)[1/4, 3/4) can shrink this way until it exhausts any fixed precision. Witten, Neal and Cleary [1] resolved this with pending bits. Numbers in [1/4,1/2)[1/4, 1/2) begin 01 and numbers in [1/2,3/4)[1/2, 3/4) begin 10, so the next two bits are certain to differ. The encoder maps xx to 2x−1/22x - 1/2 and counts a pending bit, and when a bit bb settles it emits bb followed by one 1−b1 - b per pending bit. An interval that fits none of the three cases contains [1/4,1/2][1/4, 1/2] or [1/2,3/4][1/2, 3/4], so it is wider than 1/41/4 and a register of fixed size keeps enough precision.

Range coding does the same with integers. Start from the range R=1000R = 1000, with frequencies f(a)=7f(a) = 7, f(b)=2f(b) = 2 and f(c)=1f(c) = 1, total T=10T = 10 and cumulative counts c(a)=0c(a) = 0, c(b)=7c(b) = 7 and c(c)=9c(c) = 9. Each letter ss updates

low←low+⌊R c(s)T⌋,R←⌊R f(s)T⌋.\text{low} \leftarrow \text{low} + \left\lfloor \frac{R\, c(s)}{T} \right\rfloor, \qquad R \leftarrow \left\lfloor \frac{R\, f(s)}{T} \right\rfloor.
(3)

For aabaaa the range runs 1000, 700, 490, 98, 68, 47 and 32, and the lower end moves once, to 343 at the b, giving [343,375)[343, 375) in thousandths. The exact width is 33.614, so truncation cost log⁡2(33.614/32)=0.0710\log_2(33.614/32) = 0.0710 bits.

Truncation never makes slices overlap. The slice of ss ends at or before low+⌊R (c(s)+f(s))/T⌋\text{low} + \lfloor R\,(c(s) + f(s))/T \rfloor, where the next slice begins, because ⌊α⌋+⌊β⌋≤⌊α+β⌋\lfloor \alpha \rfloor + \lfloor \beta \rfloor \le \lfloor \alpha + \beta \rfloor, and the gaps it leaves are unused code space, the source of the 0.0710 bits. To keep RR large the coder sends the leading digit once the whole interval shares it. After aaba, [343,411)[343, 411) holds numbers beginning with 3 and with 4, but every number in [343,390)[343, 390) after aabaa begins with 3, so that digit goes out and the rest is scaled by 10. Rissanen [2] introduced arithmetic codes that run in finite precision.

The decoder can follow any model it can compute itself, so the model may learn. Start each of mm letters with a count of 1, and predict each letter in proportion to its count. For aab with m=3m = 3 the predictions are 1/31/3, 2/42/4 and 1/51/5, giving the message probability 1/301/30.

The denominators are m,m+1,…,n+m−1m, m + 1, \ldots, n + m - 1 whatever the letters, and letter ss contributes the numerators 1,2,…,ks1, 2, \ldots, k_s, so

PL(x1⋯xn)=(m−1)!∏sks!(n+m−1)!.P_L(x_1 \cdots x_n) = \frac{(m - 1)! \prod_s k_s!}{(n + m - 1)!}.
(5)

For aabaaa this is 2! 5! 1! 0!/8!=1/1682!\, 5!\, 1!\, 0!/8! = 1/168, or 7.3923 bits and a 9-bit tag, against 6 bits with the true model.

Proof. By Equation 5, PL(x)=(∏sks!/n!)/(n+m−1m−1)P_L(x) = \bigl(\prod_s k_s!/n!\bigr) \big/ \binom{n+m-1}{m-1}. With qs=ks/nq_s = k_s/n, the number n!∏sks!∏sqsks\frac{n!}{\prod_s k_s!} \prod_s q_s^{k_s} is the probability that nn letters drawn with probabilities qsq_s have exactly the counts ksk_s, so it is at most 1, and ∏sks!/n!≥∏sqsks\prod_s k_s!/n! \ge \prod_s q_s^{k_s}. And log⁡2∏sqsks−log⁡2P(x)=n D(Q ∥ P)≥0\log_2 \prod_s q_s^{k_s} - \log_2 P(x) = n\, D(Q \,\|\, P) \ge 0 by Gibbs' inequality from the chapter Information and Entropy. Together these give PL(x)≥P(x)/(n+m−1m−1)P_L(x) \ge P(x)/\binom{n+m-1}{m-1}. ■\blacksquare

For three letters the price is at most log⁡2(n+22)\log_2 \binom{n+2}{2}, which is 12.331 bits at n=100n = 100 and 18.936 bits at n=1000n = 1000. Each factor (n+j)/j(n + j)/j of the binomial coefficient is at most n+1n + 1, so in general the price is at most (m−1)log⁡2(n+1)(m - 1)\log_2(n + 1) bits per message, a share per letter that falls toward zero.

04.Asymmetric Numeral Systems

Arithmetic coding carries two numbers, the lower end and the width. Asymmetric numeral systems, introduced by Duda [3], carry one integer, the state. Appending a decimal digit dd to xx gives 10x+d10x + d, which multiplies xx by about 10 and adds log⁡210=3.32\log_2 10 = 3.32 bits, the information in a uniformly random digit. ANS makes the multiplier about 1/p(s)1/p(s) for a letter of probability p(s)p(s), so each letter adds its own information.

Take M=10M = 10, the total of the frequencies 7, 2 and 1, and give a the slots 0 to 6, b the slots 7 and 8, and c the slot 9, writing fsf_s for the number of slots of ss and csc_s for its first slot. Every integer belongs to the letter that owns its slot x mod 10x \bmod 10, so the a-integers are 0 to 6, 10 to 16 and so on, as Figure 3 lays out. Pushing ss onto the state xx moves to the integer numbered xx, counting from 0, among the integers of ss. Pushing a onto 10 gives 13, since 0 to 6 are a-integers number 0 to 6, 10 to 12 are numbers 7 to 9, and 13 is number 10. The integers of ss have density fs/Mf_s/M, so number xx among them lies near xM/fsxM/f_s.

Proof. Write x=fsq+rx = f_s q + r with 0≤r<fs0 \le r < f_s, so that C(s,x)=Mq+cs+rC(s, x) = Mq + c_s + r. Since cs+r<cs+fs≤Mc_s + r < c_s + f_s \le M, the slot is cs+rc_s + r, one of the slots of ss, and ⌊C(s,x)/M⌋=q\lfloor C(s, x)/M \rfloor = q. Then D(C(s,x))=fsq+r=xD(C(s, x)) = f_s q + r = x. ■\blacksquare

With q=(x−r)/fsq = (x - r)/f_s, the push is C(s,x)=Mfsx+ϵC(s, x) = \frac{M}{f_s}x + \epsilon with ϵ=cs−r(Mfs−1)\epsilon = c_s - r\bigl(\frac{M}{f_s} - 1\bigr). The first term of ϵ\epsilon lies in [0,M−fs][0, M - f_s] and the second in [0,(fs−1)(M−fs)/fs][0, (f_s - 1)(M - f_s)/f_s], so ∣ϵ∣≤M−fs|\epsilon| \le M - f_s, and dividing by Mx/fsMx/f_s gives, for x≥1x \ge 1,

log⁡2C(s,x)=log⁡2x+log⁡21p(s)+log⁡2(1+δ),∣δ∣<fsx.\log_2 C(s, x) = \log_2 x + \log_2 \frac{1}{p(s)} + \log_2(1 + \delta), \qquad |\delta| < \frac{f_s}{x}.
(9)

The correction fades as the state grows, so over a long message each letter adds close to log⁡2(1/p(s))\log_2(1/p(s)) bits. The decoder pops the most recent letter first, like a stack, so the encoder pushes the message backward. Table 4 encodes aabaaa from the state 10.

Table 4. rANS encoding of aabaaa with , pushing its letters in reverse order from the state 10. Each step writes

PushLetter ssState before(q,r)(q, r)State after C(s,x)C(s, x)Growth factor
1a10(1, 3)131.3
2a13(1, 6)161.2308
3a16(2, 2)221.375
4b22(11, 0)1175.3182
5a117(16, 5)1651.4103
6a165(23, 4)2341.4182

Source: reproduced by ch06_compression_in_practice.py.

The factors approach the ideal 10/7=1.428610/7 = 1.4286 and 10/2=510/2 = 5 as the state grows. The b lands on 117 because the b-integers run 7, 8, 17, 18 and so on, which makes 117 number 22. The six factors multiply to log⁡223.4=4.5484\log_2 23.4 = 4.5484 bits, a little under the 4.8948 bits of information, but the final state 234 needs 8 bits, so on a short message the starting state is an overhead, like the 2 bits of arithmetic coding. Decoding starts from 234, whose slot 4 belongs to a, with D(234)=7×23+4=165D(234) = 7 \times 23 + 4 = 165, and the states run back through 117, 22, 16 and 13 to 10, popping aabaaa in order.

The states 0 to 39 of an rANS coder with M equal to 10, laid out ten to a row so that each column is one slot, x mod 10. Slots 0 to 6 belong to a, slots 7 and 8 to b, and slot 9 to c. Under each state is its index among the states of its letter: the a-states 0 to 6, 10 to 16, 20 to 26 and 30 to 36 carry a-indices 0 to 27, the b-states 7, 8, 17, 18 and so on carry b-indices 0 to 7, and the c-states 9, 19, 29 and 39 carry c-indices 0 to 3. State 13 is outlined: it is a-state number 10, so encoding a from state 10 gives 13. a: slots 0 to 6 b: 7, 8 c: 9 slot 0 1 2 3 4 5 6 7 8 9 q = 0 0 a0 1 a1 2 a2 3 a3 4 a4 5 a5 6 a6 7 b0 8 b1 9 c0 q = 1 10 a7 11 a8 12 a9 13 a10 14 a11 15 a12 16 a13 17 b2 18 b3 19 c1 q = 2 20 a14 21 a15 22 a16 23 a17 24 a18 25 a19 26 a20 27 b4 28 b5 29 c2 q = 3 30 a21 31 a22 32 a23 33 a24 34 a25 35 a26 36 a27 37 b6 38 b7 39 c3 State 13 sits in slot 3, a slot of a, and is a-state number 10, so C(a, 10) = 13. Decoding: 13 mod 10 = 3 names a, and 7 × 1 + 3 − 0 = 10 is the old state.
Figure 3. The states of an rANS coder with . Pushing a letter onto the state moves to the state numbered among that letter's states, so pushing a onto 10 gives 13.

Left alone the state grows without bound, so a practical coder keeps it in [10,100)[10, 100) by emitting digits. Before pushing ss, while x≥10fsx \ge 10 f_s, it emits the last digit of xx and divides xx by 10, rounding down. On aabaaa the first pushes give 13, 16 and 22. Before the b, 22≥2022 \ge 20, so the encoder emits 2, continues from 2 and reaches C(b,2)=17C(b, 2) = 17, then 23 and 32. It sends the state 32 and the digit 2. The decoder pops through 23 and 17 to 2, reads the digit back because 2<102 < 10, giving 22, and pops on through 16 and 13 to 10.

Proof. Let 10≤x<10010 \le x < 100. If x≥10fsx \ge 10 f_s, one division gives ⌊x/10⌋\lfloor x/10 \rfloor, which is at least fsf_s and below 10. Either way the state x′x' that is pushed satisfies fs≤x′<10fsf_s \le x' < 10 f_s, so qq lies between 1 and 9 and C(s,x′)=10q+cs+rC(s, x') = 10q + c_s + r lies in [10,100)[10, 100). A pop returns x′x', which is below 10 exactly when a digit was emitted, and reading that digit back restores xx. ■\blacksquare

Nothing here depends on base 10. Choosing MM as a power of 2 turns division by MM into a shift, and moving 8 bits at a time instead of a decimal digit changes nothing in the proof. The Zstandard format [4] uses a table-driven variant of ANS that its specification calls Finite State Entropy.

05.Dictionary Coding with LZ77

Every coder so far needs a model to supply probabilities. Dictionary coders need none. They replace a string that has occurred before by a pointer to its earlier copy, and the first, described by Ziv and Lempel [5] in 1977, is now called LZ77.

Table 5 parses abcbcbcabcd, numbering positions from 1. The first three letters are new and go as literals. At position 4 the text ahead begins with bcbc, which also begins 2 positions back, so one triple (2,4,a)(2, 4, a) covers five letters. At position 9 the text bcd begins with bc, which begins 3, 5 and 7 positions back, and the encoder takes the nearest.

Table 5. LZ77 parsing of abcbcbcabcd. Each triple gives the offset back to the copy, the length of the copy and the letter that follows it

PositionText still to codeLongest earlier matchTriple (o,ℓ,x)(o, \ell, x)
1abcbcbcabcdnone(0, 0, a)
2bcbcbcabcdnone(0, 0, b)
3cbcbcabcdnone(0, 0, c)
4bcbcabcdbcbc, starting 2 back(2, 4, a)
9bcdbc, nearest start 3 back(3, 2, d)

Source: reproduced by ch06_compression_in_practice.py.

The copy at position 4 is longer than its offset, so it overlaps its own output. The decoder copies one letter at a time, and each copy may read a letter it has just written, as the shading in Figure 4 shows. An offset of 1 therefore codes a run, so aaaaaaab becomes (0,0,a)(0, 0, a) and (1,6,b)(1, 6, b).

LZ77 parsing of the eleven letters abcbcbcabcd. The letters sit in numbered cells 1 to 11. Brackets below group them into five phrases with the triples (0,0,a), (0,0,b), (0,0,c), (2,4,a) and (3,2,d). The fourth phrase covers cells 4 to 8: an arrow from cell 2 to cell 4 is labelled offset 2, length 4, so cells 4 to 7 are copied from cells 2 to 5 and cell 8, a, is a literal. Cells 4 and 5 are written by the copy just before the copy reads them. The fifth phrase covers cells 9 to 11: an arrow from cell 6 to cell 9 is labelled offset 3, length 2, so cells 9 and 10 copy cells 6 and 7 and cell 11, d, is a literal. Copied cells are shaded. offset 2, length 4 offset 3, length 2 1 a 2 b 3 c 4 b 5 c 6 b 7 c 8 a 9 b 10 c 11 d (0,0,a) (0,0,b) (0,0,c) (2,4,a) (3,2,d) Phrase (2,4,a) copies cells 2 to 5 into cells 4 to 7, then adds the literal a. Cells 4 and 5 are written by the copy just before the copy reads them. copied from earlier text literal letter
Figure 4. LZ77 on abcbcbcabcd. The first copy reads cells 4 and 5 just after writing them, and the second takes the nearest of three equally long matches.

The DEFLATE format [6] uses a window of 32,768 bytes and copies of 3 to 258 bytes. It sends each step as a literal byte or a (length, distance) pair and codes those with Huffman codes, so the prefix codes of the previous chapter sit behind the dictionary.

06.LZ78 and LZW

LZ78 [7] builds an explicit dictionary of phrases instead. Each phrase is the longest phrase already in the dictionary plus one new letter, sent as the pair (number of that phrase, new letter) and then added. The text aababcabcd splits into a, ab, abc and abcd, sent as (0,a)(0, a), (1,b)(1, b), (2,c)(2, c) and (3,d)(3, d), where phrase 0 is the empty phrase. Phrase ii points to one of ii earlier phrases, so on a binary source cc phrases cost

B(c)=∑i=1c(⌈log⁡2i⌉+1)B(c) = \sum_{i=1}^{c} \bigl( \lceil \log_2 i \rceil + 1 \bigr)
(10)

bits, and a last phrase cut short by the end of the text costs only its pointer.

Ziv and Lempel proved that LZ78 is universal. On any stationary ergodic source, one whose statistics do not change with time and whose long-run averages match its expectations, its bits per letter approach the entropy rate, the limit of H(X1,…,Xn)/nH(X_1, \ldots, X_n)/n, without the coder being told the source. That proof needs more probability than this Part develops. Figure 5 shows how slowly the approach happens on one sequence from the source with p=0.1p = 0.1, from 0.76 bits per letter after 100 letters to 0.5586 after a million, still 19 percent above the entropy. Over the thirteen lengths plotted, the excess times log⁡2n\log_2 n stays between 1.68 and 2.34, so the excess shrinks roughly like 2/log⁡2n2/\log_2 n, far more slowly than the 2/n2/n of arithmetic coding with a known model.

Bits per source letter of LZ78 against the number of letters read, from 100 to one million on a log scale, for one sequence from a binary source with probabilities 0.9 and 0.1. The rate falls from 0.7600 at 100 letters to 0.6237 at ten thousand and 0.5586 at one million, still well above the dashed entropy line at 0.4690. A second dashed line at 1 marks Huffman coding of single letters. letters read, n (log scale) bits per source letter 0.4 0.6 0.8 1.0 100 1000 10^4 10^5 10^6 LZ78, one sequence Huffman on single letters entropy h(0.1) = 0.4690
Figure 5. LZ78 on one sequence from a binary source with . The rate approaches the entropy, but slowly, and after a million letters it is still 19 percent above it. Data: computed by ch06_charts.py in the companion repository.

Welch's variant LZW [8] drops the explicit letter. Its dictionary starts with every single letter, and each step sends only the number of the longest entry matching the text ahead, then adds that entry followed by the next letter. With a numbered 0 and b numbered 1, Table 6 codes abababa as 0, 1, 2 and 4.

Table 6. LZW coding of abababa, with a and b starting the dictionary as entries 0 and 1

StepLongest match aheadNumber sentNew entry
1a02 is ab
2b13 is ba
3ab24 is aba
4aba4none, the text has ended

Source: reproduced by ch06_compression_in_practice.py.

The decoder rebuilds the dictionary one step behind, because each new entry ends with the first letter of the next phrase. It can therefore receive a number it has not yet defined, as with 4 here. That happens only when the encoder sends the entry it has just created, which is the previous phrase followed by its own first letter, so entry 4 is ab followed by a, which is aba. On aaaaaaa the numbers are 0, 2, 3 and 0, and both 2 and 3 are such special cases.

07.Run-Length Coding

A row of a scanned page might hold 12 white pixels, 3 black, 9 white and 2 black. As bits the row is 26 long, but its four runs fit in 16 bits as 4-bit fields, and on two letters the runs alternate, so only the letter of the first run needs to be agreed.

On a memoryless binary source, run-length coding loses nothing. Let 1 have probability pp, and let RR be the gap from one 1 to the next, counting the 1 that ends it. Then P(R=r)=(1−p)r−1pP(R = r) = (1 - p)^{r-1} p, with mean E[R]=1/pE[R] = 1/p, and

H(R)=−∑r≥1(1−p)r−1p[(r−1)log⁡2(1−p)+log⁡2p]=−1−pplog⁡2(1−p)−log⁡2p=h(p)p,H(R) = -\sum_{r \ge 1} (1-p)^{r-1} p \bigl[ (r - 1) \log_2 (1-p) + \log_2 p \bigr] = -\frac{1-p}{p} \log_2(1-p) - \log_2 p = \frac{h(p)}{p},
(11)

using E[R−1]=(1−p)/pE[R - 1] = (1 - p)/p. A gap covers 1/p1/p letters on average, so coding gaps at their entropy costs h(p)h(p) bits per letter, the source's own entropy. At p=0.1p = 0.1 a gap carries 4.6900 bits over 10 letters on average, and a Shannon code on gaps, within 1 bit of H(R)H(R), spends less than h(p)+p=0.5690h(p) + p = 0.5690 bits per letter, against 1 bit for any code on single letters.

08.The Burrows–Wheeler Transform

Text is not made of runs, but it can be rearranged into something close. The Burrows–Wheeler transform [9] appends an end marker, written $, that sorts before every letter, lists the rotations of the block and sorts them, as Table 7 and Figure 6 do for couscous.

Table 7. The sorted rotations of couscous with the end marker, their last letters and the last-to-first map

Row iiSorted rotationFirst letter F[i]F[i]Last letter L[i]L[i]LF(i)\mathrm{LF}(i)
0$couscous$s5
1cous$couscs6
2couscous$c$0
3ous$couscoc1
4ouscous$coc2
5s$couscousu7
6scous$cousu8
7us$couscouo3
8uscous$couo4

Source: reproduced by ch06_compression_in_practice.py.

The last column, read downward, is the output. Sorting the rotations sorts the positions of the text by what follows them, and the last column holds the letter just before each position, so letters with the same following context gather. Both rows beginning with ous end in c, because in couscous the letters ous are always preceded by c, and the same happens for the rows beginning with s and with us.

The Burrows-Wheeler transform of couscous followed by the end marker, written as a dollar sign that sorts before every letter. The nine rotations are listed in sorted order, rows 0 to 8: $couscous, cous$cous, couscous$, ous$cousc, ouscous$c, s$couscou, scous$cou, us$cousco and uscous$co. The first column F reads $ccoossuu and the last column L reads ss$ccuuoo. Brackets join rows 3 and 4, which begin with ous and both end in c, rows 5 and 6, which begin with s and both end in u, and rows 7 and 8, which begin with us and both end in o. row F sorted rotation L 0 $ c o u s c o u s 1 c o u s $ c o u s 2 c o u s c o u s $ 3 o u s $ c o u s c 4 o u s c o u s $ c 5 s $ c o u s c o u 6 s c o u s $ c o u 7 u s $ c o u s c o 8 u s c o u s $ c o begin with ous, both end in c begin with s, both end in u begin with us, both end in o F = $ccoossuu L = ss$ccuuoo with the end marker, L alone can be inverted
Figure 6. The Burrows–Wheeler transform of couscous with an end marker. Sorting the rotations groups them by the context that follows each position, so the last column collects the letter before each context into the runs ss, cc, uu and oo.

The end marker makes the transform invertible, through a correspondence between the first and last columns.

Proof. The rows that end in a letter yy have the form WyWy, and moving yy to the front gives the rows yWyW, which are exactly the rows beginning with yy, each holding the same occurrence. The rows WyWy appear in the sorted order of WW, since all end in yy, and so do the rows yWyW, since all begin with yy. The end marker makes every rotation distinct, so both orders are strict, and the kkth of one is the kkth of the other. ■\blacksquare

The last letter of row ii precedes the position where row ii begins, so the lemma locates the rotation one step earlier. With C(y)C(y) the number of letters in the block that sort before yy, that row is

LF(i)=C(L[i])+∣{ j<i:L[j]=L[i] }∣.\mathrm{LF}(i) = C(L[i]) + \bigl|\{\, j < i : L[j] = L[i] \,\}\bigr|.
(12)

Row 0 begins with the end marker, so its last letter ends the text. Following LF from row 0, as in Table 8, collects s, u, o, c, s, u, o and c before reaching the marker, and read in reverse these spell couscous.

Table 8. Inverting the transform by following the last-to-first map from row 0

StepRow iiLetter collected L[i]L[i]Next row LF(i)\mathrm{LF}(i)
10s5
25u7
37o3
43c1
51s6
66u8
78o4
84c2
92$, stop

Source: reproduced by ch06_compression_in_practice.py.

09.Move-to-Front

The transform's output has the letter counts of couscous, so a code for single letters gains nothing from the rearrangement alone. Move-to-front [10] turns its local repetition into small numbers.

Starting from the list in sorted order, Table 9 codes the output as 3, 0, 1, 2, 0, 4, 0, 4, 0, and every second letter of a run becomes 0. The empirical entropy of a sequence, the entropy of its own letter frequencies, falls from 2.2810 bits per letter to 2.0588. A nine-letter block shows little, but on a long text the output is dominated by zeros, which run-length and arithmetic coding then compress [9].

Table 9. Move-to-front coding of the transform output, starting from the alphabet in sorted order

StepLetterList before the stepPosition sent
1s$cosu3
2ss$cou0
3$s$cou1
4c$scou2
5cc$sou0
6uc$sou4
7uuc$so0
8ouc$so4
9oouc$s0

Source: reproduced by ch06_compression_in_practice.py.

10.Lossy Coding

Every coder so far is lossless. A sample of sound or the brightness of a pixel is a real number, which takes infinitely many bits to describe exactly, so lossy coding accepts an error in exchange for a finite rate.

Quantization

Round the samples 0.37, −1.12-1.12, 2.05, 0.61 and −0.28-0.28 to the nearest multiple of Δ=0.5\Delta = 0.5. The indices k=round⁡(x/Δ)k = \operatorname{round}(x/\Delta) are 1, −2-2, 4, 1 and −1-1, the reconstructions kΔk\Delta are 0.5, −1-1, 2, 0.5 and −0.5-0.5, and the mean squared error is 0.01886.

When the step is small against the scale on which the density of the samples changes, the error is close to uniform on [−Δ/2,Δ/2)[-\Delta/2, \Delta/2), so

D≈1Δ∫−Δ/2Δ/2e2 de=Δ212,D \approx \frac{1}{\Delta} \int_{-\Delta/2}^{\Delta/2} e^2 \, de = \frac{\Delta^2}{12},
(13)

which is 0.020833 for Δ=0.5\Delta = 0.5. With 2b2^b levels over a fixed range, each extra bit halves Δ\Delta and divides DD by 4, or 10log⁡104=6.020610\log_{10} 4 = 6.0206 decibels. A full-scale signal spread evenly over the range has variance 22bΔ2/122^{2b}\Delta^2/12, so its signal-to-noise ratio is 6.0206 b6.0206\,b decibels, 48.16 dB at 8 bits and 96.33 dB at 16 bits.

Transform Coding

Neighboring samples are usually close. Rotate two pixels of brightness 100 and 104 by 45 degrees,

s=x1+x22,d=x1−x22,s2+d2=x12+x22,s = \frac{x_1 + x_2}{\sqrt{2}}, \qquad d = \frac{x_1 - x_2}{\sqrt{2}}, \qquad s^2 + d^2 = x_1^2 + x_2^2,
(14)

where the identity holds because the cross terms cancel. Here s=144.2498s = 144.2498 and d=−2.8284d = -2.8284, so the sum carries a fraction 0.99962 of the energy 20816. Quantize ss with step 4 to 144 and dd with step 8 to 0. Both pixels come back as 144/2=101.8234144/\sqrt{2} = 101.8234, with squared error 8.0624, the same in both domains, as the identity requires of any error under this linear rotation. Transform coding does this at scale. An orthonormal transform packs the energy of a block into a few coefficients, which are quantized finely, while the rest are quantized coarsely or dropped, as JPEG does on blocks of 8 by 8 pixels with the discrete cosine transform.

The Rate–Distortion Trade

For a Gaussian source of variance σ2\sigma^2 under mean squared error,

R(D)=12log⁡2σ2D(0<D≤σ2),D(R)=σ2 2−2R,R(D) = \frac{1}{2} \log_2 \frac{\sigma^2}{D} \quad (0 < D \le \sigma^2), \qquad D(R) = \sigma^2\, 2^{-2R},
(15)

which is 6.02 dB per bit. A counting sketch, not a proof, shows where this comes from. A block of nn samples lies near a sphere of radius nσ2\sqrt{n\sigma^2}, each reconstruction covers a ball of radius nD\sqrt{nD}, and volumes scale as the nnth power of the radius, so covering takes about (σ2/D)n/2(\sigma^2/D)^{n/2} reconstructions, which is 12log⁡2(σ2/D)\frac{1}{2}\log_2(\sigma^2/D) bits per sample. Cover and Thomas [11] prove that no code does better and that long blocks achieve it.

A quantizer acting on one sample at a time falls short. With one bit, reconstructing ±y\pm y by sign gives D=1−2y E∣X∣+y2D = 1 - 2y\, E|X| + y^2 for unit variance, smallest at y=E∣X∣=2/π=0.7979y = E|X| = \sqrt{2/\pi} = 0.7979, where D=1−2/π=0.36338D = 1 - 2/\pi = 0.36338, or −4.40-4.40 dB against the bound's −6.02-6.02 dB. Table 10 continues to five bits, where the gap reaches 5.54 dB, because evenly spaced levels must either clip the tails or spread thinly over the middle.

Table 10. The best uniform quantizer with levels for a Gaussian source of unit variance, against the bound

Rate RRLevelsBest stepDistortion DDDD in dBBound in dBGap in dB
121.59580.36338−4.40-4.40−6.02-6.021.62
240.99570.11885−9.25-9.25−12.04-12.042.79
380.58600.03744−14.27-14.27−18.06-18.063.80
4160.33520.01154−19.38-19.38−24.08-24.084.71
5320.18810.00350−24.57-24.57−30.10-30.105.54

Source: distortion integrated exactly over the Gaussian density, reproduced by ch06_compression_in_practice.py.

Coding the indices of a fixed-step quantizer with an entropy coder does better, because the rate becomes the entropy H(K)H(K) of the index. The differential entropy of a density ff is h(X)=−∫f(x)log⁡2f(x) dxh(X) = -\int f(x) \log_2 f(x)\, dx. For a Gaussian, −log⁡2f(x)=12log⁡2(2πσ2)+x22σ2log⁡2e-\log_2 f(x) = \frac12 \log_2(2\pi\sigma^2) + \frac{x^2}{2\sigma^2}\log_2 e, and its expectation is h(X)=12log⁡2(2πeσ2)h(X) = \frac{1}{2}\log_2(2\pi e \sigma^2), 2.0471 bits at unit variance. With a small step P(K=k)≈f(kΔ) ΔP(K = k) \approx f(k\Delta)\,\Delta, so

H(K)≈−∑kf(kΔ) Δlog⁡2(f(kΔ) Δ)≈h(X)−log⁡2Δ,H(K) \approx -\sum_k f(k\Delta)\,\Delta \log_2\bigl(f(k\Delta)\,\Delta\bigr) \approx h(X) - \log_2 \Delta,
(16)

and with log⁡2Δ=12log⁡2(12D)\log_2 \Delta = \frac12 \log_2(12 D) from Equation 13,

H(K)≈12log⁡2(2πeσ2)−12log⁡2(12D)=R(D)+12log⁡2πe6.H(K) \approx \frac{1}{2}\log_2(2\pi e\sigma^2) - \frac{1}{2}\log_2(12 D) = R(D) + \frac{1}{2}\log_2 \frac{\pi e}{6}.
(17)

The gap settles at 12log⁡2(πe/6)=0.2546\frac12\log_2(\pi e/6) = 0.2546 bits, a distortion πe/6=1.4233\pi e/6 = 1.4233 times the bound, or 1.5329 dB. Computed exactly, it is 1.88 dB at Δ=1\Delta = 1, 1.62 dB at Δ=0.5\Delta = 0.5 and 1.56 dB at Δ=0.25\Delta = 0.25, where the rate is 4.0508 bits, as Figure 7 shows.

Mean squared error in decibels against rate in bits per sample for a Gaussian source of unit variance. The rate-distortion bound is a straight line falling 6.02 dB per bit to -30.1 dB at 5 bits. The best uniform quantizers with 2, 4, 8, 16 and 32 levels reach -4.40, -9.25, -14.27, -19.38, -24.57 dB, drifting away from the bound. A dashed curve for entropy-coded uniform quantizers runs about 1.5 dB above the bound at high rates. rate R in bits per sample mean squared error in dB 0 -5 -10 -15 -20 -25 -30 0 1 2 3 4 5 bound D(R), 6.02 dB per bit best uniform quantizer, 2^R levels uniform quantizer, entropy coded
Figure 7. Distortion against rate for a Gaussian source of unit variance. Fixed-rate uniform quantizers fall further behind the bound with every bit, while entropy-coded uniform quantizers approach a constant gap of 1.53 dB. Data: computed by ch06_charts.py in the companion repository.

11.Compression Inside Memory Systems

Compression also runs between a processor's caches and its main memory. Data moves there in lines of fixed size that must be compressed in a few clock cycles, so the schemes are pattern matchers rather than entropy coders, exploiting lines of zeros, small integers or nearby pointers. A compressed line lets a cache hold more lines, or a memory channel carry more data.

A 32-byte line of eight 4-byte pointers, in hexadecimal 00403A10, 00403A18, 00403A20, 00403A28, 00403A40, 00403A48, 00403A60 and 00403A70, varies only in its last byte. With the first word as a base, the deltas are 0, 8, 16, 24, 48, 56, 80 and 96, each of which fits in a signed byte, so the line takes 12 bytes, a compression ratio of 2.67, as Figure 8 draws. This is base-delta-immediate compression, proposed by Pekhimenko and colleagues [12].

The immediate option handles mixed lines such as 0, 00403A10, 3, 00403A28, 1, 00403A18, C, 00403A40, where no 1-byte or 2-byte delta reaches from the pointers down to the small values. With zero as a second base, the deltas are 0, 0, 3, 24, 1, 8, 12 and 48 under the mask 01010101, where 1 marks the stored base, and the line takes 4+8+1=134 + 8 + 1 = 13 bytes, a ratio of 2.46. Frequent pattern compression, from Alameldeen and Wood [13], works one word at a time instead, giving each word a short prefix that names a pattern such as all zeros, a small sign-extended value or one repeated byte.

Base-delta compression of a 32-byte cache line holding eight 4-byte pointers, 00403A10, 00403A18, 00403A20, 00403A28, 00403A40, 00403A48, 00403A60 and 00403A70. Subtracting the base 00403A10 leaves the deltas 00, 08, 10, 18, 30, 38, 50 and 60 in hexadecimal, each one byte, so the line is stored as a 4-byte base and eight 1-byte deltas, 12 bytes in all, drawn to the same scale as the 32-byte original. A third row shows the effect of flipping bit 4 of the stored base, which turns it into 00403A00: every one of the eight decompressed words comes out 16 too low, outlined as wrong. Uncompressed line: eight 4-byte words, 32 bytes 00403A10 00403A18 00403A20 00403A28 00403A40 00403A48 00403A60 00403A70 keep the first word as the base, store each word minus the base Compressed: a 4-byte base and eight 1-byte deltas, 12 bytes 00403A10 00 08 10 18 30 38 50 60 compression ratio 32 / 12 = 2.67 decompress after bit 4 of the stored base flips: 00403A10 becomes 00403A00 Decompressed with the damaged base: all eight words are 16 too low 00403A00 00403A08 00403A10 00403A18 00403A30 00403A38 00403A50 00403A60 One wrong bit among the 96 stored bits spoils all 256 bits of the line.
Figure 8. Base-delta compression of a line of eight pointers, and the effect of one flipped bit in the stored base. One wrong bit among the 96 stored bits spoils all 256 bits of the line.

Compression and error correction meet in these lines. Flip bit 4 of the stored base and it becomes 00403A00, so all eight words come back 16 too low. One wrong bit among the 96 stored bits corrupts all 256 bits of the line, where in the uncompressed line it would have damaged one word. The COP design of Palframan, Kim and Lipasti [14] turns this around, storing the check bits of an error-correcting code in the space a compressed line frees.

12.Looking Ahead

The arithmetic code of aabaaa shows the same fragility at the scale of a message. Flipping the first bit of the tag 010110 gives 110110, which decodes to bbaaaa, and flipping the third gives 011110, which decodes to aacaba. Every string of six bits lies in the interval of some message, so neither error is detected. A good compressor removes exactly the redundancy that would have revealed an error.

Protecting compressed data means adding redundancy back, in a form a decoder can use. The source with p=0.1p = 0.1 compresses to h(0.1)=0.4690h(0.1) = 0.4690 bits per letter. The next chapter, Capacity and Shannon's Theorem, shows that a binary symmetric channel, which flips each bit independently with probability 0.1, carries up to 1−h(0.1)=0.53101 - h(0.1) = 0.5310 bits per use with an error probability as small as desired. Since 0.4690<0.53100.4690 < 0.5310, the source can cross that channel at one use per letter with vanishing error, and for lossy coding the condition becomes R(D)<CR(D) < C, where CC is the capacity. MacKay [15] treats arithmetic and dictionary coding at length, Cover and Thomas [11] give the theory of universal coding and of rate and distortion, and Lin and Costello [16] take up the channel codes of the coming Parts.

13.Worked Examples

14.Exercises

References

  1. [1]Witten, Ian H. and Neal, Radford M. and Cleary, John G. (1987). “Arithmetic Coding for Data Compression.” Communications of the ACM, 30(6), pp. 520–540. doi:10.1145/214762.214771
  2. [2]Rissanen, Jorma J. (1976). “Generalized Kraft Inequality and Arithmetic Coding.” IBM Journal of Research and Development, 20(3), pp. 198–203. doi:10.1147/rd.203.0198
  3. [3]Duda, Jarek (2013). “Asymmetric Numeral Systems: Entropy Coding Combining Speed of Huffman Coding with Compression Rate of Arithmetic Coding.” arXiv:1311.2540. https://arxiv.org/abs/1311.2540
  4. [4]Collet, Yann and Kucherawy, Murray S. (2021). “Zstandard Compression and the application/zstd Media Type.” RFC 8878. https://www.rfc-editor.org/rfc/rfc8878
  5. [5]Ziv, Jacob and Lempel, Abraham (1977). “A Universal Algorithm for Sequential Data Compression.” IEEE Transactions on Information Theory, 23(3), pp. 337–343. doi:10.1109/TIT.1977.1055714
  6. [6]Deutsch, L. Peter (1996). “DEFLATE Compressed Data Format Specification Version 1.3.” RFC 1951. https://www.rfc-editor.org/rfc/rfc1951
  7. [7]Ziv, Jacob and Lempel, Abraham (1978). “Compression of Individual Sequences via Variable-Rate Coding.” IEEE Transactions on Information Theory, 24(5), pp. 530–536. doi:10.1109/TIT.1978.1055934
  8. [8]Welch, Terry A. (1984). “A Technique for High-Performance Data Compression.” Computer, 17(6), pp. 8–19. doi:10.1109/MC.1984.1659158
  9. [9]Burrows, Michael and Wheeler, David J. (1994). “A Block-Sorting Lossless Data Compression Algorithm.” SRC Research Report 124, Digital Systems Research Center. https://web.archive.org/web/20230929095529/https://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-124.html
  10. [10]Bentley, Jon Louis and Sleator, Daniel D. and Tarjan, Robert E. and Wei, Victor K. (1986). “A Locally Adaptive Data Compression Scheme.” Communications of the ACM, 29(4), pp. 320–330. doi:10.1145/5684.5688
  11. [11]Cover, Thomas M. and Thomas, Joy A. (2006). “Elements of Information Theory.” 2nd edition. Wiley-Interscience. doi:10.1002/047174882X
  12. [12]Pekhimenko, Gennady and Seshadri, Vivek and Mutlu, Onur and Gibbons, Phillip B. and Kozuch, Michael A. and Mowry, Todd C. (2012). “Base-Delta-Immediate Compression: Practical Data Compression for On-Chip Caches.” In Proceedings of the 21st International Conference on Parallel Architectures and Compilation Techniques (PACT '12), pp. 377–388. doi:10.1145/2370816.2370870
  13. [13]Alameldeen, Alaa R. and Wood, David A. (2004). “Adaptive Cache Compression for High-Performance Processors.” In Proceedings of the 31st Annual International Symposium on Computer Architecture (ISCA '04), pp. 212–223. doi:10.1109/ISCA.2004.1310776
  14. [14]Palframan, David J. and Kim, Nam Sung and Lipasti, Mikko H. (2015). “COP: To Compress and Protect Main Memory.” In Proceedings of the 42nd Annual International Symposium on Computer Architecture (ISCA '15), pp. 682–693. doi:10.1145/2749469.2750377
  15. [15]MacKay, David J. C. (2003). “Information Theory, Inference, and Learning Algorithms.” Cambridge University Press. The author's site offers the whole book free to read on screen. https://www.inference.org.uk/itila/book.html
  16. [16]Lin, Shu and Costello, Jr., Daniel J. (2004). “Error Control Coding: Fundamentals and Applications.” 2nd edition. Pearson Prentice Hall.

— Anirban

Hardware–software at the seam where compilers meet microcode.

FeedbackBook modePaper
error-correction-codinginformation-and-channels