堆阵列性能缓慢
Slow heap array 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 ;
}
有什么想法吗?我正在使用带有 cflags -01 -std=c++11 -permissive 的 mingw。
更新4:我不得不说这些是我的程序片段,并且在之前和之后运行了大量代码/函数。扫描线块确实在退出前的功能结束时运行。
现在有了适当的测试程序。 谢谢@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 msn",cpu_time_used);
cpu_time_used = 0;
for(int i=0;i<SAMPLES;i++)
cpu_time_used+=heap_test();
printf("heap_: %f msn",cpu_time_used);
cpu_time_used = 0;
for(int i=0;i<SAMPLES;i++)
cpu_time_used+=heap_test2();
printf("heap2: %f msn",cpu_time_used);
}
符合未优化。
本地: 577.201000 ms
heap_: 826.802000 ms
堆2: 686.401000 ms
使用 new 和 delete 的第一次堆测试慢 2 倍。(按照建议分页?
具有重用堆数组的第二个堆仍然慢 1.2 倍。但我想第二个测试并不那么实用,因为至少在我的案例中,往往会有其他代码在之前和之后运行。就我而言,我的pixel_ptr当然只分配一次程序初始化。
但是,如果有人有加快速度的解决方案/想法,请回复!
我仍然很困惑为什么堆写入比堆栈段慢得多。当然,必须有一些技巧可以使堆更具CPU/缓存的味道。
最终更新?
我重新审视了一次,再次拆卸,这一次,突然我知道为什么我的一些断点不要激活。该程序看起来可疑地短,因此我怀疑编译器可能会删除了我输入的冗余虚拟代码,该代码解释了为什么本地数组神奇地快了很多倍。
我有点好奇,所以我做了测试,事实上我可以测量堆栈和堆访问之间的差异。
第一个猜测是生成的程序集是不同的,但是在查看之后,它实际上在堆和堆栈方面是相同的(这是有道理的,内存不应该被区分)。
如果程序集相同,则差异必须来自分页机制。猜测是在堆栈上,页面已经分配,但在堆上,首次访问会导致页面错误和页面分配(不可见,这一切都发生在内核级别)。为了验证这一点,我做了同样的测试,但首先我会在测量之前访问堆一次。该测试给出了相同的堆栈和堆时间。可以肯定的是,我还做了一个测试,我首先访问了堆,但只有每 4096 字节(每 1024 个整数),然后是 8192,因为一个页面通常有 4096 字节长。结果是,仅访问每 4096 字节也会为堆和堆栈提供相同的时间,但访问每个 8192 字节会产生差异,但不如以前根本没有访问那么多。这是因为事先只访问和分配了一半的页面。
所以答案是,在堆栈上,内存页已经分配,但在堆上,页面是动态分配的。这取决于操作系统分页策略,但所有主要的 PC 操作系统可能都有类似的策略。
对于所有测试,我都使用Windows,MS编译器面向x64。
编辑:对于测试,我测量了一个较大的循环,因此每个内存位置只有一个访问。 delete
数组并多次测量同一循环应该为堆栈和堆提供相似的时间,因为delete
内存可能不会取消分配页面,并且它们已经分配给下一个循环(如果下一个new
分配在同一空间上)。
以下两个代码示例在运行时与良好的编译器设置不应有所不同。可能您的编译器将生成相同的代码:
//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];
}
请尝试增加优化设置。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- 大小相等但成员数量不同的结构之间的性能差异
- 为什么constexpr的性能比正常表达式差
- 在类中使用随机生成器时出现性能问题
- 缓慢提升ASIO
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 海湾合作委员会 ARM 性能下降
- GCC 和 Clang 代码性能的巨大差异
- 在容量内调整矢量大小时的性能影响
- 了解算法的性能差异(如果以不同的编程语言实现)
- 未达到的情况会影响开关外壳性能
- 如何修复macOS线程互斥锁性能缓慢的问题?
- F#性能:是什么让这个代码如此缓慢
- C 11多线程在神经网络中的性能非常缓慢
- QTableView 在 1000 个可见单元格下性能缓慢
- 堆阵列性能缓慢
- 此std::矢量分区的性能缓慢
- x64免费版上的性能缓慢