使用32位的int类型而不是8位的char类型来“协助”处理器

3
在C语言中,我们通常使用char来表示小型数字。然而,处理器总是使用Int(即32位)值从寄存器中读取(或获取)数据。因此,每次我们的程序需要使用char或8位时,处理器都需要从寄存器中获取32位,并从中“解析”出8位。
因此,如果内存不是限制,更经常地使用Int代替char是否有意义呢?这会对处理器有所“帮助”吗?

2
这取决于很多事情。档案是其中之一。 - David Heffernan
4个回答

3

有编译器部分和CPU部分。

如果您告诉编译器您正在使用char而不是int,在静态分析期间,它将知道变量的边界在0-255之间,而不是0-(2 ^ 32-1)。这将使它更好地优化您的程序。

在CPU方面,您的假设并不总是正确的。以x86为例,它具有寄存器eaxal,用于32位和8位寄存器访问。如果您只想使用chars,则使用al就足够了。没有性能损失。

针对下面的评论,我进行了一些简单的基准测试:

al:

format PE GUI 4.0

xor ecx, ecx
dec ecx
loop_start:
inc al
add al, al
dec al
dec al
loopd short loop_start
ret

eax:

format PE GUI 4.0

xor ecx, ecx
dec ecx
loop_start:
inc eax
add eax, eax
dec eax
dec eax
loopd short loop_start
ret

时间:

$ time ./test_al.exe 
./test_al.exe  0.01s user 0.00s system 0% cpu 7.102 total
$ time ./test_eax.exe 
./test_eax.exe  0.01s user 0.01s system 0% cpu 7.120 total

所以在这种情况下,al 稍微快一些,但有时 eax 会更快。差异真的微不足道。但是 CPU 并不是那么简单,可能存在代码对齐问题、缓存和其他一些问题,因此最好对自己的代码进行基准测试,以查看是否有性能提升。但我认为,如果你的代码不是非常紧密,最好相信编译器来优化。


这并不是那么简单。在ARM中,如果你使用char,那么转换为int在时间和程序空间上都是代价高昂的。因此,在RAM/flash内存使用和性能之间存在权衡。 - user694733
2
没有任何证据表明对 eaxal 的操作速度相同。 - Potatoswatter
实际上,在x86中使用小于32位的值在许多情况下仍会降低性能。您可以自行验证。 - phuclv
@LưuVĩnhPhúc 运行了一些基准测试,请查看更新的答案。 - simonzack
你的代码很简单,因此性能可能没有太大差别。但在更复杂的代码中,使用8位或16位寄存器可能会导致CPU等待,因为高位存在依赖关系。这就是为什么大多数x64指令将32位寄存器的上半部分清零 - phuclv
显示剩余4条评论

2
如果我是你,我会坚持使用int作为你的平台上最本地的整数类型。内部上,你可以期望较短的类型被转换为int,因此实际上会降低性能。
你永远不应该使用char并期望它在各个平台上保持一致。虽然C标准定义sizeof(char)为1,但char本身可以是signed或unsigned。选择取决于编译器。
如果你相信你可以通过使用8位类型来挤出一些性能增益,那么请明确使用signed char或unsigned char。

2
我在我的代码中注意到,在amd和x86机器上,char被视为有符号的,而当我在powerpc上工作时,char被视为无符号的。所以现在我只是明确地声明signed charunsigned char。那么问题是什么?;) 编辑你的编辑:编译器定义,但选择由环境(特别是CPU架构)决定。 - dhein
哎呀,漏掉了那一部分。 - Bathsheba

1
如果您的程序足够简单,优化器可以在您不必担心的情况下做正确的事情。在这种情况下,普通的int将是最简单(也是前瞻性的)解决方案。
但是,如果您真的非常想要结合特定的位宽和速度,您可以使用C99标准中的7.18.1.3最快的最小宽度整数类型(需要符合C99的编译器)。
例如:
int_fast8_t x;
uint_fast8_t y;

signed和unsigned是保证能够存储至少8位数据并使用通常更快的基础类型的类型。当然,这完全取决于您之后对数据的处理方式。

例如,在我测试过的所有系统上(请参见:C ++中的标准类型大小),快速类型都是8位长的。


1

来自ARM系统开发人员指南:

大多数ARM数据处理操作仅支持32位。因此,尽可能使用32位数据类型int或long作为本地变量。即使您正在操作8位或16位值,也应避免使用char和short作为本地变量类型。

以下是书中的示例代码以证明这一点。请注意,与unsigned int相比,char的环绕处理方式不同。

int checksum_v1(int *data)
{
char i;
int sum = 0;
for (i = 0; i < 64; i++)
{
sum += data[i];
}
return sum;
}

使用char类型时的ARM7汇编

checksum_v1
MOV r2,r0 ; r2 = data
MOV r0,#0 ; sum = 0
MOV r1,#0 ; i = 0
checksum_v1_loop
LDR r3,[r2,r1,LSL #2] ; r3 = data[i]
ADD r1,r1,#1 ; r1 = i+1
AND r1,r1,#0xff ; i = (char)r1
CMP r1,#0x40 ; compare i, 64
ADD r0,r3,r0 ; sum += r3
BCC checksum_v1_loop ; if (i<64) loop
MOV pc,r14 ; return sum

当i是无符号整数时,ARM7汇编。

checksum_v2
MOV r2,r0 ; r2 = data
MOV r0,#0 ; sum = 0
MOV r1,#0 ; i = 0
checksum_v2_loop
LDR r3,[r2,r1,LSL #2] ; r3 = data[i]
ADD r1,r1,#1 ; r1++
CMP r1,#0x40 ; compare i, 64
ADD r0,r3,r0 ; sum += r3
BCC checksum_v2_loop ; if (i<64) goto loop
MOV pc,r14 ; return sum

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