【问题标题】:Performance issue in Finite difference method有限差分法的性能问题
【发布时间】:2016-09-27 03:46:01
【问题描述】:

我写了一段 C 代码,它使用有限差分法来估计值。这是一种平均方法。我分析了代码,发现一个iterate() 函数是最慢的。

void iterate(double data[][ARRAY_SIZE], int nx, int ny, int dx, int dy)
{
    for (int i = 0; i < nx; ++i)
    {
        for (int j = 0; j < ny; ++j)
        {
            if (i % (dx + 1) == 0 && j % (dy + 1) == 0)
                continue;
            else if (i == 0 && 0 < j && j < ny)
                data[i][j] = (data[i][j - 1] + data[i][j + 1] + data[i + 1][j]) / 3;
            else if (j == 0 && 0 < i && i < nx)
                data[i][j] = (data[i - 1][j] + data[i + 1][j] + data[i][j + 1]) / 3;
            else if (i == nx - 1 && 0 < j && j < ny)
                data[i][j] = (data[i][j - 1] + data[i][j + 1] + data[i - 1][j]) / 3;
            else if (j == ny - 1 && 0 < i && i < nx)
                data[i][j] = (data[i - 1][j] + data[i + 1][j] + data[i][j - 1]) / 3;
            else
                data[i][j] = (data[i - 1][j] + data[i + 1][j] + data[i][j - 1] + data[i][j + 1]) / 4;
        }
    }
}

此循环运行缓慢,我不确定我在这里缺少什么使其变慢。有没有更好的方法来做同样的事情?

使用 400x400 double 数组进行 2000 次迭代

real    0m1.950s
user    0m1.940s
sys 0m0.004s

【问题讨论】:

  • 您能否给我们一组典型的输入以及运行它需要多长时间?
  • 你用-O3编译吗?
  • 是的,我用OfastO3编译
  • 您可能希望至少处理内部循环之外的一些边缘情况。

标签: c performance loops multidimensional-array


【解决方案1】:

这里有一些想法:

  1. 看来ny 必须等于ARRAY_SIZE。您也可以将其作为参数省略,而只使用编译时常量。
  2. 除最后一个之外的所有 if/else 子句仅适用于特定的行或列。所以把它们吊起来。例如,您可以将第一行和第一列作为一维循环处理,然后再在边缘之外处理整个矩阵,然后最后处理最右边的列和底行。

最后,你的核心循环应该更像这样:

for (int i = 1; i < nx - 1; ++i)
{
    for (int j = 1; j < ARRAY_SIZE - 1; ++j)
    {
        data[i][j] = (data[i - 1][j] + data[i + 1][j] + data[i][j - 1] + data[i][j + 1]) / 4;
    }
}

【讨论】:

  • 交换ji 的顺序得到2.269s
  • 先迭代外部维度,然后迭代内部维度。他的 for 循环的顺序是正确的,但您的其他提示都很好。
  • @cmaster:我删除了那个,感谢您指出我错了。
  • 好吧,我按照你的提示猜猜是什么......同样的模拟需要1.252s
【解决方案2】:

考虑这个实现:

void iterate(double data[][ARRAY_SIZE], int nx, int ny, int dx, int dy)
{
    // because nx - 1 and ny - 1 are used
    nx--;
    ny--;
    // because dx + 1 and dy + 1 are used
    dx++;
    dy++;

    int i = 0;
    int j = 0;

    // case i == 0 && 0 < j && j < ny
    for (j = 1; j < ny; ++j)
    {
        if (j % dy)
            data[0][j] = (data[i][j - 1] + data[i][j + 1] + data[i + 1][j]) / 3.0;
    }

    j = 0;

    // case j == 0 && 0 < i && i < nx
    for (i = 1; i < nx; ++i)
    {
        if (i % dx)
            data[i][0] = (data[i - 1][j] + data[i + 1][j] + data[i][j + 1]) / 3.0;
    }

    // default case
    for (i = 1; i < nx; ++i)
    {
        for (j = 1; j < ny; ++j)
        {
            if (i % dx || j % dy)
                data[i][j] = (data[i - 1][j] + data[i + 1][j] + data[i][j - 1] + data[i][j + 1]) * 0.25;
        }
    }

    // case i == nx && 0 < j && j < ny
    for (j = 1; j < ny; ++j)
    {
        if (nx % dx || j % dy)
            data[nx][j] = (data[i][j - 1] + data[i][j + 1] + data[i - 1][j]) / 3.0;
    }

    // case j == ny && 0 < i && i < nx
    for (i = 1; i < nx; ++i)
    {
        if (ny % dy || i % dx)
            data[i][ny] = (data[i - 1][j] + data[i + 1][j] + data[i][j - 1]) / 3.0;
    }
}

主要的三点是:

  1. 为双 for 循环减少内循环的操作量
  2. 只需执行一次即可减少琐碎操作的数量
  3. 不要混合数据类型和强制强制(使用/ 3.0* 0.25

我的代码中唯一没有解释的是i % dx || j % dy 等于!(i % dx == 0 &amp;&amp; j % dy == 0)

【讨论】:

  • 我在这里几乎实现了相同的功能,谢谢。 gist.github.com/nishanthkarthik/…
  • 哦,有一些优点。我可以添加更多优化。让我试着让你不到一秒钟;)
  • @KarthikNishanth 我在您的代码中注意到一件事:您是否假设 (nx - 1) % (dx + 1) != 0ny / dy 相同?
  • 我根据你的建议更新了我的代码,获得了方式更好的结果(可视化时看起来更好),你帮助我保存了1s
猜你喜欢
  • 2015-08-24
  • 2018-02-28
  • 1970-01-01
  • 2018-01-27
  • 1970-01-01
  • 1970-01-01
  • 2023-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多