【问题标题】:Run-time efficient transposition of a rectangular matrix of arbitrary size任意大小的矩形矩阵的运行时高效转置
【发布时间】:2016-03-03 06:34:33
【问题描述】:

我需要时间来优化一大段 C 代码以提高速度,我正在寻找一种算法——最好是一个 C“sn-p”——转置一个矩形矩阵 u[r][c] 任意大小r 行数,c 列数)转换为目标矩阵 v[s][d] (s = c “缓存友好” i. 中的行数,d = r 列数)。 e.尊重数据局部性的方式。 u 的典型大小约为 5000 ... 15000 行 x 50 到 500 列,显然逐行访问元素的缓存效率非常低。

网上有很多关于这个话题的讨论(在这个thread附近),但据我所知,他们都在讨论空间情况,比如方阵、u[r][r],或者定义一个维数组, 即G。 u[r * c],而不是上面提到的“数组数组”(长度相等)在我的数字食谱上下文中使用(背景参见here)。

我非常感谢任何帮助我避免“重新发明轮子”的提示。

马丁

【问题讨论】:

  • 如果两个源元素彼此靠近,则对应的目标元素会远离,反之亦然。人们怎么可能希望缓存友好的转置呢?我真的很好奇。
  • 找到this。我不太明白它是如何工作的......
  • 这个问题是对资源或信息的需求。所以不是那个地方。尝试学术问答:例如计算机科学等。
  • 我实际上尝试了一个简单的缓存无意识转置实现,与朴素的方法相比,速度提高了 10 倍。
  • @Ike 是的,现在明白了,事后看来很明显。

标签: c performance matrix transpose cache-oblivious


【解决方案1】:

我不认为数组数组比一般的线性数组更难转置。但是,如果您要在每个数组中有 50 列,那听起来很糟糕:隐藏指针取消引用的开销可能还不够。

我认为缓存友好实现的总体策略是相同的:在块中处理您的矩阵,根据实验选择性能最佳的块大小。

template<int BLOCK>
void TransposeBlocked(Matrix &dst, const Matrix &src) {
    int r = dst.r, c = dst.c;
    assert(r == src.c && c == src.r);
    for (int i = 0; i < r; i += BLOCK)
        for (int j = 0; j < c; j += BLOCK) {
            if (i + BLOCK <= r && j + BLOCK <= c)
                ProcessFullBlock<BLOCK>(dst.data, src.data, i, j);
            else
                ProcessPartialBlock(dst.data, src.data, r, c, i, j, BLOCK);
        }
}

我试图优化 r = 10000, c = 500(float 类型)时的最佳情况。在我的本地机器上,128 x 128 瓦片可以加速 2.5 倍。此外,我尝试使用 SSE 来加速转置,但它确实不会显着改变时间。我认为这是因为问题是内存受限的。

以下是 Core2 E4700 2.6GHz 上各种实施的完整时间安排(每个 100 次):

Trivial: 6.111 sec
Blocked(4): 8.370 sec
Blocked(16): 3.934 sec
Blocked(64): 2.604 sec
Blocked(128): 2.441 sec
Blocked(256): 2.266 sec
BlockedSSE(16): 4.158 sec
BlockedSSE(64): 2.604 sec
BlockedSSE(128): 2.245 sec
BlockedSSE(256): 2.036 sec

这是使用的full code

【讨论】:

    【解决方案2】:

    所以,我猜你有一个浮点数/双精度数组。这种设置对于缓存性能已经非常不利。原因是,对于一维数组,编译器可以输出导致预取操作的代码,并且(在非常新的编译器的情况下)生成 SIMD/矢量化代码。使用指针数组,每一步都有一个尊重操作,使预取更加困难。更不用说内存对齐没有任何保证。

    如果这是为了分配而您别无选择,只能从头开始编写代码,我建议您查看 CBLAS 是如何做到的(请注意,您仍然需要将数组“展平”) .否则,您最好使用高度优化的 BLAS 实现,例如 OpenBLAS。它经过近十年的优化,将为您的目标处理器生成最快的代码(调整缓存大小和向量指令集等内容)。

    tl;dr 是使用数组数组无论如何都会导致糟糕的性能。通过使用#define 访问数组元素,展平您的数组并使您的代码易于阅读。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-09
      • 2014-10-24
      • 2019-05-25
      • 2011-09-16
      • 1970-01-01
      • 2016-11-11
      相关资源
      最近更新 更多