内存拷贝速度(memcpy())和异或速度

内存拷贝(memcpy())和异或(bit-wise XOR)是程序中运行最快的操作之一,其速度受到CPU、内存和编译器(GCC版本)的影响。

内存拷贝memcpy(des, src, len) 则是将长度为len 的数据从地址src 拷贝到地址des 。

按位异或(异或)可以分为几类:

  1. a^b:两个数值的异或。相当于两个寄存器内值在CPU 中计算异或,大多数CPU 中一个时钟周期完成。速度取决于CPU 的频率,频率越高,速度越快。
  2. a^Region:一个数值与一块内存Region 中的每个值异或,结果保存在Region 中,速度相当于顺序访存。
  3. RegionA = RegionA^RegionB:两块内存中对应位置上的值异或。
  4. 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);
}

测试结果如图:

memcpy

出现非常诡异的现象是在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 字节的间隔:

memcpy-unaglignment

上图中绿线是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 指令集进行异或运算:

xor2xor1

速度最快的都是使用SSE2 指令集的128-bit aligned 异或,每次能够计算128 Bytes,必然速度要快一些。其次是8-bit aligned,这是因为编译器进行了优化,在更高版本GCC 编译后,8-bit aligned 速度是最慢的。gf-complete w=8 是使用gf-complete 在w=8 的情况下异或内存得到的速度,gf-complete 为对位提供简单的接口牺牲了部分性能。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据