C++ STL算法中跨函数边界的循环重新排序

C++ STL算法中跨函数边界的循环重新排序,c++,stl,compiler-optimization,C++,Stl,Compiler Optimization,为了简单起见,我们假设我有一个N矩阵的向量,每个M行。我使用STLstd::acculate来计算所有矩阵的和。我传递一个二进制函子,它接受两个矩阵(通过引用)并返回它们的和(通过引用)。完全公开:我正在使用libstdc++并行模式。在函子内部,我分别在行上循环以计算和 虽然每个矩阵都太大,无法放入缓存,但一行非常适合缓存。因此,对循环进行重新排序是有利的,这样外部循环在M行上进行索引,内部循环在N矩阵上进行索引。除了内联定义函子外,我还可以做些什么来鼓励这种跨函数边界循环重新排序。我当然可以

为了简单起见,我们假设我有一个
N
矩阵的向量,每个
M
行。我使用STL
std::acculate
来计算所有矩阵的和。我传递一个二进制函子,它接受两个矩阵(通过引用)并返回它们的和(通过引用)。完全公开:我正在使用libstdc++并行模式。在函子内部,我分别在行上循环以计算和

虽然每个矩阵都太大,无法放入缓存,但一行非常适合缓存。因此,对循环进行重新排序是有利的,这样外部循环在
M
行上进行索引,内部循环在
N
矩阵上进行索引。除了内联定义函子外,我还可以做些什么来鼓励这种跨函数边界循环重新排序。我当然可以重新构造代码,但我希望保留STL算法所能提供的简单结构。如果有特定于gcc的东西,我也不会介意


我并不是在讨论矩阵,这只是一个例子,但同样的问题结构也适用。主要问题是性能问题。解释实际场景太麻烦了,但核心问题是:STL的累加需要嵌套循环之间的排序,这对缓存不是很友好,因为它试图在移动到下一个对象之前完成两个对象的添加。单个对象太大,无法保存在缓存中,但它的一部分可以保存。因此,如果一次(在所有对象上)计算一个“部分”的“加法”,则可以加快执行速度。手动重新排列循环可以大大改善翻牌。但我希望编译器能够进行重新排序,这样我就可以在STL级别(尽可能)进行编码。因此,我正在寻找一些技巧来实现这一点。

除非所有内容都是内联的,并且M和N都是常量,否则我无法想象编译器会解决这个问题。即便如此,这也是一种延伸


为了保持STL算法的风格,在累加上使用foreach M,让函子只对一行求和

编写一个新算法,或将内容包装在for循环或
std::for_each()
调用中。这将比找到适应
std::acculate()
的方法容易得多。我认为这里唯一的另一种选择是为库引入一个新的抽象级别,它超越了迭代器。编写新算法或引入额外循环更容易。

类矩阵;
class Matrix;
class Row;
struct SumNRow {
  int _rowidx;
//  Row _tempRow; //For return by reference left out for simplicity
  SumNRow(int iRowIdx): _rowIdx(iRowIdx) {}
  Row operator(const Matrix & iMarix1, const Matrix iMatrix2) {
    return iMarix1[_rowIdx] + iMatrix2[_rowIdx];
  }
};

template<class MatrixIterator>
void sum(const MatrixIterator & iMarixStart, const MatrixIterator & iMatrixEnd, Matrix & oMarix) {
  for (int i = 0; i < iMarixStart->rowCount(); ++i) {
    oMarix[i]=std::accumulate(iMarixStart, iMatrixEnd, SumNRow(i));
  }
}
班级排; SumNRow结构{ int_rowidx; //Row _tempRow;//用于通过引用返回,为简单起见省略了 SumNRow(intIrowidx):rowIdx(iRowIdx){} 行运算符(常数矩阵&iMarix1,常数矩阵iMatrix2){ 返回iMarix1[_rowIdx]+iMatrix2[_rowIdx]; } }; 模板 无效和(常数矩阵运算符和iMarixStart、常数矩阵运算符和iMatrixEnd、矩阵和oMarix){ 对于(int i=0;irowCount();++i){ oMarix[i]=std::累加(iMarixStart、iMatrixEnd、SumNRow(i)); } }
我不明白这个问题。您是否有性能问题?“你能给我们看看你有什么吗?”威廉说,我补充了一些细节。希望现在能更清楚一点。到目前为止,我对收到的三条建议都投了赞成票。我仍然在寻找一种方法来提示编译器做正确的事情。因为正确的选择会随着缓存大小的变化而变化。可能解决方案是使用预处理器进行条件编译。所有这些都是很好的答案,本质上都指向同一件事。但是我只能接受一个,所以我选择了最明确的一个。Hi@Lou M和N可以被视为编译时常量。这将导致缺乏灵活性,但我可以接受。我完全按照你所说的做,即在
累计
函子中调用STL循环构造。但它似乎并没有重新安排事情。是否应该重新排序取决于矩阵(对象)的大小和缓存大小,我认为编译器无法找到这一点。如果有一种方法(模板魔术)来强制重新排序,那就太好了。如果我没听清你的建议,请一定告诉我。@srean:你似乎误解了这个答案。我相信重新排序在这里是非常清楚的,我的答案重复了完全相同的想法。卢·弗朗哥:很抱歉错过了你的答案,重复了同样的事情。@basilev是的。我不明白Lou Franco也提出了同样的解决方案。这不是算法,而是数据结构,这是次优化的来源,即无法缓存的大型对象的向量。我可以用一种方式分割对象,它们现在是几个向量,每个向量包含对象的一部分,这是一个重要的重构。问题是,这种更改可能是正确的,也可能不是正确的,这取决于机器的缓存大小。理想情况下,我希望通过在目标m/c上重新编译来处理这些复杂问题。现在看来这是一个不切实际的目标,即没有简单的解决办法。我希望编译器能优化它,这就是wilhelmtell所描述的。