void* memcpy(void* src, void* dest, size_t len)转载

void* memcpy(void* src, void* dest, size_t len)

C++
专栏收录该内容
8 篇文章
0 订阅


关键重点考虑:防止dest 地址和 src 地址 重合

  • 按字节(Byte)拷贝实现的memcpy
void *my_memcpy_byte(void *dst, const void *src, int n)
{
	if (dst == NULL || src == NULL || n <= 0)
		return NULL;
	 
	char * pdst = (char *)dst;
	char * psrc = (char *)src;
	
	if (pdst > psrc && pdst < psrc + n)
	{
		pdst = pdst + n - 1;
		psrc = psrc + n - 1;
		while (n--)
			*pdst-- = *psrc--;
	}
	else 
	{
		while (n--)
			*pdst++ = *psrc++;
	}
	return dst;
}

在上面按字节拷贝中考虑了拷贝覆盖,连续的一段空间存放数据是从低地址到高地址进行存放。先从源地址读出数据,然后写入到目的地址空间中。目的空间的起始地址如果在源数据空间之内就会出现内存覆盖的情况。 这种情况先从尾部拷贝,避免覆盖数据,不过这种情况也会破坏src空间数据,在src前使用了const关键字,也就是空间只读,在函数内部不修改src空间数据。

重写memcpy比自带快,是因为自带的函数会涉及到各种类型转换,并不是4字节 8字节之间的比例关系


  • 按4字节拷贝
void *my_memcpy(void *dst, const void *src, int n)
{
	if (dst == NULL || src == NULL || n <= 0)
		return NULL;
	 
	int * pdst = (int *)dst;
	int * psrc = (int *)src;
	
	char *tmp1 = NULL;
	char *tmp2 = NULL;
	
	int c1 = n / 4;
	int c2 = n % 4;
	
	if (pdst > psrc && pdst < psrc + n)
	{
		tmp1 = (char *)pdst + n - 1;
		tmp2 = (char *)psrc + n - 1;
		while(c2--)
		*tmp1-- = *tmp2--;
		pdst = (int *)tmp1;
		psrc = (int *)tmp2;
		while (c1--)
			*pdst-- = *psrc--;
	}
	else
	{
		while (c1--)
		*pdst++ = *psrc++;
		tmp1 = (char *)pdst;
		tmp2 = (char *)psrc;
		while (c2--)
			*tmp1++ = *tmp2++;
	}
	return dst; 
}
}

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


  • 如何优化memcpy

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

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

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


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

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

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

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

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

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


  • 根据拷贝数据大小进行优化

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

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

2.拷贝Mb的memcpy实现

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

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


  • 总结

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

 

 

THE END
分享
二维码
< <上一篇
下一篇>>
---->