如何有效地改变矩阵的连续部分?

Posted

技术标签:

【中文标题】如何有效地改变矩阵的连续部分?【英文标题】:How to efficiently change a contiguous portion of a matrix? 【发布时间】:2013-01-19 01:18:15 【问题描述】:

给定一个由 M 行和 N 列组成的矩阵,并分配为 M*N 元素的字节数组(这些元素最初设置为零),我会根据以下规则修改此矩阵:在某个元素附近找到的元素必须设置为给定值。换句话说,给定一个矩阵,我应该设置矩阵的一个区域:为此我应该访问数组的不连续部分。

为了执行上述操作,我可以访问以下信息:

指向位于邻域中心的元素的指针(在上述操作期间不得更改此指针);还提供了该元素的位置(行和列); 邻域的大小L*LL 始终为奇数)。

实现这个操作的代码应该在 C++ 中尽可能快地执行:出于这个原因,我想到了使用上面的指针来访问数组的不同部分。相反,邻域中心元素的位置(行和列)可以让我检查指定区域是否超过矩阵的维度(例如,区域的中心可能位于矩阵的边缘) :在这种情况下,我应该只设置位于矩阵中的那部分区域。

int M = ... // number of matrix rows
int N = ... // number of matrix columns

char* centerPtr = ... // pointer to the center of the region
int i = ... // position of the central element
int j = ... // of the region to be modified

char* tempPtr = centerPtr - (N+1)*L/2;
for(int k=0; k < L; k++)

    memset(tempPtr,value,N);
    tempPtr += N;

如何改进代码? 如何处理一个区域可能超过矩阵维度的事实? 如何让代码在执行时间上更高效?

【问题讨论】:

强制性问题:您是否进行了分析以确认这是一个问题? 不,但我很想看看优化代码和非优化代码之间的区别。注意:我上面列出的代码没有处理一个区域可能超过矩阵的维度这一事实,因为我不知道如何使用指针来处理这个问题。 如果您真的想要“尽可能快”的代码,您可能需要为将要运行的平台/设备编写一些手动优化的程序集。我猜这不是你想要的。 @tc:你说得对,但我的意思是在 C++ 中“尽可能快”......我认为使用指针,代码比使用两个嵌套循环快一点。这是因为我想在使用指针和使用两个嵌套循环之间进行速度比较。 警告!指针算术取决于底层类型,因此行 tempPtr += N; 将向前移动 tempPtr N chars,而不是 N ints you'重新期待。为什么不直接使用int* 【参考方案1】:

对于区域不与矩阵外部重叠的一般情况,您的代码可能是最佳的。使用这种代码可能导致的主要效率问题是在列而不是行上进行外循环。这会破坏缓存和分页性能。你还没有这样做。

在大多数现代编译器中,使用指针几乎没有速度优势。优化器将从普通数组索引中提出非常好的指针代码。在某些情况下,我看到数组索引代码比手动调整的指针代码运行得更快。所以如果索引算术更清晰,就不要使用指针算术。

有 8 种边界情况:北、西北、西、...、东北。这些中的每一个都需要一个自定义版本的循环来接触正确的元素。我会展示西北案例,让你解决剩下的问题。

处理案例的最快方法是使用 3 级“if”树:

if (j < L/2)   // northwest, west, or southwest
  if (i < L/2)   
    // northwest
    char* tempPtr = centerPtr - (L/2 - i) * N - (L/2 - j);
    for(int k = 0; k < L; k++) 
      memset(tempPtr, value, L - j);
      tempPtr += N;
    
   else if (i >= M - L/2)  
    // southwest
   else  
    // west
  
 else if (j >= N - L/2)  // symmetrical cases for east.
  if (i < L/2)   
    // northeast
   else if (i >= M - L/2)  
    // southeast
   else  
    // east
  
 else  
  if (i < L/2)   
    // north
   else if (i >= M - L/2)  
    // south
   else  
    // no overlap
  

这样做很乏味,但每个区域最多只能进行 3 次比较。

【讨论】:

你所说的是什么意思“使用这种代码可能导致的主要效率问题是在列而不是行上进行外循环。这会破坏缓存和分页性能。”? 连续的内存访问应该靠近在一起。如果您有for (j...) for (i...) 循环,则连续内存访问的步幅等于行长度。这会给数据缓存和/或分页系统带来沉重的负担,具体取决于行长。你的内部循环是memset,它连续接触内存(for j 循环),所以你很好。 我应该补充一点,复杂的优化器会分析循环嵌套并寻找机会对它们进行重新排序以改进访问模式。超级计算机编译器至少从 80 年代就已经这样做了。我相信最新的gcc 版本能够做到这一点。至少有一个-floop-nest-optimize 选项。

以上是关于如何有效地改变矩阵的连续部分?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确使用 CuPy 流

如何跨列有效地部分 argsort Pandas 数据框

Vim - 直观地选择多个不连续的部分[重复]

matlab对矩阵/向量的常用操作(拼接矩阵向量逆序改变矩阵形状求行阶梯形矩阵提取矩阵的一部分等)

matlab对矩阵/向量的常用操作(拼接矩阵向量逆序改变矩阵形状求行阶梯形矩阵提取矩阵的一部分等)

如何将大std :: string的一部分有效地转换为浮点数?