使用SSE指令集优化memcpy

GCC 4.4.6 编译测试通过

# gcc fast_memcpy.c -o fast_memcpy

  1. #include <stdio.h>  
  2. #include <stdint.h>  
  3. /**  
  4.  * Copy 16 bytes from one location to another using optimised SSE  
  5.  * instructions. The locations should not overlap.  
  6.  *  
  7.  * @param s1  
  8.  *   Pointer to the destination of the data.  
  9.  * @param s2  
  10.  *   Pointer to the source data.  
  11.  */  
  12. static inline void  
  13. mov16(uint8_t *dst, const uint8_t *src)  
  14. {  
  15.     asm volatile ("movdqu (%[src]), %%xmm0\n\t"  
  16.               "movdqu %%xmm0, (%[dst])\n\t"  
  17.               :  
  18.               : [src] "r" (src),  
  19.             [dst] "r"(dst)  
  20.               : "xmm0", "memory");  
  21. }  
  22.   
  23. /**  
  24.  * Copy 32 bytes from one location to another using optimised SSE  
  25.  * instructions. The locations should not overlap.  
  26.  *  
  27.  * @param s1  
  28.  *   Pointer to the destination of the data.  
  29.  * @param s2  
  30.  *   Pointer to the source data.  
  31.  */  
  32. static inline void  
  33. mov32(uint8_t *dst, const uint8_t *src)  
  34. {  
  35.     asm volatile ("movdqu (%[src]), %%xmm0\n\t"  
  36.               "movdqu 16(%[src]), %%xmm1\n\t"  
  37.               "movdqu %%xmm0, (%[dst])\n\t"  
  38.               "movdqu %%xmm1, 16(%[dst])"  
  39.               :  
  40.               : [src] "r" (src),  
  41.             [dst] "r"(dst)  
  42.               : "xmm0", "xmm1", "memory");  
  43. }  
  44.   
  45. /**  
  46.  * Copy 48 bytes from one location to another using optimised SSE  
  47.  * instructions. The locations should not overlap.  
  48.  *  
  49.  * @param s1  
  50.  *   Pointer to the destination of the data.  
  51.  * @param s2  
  52.  *   Pointer to the source data.  
  53.  */  
  54. static inline void  
  55. mov48(uint8_t *dst, const uint8_t *src)  
  56. {  
  57.     asm volatile ("movdqu (%[src]), %%xmm0\n\t"  
  58.               "movdqu 16(%[src]), %%xmm1\n\t"  
  59.               "movdqu 32(%[src]), %%xmm2\n\t"  
  60.               "movdqu %%xmm0, (%[dst])\n\t"  
  61.               "movdqu %%xmm1, 16(%[dst])"  
  62.               "movdqu %%xmm2, 32(%[dst])"  
  63.               :  
  64.               : [src] "r" (src),  
  65.             [dst] "r"(dst)  
  66.               : "xmm0", "xmm1", "memory");  
  67. }  
  68.   
  69. /**  
  70.  * Copy 64 bytes from one location to another using optimised SSE  
  71.  * instructions. The locations should not overlap.  
  72.  *  
  73.  * @param s1  
  74.  *   Pointer to the destination of the data.  
  75.  * @param s2  
  76.  *   Pointer to the source data.  
  77.  */  
  78. static inline void  
  79. mov64(uint8_t *dst, const uint8_t *src)  
  80. {  
  81.     asm volatile ("movdqu (%[src]), %%xmm0\n\t"  
  82.               "movdqu 16(%[src]), %%xmm1\n\t"  
  83.               "movdqu 32(%[src]), %%xmm2\n\t"  
  84.               "movdqu 48(%[src]), %%xmm3\n\t"  
  85.               "movdqu %%xmm0, (%[dst])\n\t"  
  86.               "movdqu %%xmm1, 16(%[dst])\n\t"  
  87.               "movdqu %%xmm2, 32(%[dst])\n\t"  
  88.               "movdqu %%xmm3, 48(%[dst])"  
  89.               :  
  90.               : [src] "r" (src),  
  91.             [dst] "r"(dst)  
  92.               : "xmm0", "xmm1", "xmm2", "xmm3","memory");  
  93. }  
  94.   
  95. /**  
  96.  * Copy 128 bytes from one location to another using optimised SSE  
  97.  * instructions. The locations should not overlap.  
  98.  *  
  99.  * @param s1  
  100.  *   Pointer to the destination of the data.  
  101.  * @param s2  
  102.  *   Pointer to the source data.  
  103.  */  
  104. static inline void  
  105. mov128(uint8_t *dst, const uint8_t *src)  
  106. {  
  107.     asm volatile ("movdqu (%[src]), %%xmm0\n\t"  
  108.               "movdqu 16(%[src]), %%xmm1\n\t"  
  109.               "movdqu 32(%[src]), %%xmm2\n\t"  
  110.               "movdqu 48(%[src]), %%xmm3\n\t"  
  111.               "movdqu 64(%[src]), %%xmm4\n\t"  
  112.               "movdqu 80(%[src]), %%xmm5\n\t"  
  113.               "movdqu 96(%[src]), %%xmm6\n\t"  
  114.               "movdqu 112(%[src]), %%xmm7\n\t"  
  115.               "movdqu %%xmm0, (%[dst])\n\t"  
  116.               "movdqu %%xmm1, 16(%[dst])\n\t"  
  117.               "movdqu %%xmm2, 32(%[dst])\n\t"  
  118.               "movdqu %%xmm3, 48(%[dst])\n\t"  
  119.               "movdqu %%xmm4, 64(%[dst])\n\t"  
  120.               "movdqu %%xmm5, 80(%[dst])\n\t"  
  121.               "movdqu %%xmm6, 96(%[dst])\n\t"  
  122.               "movdqu %%xmm7, 112(%[dst])"  
  123.               :  
  124.               : [src] "r" (src),  
  125.             [dst] "r"(dst)  
  126.               : "xmm0", "xmm1", "xmm2", "xmm3",  
  127.             "xmm4", "xmm5", "xmm6", "xmm7", "memory");  
  128. }  
  129.   
  130. /**  
  131.  * Copy 256 bytes from one location to another using optimised SSE  
  132.  * instructions. The locations should not overlap.  
  133.  *  
  134.  * @param s1  
  135.  *   Pointer to the destination of the data.  
  136.  * @param s2  
  137.  *   Pointer to the source data.  
  138.  */  
  139. static inline void  
  140. mov256(uint8_t *dst, const uint8_t *src)  
  141. {  
  142.     /*  
  143.      * There are 16XMM registers, but this function does not use  
  144.      * them all so that it can still be compiled as 32bit  
  145.      * code. The performance increase was neglible if all 16  
  146.      * registers were used.  
  147.      */  
  148.     mov128(dst, src);  
  149.     mov128(dst + 128, src + 128);  
  150. }  
  151.   
  152.   
  153. /**  
  154.  * Copy bytes from one location to another. The locations should not overlap.  
  155.  *  
  156.  * @param s1  
  157.  *   Pointer to the destination of the data.  
  158.  * @param s2  
  159.  *   Pointer to the source data.  
  160.  * @param n  
  161.  *   Number of bytes to copy.  
  162.  * @return  
  163.  *   s1  
  164.  */  
  165. void *  
  166. fast_memcpy(void *s1, const void *s2, size_t n)  
  167. {  
  168.     uint8_t *dst = (uint8_t *)s1;  
  169.     const uint8_t *src = (const uint8_t *)s2;  
  170.   
  171.     /* We can't copy < 16 bytes using XMM registers so do it manually. */  
  172.     if (n < 16) {  
  173.         if (n & 0x01) {  
  174.             *dst = *src;  
  175.             dst += 1;  
  176.             src += 1;  
  177.         }  
  178.         if (n & 0x02) {  
  179.             *(uint16_t *)dst = *(const uint16_t *)src;  
  180.             dst += 2;  
  181.             src += 2;  
  182.         }  
  183.         if (n & 0x04) {  
  184.             *(uint32_t *)dst = *(const uint32_t *)src;  
  185.             dst += 4;  
  186.             src += 4;  
  187.         }  
  188.         if (n & 0x08) {  
  189.             *(uint64_t *)dst = *(const uint64_t *)src;  
  190.         }  
  191.         return dst;  
  192.     }  
  193.   
  194.     /* Special fast cases for <= 128 bytes */  
  195.     if (n <= 32) {  
  196.         mov16(dst, src);  
  197.         mov16(dst - 16 + n, src - 16 + n);  
  198.         return s1;  
  199.     }  
  200.     if (n <= 64) {  
  201.         mov32(dst, src);  
  202.         mov32(dst - 32 + n, src - 32 + n);  
  203.         return s1;  
  204.     }  
  205.     if (n <= 128) {  
  206.         mov64(dst, src);  
  207.         mov64(dst - 64 + n, src - 64 + n);  
  208.         return s1;  
  209.     }  
  210.   
  211.     /*  
  212.      * For large copies > 128 bytes. This combination of 256, 64 and 16 byte  
  213.      * copies was found to be faster than doing 128 and 32 byte copies as  
  214.      * well.  
  215.      */  
  216.     for (; n >= 256; n -= 256, dst += 256, src += 256) {  
  217.         mov256(dst, src);  
  218.     }  
  219.   
  220.     /*  
  221.      * We split the remaining bytes (which will be less than 256) into  
  222.      * 64byte (2^6) chunks.  
  223.      * Using incrementing integers in the case labels of a switch statement  
  224.      * enourages the compiler to use a jump table. To get incrementing  
  225.      * integers, we shift the 2 relevant bits to the LSB position to first  
  226.      * get decrementing integers, and then subtract.  
  227.      */  
  228.     switch (3 - (n >> 6)) {  
  229.     case 0x00:  
  230.         mov64(dst, src);  
  231.         n -= 64;  
  232.         dst += 64;  
  233.         src += 64;       /* fallthrough */  
  234.     case 0x01:  
  235.         mov64(dst, src);  
  236.         n -= 64;  
  237.         dst += 64;  
  238.         src += 64;       /* fallthrough */  
  239.     case 0x02:  
  240.         mov64(dst, src);  
  241.         n -= 64;  
  242.         dst += 64;  
  243.         src += 64;       /* fallthrough */  
  244.     default:  
  245.         ;  
  246.     }  
  247.   
  248.     /*  
  249.      * We split the remaining bytes (which will be less than 64) into  
  250.      * 16byte (2^4) chunks, using the same switch structure as above.  
  251.      */  
  252.     switch (3 - (n >> 4)) {  
  253.     case 0x00:  
  254.         mov16(dst, src);  
  255.         n -= 16;  
  256.         dst += 16;  
  257.         src += 16;       /* fallthrough */  
  258.     case 0x01:  
  259.         mov16(dst, src);  
  260.         n -= 16;  
  261.         dst += 16;  
  262.         src += 16;       /* fallthrough */  
  263.     case 0x02:  
  264.         mov16(dst, src);  
  265.         n -= 16;  
  266.         dst += 16;  
  267.         src += 16;       /* fallthrough */  
  268.     default:  
  269.         ;  
  270.     }  
  271.   
  272.     /* Copy any remaining bytes, without going beyond end of buffers */  
  273.     if (n != 0) {  
  274.         mov16(dst - 16 + n, src - 16 + n);  
  275.     }  
  276.     return s1;  
  277. }  
  278.   
  279. uint8_t src[1024] = {0};  
  280. uint8_t dst[1024] = {0};  
  281.   
  282. int main(int argc, char **argv)  
  283. {  
  284.     fast_memcpy(dst, src, 1024);  
  285.     return 0;  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值