Lapack: LAPACK 的处理器要求

创建于 2021-06-04  ·  16评论  ·  资料来源: Reference-LAPACK/lapack

我们正在将 LAPACK 翻译成 .NET。 我们编写了一个 FORTRAN 编译器,它成功地翻译了所有 LAPACK,包括所有测试。 在实际数据类型上(几乎)所有测试都通过。 在复杂数据上,我们仍然看到一些精度问题。

例子:
XEIGTSTZ < zec.in - 由于 ZLAHQR 中的下溢而失败。
重现步骤:ZGET37 -> knt == 31, ZHSEQR -> ZLAHQR -> 在第二个 QR 步骤(ITS == 2)的末尾,以下代码导致下溢(在某些寄存器上,见下文)

TEMP = H( I, I-1 )
    IF( DIMAG( TEMP ).NE.RZERO ) THEN
        RTEMP = ABS( TEMP)    ! <-- underflow on TEMP = (~1e-0173, ~1e-0173)
        IF (RTEMP .EQ. RZERO) RTEMP = CABS1(TEMP)
        H( I, I-1 ) = RTEMP
        TEMP = TEMP / RTEMP
        IF( I2.GT.I )

我们的编译器针对 .NET CLR。 它的JIT决定对ABS(TEMP)使用SSE寄存器,导致中间计算幅度下溢。 Ifort(作为另一个例子)在这种情况下使用浮点寄存器,因此不会下溢(因为它的长度较大:80 位)。 我试图从 LAPACK 获得一个清晰的(呃)图片,关于它在运行时从编译器/处理器那里需要的精度/数字范围。

是否所有双精度测试都设计为至少需要 64 位寄存器? 或者它们的设计方式是否可以成功用于当今可用的一组流行的 FORTRAN 编译器? (在上述第一种情况下(和其他类似问题)可能需要注意。我应该为他们提交问题吗?)

我寻找了一些规范,但还没有找到。 任何链接也将不胜感激。 提前致谢!

Question

所有16条评论

下溢本身并不是真正的问题。 下溢后,算法切换到 CABS1,它不太容易发生下溢。 造成的问题是 TEMP 不会完全统一,导致 Z 中的四舍五入。

一种可能的解决方案是使用 CABS1 进行预分频,然后使用 ABS 进行校正(由于第一次定标,ABS 不应再溢出)。 (我的机器上没有下溢,所以我不能为你测试)

IF (RTEMP .EQ. RZERO) THEN
    RTEMP = CABS1(TEMP)
    H( I, I-1 ) = RTEMP
    TEMP = TEMP / RTEMP
    RTEMP = ABS( TEMP)
    H( I, I-1 ) = H( I, I-1 )*RTEMP
    TEMP = TEMP / RTEMP
END IF

我认为这些测试肯定是为了成功用于流行的 FORTRAN 编译器集而设计的,因为这就是它们的运行方式。 预测下溢/溢出非常困难。 至少在我的情况下,这些子例程是通过简单地测试它们(使用流行的编译器)并修复我们发现的任何溢出/下溢来设计的。

谢谢! 这非常有帮助。
我们曾尝试使用 CABS1 从下溢中恢复。 但是我们的尝试还远远不够。 你的建议似乎做得更好。 使用 ...

*
*        Ensure that H(I,I-1) is real.
*
         TEMP = H( I, I-1 )
         IF( DIMAG( TEMP ).NE.RZERO ) THEN
            RTEMP = ABS( TEMP)
            IF (RTEMP .EQ. RZERO) THEN 
                RTEMP = CABS1(TEMP)
                H( I, I-1 ) = RTEMP
                TEMP = TEMP / RTEMP
                RTEMP = ABS( TEMP)
                H( I, I-1 ) = H( I, I-1 )*RTEMP
            ELSE 
                H( I, I-1 ) = RTEMP
            END IF
            TEMP = TEMP / RTEMP
            IF( I2.GT.I )
     $         CALL ZSCAL( I2-I, DCONJG( TEMP ), H( I, I+1 ), LDH )
            CALL ZSCAL( I-I1, TEMP, H( I1, I ), 1 )
            IF( WANTZ ) THEN
               CALL ZSCAL( NZ, TEMP, Z( ILOZ, I ), 1 )
            END IF
         END IF
*
  130 CONTINUE

...此迭代成功完成(即使在为 ABS() 使用 SSE 寄存器时)。

我认为这些测试肯定是为了成功用于流行的 FORTRAN 编译器集而设计的,因为这就是它们的运行方式。 预测下溢/溢出非常困难。 至少在我的情况下,这些子例程是通过简单地测试它们(使用流行的编译器)并修复我们发现的任何溢出/下溢来设计的。

测试套件有很大帮助! 我的粗略估计是,只有不到 1% 的测试受到此或类似溢出问题的影响(使用我们的编译器时)。 使针对欠/溢出的测试更加稳健可能有助于将 LAPACK 带到更多平台。 我们上面的(失败的)尝试只是一个例子,它清楚地表明,我们几乎无法提出解决方案。 在打开多个相关问题之前,我想开始讨论,是否对这样的旅程感兴趣,以及什么是好的方法。

感谢@hokb和@thijssteel 的改进! 我应该写一个带有修改的 PR 还是你愿意这样做,@hokb?

鉴于我对该项目的有限经验,我将感谢您的努力以及将您的 PR 作为锅的指南的机会。 我们未来的 PR ......(如果可以?)

@hokb

我寻找了一些规范,但还没有找到。 任何链接也将不胜感激。 提前致谢!

我不确定任何地方都指定了任何内容。

我们的编译器针对 .NET CLR。 它的JIT决定对ABS(TEMP)使用SSE寄存器,导致中间计算幅度下溢。 Ifort(作为另一个例子)在这种情况下使用浮点寄存器,因此不会下溢(因为它的长度较大:80 位)。 我试图从 LAPACK 获得一个清晰的(呃)图片,关于它在运行时从编译器/处理器那里需要的精度/数字范围。

粗体声明:如果所有计算都是使用 IEEE 64 位算法完成的,那么 LAPACK 应该可以工作。

LAPACK 不期望 80 位寄存器随时帮助其计算。 这些算法在设计时考虑了 64 位算术。 现在,正如@thijssteel所提到的,LAPACK 用各种编译器/架构进行了测试,这些编译器/架构有时使用 80 位寄存器,我们可能认为我们的算法一直只需要 64 位,但它们不需要,并且实际上,它们确实需要 80 位。

在解决这些问题的过程中,我们没有做任何系统的事情。 总的来说,当算法通过测试套件时,我们很高兴,并且,如果有来自 80 位寄存器的一些帮助,那就去吧。

是否所有双精度测试都设计为至少需要 64 位寄存器? 或者它们的设计方式是否可以成功用于当今可用的一组流行的 FORTRAN 编译器? (在上述第一种情况下(和其他类似问题)可能需要注意。我应该为他们提交问题吗?)

我的粗略估计是,只有不到 1% 的测试受到此或类似溢出问题的影响(使用我们的编译器时)。

天啊。 1%? 这是一个可怕的大数字。

测试围绕下溢和溢出区域进行了大量测试,因此可以预期这些测试比用户的代码更有可能触发此问题,但仍然如此。

使针对欠/溢出的测试更加稳健可能有助于将 LAPACK 带到更多平台。

可移植到更多平台确实是一种兴趣。 另一个兴趣是使用 GMP 等包扩展精度,据我所知,在整个计算过程中精度是固定的。 (例如,您是 256 位思想,并且没有 300 位寄存器来帮助您。)

我们上面的(失败的)尝试只是一个例子,它清楚地表明,我们几乎无法提出解决方案。 在打开多个相关问题之前,我想开始讨论,是否对这样的旅程感兴趣,以及什么是好的方法。

是的。 我们感兴趣。 然而我们只能做这么多。 我们的盘子里有很多东西。 所以也许我们一次处理一个问题,然后看看我们能走多远。

无论如何,在 GitHub 上发布问题总是一个好主意。 它提高了对问题的认识,并有助于收集帮助和解决问题的想法。

我很高兴我们沿着这条路走下去,但我建议放轻松。

也许,对于 gfortran,我们应该使用标志-mfpmath=sse -msse2进行编译以进行测试。 我认为这将强制所有计算都使用 64 位算法完成。 我不确定。

鉴于我对该项目的有限经验,我将感谢您的努力以及将您的 PR 作为锅的指南的机会。 我们未来的 PR ......(如果可以?)

当然! 请参阅#577。

@weslleyspereira 太棒了! 我仍在检查,如果这同样适用于 CLAHQR。 将尽快发布我的结果(明天)

你好@langou

粗体声明:如果所有计算都是使用 IEEE 64 位算法完成的,那么 LAPACK 应该可以工作。

好的! 我想,“工作”的意思是:当输入“一定范围内”的数据时,它不会因为给定的寄存器大小而溢出?

LAPACK 不期望 80 位寄存器随时帮助其计算。 这些算法在设计时考虑了 64 位算术。 现在,正如@thijssteel所提到的,LAPACK 用各种编译器/架构进行了测试,这些编译器/架构有时使用 80 位寄存器,我们可能认为我们的算法一直只需要 64 位,但它们不需要,并且实际上,它们确实需要 80 位。

在解决这些问题的过程中,我们没有做任何系统的事情。 总的来说,当算法通过测试套件时,我们很高兴,并且,如果有来自 80 位寄存器的一些帮助,那就去吧。

听起来很有道理!

我的粗略估计是,只有不到 1% 的测试受到此或类似溢出问题的影响(使用我们的编译器时)。

天啊。 1%? 这是一个可怕的大数字。

好吧,可能比那“少得多”;)

可移植到更多平台确实是一种兴趣。 另一个兴趣是使用 GMP 等包扩展精度,据我所知,在整个计算过程中精度是固定的。 (例如,您是 256 位思想,并且没有 300 位寄存器来帮助您。)

听起来很有趣,但我不能对此发表评论,因为我缺乏这种固定精度尝试的经验。

是的。 我们感兴趣。 然而我们只能做这么多。 我们的盘子里有很多东西。 所以也许我们一次处理一个问题,然后看看我们能走多远。

我仍然不确定什么是好的通用方法。 如果我的理解太幼稚,请不要理我。 但是上溢/下溢不总是取决于两者:输入数据和算法吗? 因此,与其用新的条件测试和用于从中恢复的新代码淹没代码,不如减少输入数据的“允许范围”? 不过,我对这两种方法所需的工作没有必要的了解。 所以我无法判断什么更可行。

无论如何,在 GitHub 上发布问题总是一个好主意。 它提高了对问题的认识,并有助于收集帮助和解决问题的想法。

好的。 我们将随时提交问题。 我知道在无法重现下溢的情况下提出修复方案将是一个挑战。 那么,我们可以提供哪些信息来使问题更加清晰? 通往具体下溢的路径有帮助吗? 即:提供迭代计数、局部变量的当前值以及文件名等?

我很高兴我们沿着这条路走下去,但我建议放轻松。

这里也一样! :)

#577 的一个结果是 LAPACK 依赖于 FORTRAN 编译器来实现合理稳健(欠/溢出)的复数除法和 ABS()。 我想知道我们是否应该开始维护文档,收集此类和类似的要求? 对于想要将 LAPACK 与其他/新编译器一起使用的人、编译器构建器以及将部分或全部 LAPACK 算法转移到其他语言的人来说,它们将同样重要和有用?

当然! 最好将这些信息记录在案。

首先,我花了一些时间跟踪(也许)表格中的文件LAPACK/SRC/z*.f (COMPLEX*16 算法)中的所有部门

 REAL / COMPLEX   or   COMPLEX / COMPLEX

我一共找到了 53 个文件。 见附件: complexDivisionFound.code-search

  • 为此,我在 Visual Studio Code 中使用了 REGEX 表达式:

    \n .*/ ^0-9 (?!DBLE)(?!REAL)(?!MIN)(?!MAX)[^0-9]

也许,对于 gfortran,我们应该使用标志-mfpmath=sse -msse2进行编译以进行测试。 我认为这将强制所有计算都使用 64 位算法完成。 我不确定。

是的,在使用 GCC 时应该这样做,但默认情况下也应该在 x86-64 上设置此标志。 下面的文档摘录适用于 GCC 11,但更旧的 GCC 版本应该表现出相同的行为。 使用 GNU 编译器集合 (GCC):3.19.59 x86 选项

sse

使用 SSE 指令集中存在的标量浮点指令。 Pentium III 和更新的芯片支持该指令集,Athlon-4、Athlon XP 和 Athlon MP 芯片在 AMD 产品线中支持该指令集。 较早版本的SSE指令集仅支持单精度运算,因此双精度和扩展精度运算仍使用387。较晚的版本仅在Pentium 4和AMD x86-64芯片上支持双精度运算也。

对于 x86-32 编译器,您必须使用-march=cpu-type-msse-msse2开关来启用 SSE 扩展并使该选项生效。 对于 x86-64 编译器,默认情况下启用这些扩展。

在大多数情况下,生成的代码应该快得多,并避免 387 代码的数值不稳定问题,但可能会破坏一些期望临时为 80 位的现有代码。

这是 x86-64 编译器、Darwin x86-32 目标的默认选择,也是启用-ffast-math时带有 SSE2 指令集的 x86-32 目标的默认选择。

例子:
XEIGTSTZ < zec.in - 由于 ZLAHQR 中的下溢而失败。
重现步骤:ZGET37 -> knt == 31, ZHSEQR -> ZLAHQR -> 在第二个 QR 步骤(ITS == 2)的末尾,以下代码导致下溢(在某些寄存器上,见下文)

TEMP = H( I, I-1 )
    IF( DIMAG( TEMP ).NE.RZERO ) THEN
        RTEMP = ABS( TEMP)    ! <-- underflow on TEMP = (~1e-0173, ~1e-0173)
        IF (RTEMP .EQ. RZERO) RTEMP = CABS1(TEMP)
        H( I, I-1 ) = RTEMP
        TEMP = TEMP / RTEMP
        IF( I2.GT.I )

我们的编译器针对 .NET CLR。 它的JIT决定对ABS(TEMP)使用SSE寄存器,导致中间计算幅度下溢。 Ifort(作为另一个例子)在这种情况下使用浮点寄存器,因此不会下溢(因为它的长度较大:80 位)。 我试图从 LAPACK 获得一个清晰的(呃)图片,关于它在运行时从编译器/处理器那里需要的精度/数字范围。

回顾一下:

  • 您将 50 万行 Fortran77 转译为 C#。
  • 使用 .NET 即时编译器时,对转译代码的测试失败。
  • 使用英特尔 Fortran 编译器 (ifort) 时,vanilla LAPACK 代码的测试成功。
  • 观察到的两种情况之间的差异是 ifort 使用 80 位中间体以避免下溢。

正确的?

默认情况下,GCC 只为 x86-64 上的 64 位浮点寄存器生成代码,在我的机器上,除了一两个之外,通常所有 LAPACK 测试都通过。

使用 GCC 编译时,Netlib LAPACK 测试套件是否通过?

编辑:已解决https://github.com/Reference-LAPACK/lapack/pull/577#issuecomment -859496175

也许,对于 gfortran,我们应该使用标志 -mfpmath=sse -msse2 进行编译以进行测试。 我认为这将强制所有计算都使用 64 位算法完成。 我不确定。

我在 MacOS 和 Linux 上使用 GCC 11 尝试了-mfpmath=sse -msse2https : https :

@hokb ,您能否使用 SSE 标志使用 GCC 重现您在https://github.com/Reference-LAPACK/lapack/issues/575#issuecomment -855880000 中提到的溢出问题? 你能帮我解决这个问题吗?

@weslleyspereira我什至没有尝试过 GCC。 我所能访问的/正在运行的设置是 Windows 上的 ifort。 我需要几天时间才能通过 cygwin 启动并运行 GCC 进行测试(尤其是从我目前的假日酒店房间...... :|)如果你需要我接受这个挑战,请告诉我!
至少并且根据https://godbolt.org/z/YYv5oPxe9使用标志不会影响由 gfortran 生成的代码。 但当然只有试运行才能确定......

我不使用窗户,但我这里有。 我将首先在我的 Ubuntu 上使用 ifort 测试 LAPACK,看看会发生什么。 享受假期!

此页面是否有帮助?
0 / 5 - 0 等级