关于memcpy()的实现

       关于memcpy的实现很多同学可能一开始有和我一样的想法,就是将src的内容一个字节一个字节的复制到dest中。这样实现方法未尝不可,但是我们都知道CPU每次取存内存中的数据时,是按照地址总线大小来存取的。以32位的CPU为例子,CPU每次存取数据都是一次4个字节(32位)为最小单位存取的。但按照我们前面所说的方法实现的memcpy,在拷贝大于4字节内存时,CPU需要拷贝4次以上。但我们如果每次拷贝的时候都是按照CPU最大存取字节数来存取的话,在理想的情况是不是可以将拷贝的时间缩小为原来的1/4的时间。

此段代码是在openwrt源码中 build_dir/host/u-boot-2018.03/lib/string.c

void * memcpy(void *dest, const void *src, size_t count)
{
        unsigned long *dl = (unsigned long *)dest, *sl = (unsigned long *)src;
        char *d8, *s8;

        if (src == dest)
                return dest;
        
        /* 判断拷贝的目的地址与源地址是否以对齐 */
        /* while all data is aligned (common case), copy a word at a time */
        if ( (((ulong)dest | (ulong)src) & (sizeof(*dl) - 1)) == 0) {
                while (count >= sizeof(*dl)) {
                        *dl++ = *sl++;  /* 以unsigned long字节数来拷贝数据,在32位系统的下unsigned long为4字节,64位系统为8字节 */
                        count -= sizeof(*dl);
                }
        }

        /* 对剩下的不够按unsigned long字节数拿去的数据按char方式拷贝 */
        /* copy the reset one byte at a time */
        d8 = (char *)dl;
        s8 = (char *)sl;
        while (count--)
                *d8++ = *s8++;

        return dest;
}

 

上述代码对memcpy()进行了小优化使其在拷贝大内存的时候速度更快。但是若是dest和src的内存地址并没有对齐,与此同时我们刚好要拷贝的内存又相当的大,是不是就没有办法只能按字节慢慢拷贝了呢,答案当然不是。

我们来看glibc源码中是怎么实现这个函数的:string/memcpy.c

void *
memcpy (void *dstpp, const void *srcpp, size_t len)
{
  unsigned long int dstp = (long int) dstpp;
  unsigned long int srcp = (long int) srcpp;

  /* Copy from the beginning to the end.  */

  /* 若拷贝的字节数大于OP_T_THRES则使用下面的方法拷贝,我的glibc源码定义OP_T_THRES为16 */
  /* If there not too few bytes to copy, use word copy.  */
  if (len >= OP_T_THRES)
    {
      /* 计算出dest还差多少个字节对齐 */
      /* Copy just a few bytes to make DSTP aligned.  */
      len -= (-dstp) % OPSIZ;
      /* 以BYTE的方式拷贝使dest地址对齐 */
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);

      /* Copy whole pages from SRCP to DSTP by virtual address manipulation,
         as much as possible.  */
      /* 使用PAGE的方式进行拷贝 */
      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);

      /* Copy from SRCP to DSTP taking advantage of the known alignment of
         DSTP.  Number of bytes remaining is put in the third argument,
         i.e. in LEN.  This number may vary from machine to machine.  */
      /* 使用WORD的方式拷贝 */
      WORD_COPY_FWD (dstp, srcp, len, len);

      /* Fall out and copy the tail.  */
    }
  /* 若len

 

怎么样平时不起眼的memcpy也是内藏乾坤,最好的学习方法应该就是Linus大神说的RTFSC。

 

你可能感兴趣的:(关于memcpy()的实现)