我正在阅读手册并提出这个建议,但没有提到原因。
为什么无符号类型更快?
答案 0 :(得分:20)
在ARMv4之前,ARM没有本机支持加载半字和有符号字节。要加载已签名的字节,您必须LDRB
然后签名将值{LSL
向上扩展,然后ASR
向下扩展。这很痛苦,因此char
默认为unsigned
。
在ARMv4中添加了指令以处理半字和有符号值。这些新指令必须挤入可用的指令空间。对可用空间的限制意味着它们不能像原始指令一样灵活,原始指令在加载值时能够进行各种地址计算。
因此,您可能会发现LDRSB
无法将内存中的提取与地址计算相结合,而LDRB
则可以。这可能是成本周期。有时我们可以重做short
- 繁重的代码来对ints
对进行操作以避免这种情况。
我的网站上有更多信息:http://www.davespace.co.uk/arm/efficient-c-for-arm/memaccess.html
答案 1 :(得分:2)
我认为只是ARM CPU的指令集针对unsigned进行了优化。对于无符号类型,可以使用一条指令完成某些操作,但如果已签名则需要多条指令。这就是为什么我认为如果在大多数(所有?)C和C ++编译器中编译ARM,它默认为unsigned char而不是更常用的signed char。
答案 2 :(得分:2)
我能想到的无符号类型的唯一优点是除法和模数实现可能稍微快一些,你可以做if (unsigned_value < limit)
而不是if (signed_value >= 0 && signed_value < limit)
的测试。
我怀疑您的手册可能已过期。今天使用的任何ARM都有v4或更高版本的指令集,我很确定没有指令可以更快或更慢,具体取决于签名。
在较旧的ARM上,我认为有符号的乘法可能会更慢;我认为提前终止只查找顶部位中的全零,而不是所有的零,因此涉及负数的乘法总是占用最大时间。虽然这取决于值,而不取决于类型是签名还是未签名。至少在ARMv4及更高版本中,提前终止适用于负值。
另外,我认为早期的ARM无法加载单个字节,只能加载一个字。所以你需要两个指令来加载一个无符号字节,三个用于加载一个带符号的字节:
ldr r0, [r1]
and r0, r0, #0xff
与
ldr r0, [r1]
mov r0, r0, asl #24
mov r0, r0, asr #24 ; but this could maybe be combined with later instructions
与(这些天)ldrb r0, [r1]
或ldrsb r0, [r1]
进行单字节加载。
在现代处理器上,使用无符号类型不太可能对性能产生可测量的影响。使用最合理的类型,然后在确定任何性能瓶颈后详细查看代码。