CUDA C最佳实践:无符号 vs 有符号优化

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

14

这段文本展示了一个例子:

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

它还提到了“强度降低”。编译器可以将其替换为以下“伪优化C”代码:

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

现在,想象一下一个仅支持浮点数(且整数为其子集)的处理器。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;
     }
}

但是在有符号整数的情况下,编译器可以随意处理。它不需要检查溢出-如果溢出了,那么就是开发人员的问题(可能会导致异常或产生错误值)。因此,代码可以更快。


1
编译器在执行加法之前必须检查溢出,否则将会产生未定义行为。 - K-ballo
1
@K-ballo,场景本来就是虚构的...我的假设是tmp是一个“非常大的数字”(例如长双精度),因此可以在加法后检查它。但这只是一个奇怪的例子,当然取决于实际处理器。 - Omri Barel
为什么不让无符号整数绕过来,就像有符号整数一样,归咎于程序员呢? - Barry Brown
1
@BarryBrown 根据标准,无符号整数会自动绕回。关键是有符号整数甚至不能保证“绕回”,程序可能会在有符号溢出时崩溃。区别在于标准规定了一种情况下要做什么,而将另一种情况留作“未定义行为”。 - Omri Barel

2

这是因为C语言对于无符号整数的定义限制了编译器编写者在处理溢出时的操作。相比之下,对于有符号整数溢出时的处理方式更加灵活。可以说,编译器编写者在处理有符号整数时有更多的操作空间。

这是我理解的意思。


1
< p>在不支持 C 定义的所有字长的处理器上,有符号和无符号的语义差异对性能变得重要。例如,假设您拥有一个仅支持 32 位操作并具有 32 位寄存器的 CPU,并编写了一个使用 int(32 位)和 char(8 位 *)两种数据类型的 C 函数:

int test(char a) {
  char b = a * 100;
  return b;
}

由于CPU只能在32位寄存器中存储char并且只能对32位值执行算术运算,因此它将使用32位寄存器来保存b,并进行32位乘法运算。

由于C标准规定有符号整数溢出会导致未定义的结果,因此编译器可以为上述函数创建代码,当a大于2时返回一个高于127的值。

然而,如果使用无符号值:

unsigned int test(unsigned char a) {
  unsigned char b = a * 100;
  return b;
}

C标准定义了无符号操作的溢出语义,因此编译器将不得不添加掩码操作,以确保即使a大于2,函数也不会返回高于255的值。

* C语言规范允许 char 的宽度超过8位,但这会破坏许多程序,因此在本例中我们假定编译器使用8位值来表示 char


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接