【问题标题】:How can I optimise array multiplication by constant?如何通过常数优化数组乘法?
【发布时间】:2015-03-13 18:01:18
【问题描述】:

给定以下代码,其中 a、b、c、d 等是常量:

Data[] dataArray;
Intermediate[] interArray;
Output[] outputArray;

for (int i = 0; i < length; i++)
{
  interArray[i] = (c * dataArray[i]) + (a * dataArray[i+1]);
  interArray[i] -= (b * interArray[i - 1]) + (d * interArray[i - 2]);
  outputArray[i] = interArray[i];
}

for (int i = ln-1; i > 0; i--)
{
  interArray[i - 1] = (e * dataArray[i]) + (f * dataArray[i-1]);
  interArray[i - 1] -= (g * interArray[i]) + (h * interArray[i+1]);
  outputArray[i] += interArray[i]; 
} 

如何优化?

我只想遍历数组一次。不幸的是,我依赖于这样一个事实,即第二个循环需要 interArray 由第一个循环填充。

我想这样做的原因是因为这个过程占用了我总运行时间的 20%,我正在尝试优化它。数组可能非常大,类型通常是大型 POD。我假设我正在进入缓存垃圾领域,这就是为什么我试图减少我走过阵列的次数。没有 * 运算符,它只是标准乘法。

注意:我知道数组的上限和下限在此处崩溃并烧毁,因为超出了界限。我会手动处理这些。

任何建议将不胜感激!可能我做不到更快,但我想至少尝试一下!

【问题讨论】:

  • 您是否在发布时对此进行测量? - 只是为了检查。
  • 您可以尝试使用 simd,或在编译器中启用自动矢量化。但我的猜测是你已经受到内存带宽的限制(一个好的分析器可以告诉你这一点)。您可以随时尝试交织您的值以可能会有所改善。
  • @LogicStuff:是的。我只是有一个想法——我想我可以先发制人地计算出我需要的值,然后一次性完成?
  • interArray[i] 依赖于 interArray[i - 1]interArray[i - 2] 所以向量化这个并不容易
  • 说到缓存,如果您的阵列不在同一缓存行上竞争,它可能会有所帮助。阅读 Sutter 关于它的文章(它与线程相关,但也可能在这里工作):drdobbs.com/parallel/eliminate-false-sharing/217500206

标签: c++ arrays optimization


【解决方案1】:

我不确定这是否会为您节省大量时间,但我认为您可以通过扩展条款一次完成计算。与 8 次乘法相比,这可以减少到 6 次乘法和累加。此外,您不需要中间数组。它看起来像这样(请仔细检查这个的扩展)

Data[] dataArray;
Output[] outputArray;
auto DMinus2 = -c * d;
auto DMinus1 = -a - b*c;
auto D = c - a * b + f;
...

for (int i = 0; i < length; i++)
{
    outputArray[i] = DMinus2 * dataArray[i-2] + 
        DMinus1[i-1] * dataArray[i-1] +
        D * dataArray[i] + ....
        DPlus3 + dataArray[i+3];
}

编辑:

首先,抱歉,我的第一个答案并不完全正确。不过,我相当有信心简化循环。

例如在第一个循环中

interArray[i] = (c * dataArray[i]) + (a * dataArray[i+1]);
interArray[i] -= (b * interArray[i - 1]) + (d * interArray[i - 2]);
outputArray[i] = interArray[i];

可以简化为

interArray[i] = (c * dataArray[i]) + (a * dataArray[i+1]) -
    (b * interArray[i - 1]) + (d * interArray[i - 2]);
outputArray[i] = interArray[i];

我假设范围之外的值是 0 考虑 i = 0 那么我们就有了

outputArray[0] = (c * dataArray[0]) + (a * dataArray[1]);

i = 1 给出

outputArray[1] = (c * dataArray[1]) + (a * dataArray[2]) - 
    b * outputArray[0];

i = 2 给出

outputArray[2] = (c * dataArray[2]) + (a * dataArray[3]) - 
    b * outputArray[1] - d * outputArray[0];

所以,我认为我们可以推广第一个循环来移除中间数组

outputArray[i] = (c * dataArray[i]) + (a * dataArray[i+1]) - 
    b * outputArray[i-1] - d * outputArray[i-2];

第二个循环也应该如此。再次复习我的数学后,我不完全确定是否有可能将这两个循环结合起来。我会继续考虑这个,因为可能有办法做到这一点。希望删除中间存储会有所帮助。

【讨论】:

  • 这看起来很有趣,但我真的不明白它背后的数学原理:(我会试着弄清楚,看看我是否可以实现它。
  • 你能详细说明你是怎么想到这个的吗?
猜你喜欢
  • 1970-01-01
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-28
  • 2020-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多