• 实现memcpy()函数及过程总结


    1.为什么会写memcpy

    在之前的应聘笔试上遇到一道笔试题,题目要求实现一个my_memcpy函数。函数原型:void * my_memcpy(void *dst, const void *src, int n);

    之前使用的内存拷贝函数是标准库memcpy函数,拿来就用,真没有对这个函数做过多了解。在网上查了一下,有好多关于memcpy函数优化的文章。

    在实现过程中了解的越多,往往实现起来越麻烦。还是先实现简单的memcpy函数。

    2.按字节(Byte)拷贝实现的memcpy

     1 void *my_memcpy_byte(void *dst, const void *src, int n)
     2 {
     3     if (dst == NULL || src == NULL || n <= 0)
     4         return NULL;
     5 
     6     char * pdst = (char *)dst;
     7     char * psrc = (char *)src;
     8 
     9     if (pdst > psrc && pdst < psrc + n)
    10     {
    11         pdst = pdst + n - 1;
    12         psrc = psrc + n - 1;
    13         while (n--)
    14             *pdst-- = *psrc--;
    15     }
    16     else
    17     {
    18         while (n--)
    19             *pdst++ = *psrc++;
    20     }
    21     return dst;
    22 }
        //20200104 看到评论,又看了下之前写的memcpy实现
        //按字节拷贝实现的memcpy没有问题
        //*pdst-- = *psrc--; 查了下运算符优先级(*,--)优先级相同,从右向左结合,psrc--是先使用,后减减
        //等价于*pdst = *psrc;psrc--;pdst--;

     这里要考虑写覆盖的情况

    3.按4字节拷贝实现的memcpy

     1 void *my_memcpy(void *dst, const void *src, int n)
     2 {
     3     if (dst == NULL || src == NULL || n <= 0)
     4         return NULL;
     5 
     6     int * pdst = (int *)dst;
     7     int * psrc = (int *)src;
     8     char *tmp1 = NULL;
     9     char *tmp2 = NULL;
    10     int c1 = n / 4;
    11     int c2 = n % 4;
    12 
    13     /*if (pdst > psrc && pdst < psrc + n) 这样判断有问题*/
    14     if (pdst > psrc && pdst < (char *)psrc + n)
    15     {
    16         tmp1 = (char *)pdst + n - 1;
    17         tmp2 = (char *)psrc + n - 1;
    18         while(c2--)
    19             *tmp1-- = *tmp2--;
    20         /*这样有问题,忘记字节偏移
    21         pdst = (int *)tmp1;
    22         psrc = (int *)tmp2;
    23         */
    24         tmp1++;tmp2++;
    25         pdst = (int *)tmp1;
    26         psrc = (int *)tmp2;
    27         pdst--;psrc--;
    28         while (c1--)
    29             *pdst-- = *psrc--;
    30     }
    31     else
    32     {
    33         while (c1--)
    34             *pdst++ = *psrc++;
    35
    36
    tmp1 = (char *)pdst; 37 tmp2 = (char *)psrc; 38 while (c2--) 39 *tmp1++ = *tmp2++; 40 } 41 return dst; 42 }
          //20200104 查看评论说四字节写覆盖拷贝问题,现在已修改
          //备注:void *dst, const void *src这两个参数是需要按4字节对齐的,如果本身不是4字节对齐,按4字节拷贝效率也会变低。

    这里还是考虑了写覆盖的代码。对比按字节拷贝,拷贝速度是提高不少。

    以上是针对笔试过程中写memcpy。

    4.如何优化memcpy

    高性能的memcpy与很多因数相关,与平台,处理器,编译器,具体拷贝情形等相关。

    VS2017中对C库的memcpy进行优化,glibc对memcpy也有优化

    参考:怎样写出一个更快的 memset/memcpy ?

    5.是否需要考虑内存对齐拷贝?

    内存读写效率影响之一:内存对齐

    参考:1.浅谈CPU内存访问要求对齐的原因     2.解析内存对齐

    如果src,dst的地址是不对齐的,读写效率变低。

    通过代码实现不对齐的拷贝,memcpy的实现会变得复杂,反而影响拷贝效率。

    这种不对齐情况我们可以预先避免,因为编译器在给我们分配空间时是按照内存对齐进行分配的

    6.根据拷贝数据大小进行优化

    1.多次调用memcpy,而每次拷贝数据大小Kb下的小拷贝

      这种情况下尽量减少分支预测,代码精简。

    2.拷贝Mb的memcpy实现

      这种情况影响拷贝效率主要在寻址上。

    参考:闲着没事测了一下memcpy

    7.总结

    memcpy需要根据情况优化,如 平台,处理器,拷贝大小。

  • 相关阅读:
    使用ar_receipt_api_pub.apply失误
    部门组织架构和岗位变更
    值集不能修改的原因
    ShopEx安装过程
    javascript二分法
    增加TableSpace
    刚毕业的大学生,你到底为什么被解雇掉了?(转)
    oracle EBS关闭窗口出现红条(进度/进展)
    分享一个jQuery的时间轴插件:TimergliderJS
    分享另外一个jQuery的超酷幻灯插件:jQuery Chop Slider 2.0
  • 原文地址:https://www.cnblogs.com/chuanfengzhang/p/8447251.html
Copyright © 2020-2023  润新知