慢堆数组性能

时间:2015-01-08 06:21:27

标签: c++ arrays performance

我遇到了奇怪的内存访问性能问题,有什么想法吗?

int* pixel_ptr = somewhereFromHeap;

int local_ptr[307200]; //local

//this is very slow
for(int i=0;i<307200;i++){
  pixel_ptr[i] = someCalculatedVal ;
}

//this is very slow
for(int i=0;i<307200;i++){
  pixel_ptr[i] = 1 ; //constant
}

//this is fast
for(int i=0;i<307200;i++){
  int val = pixel_ptr[i];
  local_ptr[i] = val;
}

//this is fast
for(int i=0;i<307200;i++){
  local_ptr[i] = someCalculatedVal ;
}

尝试将值合并到本地扫描线

int scanline[640]; // local

//this is very slow
for(int i=xMin;i<xMax;i++){
  int screen_pos = sy*screen_width+i;
  int val = scanline[i];
  pixel_ptr[screen_pos] = val ;
}

//this is fast
for(int i=xMin;i<xMax;i++){
  int screen_pos = sy*screen_width+i;
  int val = scanline[i];
  pixel_ptr[screen_pos] = 1 ; //constant
}

//this is fast
for(int i=xMin;i<xMax;i++){
  int screen_pos = sy*screen_width+i;
  int val = i; //or a constant
  pixel_ptr[screen_pos] = val ;
}

//this is slow
for(int i=xMin;i<xMax;i++){
  int screen_pos = sy*screen_width+i;
  int val = scanline[0];
  pixel_ptr[screen_pos] = val ;
}

有什么想法吗?我使用mingw和cflags -01 -std = c ++ 11 -fpermissive。

UPDATE4: 我不得不说这些是我程序中的代码片段,并且前后都有大量的代码/函数在运行。扫描线块在退出前确实在功能结束时运行。

现在有适当的测试程序。请问@Iwillnotexist。

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

#define SIZE 307200
#define SAMPLES 1000

double local_test(){
    int local_array[SIZE];

    timeval start, end;
    long cpu_time_used_sec,cpu_time_used_usec;
    double cpu_time_used;

    gettimeofday(&start, NULL);
    for(int i=0;i<SIZE;i++){
        local_array[i] = i;
    }
    gettimeofday(&end, NULL);
    cpu_time_used_sec = end.tv_sec- start.tv_sec;
    cpu_time_used_usec = end.tv_usec- start.tv_usec;
    cpu_time_used = cpu_time_used_sec*1000 + cpu_time_used_usec/1000.0;

    return cpu_time_used;
}

double heap_test(){
    int* heap_array=new int[SIZE];

    timeval start, end;
    long cpu_time_used_sec,cpu_time_used_usec;
    double cpu_time_used;

    gettimeofday(&start, NULL);
    for(int i=0;i<SIZE;i++){
        heap_array[i] = i;
    }
    gettimeofday(&end, NULL);
    cpu_time_used_sec = end.tv_sec- start.tv_sec;
    cpu_time_used_usec = end.tv_usec- start.tv_usec;
    cpu_time_used = cpu_time_used_sec*1000 + cpu_time_used_usec/1000.0;

    delete[] heap_array;

    return cpu_time_used;
}


double heap_test2(){
    static int* heap_array = NULL;

    if(heap_array==NULL){
        heap_array = new int[SIZE];
    }

    timeval start, end;
    long cpu_time_used_sec,cpu_time_used_usec;
    double cpu_time_used;

    gettimeofday(&start, NULL);
    for(int i=0;i<SIZE;i++){
        heap_array[i] = i;
    }
    gettimeofday(&end, NULL);
    cpu_time_used_sec = end.tv_sec- start.tv_sec;
    cpu_time_used_usec = end.tv_usec- start.tv_usec;
    cpu_time_used = cpu_time_used_sec*1000 + cpu_time_used_usec/1000.0;

    return cpu_time_used;
}


int main (int argc, char** argv){
    double cpu_time_used = 0;

    for(int i=0;i<SAMPLES;i++)
        cpu_time_used+=local_test();

    printf("local: %f ms\n",cpu_time_used);

    cpu_time_used = 0;

    for(int i=0;i<SAMPLES;i++)
        cpu_time_used+=heap_test();

    printf("heap_: %f ms\n",cpu_time_used);

    cpu_time_used = 0;

    for(int i=0;i<SAMPLES;i++)
        cpu_time_used+=heap_test2();

    printf("heap2: %f ms\n",cpu_time_used);

}

没有优化。

local:577.201000 ms

heap_:826.802000 ms

heap2:686.401000 ms

使用new和delete进行的第一次堆测试速度慢了2倍。 (按建议分页?)

具有重用堆数组的第二个堆仍然慢1.2倍。 但我想第二次测试并不实用,因为至少在我的情况下,往往会运行其他代码。对于我的情况,我的pixel_ptr当然只分配了一次 prograim初始化。

但如果有人有解决方案/想法加快速度,请回复!

我仍然感到困惑,为什么堆写入比堆栈段慢得多。 当然必须有一些技巧来使堆更多cpu / cache可以调味。

最终更新?:

我再次访问,这次再次拆卸,突然间,我知道为什么我的一些断点 不要激活。程序看起来很短,因此我怀疑编译器可能 已经删除了我放入的冗余虚拟代码,这解释了为什么本地数组神奇地快了很多倍。

2 个答案:

答案 0 :(得分:3)

我有点好奇所以我做了测试,实际上我可以衡量堆栈和堆访问之间的区别。

第一个猜测是生成的程序集是不同的,但看了之后,堆和堆栈实际上是相同的(这是有道理的,内存不应该被区分)。

如果程序集相同,那么差异必须来自paging mechanism。猜测是在堆栈上,页面已经被分配,但是在堆上,第一次访问会导致页面错误和页面分配(不可见,这一切都发生在内核级别)。为了验证这一点,我做了相同的测试,但首先我会在测量之前访问堆。测试给出了堆栈和堆的相同时间。可以肯定的是,我还做了一个测试,我首先访问了堆,但只有每4096个字节(每1024个int),然后是8192,因为一个页面通常是4096个字节长。结果是,每次只访问4096个字节也会为堆和堆栈提供相同的时间,但访问每个8192会产生差异,但不会像以前没有访问那么多。这是因为事先只有一半的页面被访问和分配。

所以答案是在堆栈上已经分配了内存页面,但是在堆上,页面是即时分配的。这取决于操作系统分页策略,但所有主要的PC操作系统可能都有类似的操作系统。

对于我使用Windows的所有测试,使用MS编译器定位x64。

编辑:对于测试,我测量了一个更大的循环,因此每个内存位置只有一个访问权限。 delete数组并多次测量相同的循环应该给堆栈和堆提供相似的时间,因为delete内存可能不会取消分配页面,并且它们已经分配给下一个循环(如果在同一空间分配下一个new)。

答案 1 :(得分:0)

以下两个代码示例在运行时应该与良好的编译器设置不同。可能你的编译器会生成相同的代码:

//this is fast
for(int i=0;i<307200;i++){
  int val = pixel_ptr[i];
  local_ptr[i] = val;
}

//this is fast
for(int i=0;i<307200;i++){
  local_ptr[i] = pixel_ptr[i];
}

请尝试增加优化设置。