为什么要将两个32位整数合并成一个64位整数?

15

最近我看到了一种编译器是如何将一个类的两个32位整数属性值合并并存储为64位整数的。我的问题是,为什么要这样做?在合并整数时有什么优势吗?

例如,如果我们有以下的类属性:

class FooBar {
 int x = 1;
 int y = 100;
}

所以,与其
i32 = 00000001
i32 = 01100100

我们得到:

i64 = 0000000101100100

为什么要将它们结合起来?


6
我的猜测是该寄存器使用64位。它只使用一个寄存器来存储两个值,而不是使用两个寄存器。 - R Sahu
2
CPU通常没有类型。像“int”这样的东西是您程序的属性,而不是生成的机器代码的属性。将其称为“组合”并不真正合适,因为前后两者属于完全不同的领域。 - Kerrek SB
4
如果机器使用64位寄存器和内存系统,那么使用32位变量就有些浪费空间了,因此合并这两个变量。然而缺点是,对这些变量进行操作会需要更长的时间,因为当你想要使用它们时,它们必须与0x00FF和0xFF00(+移位)进行AND运算。 - Maliafo
2
很遗憾它不在答案部分,那是它应该呆的地方。 - Lightness Races in Orbit
1
你能举个例子展示一下你所指的实际“合并”吗?例如,如果你在生成的汇编代码中注意到了它,请提供该汇编代码。 - BeeOnRope
显示剩余3条评论
2个回答

11

现有的回答和评论,虽然部分正确,但错失了这种优化的重点。它是用一条指令(使用64位数据)替换两条指令(使用32位数据)。这将导致代码大小略微减小,并可能减少执行时间。

编译器使用一条64位指令初始化两个变量(因为它们共享连续的内存地址)。这些变量是独立的,将分别访问。不需要移位或掩码。

在构造函数中经常看到这种情况,当多个成员被初始化时。一个常见的情况是零初始化,在这种情况下,编译器将清零一个寄存器,然后使用该值来初始化多个成员,通过一次更大的写操作将连续的内存地址组合在一起(例如,通过写入16位短零值而不是两个8位字节)。


我接受了你的答案,因为我觉得它更加完整。 - Asperger
这是一个完美的答案,真的。谢谢 - Asperger
3
我不同意这个观点。为了使用4字节对齐,必须对内存空间进行别名处理。请参阅此链接:https://software.intel.com/en-us/articles/coding-for-performance-data-alignment-and-structures话虽如此,我认为您是正确的,将地址别名为32或64几乎不会影响性能。回答得好。 - Sam
1
@Sam 优化器看到对地址A的4字节写入,紧接着是对地址(A+4)的4字节写入。这些被合并成一个对地址A的8字节写入。_代码不需要或进行任何其他更改。_如果存在对齐或别名问题,则不会进行此优化。 - 1201ProgramAlarm

5

我认为你正在观察一种优化。英特尔指令(例如PADDSW)假设有多个打包的操作数。

https://en.wikipedia.org/wiki/X86_instruction_listings

在64位架构缓存中,只使用1个条目也有好处。

如果您只想要其中一个值,则解包会产生成本,但我怀疑运行的任何代码优化器都会估计到通过打包值可以获得更好的节省。

以前,将C结构的所有成员都对齐到字边界是很正常的。也就是说,单个char和一个int不会被打包,而是对齐到机器的字大小。因此,struct { char, int}sizeof(..) 将为8字节。我猜这种情况发生了改变?

非常有趣。


你可能听说过WebAssembly吧?他们正在那里使用它。你所说的真的很有趣。 - Asperger
PAD...指令用于SIMD。打包的值都是相同类型的。解包没有成本,因为单个值仍然具有正常地址。 - JDługosz

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