CUDA C最佳实践:unsigned vs signed optimization

Bar*_*own 8 c cuda

CUDA C最佳实践指南中,有一小部分关于使用有符号和无符号整数.

在C语言标准中,无符号整数溢出语义被很好地定义,而有符号整数溢出导致未定义的结果.因此,编译器可以使用带符号算法比使用无符号算法更积极地进行优化.循环计数器特别注意这一点:因为循环计数器通常具有始终为正的值,所以将计数器声明为无符号可能很有吸引力.但是,为了稍微提高性能,应将它们声明为signed.

例如,请考虑以下代码:

    for (i = 0; i < n; i++) {  
         out[i] = in[offset + stride*i];  
    }

这里,子表达式stride*i可以溢出32位整数,因此如果i被声明为无符号,溢出语义会阻止编译器使用可能已经应用的某些优化,例如强度降低.如果我声明为signed,那么溢出语义是未定义的,编译器有更多的余地来使用这些优化.

前两句特别让我感到困惑.如果未定义的值的语义被很好地定义并且有符号的值可以产生未定义的结果,那么编译器如何为后者生成更好的代码呢?

Omr*_*rel 11

文本显示了这个例子:

for (i = 0; i < n; i++) {  
     out[i] = in[offset + stride*i];  
}
Run Code Online (Sandbox Code Playgroud)

它还提到了"力量减少".允许编译器将此替换为以下"伪优化-C"代码:

tmp = offset;
for (i = 0; i < n; i++) {  
     out[i] = in[tmp];
     tmp += stride;
}
Run Code Online (Sandbox Code Playgroud)

现在,设想一个只支持浮点数(和整数作为子集)的处理器.tmp将是"非常大的数字"类型.

现在,C标准说涉及无符号操作数的计算永远不会溢出,而是以最大值+ 1的模数减少.这意味着在无符号i的情况下,编译器必须这样做:

tmp = offset;
for (i = 0; i < n; i++) {  
     out[i] = in[tmp];
     tmp += stride;
     if (tmp > UINT_MAX)
     {
         tmp -= UINT_MAX + 1;
     }
}
Run Code Online (Sandbox Code Playgroud)

但是在有符号整数的情况下,编译器可以做任何想做的事情.它不需要检查溢出 - 如果它确实溢出那么它是开发人员的问题(它可能导致异常,或产生错误的值).所以代码可以更快.