home *** CD-ROM | disk | FTP | other *** search
/ Serving the Web / ServingTheWeb1995.disc1of1.iso / linux / slacksrce / d / libc / libc-4.6 / libc-4 / libc-linux / sysdeps / i386 / README.string < prev    next >
Encoding:
Text File  |  1994-11-28  |  9.7 KB  |  227 lines

  1.   String functions that meet the needs of i[45]86 
  2.   November, 1994
  3.   Ulrich Drepper <drepper@ira.uka.de>
  4.  
  5.  
  6. Torbjorn Granlund made in 1991 a great job while he wrote the
  7. assembler versions of nearly all the essential strings functions in
  8. the GNU libc.  Most are written in assembler for speed in the features
  9. of the processor are used for better speed.
  10.  
  11. At that time the i386 was the CPU one found.  In the development for the
  12. i486 Intel manage to implement the most important assembler instructions
  13. to use only one clock cycle.  But this could not be done for all instruc-
  14. tions.  So some of them now use the same or a bigger number of cycles.
  15. That's the way the CISC CPU get closer to RISC.
  16.  
  17. Among those instructions which suffer from speedup are the "unimportant"
  18. string instructions: lods, stos, movs, scas, cmps, ins, and outs.
  19. Formerly very fast in comparison with the two or three instructions they
  20. substitute now they take longer.  E.g.
  21.  
  22.               lodsl  movl (%esi),%eax         stosl  movl %eax,(%edi)
  23.                      addl $4,%esi                    addl $4,%edi
  24.  
  25.  i386          5        4 + 2                   4        2 + 2
  26.  
  27.  i486          5        1 + 1                   5        1 + 1
  28.  
  29.  
  30. The only situation where the string instructions can still compete is
  31. when they are compined with 'rep' prefix and can process 32 bits in one
  32. step.  This is the case for functions like 'memset', 'memcpy' etc.  You
  33. will not find these functions in the collection.
  34.  
  35.  
  36. In the process of writing this functions I found several more techniques
  37. that can be used when breaking the string instructions in the sequence.
  38. For understanding this one must know that all kind of jumps (transfer of
  39. control) is expensive.  The main goal should be to keep the main path of
  40. execution free of jumps.  Of course we need loops but with some tricks we
  41. can get more power out of a simple loop.
  42.  
  43.  
  44. Some optimization techniques
  45.  
  46. This is not intend to be a course in assembler programming but just a
  47. collection of the experiences I made.  Perhaps somebody doing work on
  48. further functions (or even myself) want to read this instead of
  49. re-invent the wheel.
  50.  
  51. 0.  (As said above)  Optimize the path of execution to use as little
  52.     _TAKEN_ jumps as possible.
  53.  
  54.     In conditional jumps the main path should follow the way of a not
  55.     fulfilled condition. 
  56.  
  57. 1.  Unroll loops.
  58.  
  59.     The contents of loops should be unrolled to have the loop check to
  60.     occure not every step.  A good number I found is 4: nearly all the
  61.     functions I wrote have a four times unfolded loop.
  62.  
  63.     To achieve this for directly bounded loops (such as in strncmp,
  64.     bound by the length parameter) one have to do a check for the
  65.     whole loop.  This leads to some extra code which can process the
  66.     remaining bytes but this is limited to a small range.  E.g. the
  67.     loop processes 16 bytes each round one has to write a tail code
  68.     which can process 0 to 15 bytes.  This number is so small that one
  69.     need not process it in a loop but instead write a straight code.
  70.  
  71. 2.  Use the registers which are used for register variables only if
  72.     necessary. 
  73.  
  74.     The binary format specification for a.out and ELF on i386 CPUs
  75.     says that the registers %ebx, %edi, and %esi must not be changed
  76.     by a function (they are 'callee-save-registers').  A saving is
  77.     generally done by a push/pop pair.  This costs 5 cycles.  Not much
  78.     but we are looking for every cycle :).
  79.  
  80.     Before using tempories on the stack there is another possibility.
  81.     Most those functions are tail-functions.  I.e. they won't call any
  82.     other functions.  Therefor the base register is not needed.  This
  83.     way one has another register available.  (Caution: you won't be
  84.     able to use a debugger anymore if you use this technique.)
  85.  
  86. 3.  Exploit the adressing modes of the i386 CPUs
  87.  
  88.     The processor has a rather complex general adressing mode:
  89.  
  90.            offset + base register + index register * index factor
  91.  
  92.     where   offset           is a constant
  93.             base register    is a general register
  94.             index register   another general register
  95.             index factor     is 1, 2, 4, or 8
  96.  
  97.     One possible use is found in strcmp.  We have to process two
  98.     strings and therefor have to deal with two pointers.  But these
  99.     pointers are NOT unrelated.  If we used one pointer and a constant,
  100.     but only known at runtime, offset we can write loops simpler.  An
  101.     example:
  102.  
  103.                str1 =>  %edi           str2 => %esi
  104.  
  105.  
  106.          normal code                        optimized code    
  107.  
  108.                                              subl %esi,%edi
  109.  
  110.       L:   mov (%edi),...               L:   mov (%edi,%esi),...
  111.            mov (%esi),...                    mov (%esi),
  112.  
  113.            ...                               ...
  114.  
  115.            add $x,%edi                       add $x,%esi
  116.            add $x,%esi
  117.  
  118.            jmp L                             jmp L
  119.  
  120.     The CPU is able to compute the adrress (most of the time) in one
  121.     cycle.
  122.  
  123.  
  124. 4.  Use 32 bit for processing
  125.     (This is what gains the most speedup !!!)
  126.  
  127.     The nature of the string functions generally appears to be only
  128.     solvable by processing byte by byte.  One has to match a character
  129.     and also look for the end of the string (NULL char).
  130.  
  131.     But this not true.  Basing on an algorithm I found in the C
  132.     version of 'memchr' by Roland McGrath based on a work of (again)
  133.     Torbjorn Granlund one has an efficient possiblity of matching a
  134.     specific character out of a 32-bit word.  I don't describe it here
  135.     (look into the source code of the assembler version of 'memchr').
  136.     The only thing to say here is that it can be implemented with very
  137.     cheap assembler instruction.
  138.  
  139.     (Unfortunately the test is only a necessary condition but not
  140.      sufficient.  If you look through the code you will known what I
  141.      mean.)
  142.  
  143.     But does this extra arithmetic is it worth?  Is a simple
  144.     comparison not of similar or greater speed?
  145.  
  146.     With a simple word: no.  An extreme example: 'strlen'.  One could
  147.     think of a naive implementation, perhaps with an unfolded loop.
  148.     But we have one comparison and one conditional jump for each byte.
  149.     With this nice little trick we have some arithmetic and two
  150.     conditionals for each 32-bit word.  That's worth it.
  151.  
  152.  
  153.     But there must be some problems.  The world would not wait so long
  154.     with the invention if the gain is so big.  And in fact: there is a
  155.     problem.
  156.  
  157.     The address space in modern OS is a virtual one.  One effect of
  158.     this is that memory access to some areas of the address space is
  159.     illegal.  If we now process string which is located at the top of
  160.     a valid area and an 32-bit memory access crosses the boundary to
  161.     the not mapped area we get a SEGFAULT.  But what to do?  We cannot
  162.     check before every memory access if it is valid.  This would
  163.     nullify the gains.
  164.  
  165.     The solution comes if we put some knowledge about the environment
  166.     in the algorithm.  The MMU of the i386 CPU is only able to map
  167.     memory areas on 4096-bytes boundaries (I assume the big grainity
  168.     which is found in all interesting OS).  If we now align all memory
  169.     access on a 4-byte boundary we can never cross the line between to
  170.     areas with one memory access.  If we access an illegal address
  171.     (perhaps because of a non-terminated string) the simple algorithm
  172.     would make this, too.  The behaviour is identical!  (Another
  173.     profit from aligned memory access is that it is faster.)
  174.  
  175.     You might say that this sounds nice but if you have two strings to
  176.     process you cannot assure to align them both simultaneously.  And
  177.     you are right.  This was one of the biggest problems I come over
  178.     but if you look through the code of 'strcmp' you will note that
  179.     there is a solution.
  180.  
  181. NOTE NOTE NOTE:  PLEASE check it out !!!!!!!!!!
  182.  
  183.     One can identify two big areas of memory where the 32 bit access
  184.     is valid:  The normal data space upto the '___brk_addr' and the
  185.     stack (from %esp upto 0xc0000000).  At the beginning of 'strcmp'
  186.     and 'strncmp' we check if both pointers fall into one of these
  187.     areas.  If not we use the simple byte-by-byte algorithm.
  188.  
  189.     In the other case there are three possibilities:
  190.     a) both pointers are in the data areas
  191.  
  192.        Here it is sufficient to align the bigger pointer because it is
  193.        the only one which could cause a boundary crossing.
  194.  
  195.     b) both pointers are in the stack area
  196.  
  197.        We need not do any special action.  The first illegal address
  198.        is 0xc000000.  But at the top of the stack there is (at least)
  199.        the return address for the 'main' function and its parameter
  200.        pointers.  Every access in this area can only be a fault and
  201.        perhaps a resulting SEGFAULT will help to debug.
  202.        (If somebody says that this is not right we also here can align
  203.         the bigger pointer.)
  204.  
  205.     c) one pointer in data the other in stack area
  206.  
  207.        As one can guess we have to align the pointer in the data area.
  208.        This works with the same arguments as above.
  209.  
  210.  
  211. 5.  Align loops.
  212.  
  213.     It is important to align the main loop according to the needs of
  214.     the processor.  The i386 only needs a 4-byte alignment but the
  215.     i486 a 16-byte alignment (no info about i586).  I use the generic
  216.     macro 'ALIGN' which is defined in 'asm-ops.h'.
  217.  
  218.     In some files you find another optimization with this alignment.
  219.     The aligning is done by padding with a fill-instruction: NOP.
  220.     This cannot be executed for free, it costs on i386 3 cycles and on
  221.     i486 1 cycle.  Many functions have a jump to an instruction
  222.     immediately in front of the loop.  If we now arrange the code that
  223.     the instructions are placed behind the NOP many times the NOPs
  224.     need not be executed.  Please look for a conrete example.  It is
  225.     a rather ugly method but the assembler does not have a directive
  226.     like 'align to XXX but place the NOP before YYY'.
  227.