前阵的时候领导提出要对memcpy做一个测试。也没有交代具体的细节。我就直接自已写了个常规memcpy,然后再把内核的__memcpy和glibc的memcpy整出来,对三者进行比较,不比不知道,一比吓一大跳。
测试结论是:
kernel和glibc的memcpy性能良好,glibc比内核的略高。自已写的就惨不忍睹了。
测试程序大概是先定义两块1G的全局内存。然后先用memset将其全置为1或者其它数。原理道上的兄弟都知道,linux内核很聪明,只要在你实际要用的到的时候才会产生缺页中断为你分配内存,否则只会在虚拟内存映射表中占个位置。
接下来就是循环拷贝了。
通过对每次拷贝的buffersize设置不同的值,发现不论是linux还是glibc中的memcpy效率都会有惊人的差异。
另外可以看来glibc的memcpy在用户空间几乎是完胜内核的memcpy的。我后面看了一下glibc的代码,发现glibc中memcpy对传入的长度做了精细的判断再调用不同的内嵌汇编代码。linux内核的memcpy相比glibc就简洁多了。
其实我们感兴趣的主要有两个:
1.内核和glibc的memcpy为什么比我们自已写的性能高这么多?
2.为什么buffersize不一样,性能差异会这么大?
OK,我们先来分析第一个问题.
请看。
常规的memcpy是:
inline int mymemcpy(char *dest,char *src,int len)
{
while(len--){
*(dest++) = *(src++);
}
return 0;
}
在不加优化选项的情况下反汇编后如下:
0x08048354 <+0>: push %ebp
0x08048355 <+1>: mov %esp,%ebp
0x08048357 <+3>: jmp 0x804836c <mymemcpy+24>
0x08048359 <+5>: mov 0xc(%ebp),%eax //内存操作
0x0804835c <+8>: movzbl (%eax),%edx //内存操作
0x0804835f <+11>: mov 0x8(%ebp),%eax //内存操作
0x08048362 <+14>: mov %dl,(%eax) //内存操作
0x08048364 <+16>: addl $0x1,0x8(%ebp) //内存操作
0x08048368 <+20>: addl $0x1,0xc(%ebp) //内存操作
0x0804836c <+24>: subl $0x1,0x10(%ebp) //内存操作
0x08048370 <+28>: cmpl $0xffffffff,0x10(%ebp) //内存操作
0x08048374 <+32>: jne 0x8048359 <mymemcpy+5> //立即数操作
0x08048376 <+34>: mov $0x0,%eax
0x0804837b <+39>: pop %ebp
0x0804837c <+40>: ret
内核的memcpy:
static void *__memcpy(void *to, const void *from, int n)
{
int d0, d1, d2;
asm volatile("rep ; movsl/n/t"
"movl %4,%%ecx/n/t"
"andl $3,%%ecx/n/t"