先上结论:
- 二者不一定谁快
- 通常情况下,数组维度越大,使用memcpy效率更高
- 数组维度越大,直接赋值耗时主体是循环耗时
Note:
- “等号赋值”被编译器翻译成一连串的MOV指令,而memcpy则是一个循环。“等号赋值”比memcpy快,并不是快在拷贝方式上,而是快在程序流程上
- 连续的MOV指令要比循环MOV快
- 在循环方式下,每一次MOV过后,需要: ①判断是否拷贝完成;②跳转以便继续拷贝
- 循环除了增加了判断和跳转指令以外,对于CPU处理流水产生的影响也是不可不计的
转自传送门
如果是低维数组,直接展开循环进行赋值效率更高,这样可以避免判断与跳转对CPU时间的占用,也就是空间换时间,但要注意的是,并不是越展开越好(即使不考虑对空间的浪费),展开也应该有个度。
因为CPU的快速执行很依赖于cache,如果cache不命中,CPU将浪费不少的时钟周期在等待内存上(内存的速度一般比CPU低一个数量级)。而小段循环结构就比较有利于cache命中,因为重复执行的一段代码很容易被硬件放在cache中,这就是代码局部性带来的好处。而过度的循环展开就打破了代码的局部性。
GCC有自动将循环展开的编译选项,如-funroll-loops
测试代码见文末,下面分情况进行几个小实验。
1. 数组1维,对比memcpy与赋值
- 无优化选项
- -O3编译
2. 数组1000维,对比memcpy与赋值
-
无优化选项
-
-O3编译
-
屏蔽循环中的赋值,无优化选项
-
屏蔽循环中的赋值,-O3编译
测试代码如下:
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>
#define DIMENSION_MIN 10
#define DIMENSION_MAX 200
int main()
{
int count = 1000000;
double data[DIMENSION_MIN] = {0};
double bate[DIMENSION_MIN] = {0};
struct timeval tv;
double dT1=0,dT2=0;
double start,stop;
gettimeofday(&tv,NULL);
start = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
for(int i=0;i<count;i++)
{
memcpy(bate,data,sizeof(bate));
}
gettimeofday(&tv,NULL);
stop = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
dT1 = stop - start;
gettimeofday(&tv,NULL);
start = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
for(int i=0;i<count;i++)
{
for(int j=0;j<DIMENSION_MIN;j++)
{
bate[j]=data[j];
}
}
gettimeofday(&tv,NULL);
stop = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
dT2 = stop - start;
printf("\n\n\n%d维数组的比较:\n",DIMENSION_MIN);
printf(" for take time: %lfms\n",dT2);
printf(" memcpy take time: %lfms\n\n\n",dT1);
/***********************************************************************/
double data1[DIMENSION_MAX] = {0};
double bate1[DIMENSION_MAX] = {0};
gettimeofday(&tv,NULL);
start = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
for(int i=0;i<count;i++)
{
memcpy(bate1,data1,sizeof(bate));
}
gettimeofday(&tv,NULL);
stop = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
dT1 = stop - start;
gettimeofday(&tv,NULL);
start = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
for(int i=0;i<count;i++)
{
for(int j=0;j<DIMENSION_MAX;j++)
{
bate1[j]=data1[j];
}
}
gettimeofday(&tv,NULL);
stop = (double)(tv.tv_sec*1000 + tv.tv_usec*0.001); //ms
dT2 = stop - start;
printf("%d维数组的比较:\n",DIMENSION_MAX);
printf(" for take time: %lfms\n",dT2);
printf(" memcpy take time: %lfms\n\n\n",dT1);
return 0;
}
参考文献:
- https://blog.csdn.net/pngynghay/article/details/17142401
- https://developer.aliyun.com/article/8924
- https://www.zhihu.com/question/356017800/answer/2786064715