Cuda 有符号 128 位乘法错误

Cuda signed 128-bit multiplication error

我想我在 cuda PTX 中使用有符号整数进行 128 位有符号乘法时发现了一个问题。 这是我的示例代码:

long long result_lo, result_hi;
asm(" mul.lo.s64 %0, 0, -1;     \n\t" // 0 * -1 = 0
    " mul.hi.s64 %1, 0, -1;     \n\t"
    : "=l"(result_lo), "=l"(result_hi));

这应该会产生结果 result_lo = 0x0, result_hi = 0x0。然而,这会产生结果:result_lo = 0x0, result_hi = 0xFFFFFFFFFFFFFFFF 如果我没记错的话,它实际上是值 2^127 - (2^126 - 1) 并且显然不为零。

首先,我想确保我的理解是正确的,但还有,有没有办法解决这个问题?

UpdateDebug mod 更改为 Release mode 修复了这个问题,仍然想知道这是否是一个cuda 中的错误?

更新 2 已将此错误报告给 NVIDIA

在 Visual Studio 2013 中使用了 Cuda 工具包 7.5。x64 Debugsm_52compute_52

TL;DR 这似乎是 PTX 指令 mul.hi.s64 仿真中的错误,该指令特定于 sm_5x 平台,因此提交向 NVIDIA 报告错误是推荐的做法。

通常,NVIDIA GPU 是 32 位架构,因此所有 64 位整数指令都需要仿真序列。在 64 位整数乘法的特殊情况下,对于 sm_2xsm_3x 平台,这些是从机器代码指令 IMAD.U32 构造的,它是一个 32 位整数乘加指令.

对于 Maxwell 架构(即 sm_5x),引入了高吞吐量但宽度较低的整数乘加指令 XMAD,尽管低吞吐量遗留 32 -bit integer multipy IMUL 显然被保留了下来。检查使用 cuobjdump --dumpsass 的 CUDA 7.5 工具链为 sm_5x 生成的反汇编机器代码表明,对于 ptxas 优化级别 -O0(用于调试构建),64-使用 IMUL 指令模拟位乘法,而对于优化级别 -O1 和更高级别,使用 XMAD。我想不出使用两个根本不同的仿真序列的原因。

事实证明,mul.hi.s64sm_5x 的基于 IMUL 的仿真被破坏,而基于 XMAD 的仿真工作正常。因此,一种可能的解决方法是通过在 nvcc 命令行上指定 -Xptxas -O1,为 ptxas 使用至少 -O1 的优化级别。请注意,默认情况下发布版本使用 -Xptxas -O3,因此发布版本不需要更正操作。

从代码分析来看,mul.hi.s64 的仿真是作为 mul.hi.u64 仿真的包装器实现的,后一种仿真似乎在包括 sm_5x 在内的所有平台上都能正常工作。因此,另一种可能的解决方法是在 mul.hi.u64 周围使用我们自己的包装器。在这种情况下,使用内联 PTX 编码是不必要的,因为 mul.hi.s64mul.hi.u64 可以通过设备内部函数 __mul64hi()__umul64hi() 访问。从下面的代码可以看出,将结果从无符号乘法转换为有符号乘法的调整非常简单。

    long long int m1, m2, result;
#if 0 // broken on sm_5x at optimization level -O0
    asm(" mul.hi.s64 %0, %1, %2;     \n\t"
        : "=l"(result)
        : "l"(m1), "l"(m2));
#else
    result = __umul64hi (m1, m2);
    if (m1 < 0LL) result -= m2;
    if (m2 < 0LL) result -= m1;
#endif