或者重新提出问题:使用无符号值是否存在性能损失?
总的来说:iPhone ARM处理器上性能最高的类型(16位签名?,32位签名?等)是什么?
答案 0 :(得分:10)
C99标准允许您回答一般性问题;目标系统上最快的类型高于特定的所需宽度,在stdint.h
中定义。想象一下,我至少需要一个8位宽的整数:
#include <stdio.h>
#include <stdint.h>
int main (int argc, char **argv)
{
uint_fast8_t i;
printf("Width of uint_fast8_t is %d\n", sizeof(i));
return 0;
}
关于有符号或无符号的使用,还有其他要求而不是性能,例如您是否确实需要使用无符号类型或者在溢出的情况下要执行的操作。鉴于我对自己的代码的了解,我愿意打赌除了选择原始整数类型之外,你的代码中还有其他减速; - )。
答案 1 :(得分:10)
总是取决于:
for循环有符号整数作为计数器和限制有点快,因为在C中编译器可以自由地假设溢出永远不会发生。
考虑一下:你有一个带有无符号循环计数器的循环,如下所示:
void function (unsigned int first, unsigned int last)
{
unsigned int i;
for (i=first; i!=last; i++)
{
// do something here...
}
}
在这个循环中,编译器必须确保循环甚至终止,如果first大于last,因为我将在溢出时从UINT_MAX换行为0(仅举一个例子 - 还有其他情况)。这消除了一些循环优化的机会。使用带符号的循环计数器,编译器假定不会发生回绕,可能生成更好的代码。
对于整数除法,无符号整数在ARM上要快一些。 ARM没有硬件除法单元,因此除法在软件中完成,并且总是在无符号值上完成。您将为将有符号的除法转换为无符号除法所需的额外代码保存一些周期。
对于算术,逻辑,加载和写入内存等所有其他内容,符号的选择不会使任何区别。
关于数据大小:正如Rune指出的那样,它们或多或少具有相同的速度,32位类型的速度最快。有时需要在处理后调整字节和字,因为它们驻留在32位寄存器中,而上(未使用)位需要符号或零扩展。
但是,ARM CPU具有相对较小的数据缓存,并且通常连接到相对较慢的内存。如果您能够通过选择较小的数据类型来更有效地利用缓存,即使理论循环计数增加,代码也可以更快地执行。
你必须在这里试验。
答案 2 :(得分:6)
ARM是一种32位架构,因此32位整数是最快的。但是,16位整数和8位整数仅略慢。签名与未签名无关紧要,除非在特殊情况下(如此处其他答案所述)。因此,两个或更多32位操作将模拟64位整数,因此速度较慢。
对于浮点类型,在iPhone处理器(带有VFP硬件浮点的ARM11)中,32位浮点数比64位双倍更快。
答案 3 :(得分:3)
我对Nils的回答很好奇,所以这些问题都是针对他的。这不是原始问题的答案。
在此循环中,编译器必须生成 确保循环甚至终止 第一个比最后一个更大,因为我 将从UINT_MAX换行到0 溢出
for (i=first; i!=last; i++)
{
// do something here...
}
我不认为这样。编译器只需要在每次循环迭代开始时检查i!=last
:
i=first;
if (i == last) goto END;
START:
// do sth
++i;
if (i != last) goto START;
END:
变量的Signess不会改变代码,所以我认为这个例子是错误的。我甚至使用msvc08 / release编译了代码并比较了汇编程序的结果 - 所有签名/未签名的基本相同(跳转类型除外)和!= /&lt;组合
现在,我确实同意编译器可以在某些情况下优化代码,但我想不出任何好的例子 - 如果有人可以做出回应。
我只能想到一个“坏”的例子:
signed i, j, k;
if (i > k)
{
i += j;
if (i > k)
{
}
}
i+= j
可能会溢出,但在C中未定义签名溢出,所以任何事情都会发生。可能会发生两件事:
正如我所说,我确信Nils指出可能存在合理的优化,但据我所知,发布的循环不在其中。
至于原来的问题:
答案 4 :(得分:1)
由于unsigned和signed int具有相同的大小和基本相同的性能,因此在此阶段担心任何可能的优化(如果可能,并且不是),都是邪恶的过早优化(在Google上搜索它)了解更多),甚至在iPhone上。 关于正确性和思想经济性的争论首先出现,除非这是您最重要的执行热点,并且您已经测量了实际的显着性能差异。否则,这个只是浪费你可以通过其他方式花费2倍加速的时间。
<小时/> 编辑:确实,签名溢出的行为是未定义的,并且编译器可以(现在也可以)利用它进行优化,正如HrvojePrgeša在他的回答和@martinkunev在他的评论中指出的那样。