home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / utils / squsq / history.doc < prev    next >
Text File  |  1994-07-13  |  8KB  |  186 lines

  1. Development History Of a File Compression Utility
  2.           by   Richard Greenlaw
  3.                251 Colony Ct.
  4.                Gahanna, Ohio 43230
  5.  
  6.                May 18, 1981
  7.                Revised August 29, 1981
  8.  
  9.  
  10. Introduction:
  11.  
  12. The file compression system consists of two programs, SQ and 
  13. USQ,  meaning squeeze and unsqueeze. They are written in the 
  14. C language for the BDS C compiler.  The executable files are 
  15. SQ.COM and USQ.COM,  which are self-sufficient to run  under 
  16. the CP/M operating system and consist of 8080 machine code.
  17.  
  18. SQ.COM is compiled from files SQ.C,  SQDIO.C,  TR1.C, TR2.C, 
  19. IO.C,  SQ.H,  DIO.H and SQCOM.H.   USQ.COM is compiled  from 
  20. files USQ.C,  USQDIO.C,  UTR.C,  USQ.H and (again) DIO.H and 
  21. SQCOM.H.  Both  COM  files  also  include  standard  library 
  22. functions and the BDS-C run-time package.
  23.  
  24. SQDIO.C  and  USQDIO.H  provide  i/o  redirection  and  pipe 
  25. simulation  and  are  just the BDS dio  package  renamed  to 
  26. produce  distinct  CRL files corresponding to the  different 
  27. addresses   of  external  variables  with  which  they   are 
  28. compiled. 
  29.  
  30. The  SQ  program  builds a squeezed  file  by  applying  two 
  31. transformations:
  32.  
  33. First, byte values which are repeated consecutively three or 
  34. more  times  are  reduced  to  the  value,   the  token  DLE 
  35. (delimiter), and a count. The penalty is that occurrances of 
  36. DLE are encoded as DLE, zero.
  37.  
  38. Second,  the  Huffman algorithm encodes each resulting  byte 
  39. value  or  endfile as a bit string having  length  inversely 
  40. proportional  to  its frequency of  occurrance.  This  is  a 
  41. complex process requiring reading the input file twice.
  42.  
  43. The  squeezed file contains various information to allow the 
  44. USQ  program  to decode it and recreate  the  original  file 
  45. exactly.
  46.  
  47. The environment:
  48.  
  49. The  programs  should be nearly portable.  The  CP/M  system 
  50. actually used is single user 2MHz 8080 without interrupts.
  51.  
  52. The  BDS  C compiler supports a subset of  C.  It  does  not 
  53. support  register variables,  long integers or floats.  That 
  54. leads  to  complexity  in  collecting  and  processing   the 
  55. frequencies  of  occurance of the various byte values  being 
  56. encoded.
  57.  
  58. Outline of SQ
  59.  
  60. The  interesting  work begins in function  squeeze  in  file 
  61. SQ.C.  In the first pass,  init_huff in file TR2.C reads the 
  62. input  through  the first encoder,  getcnr,  in file  TR1.C, 
  63. collects the frequency distribution and builds the  decoding 
  64. and encoding structures.  Then wrt_head in file TR2.C writes 
  65. control information and the decoding structure to the output 
  66. file.
  67.  
  68. In  the  second pass,  encoded bytes consisting of bits  and 
  69. pieces  of bit strings are generated by function gethuff  in 
  70. file TR2.C and are simply written to the output by squeeze.
  71.  
  72. Development History of SQ
  73.  
  74. There  have  been seven operational pre-release versions  of 
  75. SQ.  The  motive  for  change in  each  case  was  primarily 
  76. increased  execution  speed,  although the  conveniences  of 
  77. operating  on lists of files,  automatic name generation for 
  78. squeezed files,  and output drive specifiers were also added 
  79. in the later versions.
  80.  
  81. Early  versions called the following chain of  functions  to 
  82. get  a  byte of  encoded  data:  gethuff,  getbit,  get_cnr, 
  83. getc_crc and getc. It wrote through putce and putc. That's a 
  84. lot  of function calling.  In addittion,  gethuff and getbit 
  85. were passed pointers to functions to identify get_cnr.
  86.  
  87. Actually,  those versions used a dummy function for get_cnr, 
  88. the  repeated value encoder,  although the actual  code  was 
  89. present.  This was to simplify debugging and because USQ did 
  90. not yet have the inverse of that translation.
  91.  
  92. The  benchmark  for comparisons was not  consistent  because 
  93. files were lost at two points. In effect, the current SQ.COM 
  94. squeezed itself!  It typically acheived 6-7% compression  on 
  95. a  machine code file of 8K to 10K bytes.  Of course  machine 
  96. code  is not a practical case,  but it is a rugged  workout. 
  97. Text  files  are  compressed 33% to  46%  depending  on  the 
  98. richness and distribution of the alphabet.
  99.  
  100. V0,  for  which listings have not survived,  took 5:10 (five 
  101. minutes, 10 seconds) to squeeze itself! This was improved to 
  102. 4:23 by the optimizer option of the compiler,  which  simply 
  103. generates  in-line code rather than subroutine calls for all 
  104. local  and  external  variable  accesses.   It  was  further 
  105. improved  to 4:18 (and restored to its original  length)  by 
  106. the  -e  compiler option which specifies the origin  of  the 
  107. external variable area to allow direct addressing.  (The BDS 
  108. linker resolves only function names - externals are actually 
  109. like  FORTRAN COMMON and are normally accessed relative to a 
  110. pointer kept in RAM!).
  111.  
  112. Subsequent  improvements  came mostly from recoding the  key 
  113. routine.  Copies  of  gethuff  and its  partner  getbit  are 
  114. attached  for  versions  V1  through  V6  and  the  complete 
  115. listings (20 pages) for V6 are included. 
  116.  
  117. In  V0 through V2,  gethuff forms an output byte by  calling 
  118. getbit eight times and packing the bits. This is the obvious 
  119. method  because  the Huffman translation  produces  variable 
  120. length bit strings, not a byte for a byte.
  121.  
  122. V1 introduced the variable codebyte to the getbit  function. 
  123. It  was  rotated  each  time a  bit  was  removed,  so  that 
  124. subsequent calls had to shift it only one bit position. This 
  125. involved considerable change. Timing is uncertain now.
  126.  
  127. V2  continued to improve the getbit function by  customizing 
  128. the  three  basic cases and providing seperate returns  from 
  129. each to avoid unnecessary work.
  130.  
  131. The  changes  of  V1 and V2 reduced  run  time  to  1:41,  a 
  132. whopping 61% reduction!
  133.  
  134. V3 incooperated getbit into gethuff.  This wasn't  difficult 
  135. because  getbit was called only once by gethuff.  It ran  in 
  136. 1:27 (on a slightly smaller file), another 14% reduction.
  137.  
  138. V4  removed the pointers to functions mentioned earlier  and 
  139. substituted  direct calls.  However,  at this point the real 
  140. translation for repeated values was enabled.  The net result 
  141. was a slight loss of ground to 1:30, but more productive work.
  142.  
  143. V0  through  V4  worked from Huffman  code  bit  strings  of 
  144. indefinite  length  accessed through an array  of  pointers. 
  145. Each  string  was  byte alligned (unlike the  final  encoded 
  146. data).
  147.  
  148. V5  was a complete redesign of the storage and retreival  of 
  149. the  array  of  code strings.  I had  finally  succeeded  in 
  150. proving* that the maximum length code string would fit in the 
  151. same space as the sum of all frequency counts, so scaling in 
  152. init_huff  was made more rigorous to fit them into  unsigned 
  153. integers (16 bits).
  154.  
  155. * The proof was proven wrong in practice at least for the first
  156. implementation of the algorithm. Sq 1.5 (8/29/81) tries harder
  157. to generate codes no longer than it can handle (16 bits)
  158. and if it fails at this it fudges the counts and tries
  159. again.
  160.  
  161. This  redesign paved the way for a relatively simple  method 
  162. of  processing  the  code strings several bits  at  a  time, 
  163. rather  than singly in an eight pass loop to form an  output 
  164. byte.
  165.  
  166. At  this  this point the fancy file name  processing,  etc., 
  167. were added, increasing the size of SQ.COM from 7680 bytes to 
  168. 10,112  bytes,  an increase of 32% in the work performed  by 
  169. the "benchmark".  V5 ran in 1:40,  which scales to  1:16,  a 
  170. reduction of 16%. In a second variant, changing the variable 
  171. cbitsrem to a char from an integer saved another 5%.
  172.  
  173. V6 restructures the gethuff of V5,  replacing the while loop 
  174. with  a  custom (goto) loop with the exit  condition  tested 
  175. only in a special case. The two basic cases also do only the 
  176. work necessary to their cases.  Also,  squeeze in SQ.C calls 
  177. putc  directly  and  does its own check for  write  failure, 
  178. saving one layer of function calls.  It ran in 1:29  (scales 
  179. to 1:08), a reduction of 6% from the second variant of V5.
  180.  
  181. The  overall performance improvement ratio,  scaled for  the 
  182. one  major change in the benchmark workload (but not  taking 
  183. credit  for the enabling of the repeated character encoding) 
  184. was  about  4.5  :  1,  or a  reduction  of  78%.  The  true 
  185. improvement was probably a factor of 5.
  186.