64位机器上的C++ int和long long有什么区别?

26

我的电脑有64位处理器,当我查找sizeof(int)sizeof(long)sizeof(long long)时,结果显示intlong是32位,而long long是64位。我进行了研究,发现流行的假设认为C++中的int适合于机器的字长是错误的。据我所知,编译器定义类型大小取决于它自己,而我的编译器是Mingw-w64。我进行这项研究的原因是要理解如果使用比字长更小的类型是否有利于速度(例如,short vs int),或者是否会产生负面影响。在32位系统中,一个流行的观点是:由于字长是int,所以short将转换为int,这将导致额外的位移等操作,从而导致性能降低。反对意见是,在缓存级别上会有好处(我没有深入研究),使用short将有助于虚拟内存的经济性。因此,除了困扰这个问题之外,我还面临着另一个问题。我的系统是64位的,无论我使用int还是short,它仍然小于字长,并且我开始思考是否使用64位的long long会更有效,因为它正好处于系统设计的级别上。我还读到另一个限制,即操作系统的库(ILP64、LP64)定义了类型大小。在ILP64中,默认情况下int是64位的,与LP64相反,如果我使用支持ILP64的操作系统,程序会加速吗?一旦我开始询问应该使用哪种类型来加速我的C++程序,我就面临了更深入的话题,其中有些解释似乎相互矛盾,而我又没有这方面的专业知识。请问:

1) 使用 long long 在 x64 系统中是否是实现最大性能的最佳实践,即使用于处理 1-4 字节数据?

2) 在使用小于字长的数据类型时存在折衷(内存优势 vs 需要额外操作)。

3) 在x64系统中,字和int大小均为64位,是否有可能通过所谓的向后兼容性来处理短型数据,例如使用16位字长?或者必须将16位文件放入64位文件中,能够完成这项操作定义该系统具有向后兼容性。

4) 我们可以强制编译器将 int 定义为64位吗?

5) 如何将 ILP64 结构引入使用 LP64 的个人计算机中?

6) 使用适应上述问题的代码可能会在其他编译器、操作系统和架构(32位处理器)上出现哪些问题?


2
永远不要依赖于标准数据类型具有特定的大小。C++11具有固定宽度整数类型来解决这个问题。(在C++11之前,有专门针对编译器的类型来处理这个问题) - UnholySheep
你可以拥有一个512位的CPU,而16位的int仍然是100%标准兼容的。为什么有人会这样做超出了我的理解,但它仍然是合法的。 - user4581301
1个回答

51

1) 如果在x64中使用long long以实现最大性能,即使对于1-4字节的数据,这是否是最佳实践?

不是最佳实践,实际上可能会降低性能。例如,如果您在可以使用32位整数的情况下使用64位整数,则刚刚将必须在处理器和内存之间发送的数据量加倍,而内存速度要慢得多。所有缓存和内存总线都将两倍崩溃。

2) 使用小于字长的类型的权衡(内存优势与额外操作)

通常,在现代计算机中,性能的主要驱动因素将是运行程序所需存储的数据量。一旦程序的工作集大小超过寄存器、L1高速缓存、L2高速缓存、L3高速缓存和RAM的容量,你将看到显著的性能下降。

此外,如果编译器足够聪明,能够找出如何使用处理器的矢量指令(也称为SSE指令),使用较小的数据类型可能会更好。现代矢量处理单元足够聪明,可以将8个16位短整数压缩到与两个64位long long整数相同的空间中,因此您可以同时执行四倍的操作。

3) 在使用64位字和整数的x64计算机上,是否可以通过所谓的向后兼容性使用16位字大小来处理short?或者必须将16位文件放入64位文件中,并且能够这样做的事实定义了系统为向后兼容。

我不确定您在此处询问的是什么。一般来说,64位计算机能够执行32位和16位可执行文件,因为这些早期的可执行文件使用了64位计算机潜力的子集。

硬件指令集通常是向后兼容的,这意味着处理器设计人员倾向于添加功能,但很少或从不删除功能。

4) 我们能强制编译器使int为64位吗?

所有编译器都有相当标准的扩展,允许您使用固定位大小的数据。例如,头文件stdint.h声明了类型,如int64_tuint64_t等。

5) 如何将ILP64合并到使用LP64的PC中?

https://software.intel.com/en-us/node/528682

6) 使用适应上述问题的代码与其他编译器、操作系统和架构(32位处理器)可能存在哪些问题?

通常情况下,编译器和系统都足够聪明,可以在任何给定的系统上执行您的代码。然而,32位处理器需要额外的工作才能处理64位数据。换句话说,正确性不应该是一个问题,但性能会受到影响。

但通常情况下,如果性能对您来说真的很重要,那么您需要为特定的架构和平台编程。

澄清请求:非常感谢! 我想澄清第1个问题。您说它对内存有害。让我们以32位int为例。当您将其发送到内存时,因为它是64位系统,对于所需的整数0xee ee ee ee,当我们发送它时,它不会变成0x ee ee ee ee + 32个其他位吗?当字长为64位时,处理器如何发送32位?32位是期望的值,但它不会与32个未使用的位组合并以这种方式发送吗?如果我的假设是正确的,那么对于内存就没有区别了。

这里有两件事需要讨论。

首先,你所讨论的情况并不存在。处理器不需要将32位值“提升”为64位值以便适当使用它。这是因为现代处理器具有不同的访问模式,能够适当地处理不同大小的数据。

例如,64位英特尔处理器有一个名为RAX的64位寄存器。但是,通过将其称为EAX,甚至在16位和8位模式下,同样的寄存器可以在32位模式下使用。我从这里偷了一张图:

x86_64 registers rax/eax/ax/al overwriting full register contents

1122334455667788
================ rax (64 bits)
        ======== eax (32 bits)
            ====  ax (16 bits)
            ==    ah (8 bits)
              ==  al (8 bits)

在编译器和汇编器之间,生成正确的代码以便适当处理32位值。
其次,当我们谈论内存开销和性能时,应该更加具体。现代内存系统由磁盘、主存储器(RAM)和通常两到三个高速缓存(例如L3、L2和L1)组成。可以在磁盘上寻址的最小数据量称为“页面”,页面大小通常为4096字节(尽管不必如此)。然后,在内存中可以寻址的最小数据量称为“缓存行”,它通常比32位或64位大得多。在我的电脑上,缓存行大小为64字节。处理器是唯一实际传输数据并在字级别及以下地址的地方。
因此,如果您想要更改存储在磁盘上的一个64位单词,则在我的计算机上,这实际上需要将4096字节从磁盘加载到内存中,然后将64字节从内存加载到L3、L2和L1缓存中,然后处理器从L1缓存中取出一个64位单词。
结果是字长对内存带宽毫无意义。然而,你可以在相同的空间里放入16个32位整数或8个64位整数。或者你甚至可以在相同的空间里放置32个16位值或64个8位值。如果你的程序使用了许多不同的数据值,你可以通过使用最小的数据类型显著提高性能。

4
这个回答所付出的努力远远超过了提问时的努力。太棒了! - fche
4
也许这是一个复杂的问题,需要大量信息才能完整回答。 - stu
3
我仍不明白为什么人们通常建议使用4字节整数而不是2字节短整数,并且理由是4字节是处理器的自然字长,因此通常可以产生最优性能。但是你上面说在今天的64位处理器世界中,你不想在所有地方都使用8字节整数。你如何调和这两种观念?请注意,我这里纯粹是指性能,而不是内存占用。 - Siddhartha Gandhi
1
@SiddharthaGandhi 我认为通常不建议使用4字节整数。这只是许多编译器默认的整数大小而已。对于只有几个变量的程序,本答案中的考虑因素并不相关。然而,处理大量数据的程序员绝对关心并选择适当大小的数字类型用于他们的应用程序。我认为4字节变量很常见,因为2字节变量在许多情况下太小了,具有0-65,000或-32,000-32,000(取决于符号)的限制。 - David
1
这些链接都没有说你应该使用4字节整数,它们说在2字节和4字节整数之间的选择大多是不重要的,除非它对内存性能有影响。绝对的内存消耗几乎从来不是无关紧要的,因为在现代系统上,内存使用是影响性能的主要因素。只要整数大小小于字长,处理器操作不同大小整数所需的时间几乎没有区别。任何使用超过64字节数据的程序都会在现代系统上产生一些缓存惩罚。 - David
显示剩余5条评论

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