tech_report.tex 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. \documentclass[a4paper]{article}
  2. \begin{document}
  3. \title{The rsync algorithm}
  4. \author{Andrew Tridgell \quad\quad Paul Mackerras\\
  5. Department of Computer Science \\
  6. Australian National University \\
  7. Canberra, ACT 0200, Australia}
  8. \maketitle
  9. \begin{abstract}
  10. This report presents an algorithm for updating a file on one machine
  11. to be identical to a file on another machine. We assume that the
  12. two machines are connected by a low-bandwidth high-latency
  13. bi-directional communications link. The algorithm identifies parts
  14. of the source file which are identical to some part of the
  15. destination file, and only sends those parts which cannot be matched
  16. in this way. Effectively, the algorithm computes a set of
  17. differences without having both files on the same machine. The
  18. algorithm works best when the files are similar, but will also
  19. function correctly and reasonably efficiently when the files are
  20. quite different.
  21. \end{abstract}
  22. \section{The problem}
  23. Imagine you have two files, $A$ and $B$, and you wish to update $B$ to be
  24. the same as $A$. The obvious method is to copy $A$ onto $B$.
  25. Now imagine that the two files are on machines connected by a slow
  26. communications link, for example a dialup IP link. If $A$ is large,
  27. copying $A$ onto $B$ will be slow. To make it faster you could
  28. compress $A$ before sending it, but that will usually only gain a
  29. factor of 2 to 4.
  30. Now assume that $A$ and $B$ are quite similar, perhaps both derived
  31. from the same original file. To really speed things up you would need
  32. to take advantage of this similarity. A common method is to send just
  33. the differences between $A$ and $B$ down the link and then use this
  34. list of differences to reconstruct the file.
  35. The problem is that the normal methods for creating a set of
  36. differences between two files rely on being able to read both files.
  37. Thus they require that both files are available beforehand at one end
  38. of the link. If they are not both available on the same machine,
  39. these algorithms cannot be used (once you had copied the file over,
  40. you wouldn't need the differences). This is the problem that rsync
  41. addresses.
  42. The rsync algorithm efficiently computes which parts of a source file
  43. match some part of an existing destination file. These parts need not
  44. be sent across the link; all that is needed is a reference to the part
  45. of the destination file. Only parts of the source file which are not
  46. matched in this way need to be sent verbatim. The receiver can then
  47. construct a copy of the source file using the references to parts of
  48. the existing destination file and the verbatim material.
  49. Trivially, the data sent to the receiver can be compressed using any
  50. of a range of common compression algorithms, for further speed
  51. improvements.
  52. \section{The rsync algorithm}
  53. Suppose we have two general purpose computers $\alpha$ and $\beta$.
  54. Computer $\alpha$ has access to a file $A$ and $\beta$ has access to
  55. file $B$, where $A$ and $B$ are ``similar''. There is a slow
  56. communications link between $\alpha$ and $\beta$.
  57. The rsync algorithm consists of the following steps:
  58. \begin{enumerate}
  59. \item $\beta$ splits the file $B$ into a series of non-overlapping
  60. fixed-sized blocks of size S bytes\footnote{We have found that
  61. values of S between 500 and 1000 are quite good for most purposes}.
  62. The last block may be shorter than $S$ bytes.
  63. \item For each of these blocks $\beta$ calculates two checksums:
  64. a weak ``rolling'' 32-bit checksum (described below) and a strong
  65. 128-bit MD4 checksum.
  66. \item $\beta$ sends these checksums to $\alpha$.
  67. \item $\alpha$ searches through $A$ to find all blocks of length $S$
  68. bytes (at any offset, not just multiples of $S$) that have the same
  69. weak and strong checksum as one of the blocks of $B$. This can be
  70. done in a single pass very quickly using a special property of the
  71. rolling checksum described below.
  72. \item $\alpha$ sends $\beta$ a sequence of instructions for
  73. constructing a copy of $A$. Each instruction is either a reference
  74. to a block of $B$, or literal data. Literal data is sent only for
  75. those sections of $A$ which did not match any of the blocks of $B$.
  76. \end{enumerate}
  77. The end result is that $\beta$ gets a copy of $A$, but only the pieces
  78. of $A$ that are not found in $B$ (plus a small amount of data for
  79. checksums and block indexes) are sent over the link. The algorithm
  80. also only requires one round trip, which minimises the impact of the
  81. link latency.
  82. The most important details of the algorithm are the rolling checksum
  83. and the associated multi-alternate search mechanism which allows the
  84. all-offsets checksum search to proceed very quickly. These will be
  85. discussed in greater detail below.
  86. \section{Rolling checksum}
  87. The weak rolling checksum used in the rsync algorithm needs to have
  88. the property that it is very cheap to calculate the checksum of a
  89. buffer $X_2 .. X_{n+1}$ given the checksum of buffer $X_1 .. X_n$ and
  90. the values of the bytes $X_1$ and $X_{n+1}$.
  91. The weak checksum algorithm we used in our implementation was inspired
  92. by Mark Adler's adler-32 checksum. Our checksum is defined by
  93. $$ a(k,l) = (\sum_{i=k}^l X_i) \bmod M $$
  94. $$ b(k,l) = (\sum_{i=k}^l (l-i+1)X_i) \bmod M $$
  95. $$ s(k,l) = a(k,l) + 2^{16} b(k,l) $$
  96. where $s(k,l)$ is the rolling checksum of the bytes $X_k \ldots X_l$.
  97. For simplicity and speed, we use $M = 2^{16}$.
  98. The important property of this checksum is that successive values can
  99. be computed very efficiently using the recurrence relations
  100. $$ a(k+1,l+1) = (a(k,l) - X_k + X_{l+1}) \bmod M $$
  101. $$ b(k+1,l+1) = (b(k,l) - (l-k+1) X_k + a(k+1,l+1)) \bmod M $$
  102. Thus the checksum can be calculated for blocks of length S at all
  103. possible offsets within a file in a ``rolling'' fashion, with very
  104. little computation at each point.
  105. Despite its simplicity, this checksum was found to be quite adequate as
  106. a first-level check for a match of two file blocks. We have found in
  107. practice that the probability of this checksum matching when the
  108. blocks are not equal is quite low. This is important because the much
  109. more expensive strong checksum must be calculated for each block where
  110. the weak checksum matches.
  111. \section{Checksum searching}
  112. Once $\alpha$ has received the list of checksums of the blocks of $B$,
  113. it must search $A$ for any blocks at any offset that match the
  114. checksum of some block of $B$. The basic strategy is to compute the
  115. 32-bit rolling checksum for a block of length $S$ starting at each
  116. byte of $A$ in turn, and for each checksum, search the list for a
  117. match. To do this our implementation uses a
  118. simple 3 level searching scheme.
  119. The first level uses a 16-bit hash of the 32-bit rolling checksum and
  120. a $2^{16}$ entry hash table. The list of checksum values (i.e., the
  121. checksums from the blocks of $B$) is sorted according to the 16-bit
  122. hash of the 32-bit rolling checksum. Each entry in the hash table
  123. points to the first element of the list for that hash value, or
  124. contains a null value if no element of the list has that hash value.
  125. At each offset in the file the 32-bit rolling checksum and its 16-bit
  126. hash are calculated. If the hash table entry for that hash value is
  127. not a null value, the second-level check is invoked.
  128. The second-level check involves scanning the sorted checksum list
  129. starting with the entry pointed to by the hash table entry, looking
  130. for an entry whose 32-bit rolling checksum matches the current value.
  131. The scan terminates when it reaches an entry whose 16-bit hash
  132. differs. If this search finds a match, the third-level check is
  133. invoked.
  134. The third-level check involves calculating the strong checksum for the
  135. current offset in the file and comparing it with the strong checksum
  136. value in the current list entry. If the two strong checksums match,
  137. we assume that we have found a block of $A$ which matches a block of
  138. $B$. In fact the blocks could be different, but the probability of
  139. this is microscopic, and in practice this is a reasonable assumption.
  140. When a match is found, $\alpha$ sends $\beta$ the data in $A$ between
  141. the current file offset and the end of the previous match, followed by
  142. the index of the block in $B$ that matched. This data is sent
  143. immediately a match is found, which allows us to overlap the
  144. communication with further computation.
  145. If no match is found at a given offset in the file, the rolling
  146. checksum is updated to the next offset and the search proceeds. If a
  147. match is found, the search is restarted at the end of the matched
  148. block. This strategy saves a considerable amount of computation for
  149. the common case where the two files are nearly identical. In
  150. addition, it would be a simple matter to encode the block indexes as
  151. runs, for the common case where a portion of $A$ matches a series of
  152. blocks of $B$ in order.
  153. \section{Pipelining}
  154. The above sections describe the process for constructing a copy of one
  155. file on a remote system. If we have a several files to copy, we can
  156. gain a considerable latency advantage by pipelining the process.
  157. This involves $\beta$ initiating two independent processes. One of the
  158. processes generates and sends the checksums to $\alpha$ while the
  159. other receives the difference information from $\alpha$ and
  160. reconstructs the files.
  161. If the communications link is buffered then these two processes can
  162. proceed independently and the link should be kept fully utilised in
  163. both directions for most of the time.
  164. \section{Results}
  165. To test the algorithm, tar files were created of the Linux kernel
  166. sources for two versions of the kernel. The two kernel versions were
  167. 1.99.10 and 2.0.0. These tar files are approximately 24MB in size and
  168. are separated by 5 released patch levels.
  169. Out of the 2441 files in the 1.99.10 release, 291 files had changed in
  170. the 2.0.0 release, 19 files had been removed and 25 files had been
  171. added.
  172. A ``diff'' of the two tar files using the standard GNU diff utility
  173. produced over 32 thousand lines of output totalling 2.1 MB.
  174. The following table shows the results for rsync between the two files
  175. with a varying block size.\footnote{All the tests in this section were
  176. carried out using rsync version 0.5}
  177. \vspace*{5mm}
  178. \begin{tabular}{|l|l|l|l|l|l|l|} \hline
  179. {\bf block} & {\bf matches} & {\bf tag} & {\bf false} & {\bf data} & {\bf written} & {\bf read} \\
  180. {\bf size} & & {\bf hits} & {\bf alarms} & & & \\ \hline \hline
  181. 300 & 64247 & 3817434 & 948 & 5312200 & 5629158 & 1632284 \\ \hline
  182. 500 & 46989 & 620013 & 64 & 1091900 & 1283906 & 979384 \\ \hline
  183. 700 & 33255 & 571970 & 22 & 1307800 & 1444346 & 699564 \\ \hline
  184. 900 & 25686 & 525058 & 24 & 1469500 & 1575438 & 544124 \\ \hline
  185. 1100 & 20848 & 496844 & 21 & 1654500 & 1740838 & 445204 \\ \hline
  186. \end{tabular}
  187. \vspace*{5mm}
  188. In each case, the CPU time taken was less than the
  189. time it takes to run ``diff'' on the two files.\footnote{The wall
  190. clock time was approximately 2 minutes per run on a 50 MHz SPARC 10
  191. running SunOS, using rsh over loopback for communication. GNU diff
  192. took about 4 minutes.}
  193. The columns in the table are as follows:
  194. \begin{description}
  195. \item [block size] The size in bytes of the checksummed blocks.
  196. \item [matches] The number of times a block of $B$ was found in $A$.
  197. \item [tag hits] The number of times the 16-bit hash of the rolling
  198. checksum matched a hash of one of the checksums from $B$.
  199. \item [false alarms] The number of times the 32-bit rolling checksum
  200. matched but the strong checksum didn't.
  201. \item [data] The amount of file data transferred verbatim, in bytes.
  202. \item [written] The total number of bytes written by $\alpha$,
  203. including protocol overheads. This is almost all file data.
  204. \item [read] The total number of bytes read by $\alpha$, including
  205. protocol overheads. This is almost all checksum information.
  206. \end{description}
  207. The results demonstrate that for block sizes above 300 bytes, only a
  208. small fraction (around 5\%) of the file was transferred. The amount
  209. transferred was also considerably less than the size of the diff file
  210. that would have been transferred if the diff/patch method of updating
  211. a remote file was used.
  212. The checksums themselves took up a considerable amount of space,
  213. although much less than the size of the data transferred in each
  214. case. Each pair of checksums consumes 20 bytes: 4 bytes for the
  215. rolling checksum plus 16 bytes for the 128-bit MD4 checksum.
  216. The number of false alarms was less than $1/1000$ of the number of
  217. true matches, indicating that the 32-bit rolling checksum is quite
  218. good at screening out false matches.
  219. The number of tag hits indicates that the second level of the
  220. checksum search algorithm was invoked about once every 50
  221. characters. This is quite high because the total number of blocks in
  222. the file is a large fraction of the size of the tag hash table. For
  223. smaller files we would expect the tag hit rate to be much closer to
  224. the number of matches. For extremely large files, we should probably
  225. increase the size of the hash table.
  226. The next table shows similar results for a much smaller set of files.
  227. In this case the files were not packed into a tar file first. Rather,
  228. rsync was invoked with an option to recursively descend the directory
  229. tree. The files used were from two source releases of another software
  230. package called Samba. The total source code size is 1.7 MB and the
  231. diff between the two releases is 4155 lines long totalling 120 kB.
  232. \vspace*{5mm}
  233. \begin{tabular}{|l|l|l|l|l|l|l|} \hline
  234. {\bf block} & {\bf matches} & {\bf tag} & {\bf false} & {\bf data} & {\bf written} & {\bf read} \\
  235. {\bf size} & & {\bf hits} & {\bf alarms} & & & \\ \hline \hline
  236. 300 & 3727 & 3899 & 0 & 129775 & 153999 & 83948 \\ \hline
  237. 500 & 2158 & 2325 & 0 & 171574 & 189330 & 50908 \\ \hline
  238. 700 & 1517 & 1649 & 0 & 195024 & 210144 & 36828 \\ \hline
  239. 900 & 1156 & 1281 & 0 & 222847 & 236471 & 29048 \\ \hline
  240. 1100 & 921 & 1049 & 0 & 250073 & 262725 & 23988 \\ \hline
  241. \end{tabular}
  242. \vspace*{5mm}
  243. \section{Availability}
  244. An implementation of rsync which provides a convenient interface
  245. similar to the common UNIX command rcp has been written and is
  246. available for download from http://rsync.samba.org/
  247. \end{document}