【问题标题】:Simple and fast matrix-vector multiplication in C / C++C / C ++中简单快速的矩阵向量乘法
【发布时间】:2012-08-30 15:11:46
【问题描述】:

我需要经常使用matrix_vector_mult(),它将矩阵与向量相乘,下面是它的实现。

问题:有没有一种简单的方法可以显着提高速度,至少快两倍?

备注:1)矩阵大小约为300x50。期间不变 跑步。 2) 它必须在 Windows 和 Linux 上都可以运行。

double vectors_dot_prod(const double *x, const double *y, int n)
{
    double res = 0.0;
    int i;
    for (i = 0; i < n; i++)
    {
        res += x[i] * y[i];
    }
    return res;
}

void matrix_vector_mult(const double **mat, const double *vec, double *result, int rows, int cols)
{ // in matrix form: result = mat * vec;
    int i;
    for (i = 0; i < rows; i++)
    {
        result[i] = vectors_dot_prod(mat[i], vec, cols);
    }
}

【问题讨论】:

  • 我相信有专门为做点积而设计的 SIMD 指令,但我可能弄错了。
  • @SeanBright,点积确实是瓶颈
  • 通常情况下,很难击败优化的BLAS 实现,而dgemv 似乎正是您要寻找的东西
  • 如果您不想通过使用优化库或手动编码的 SSE/AVX 指令来更改代码,那么编译器开关和 pragma 可以帮助您调整代码。也许Table 4: Compiler Hints for Intra-Register Vectorization 会有所帮助 - software.intel.com/en-us/articles/…
  • 您可以尝试通过同时计算多个结果来使用多个 CPU。

标签: c++ c matrix


【解决方案1】:

这是理论上一个好的编译器应该自己做的事情,但是我尝试使用我的系统 (g++ 4.6.3) 并通过手动展开 4 次乘法(大约 18us 每矩阵而不是每个矩阵 34us):

double vectors_dot_prod2(const double *x, const double *y, int n)
{
    double res = 0.0;
    int i = 0;
    for (; i <= n-4; i+=4)
    {
        res += (x[i] * y[i] +
                x[i+1] * y[i+1] +
                x[i+2] * y[i+2] +
                x[i+3] * y[i+3]);
    }
    for (; i < n; i++)
    {
        res += x[i] * y[i];
    }
    return res;
}

但是,我希望这种级别的微优化的结果在系统之间会有很大差异。

【讨论】:

  • 你能把代码贴在ideone上吗?我自己试过了,没有任何区别(展开的循环甚至有点慢)
  • @LuchianGrigore: ideone.com/JXXtn ,而且运行速度似乎也快了两倍
  • 我猜 MSVS 会自动完成,这就是为什么对我来说没有区别。 +1
  • 这取决于优化级别。在 Ubuntu Lucid 和 -O3 上使用 gcc 4.4 可以将差异降低到大约 20%(从 14us 到 10us)。
  • @LuchianGrigore 使用 g++ -O3 -funroll-loops 已经将它降低到 11.6us 和 10.6us。当然还有更多的空间可以使用编译行选项:-)
【解决方案2】:

正如甄亚所说,只要使用一个好的 BLAS 或矩阵数学库。

如果由于某种原因您不能这样做,请查看您的编译器是否可以展开和/或矢量化您的循环;确保 rowscols 都是调用站点的常量可能会有所帮助,假设您发布的函数可用于内联

如果您仍然无法获得所需的加速,您可以考虑手动展开,并使用扩展或内联汇编器进行矢量化。

【讨论】:

    【解决方案3】:

    如果大小是恒定的并且事先已知,则将其作为预编译器变量传入,这将允许编译器进行更充分的优化。

    【讨论】:

    • 大小由参数实现,在初始化时。我无法对其进行硬编码。
    • 穷人的记忆:编写适合您预期代码的自己的记忆,如果参数恰好是您所期望的,您生产的第一行将直接指向您的函数。
    猜你喜欢
    • 2020-02-28
    • 2015-04-08
    • 1970-01-01
    • 2017-01-25
    • 2011-05-31
    • 2012-11-24
    • 2021-08-30
    • 2016-05-22
    • 1970-01-01
    相关资源
    最近更新 更多