Fishwaldo opened a new pull request, #19857:
URL: https://github.com/apache/nuttx/pull/19857

   ## Summary
   
   Nine of the BSD string routines take their word path only when both pointers
   are already on a `long` boundary:
   
   ```c
   #define UNALIGNED(x, y) \
     (((long)(uintptr_t)(x) & (sizeof(long) - 1)) | \
      ((long)(uintptr_t)(y) & (sizeof(long) - 1)))
   ```
   
   That asks more than the loops need. They read and write at the same boundary
   in both operands, so what matters is whether the two *agree* about where a
   boundary falls, not whether either is already on one. A pair offset by the
   same amount is walked up to the boundary a byte at a time and handled a word
   at a time from there, because aligning one aligns the other.
   
   The union also holds far less often than the difference. For arbitrary
   pointers on a 64 bit target it is true about one time in 64 against one in
   eight, and the case it rejects, two strings carved out of the same buffer or 
a
   structure copied field by field, is a common one.
   
   `memccpy`, `memcmp`, `memcpy`, `stpcpy`, `stpncpy`, `strcmp`, `strcpy`,
   `strncmp`, `strncpy`. The five single pointer routines cannot benefit and are
   untouched.
   
   **No unaligned access is introduced.** Every word read and write is still on 
a
   boundary, so this is safe where a misaligned access faults or is emulated.
   
   ## Where this comes from
   
   @xiaoxiang781216 asked for exactly this in #19735: keep the general
   optimisation out of the arch specific code and put it in the BSD
   implementation. #19856 corrects the RISC-V assembly that has the same 
problem;
   this is the portable half, and it is the one that reaches every architecture.
   
   ## Measured
   
   EIC7700X, rv64 at 1.4 GHz, `CONFIG_ALLOW_BSD_COMPONENTS=y` with
   `CONFIG_LIBC_NEWLIB_OPTSPEED=y`, 4 KB operands, taken with the benchmark in
   apache/nuttx-apps#3706.
   
   Both pointers offset by one, the case the union rejects:
   
   ```
                before      after
     memcpy        381       2777 MB/s
     memcmp         40        330
     strcpy        621       1836
     stpcpy        551       1835
     strcmp         41        273
     strncmp        28        203
     strncpy       376       1658
     stpncpy       276       1646
     memccpy       545       1439
   ```
   
   Every one of those `before` figures is the byte loop. After the change each
   lands within a few percent of the same function's aligned rate, which is what
   says the prologue is doing what it should and nothing more.
   
   Both pointers aligned, where the new test costs an extra branch and nothing
   else:
   
   ```
                before      after
     memcpy       2885       2876 MB/s
     memcmp        354        335
     strcpy       1970       1957
     stpcpy       1906       1950
     strcmp        277        277
     strncmp       207        206
     strncpy      1776       1650
     stpncpy      1836       1644
     memccpy      1626       1434
   ```
   
   `strncpy`, `stpncpy` and `memccpy` lose 7 to 12 per cent there. Those three
   return from inside the prologue to handle padding or the stop character, 
which
   adds a branch to the aligned path as well. The rest are unchanged.
   
   Pointers that genuinely disagree are unaffected, as they must be: no single
   boundary serves both.
   
   ## This needs measuring on other hardware
   
   I can only speak for one part. The gain depends on how expensive a byte loop
   is relative to a word loop on a given core, and the loss on those three
   functions depends on branch prediction, so both numbers will move.
   
   **Please do not take QEMU numbers for this.** QEMU executes misaligned
   accesses natively at full speed and does not model the alignment behaviour of
   the part it emulates, so it flatters the code being replaced and hides what
   this is worth. It is fine for correctness and useless for this measurement.
   Every figure above is from silicon.
   
   If you have a board to hand, apache/nuttx-apps#3706 sweeps sizes against 
every
   source and destination alignment pair and reports MB/s and cycles per byte.
   
   ## Testing
   
   - Correctness: `testing/libc/arch_libc` under qemu rv64, all nineteen
     functions pass. `memccpy` and `stpncpy` had no coverage anywhere, so tests
     for both went into apache/nuttx-apps#3706 first; the padding, the stop
     character and the return values are what the prologue is easiest to get
     wrong on.
   - Throughput: rv64 silicon as above. Also measured on rv32 under QEMU with
     `-icount` and on x86_64 under KVM, both of which agree in direction, though
     for the reason above I would not put weight on the QEMU figures.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to