调试与发布版本中负零的行为不一致

时间:2013-12-03 00:46:42

标签: cuda

我的印象是,在负零点处添加正零应产生正零。引用IEEE 754 2008:

  

当具有相反符号的两个操作数的总和(或具有相同符号的两个操作数的差异)恰好为零时,除了roundTowardNegative之外,在所有舍入方向属性中该和(或差)的符号应为+0;在该属性下,精确零和(或差)的符号应为-0。但是,即使x为零,x + x = x - ( - x)也保持与x相同的符号。

然而,在CUDA的情况下,编译器似乎过于积极地优化在Release版本中添加正零。普通的C / C ++(或C#/ .NET)正在按预期工作。我已经查看了编译器为不同版本生成的PTX代码,并且发布版本确实缺少add.f32指令。

我在这里遗漏了什么吗?

__global__ void convertToPositiveZero(float* dst, int size)
{
    int index = blockIdx.x * blockDim.x + threadIdx.x;
    if (index < size)
    {
        dst[index] += 0;
    }
}

// Host code
    int size = 100;
    float* zzh = (float*)malloc(size * sizeof(float));
    zzh[0] = -0.0f;
    zzh[1] = 0.0f;
    assert(0x80000000 == *((int*)&zzh[0]));
    if (0x80000000 != *((int*)&zzh[0]))
    {
        printf("Expected negative zero.\n");
        exit(-1);
    }
    assert(0x00000000 == *((int*)&zzh[1]));
    float* zzd;
    cudaMalloc(&zzd, size * sizeof(float));
    cudaMemcpy(zzd, zzh, size * sizeof(float), cudaMemcpyHostToDevice);
    convertToPositiveZero<<<1, 100>>>(zzd, size);
    cudaMemcpy(zzh, zzd, size * sizeof(float), cudaMemcpyDeviceToHost);
    //zzh[0] += 0.0f;
    assert(0x00000000 == *((int*)&zzh[0]));
    if (0x00000000 != *((int*)&zzh[0]))
    {
        printf("Expected positive zero.\n");
        exit(-1);
    }
    assert(0x00000000 == *((int*)&zzh[1]));
    printf("Done.\n");

1 个答案:

答案 0 :(得分:1)

您的问题似乎是由于nvccFADDFMUL融合到FMAD操作时所执行的优化。

我能够在发布模式下重现您的问题。由CUDA 5.5和sm=2.1编译的生成的反汇编代码是

code for sm_21
    Function : _Z21convertToPositiveZeroPfi
.headerflags    @"EF_CUDA_SM20 EF_CUDA_PTX_SM(EF_CUDA_SM20)
/*0000*/        MOV R1, c[0x1][0x100];
/*0008*/        S2R R0, SR_CTAID.X;
/*0010*/        S2R R2, SR_TID.X;
/*0018*/        IMAD R0, R0, c[0x0][0x8], R2;
/*0020*/        ISETP.GE.AND P0, PT, R0, c[0x0][0x28], PT;
/*0028*/    @P0 BRA.U 0x60;
/*0030*/   @!P0 MOV32I R3, 0x4;
/*0038*/   @!P0 IMAD R2.CC, R0, R3, c[0x0][0x20];
/*0040*/   @!P0 IMAD.HI.X R3, R0, R3, c[0x0][0x24];
/*0048*/   @!P0 LD.E R0, [R2];
/*0050*/   @!P0 F2F.F32.F32 R0, R0;
/*0058*/   @!P0 ST.E [R2], R0;
/*0060*/        EXIT ;

正如您在PTX文件中也注意到的那样,没有浮点添加操作。现在,如果使用-fmad=false选项进行编译,则反汇编代码将变为

code for sm_21
     Function : _Z21convertToPositiveZeroPfi
.headerflags    @"EF_CUDA_SM20 EF_CUDA_PTX_SM(EF_CUDA_SM20)
/*0000*/        MOV R1, c[0x1][0x100];
/*0008*/        S2R R0, SR_CTAID.X;
/*0010*/        S2R R2, SR_TID.X;
/*0018*/        IMAD R0, R0, c[0x0][0x8], R2;
/*0020*/        ISETP.GE.AND P0, PT, R0, c[0x0][0x28], PT;
/*0028*/    @P0 BRA.U 0x60;
/*0030*/   @!P0 MOV32I R3, 0x4;
/*0038*/   @!P0 IMAD R2.CC, R0, R3, c[0x0][0x20];
/*0040*/   @!P0 IMAD.HI.X R3, R0, R3, c[0x0][0x24];
/*0048*/   @!P0 LD.E R0, [R2];
/*0050*/   @!P0 FADD R0, R0, RZ;
/*0058*/   @!P0 ST.E [R2], R0;
/*0060*/        EXIT ;

如您所见,恢复了FADD操作的存在,并且还恢复了0的“正确”符号。