内存拷贝(memcpy())和异或(bit-wise XOR)是程序中运行最快的操作之一,其速度受到CPU、内存和编译器(GCC版本)的影响。
内存拷贝memcpy(des, src, len) 则是将长度为len 的数据从地址src 拷贝到地址des 。
按位异或(异或)可以分为几类:
- a^b:两个数值的异或。相当于两个寄存器内值在CPU 中计算异或,大多数CPU 中一个时钟周期完成。速度取决于CPU 的频率,频率越高,速度越快。
- a^Region:一个数值与一块内存Region 中的每个值异或,结果保存在Region 中,速度相当于顺序访存。
- RegionA = RegionA^RegionB:两块内存中对应位置上的值异或。
- RegionC = RegionA^RegionB:两块内存中对应位置上的值异或,结果保存在另一块内存中。
四类异或速度依次递减。平常中越到的是第3 种,本文中异或速度也是第3 种。
实验一(memcpy())
环境:
CPU : Intel(R) Xeon(R) CPU E5620 @ 2.40GHz 32KB L1 cache,12MB L2 cache
OS : 2.6.35-22-generic #33-Ubuntu
GCC Version : 4.4.5
compiler flags : -O3 -msse4 -DINTEL_SSE4 -Wall -std=c99
首先在服务器上测试1KB – 2MB的异或速度,代码如下:
void memcpy_speed(unsigned long buf_size, unsigned long iters){
struct timeval start, end;
unsigned char * pbuff_1;
unsigned char * pbuff_2;
pbuff_1 = (void *)malloc(buf_size);
pbuff_2 = (void *)malloc(buf_size);
gettimeofday(&start, NULL);
for(int i = 0; i < iters; ++i){
memcpy(pbuff_2, pbuff_1, buf_size);
}
gettimeofday(&end, NULL);
printf("%5.3f\n", ((buf_size*iters)/(1.024*1.024))/((end.tv_sec - \
start.tv_sec)*1000*1000+(end.tv_usec - start.tv_usec)));
free(pbuff_1);
free(pbuff_2);
}
测试结果如图:
出现非常诡异的现象是在140KB 以下时,memcpy() 在4KB 的倍数时速度有剧烈的下降,而其他buff 大小速度则没有明显的下降。猜想估计和cache 相关,于是修改了内存的分配方式:
if(type == 1){
pbuff_1 = (void *)malloc(2*buff_size);
pbuff_2 = pbuff_1+buff_size;
}else{
pbuff_1 = (void *)malloc(buff_size);
pbuff_2 = (void *)malloc(buff_size);
}
按照type == 1 的对齐分配方式,pbuff_1 和pbuff_2 两块是连续的内存;而另一个非对齐的分配方式,会导致pbuff_1 和pbuff_2 之间有16 字节的间隔:
上图中绿线是type==1 的对齐方式分配memcpy() 速度,红线为之前非对齐方式测试得到速度。后来在不同GCC 版本下进行了测试,发现造成4KB 速度下降的主要还是编译器的版本原因,在相同CPU 中不同GCC 编译器下进行了速度测试,Time Used(1) 是type==1 所花时间。在4.6.3 版本后两种分配内存方式对memcpy()速度的影响可以忽略不计。
GCC version--------
4.1.3
--------4.4.5
--------4.6.3
Time Used(1)-----
0m0.183s
----0m0.128s
----0m0.110s
Time Used(0)-----
0m1.788s
----0m0.422s
----0m0.108s
所以说及时更新、升级系统和软件是个好习惯。
实验二(异或)
环境同实验一,测试了1KB-2MB 和1KB-1GB 的异或速度:
*pa = (*pa)^(*pb)
8-bit aligned 至128-bit aligned 对应的数据结构为uint8_t/uint16_t/uint32_t/uint64_t/__m128i,128位使用SSE2 指令集进行异或运算:
速度最快的都是使用SSE2 指令集的128-bit aligned 异或,每次能够计算128 Bytes,必然速度要快一些。其次是8-bit aligned,这是因为编译器进行了优化,在更高版本GCC 编译后,8-bit aligned 速度是最慢的。gf-complete w=8 是使用gf-complete 在w=8 的情况下异或内存得到的速度,gf-complete 为对位提供简单的接口牺牲了部分性能。
无意间逛到您的网站,整体风格和专业性让人看着真舒服
谢谢!