Linux中strcpy()与memcpy()效率的比较

优化程序性能时,后知后觉地才发现strcpy()很耗时,特别是与memcpy()相比较。因此查看了一下两个函数的Linux实现源码。

strlen()和strcpy()的实现:

  1. static inline size_t __kernel_strlen(const char *s)  
  2. {  
  3.     const char *sc;  
  4.   
  5.     for (sc = s; *sc++; )  
  6.         ;  
  7.     return sc - s - 1;  
  8. }  
  9.   
  10. static inline char *__kernel_strcpy(char *dest, const char *src)  
  11. {  
  12.     char *xdest = dest;  
  13.   
  14.     asm volatile ("\n"  
  15.         "1: move.b  (%1)+,(%0)+\n"  
  16.         "   jne 1b"  
  17.         : "+a" (dest), "+a" (src)  
  18.         : : "memory");  
  19.     return xdest;  
  20. }  

strlen()是C语言实现,逐字节取出字符串的内容,并判断是否为结束标志(0);strcpy()为了提高拷贝效率,使用了汇编语言来实现,基本作法也是逐字节拷贝,直到碰上字符串结束字符(0)。

而Linux内核源码中,可以找到多份不同的memcpy()实现。除了因为内嵌汇编因此必须针对不同处理器平台给出不同实现外,memcpy()还区分了32位系统和64位系统。以下以x86平台32位的一个实现为例:

  1. static __always_inline void *__memcpy(void *to, const void *from, size_t n)  
  2. {  
  3.     int d0, d1, d2;  
  4.     asm volatile("rep ; movsl\n\t"  
  5.              "movl %4,%%ecx\n\t"  
  6.              "andl $3,%%ecx\n\t"  
  7.              "jz 1f\n\t"  
  8.              "rep ; movsb\n\t"  
  9.              "1:"  
  10.              : "=&c" (d0), "=&D" (d1), "=&S" (d2)  
  11.              : "0" (n / 4), "g" (n), "1" ((long)to), "2" ((long)from)  
  12.              : "memory");  
  13.     return to;  
  14. }  

为了提高效率,memcpy()也采用了内嵌汇编的作法。但memcpy()充分利用了机器字长为32位的特性(32位系统,一次内存读/写可操作4字节的数据, 对于64位系统,则一次可操作8字节数据)。先按4字节一组(movsl)拷贝,共复制n/4次;对剩下的零头再逐字节拷贝。如果支持,memcpy()还可以使用了MMX/SSE指令增加一次操作中的字节数,进一步提高效率。

比较strcpy()和memcpy()的实现,就知道了对于长符串,为什么strcpy()会消耗数倍于memcpy()的时间。

测算性能用到的几个方法:
1. 多次操作(比如重复100次)累加后再求平均用时
2. x86体系中的RDTSC指令也常被用来获取精确时间(CPU时钟滴哒数),但在多核系统中,由于多核之间的时钟计数存在不同步以及CPU降频运行的可能,因此,Win/Linux平台上,现在已不推荐使用此指令进行精确计时,而代之以clock_gettime()(linux系统)或QueryPerformanceCounter()QueryPerformanceFrequency()(win系统)。

此条目发表在  C/C++Unix-like 分类目录,贴了   标签。将 固定链接加入收藏夹。

Linux中strcpy()与memcpy()效率的比较》有 1 条评论

  1. Cnfn  说:

    以前只是听说memcpy效率高,但从没有想到去看看它如何实现的,再次受教

Logo

更多推荐