Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C/C++;优化数据结构、阵列阵列或仅阵列_C++_C_Arrays - Fatal编程技术网

C++ C/C++;优化数据结构、阵列阵列或仅阵列

C++ C/C++;优化数据结构、阵列阵列或仅阵列,c++,c,arrays,C++,C,Arrays,使用使用16字节4v4单字节矩阵的程序: unsigned char matrix[4][4]; 和一些256字节16v16单字节矩阵: unsigned char bigMatrix[16][16]; 通常由于数据操作,我被迫在程序中按列循环,从而导致缓存未命中 如果改用阵列,性能会提高吗 unsigned char matrix[16]; unsigned char matrix[256]; 并通过使用一些变量来检索元素来访问元素,即 matrix[variableA*variable

使用使用16字节4v4单字节矩阵的程序:

unsigned char matrix[4][4];
和一些256字节16v16单字节矩阵:

unsigned char bigMatrix[16][16];
通常由于数据操作,我被迫在程序中按列循环,从而导致缓存未命中

如果改用阵列,性能会提高吗

unsigned char matrix[16];
unsigned char matrix[256];
并通过使用一些变量来检索元素来访问元素,即

matrix[variableA*variableB + i];
其中variableA*variableB+i需要在每次访问元素时重新计算


我只想速度优化和内存是没有问题的。这会有帮助吗,比如给性能带来一些打击或损失,或者差异太小以至于根本不在乎吗?

这没有什么区别。在这两种情况下,数据的布局方式完全相同,访问方式也完全相同。即使它没有生成完全相同的程序集,我也会感到惊讶


然而,对于256字节的表,在任何情况下都不太可能出现缓存未命中。CPU的一级缓存通常在32到128KB之间,因此我怀疑在任何情况下都会出现许多缓存未命中。

您说每次访问元素时都需要重新计算
variableA*variableB+I
,即使使用多维数组,这种情况也会发生。唯一的区别是,在多维数组中,编译器会发出此代码,因此您不会看到它,而在一维数组中,您会看到源代码中的代码。

如果您对数组进行顺序访问,则大型线性数组的速度可能会稍快一些,因为您在每个索引处都保存了乘法操作。如果按列循环,则按顺序访问;至少在[row][col]符号中是这样,这是我和每个人交谈过的“标准”


我怀疑您的256元素阵列在现代硬件上会导致缓存丢失,但我愿意被证明是错误的。cachegrind告诉您什么?

虽然编译后的代码运行速度同样快,但存在一些设计问题:可以最大限度地重用索引代码

imho,最好的方法是将其包装在一个知道如何以最快的方式循环其元素的容器中。他们为此命名为“内部迭代器”,如GoF设计模式“迭代器”模式中所述

一个简单的例子:

 template< int N >
 struct CNxN { 
     typedef int t_row[N];
     typedef t_row t_matrix[N];
     t_matrix m_Contents; 

     template< typename Functor >
     void each( Functor & f ) {
         for( int col = 0; col != N; ++col )
             for( int row = 0; row != N; ++row )
                 f( m_Contents[row][col] );
     }
 };

 // client code
 CNxN<3> matrix = { { {1,1,1},{1,1,1},{1,1,1} } };

 struct sum { 
      long result; 
      sum():result(0){} 
      void operator()( int i ){ result +=i; } 
 };
 matrix.each( sum );
 assert(sum.result==0); 
 assert(has_performed_in_the_fastest_possible_way);//;)
模板
结构CNxN{
typedef int t_行[N];
typedef t_行t_矩阵[N];
t_矩阵m_内容;
模板
使每个无效(函子和f){
for(int col=0;col!=N;++col)
对于(int行=0;行!=N;++行)
f(m_内容[行][列]);
}
};
//客户端代码
CNxN矩阵={{{1,1,1},{1,1,1},{1,1,1}};
结构和{
长期结果;
sum():结果(0){}
void运算符()(int i){result+=i;}
};
矩阵。每个(总和);
断言(sum.result==0);
断言(是否以最快的方式执行);//)

jalf基本上是对的。一级缓存被分为块,块的大小取决于处理器,但顺序为32字节。因此,如果您一次通过一个字节的内存,那么每32个字节(或无论块大小如何)就会出现一次缓存丢失。现在,Intel芯片在这方面非常聪明,可以检测顺序读取并预取数据,从而减少缓存丢失的影响

4x4矩阵很可能驻留在单个一级数据块(或缓存线)中,因此按行或列访问它没有什么区别。当然,您不希望将矩阵拆分为两条缓存线,因此对齐良好的内存非常重要

但是,16x16矩阵无法装入缓存线。因此,如果跳过数组处理列,就会出现大量缓存未命中。正如jalf所说,索引的计算几乎没有什么区别,因为CPU和内存之间的比率很高(即,每次缓存未命中,您都可以进行大量CPU工作)

现在,如果您主要以列为导向的方式处理矩阵,那么您最好的选择是转置所有矩阵(用列交换行),这样您的内存访问将更加有序,缓存未命中的数量将减少,CPU将能够更好地预取数据。因此,不要像这样组织矩阵:

  0   1   2 .... 15
 16  17  18 .... 31
....
240 241 242 .... 255
如果数字是从矩阵开始的内存偏移量,则组织如下:

 0 16 32 ... 240
 1 17 33 ... 241
 ...
15 31 47 ... 255

当我在学校时,我的一位CS老师坚持说,如果你为一维数组做数组,它会更快。 那天我很生气

通常由于数据操作,我被迫按列循环[…]

不能两全其美:如果矩阵“足够大”,行循环或列循环都会导致缓存未命中(请参阅)。优化执行频率更高的循环类型


如果内存消耗不是问题,你也可以考虑保存矩阵和它的转置。

我会说,太小而不关心,但是我会等待一个真正知道的人给出一个真正的答案。+ 1我希望看到关于转换矩阵的“提示”。这几乎是不可避免的。但是一旦所有的数据都被触动了,它就会很容易地进入缓存,因此除非你突然拉入更多的数据,否则你不会得到缓存未命中。而且索引的计算无论CPU/内存的比率如何都没有区别,因为在这两种情况下都是完全相同的。二维数组的索引方式与他对一维数组的索引方式完全相同。如果只处理几个矩阵,那么它的行或列顺序可能没有什么区别。如果您正在处理数千个矩阵,那么转置矩阵的吞吐量应该会更好,因为MMU可以更好地发现线性内存访问并预取它。您可以使用指针以相同的方式在任意一个数组上迭代。对于spe,在每个函数中使用指针而不是索引