在CUDA C Best Practices Guide中,有一小节关于使用带符号和无符号整数的问题。
在C语言标准中,无符号整数溢出的语义定义得很好,而带符号整数溢出会导致未定义的结果。因此,编译器可以比使用无符号算术更加积极地进行优化。这在循环计数器中尤其值得注意:由于循环计数器的值通常是始终为正的,所以可能会诱人将计数器声明为无符号。但是,为了获得稍微更好的性能,它们应该声明为带符号类型。
例如,考虑以下代码:
特别是前两个句子让我困惑。如果无符号值的语义被定义得很好,而带符号值可以产生未定义的结果,那么编译器如何为后者生成更好的代码呢?
在C语言标准中,无符号整数溢出的语义定义得很好,而带符号整数溢出会导致未定义的结果。因此,编译器可以比使用无符号算术更加积极地进行优化。这在循环计数器中尤其值得注意:由于循环计数器的值通常是始终为正的,所以可能会诱人将计数器声明为无符号。但是,为了获得稍微更好的性能,它们应该声明为带符号类型。
例如,考虑以下代码:
for (i = 0; i < n; i++) { out[i] = in[offset + stride*i]; }在这里,子表达式
stride*i
可能会导致32位整数溢出,因此如果i被声明为无符号,溢出语义会阻止编译器使用某些可能适用的优化,例如强度缩减。如果相反,i被声明为带符号,其中溢出语义是未定义的,则编译器具有更大的灵活性来使用这些优化。特别是前两个句子让我困惑。如果无符号值的语义被定义得很好,而带符号值可以产生未定义的结果,那么编译器如何为后者生成更好的代码呢?
tmp
是一个“非常大的数字”(例如长双精度),因此可以在加法后检查它。但这只是一个奇怪的例子,当然取决于实际处理器。 - Omri Barel