C++性能优化篇:使用memcpy替代for循环

引言

最近发现项目代码中有一个对性能影响极大的for循环,该循环在处理2GB数据的时候,执行时间超长,导致程序直接卡死。因为最初没有涉及到大数据处理的场景,所以其对性能的影响是可以容忍的,但是最近开发了新的功能,需要处理大数据(GB),也用到了此处的代码,所以耗时的影响极其明显。

问题分析

先看如下示例代码:

class SampleData {
public:
	void setData(char* data) {
		int i = 0;
		int word = _width / 32;
		int n = word * 32;
		for (; i < n; i += 32) {
			process(_data, data, i, 32);
		}
		process(_data, data, i, _width - n);
	}
private:
	int _width;
	char* _data;
};

问题就出在setData这个方法中,其中_width表示比特数,process方法按照比特位处理数据,并填充到_data中,经过分析发现,当process处理32位数据,且起始位置和32对齐时,等价于memcpy(_data, data, 4),因此上述循环完全可以避免,改成如下形式:

void setData(char* data) {
	int word = _width / 32;
	int n = word * 32;
	memcpy(_data, data, word * 4);
	process(_data, data, n, _width - n);
}

改完之后,再处理大数据时,不再出现卡死现象了。完美解决!

memcpy源码分析

秉着知其然知其所以然的态度,我又分析了下memcpy源码,他究竟是如何实现高性能字节数据的拷贝呢?首先看下其函数原型:

void *memcpy(void *dstpp, const void *srcpp, size_t len)
  • dstpp表示复制的目的指针
  • srcpp表示要被复制的源数据指针
  • len表示复制的字节数

其源码如下:

#ifndef MEMCPY
#define MEMCPY memcpy
#endif

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.  */

  /* If there not too few bytes to copy, use word copy.  */
  // 如果有足够的数据(大于OP_T_THRES,其值一般为16或者8),就使用效率更高的拷贝(页拷贝、字拷贝)
  if (len >= OP_T_THRES)
    {
      /* Copy just a few bytes to make DSTP aligned.  */
      // (-dstp) % OPSIZ是srcpp到最近的内存对齐地址的距离
      len -= (-dstp) % OPSIZ;
      // 前面没有对齐的内存使用字节拷贝
      BYTE_COPY_FWD (dstp, srcp, (-dstp) % OPSIZ);

      // 有可能使用页拷贝,此效率最高
      PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len);

      // 不足一页的数据,使用字拷贝,如64位系统,一次拷贝8字节
      WORD_COPY_FWD (dstp, srcp, len, len);

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

  /* There are just a few bytes to copy.  Use byte memory operations.  */
  // 尾部剩余的不足一个字的数据,按照字节拷贝
  BYTE_COPY_FWD (dstp, srcp, len);

  return dstpp;
}
libc_hidden_builtin_def (MEMCPY)

从源码不难看出,其提升性能的方式主要在于:

  1. 进行内存对齐,内存对齐有利于提高cpu从内存取数据的效率。
  2. 一次拷贝足够多的数据,可以减少拷贝次数,从而也能提高性能。此处拷贝足够多的数据,主要依赖于指令集能够支持什么样的拷贝指令,如在x86架构中,常见的拷贝指令有movesb、movesw、movesd、movesq,而字拷贝就可以使用movesq指令。
  3. 相较于for循环,可以使用rep movesq指令,并配合相关的寄存器,就可以实现循环拷贝,比for循环使用了更少的指令,指令越少效率也就越高。

总结

  1. 在做内存数据拷贝的时候,应减少循环,尤其是嵌套循环,多使用memcpy,这样既容易理解,又可以提高程序性能,何乐而不为呢?
  2. 还有一种提升内存拷贝性能的方式,就是使用并行拷贝的方式。在编写程序的时候,可以考虑使用多个线程,每个线程拷贝其中一段内存。
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值