代码之家  ›  专栏  ›  技术社区  ›  Thomas

为什么这个sor解算器的速度依赖于输入?

  •  6
  • Thomas  · 技术社区  · 14 年前

    与我的 other question ,我现在修改了稀疏矩阵解算器以使用sor(逐次过松弛)方法。代码如下:

    void SORSolver::step() {
        float const omega = 1.0f;
        float const
            *b = &d_b(1, 1),
            *w = &d_w(1, 1), *e = &d_e(1, 1), *s = &d_s(1, 1), *n = &d_n(1, 1),
            *xw = &d_x(0, 1), *xe = &d_x(2, 1), *xs = &d_x(1, 0), *xn = &d_x(1, 2);
        float *xc = &d_x(1, 1);
        for (size_t y = 1; y < d_ny - 1; ++y) {
            for (size_t x = 1; x < d_nx - 1; ++x) {
                float diff = *b
                    - *xc
                    - *e * *xe
                    - *s * *xs - *n * *xn
                    - *w * *xw;
                *xc += omega * diff;
                ++b;
                ++w; ++e; ++s; ++n;
                ++xw; ++xe; ++xs; ++xn;
                ++xc;
            }
            b += 2;
            w += 2; e += 2; s += 2; n += 2;
            xw += 2; xe += 2; xs += 2; xn += 2;
            xc += 2;
        }
    }
    

    现在奇怪的是:如果我增加 omega (松弛因子),执行速度开始取决于 戏剧性地 在各种数组中的值上!

    为了 omega = 1.0f ,执行时间或多或少是固定的。为了 omega = 1.8 ,第一次执行此命令通常需要5毫秒。 step() 10次,但在模拟过程中这将逐渐增加到接近100毫秒。如果我设置 omega = 1.0001f ,我看到执行时间相应地略有增加;越高 欧米茄 在模拟过程中,更快的执行时间将增加。

    因为所有这些都嵌入到流体解算器中,所以很难想出一个独立的示例。但我已经保存了初始状态,并在每个时间步上重新运行解算器,以及为实际时间步进行解算。对于初始状态,它是快速的,对于随后的时间步,则是逐渐变慢的。因为所有其他的都是相等的,这证明了这段代码的执行速度取决于这六个数组中的值。

    这在使用g++的ubuntu上是可复制的,在使用vs2008为32位编译时在64位windows 7上也是可复制的。

    我听说NaN和INF值对于浮点计算可能会慢一些,但是不存在NaN或INF。浮点运算的速度是否可能依赖于输入数的值?

    2 回复  |  直到 14 年前
        1
  •  5
  •   Community PPrice    7 年前

    最后一个问题的简短答案是“是”-非规范化(非常接近于零)的数字需要特殊处理,而且速度可能要慢得多。我的猜测是,随着时间的推移,它们正在潜入模拟。请参阅相关的SO帖子: Floating Point Math Execution Time

    将浮点控制设置为flush denormals为零应该可以处理模拟质量上imapct可以忽略的问题。

        2
  •  0
  •   Community PPrice    7 年前

    celion's answer 结果是正确的。他链接到的帖子说,要打开非规范化值的刷新到零:

    #include <float.h>
    _controlfp(_MCW_DN, _DN_FLUSH);
    

    但是,这只是windows。在linux上使用gcc时,我也做了同样的事情,使用了一些内联程序集:

    int mxcsr;
    __asm__("stmxcsr %0" : "=m"(mxcsr) : :);
    mxcsr |= (1 << 15); // set bit 15: flush-to-zero mode
    __asm__("ldmxcsr %0" : : "m"(mxcsr) :);
    

    这是我有史以来第一个x86程序集,因此它可能会得到改进。但这对我有好处。