C++ int 与 64 位机器中的 long long
C++ int vs long long in 64 bit machine
我的电脑有 64 位处理器,当我查找 sizeof(int)
、sizeof(long)
和 sizeof(long long)
时,结果是 int long 是 32 位,long long 是 64 位。我研究了原因,看来 C++ 中 int 适合机器字长的流行假设是错误的。据我了解,由编译器来定义大小,而我的是 Mingw-w64。我研究的原因是理解如果使用小于字长的类型有利于速度(例如,short vs int) 或者如果它有负面影响。在32位系统中,一个流行的观点是:由于word size是int,short会被转换成 int 并且它会导致额外的位移等,从而导致更差的性能。反对意见是缓存级别会有好处(我没有深入研究),使用 short 将对虚拟内存经济有用。所以,除了这种两难之间的困惑,我还面临着另一个问题。我的系统是 64 位的,不管我用 int 还是 short ,它仍然会小于字长,并且我开始考虑使用 64 位 long long 不是很有效,因为它处于系统设计的级别。我还读到还有另一个约束,即定义类型大小的 OS 的库(ILP64,LP64)。在 ILP64 中默认 int 与 LP64 相比是 64 位的,如果我使用支持 ILP64 的 OS 会加速程序吗?一旦我开始询问我应该使用哪种类型来加速我的 C++ 程序,我就会面临更深层次的话题,我在这些话题上并不专业,而且一些解释似乎相互矛盾。能否解释一下:
1) 如果最好的做法是在 x64 中使用 long long 以获得最大性能,即使对于 1-4 字节数据也是如此?
2) 使用小于字长的类型的权衡(内存赢取与额外操作)
3) word&int 大小为 64 位的 x64 计算机是否有可能通过所谓的向后兼容性处理短的、使用 16 位字大小的代码?或者它必须将 16 位文件转换为 64 位文件,并且它可以完成的事实定义系统是向后兼容的。
4) 我们可以强制编译器将 int 设为 64 位吗?
5) 如何将ILP64整合到使用LP64的PC中?
6) 使用适用于其他编译器、OS 和体系结构(32 位处理器)的代码可能会出现哪些问题?
1) 如果最好的做法是在 x64 中使用 long long 来实现最高性能,即使对于 1-4 字节数据也是如此?
不,它实际上可能会使您的表现变差。例如,如果您使用 64 位整数,而您可以使用 32 位整数,那么您只是将必须在处理器和内存之间发送的数据量增加了一倍,而内存速度要慢几个数量级。你所有的缓存和内存总线都会以两倍的速度崩溃。
2) 使用小于字大小的类型的权衡(内存赢取与额外操作)
一般来说,现代机器性能的主要驱动因素是 运行 一个程序需要存储多少数据。一旦程序的工作集大小按顺序超过寄存器、L1 缓存、L2 缓存、L3 缓存和 RAM 的容量,您将看到明显的性能悬崖。
此外,如果您的编译器足够聪明,可以弄清楚如何使用处理器的向量指令(也称为 SSE 指令),那么使用较小的数据类型可能会更好。现代矢量处理单元足够聪明,可以将八个 16 位短整数塞入与两个 64 位长整数相同的 space,因此您可以同时执行四倍的运算。
3) word&int 大小为 64 位的 x64 计算机是否有可能通过所谓的向后兼容性处理使用 16 位字大小的 short?或者它必须把16位的文件变成64位的文件,能做到这一点就说明系统是向后兼容的。
我不确定你在这里问什么。通常,64 位机器能够执行 32 位和 16 位可执行文件,因为那些较早的可执行文件使用了 64 位机器潜力的子集。
硬件指令集通常是向后兼容的,这意味着处理器设计人员倾向于添加功能,但很少删除功能。
4) 我们可以强制编译器将 int 设为 64 位吗?
所有编译器都有相当标准的扩展,允许您处理固定位大小的数据。比如头文件stdint.h
声明了int64_t
、uint64_t
等类型
5) 如何将ILP64整合到使用LP64的PC中?
https://software.intel.com/en-us/node/528682
6) 使用适用于其他编译器、OS 和体系结构(32 位处理器)的代码可能会出现哪些问题?
通常,编译器和系统足够聪明,可以弄清楚如何在任何给定系统上执行您的代码。然而,32 位处理器将不得不做额外的工作来处理 64 位数据。换句话说,正确性应该不是问题,但性能才是问题。
但通常情况下,如果性能对您来说真的很重要,那么无论如何您都需要针对特定的体系结构和平台进行编程。
澄清请求:非常感谢!我想澄清问题 no:1。你说这对记忆不好。让我们以 32 位 int 为例。当你把它发送到内存时,因为它是64位系统,对于一个想要的整数0xee ee ee ee,当我们发送它时它会不会变成0x ee ee ee ee + 32位其他?当字长为 64 位时,处理器如何发送 32 位? 32 位是想要的值,但它不会与 32 个未使用的位组合并以这种方式发送吗?如果我的假设是正确的,那么内存没有区别。
这里有两件事要讨论。
首先,您讨论的情况不会发生。处理器不需要 "promote" 将 32 位值转换为 64 位值即可正确使用它。这是因为现代处理器具有不同的访问模式,能够适当地处理不同大小的数据。
例如,64 位 Intel 处理器有一个名为 RAX 的 64 位寄存器。但是,通过将其称为 EAX,可以在 32 位模式下使用这个相同的寄存器,甚至可以在 16 位和 8 位模式下使用。我从这里偷了一张图:
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 位字。
结果是字长对内存带宽没有任何意义。但是,您可以将这些 32 位整数中的 16 个放入同一个 space 中,您可以将这些 64 位整数中的 8 个打包。或者您甚至可以在同一个 space 中容纳 32 个 16 位值或 64 个 8 位值。如果您的程序使用大量不同的数据值,您可以通过使用必要的最小数据类型来显着提高性能。
我的电脑有 64 位处理器,当我查找 sizeof(int)
、sizeof(long)
和 sizeof(long long)
时,结果是 int long 是 32 位,long long 是 64 位。我研究了原因,看来 C++ 中 int 适合机器字长的流行假设是错误的。据我了解,由编译器来定义大小,而我的是 Mingw-w64。我研究的原因是理解如果使用小于字长的类型有利于速度(例如,short vs int) 或者如果它有负面影响。在32位系统中,一个流行的观点是:由于word size是int,short会被转换成 int 并且它会导致额外的位移等,从而导致更差的性能。反对意见是缓存级别会有好处(我没有深入研究),使用 short 将对虚拟内存经济有用。所以,除了这种两难之间的困惑,我还面临着另一个问题。我的系统是 64 位的,不管我用 int 还是 short ,它仍然会小于字长,并且我开始考虑使用 64 位 long long 不是很有效,因为它处于系统设计的级别。我还读到还有另一个约束,即定义类型大小的 OS 的库(ILP64,LP64)。在 ILP64 中默认 int 与 LP64 相比是 64 位的,如果我使用支持 ILP64 的 OS 会加速程序吗?一旦我开始询问我应该使用哪种类型来加速我的 C++ 程序,我就会面临更深层次的话题,我在这些话题上并不专业,而且一些解释似乎相互矛盾。能否解释一下:
1) 如果最好的做法是在 x64 中使用 long long 以获得最大性能,即使对于 1-4 字节数据也是如此?
2) 使用小于字长的类型的权衡(内存赢取与额外操作)
3) word&int 大小为 64 位的 x64 计算机是否有可能通过所谓的向后兼容性处理短的、使用 16 位字大小的代码?或者它必须将 16 位文件转换为 64 位文件,并且它可以完成的事实定义系统是向后兼容的。
4) 我们可以强制编译器将 int 设为 64 位吗?
5) 如何将ILP64整合到使用LP64的PC中?
6) 使用适用于其他编译器、OS 和体系结构(32 位处理器)的代码可能会出现哪些问题?
1) 如果最好的做法是在 x64 中使用 long long 来实现最高性能,即使对于 1-4 字节数据也是如此?
不,它实际上可能会使您的表现变差。例如,如果您使用 64 位整数,而您可以使用 32 位整数,那么您只是将必须在处理器和内存之间发送的数据量增加了一倍,而内存速度要慢几个数量级。你所有的缓存和内存总线都会以两倍的速度崩溃。
2) 使用小于字大小的类型的权衡(内存赢取与额外操作)
一般来说,现代机器性能的主要驱动因素是 运行 一个程序需要存储多少数据。一旦程序的工作集大小按顺序超过寄存器、L1 缓存、L2 缓存、L3 缓存和 RAM 的容量,您将看到明显的性能悬崖。
此外,如果您的编译器足够聪明,可以弄清楚如何使用处理器的向量指令(也称为 SSE 指令),那么使用较小的数据类型可能会更好。现代矢量处理单元足够聪明,可以将八个 16 位短整数塞入与两个 64 位长整数相同的 space,因此您可以同时执行四倍的运算。
3) word&int 大小为 64 位的 x64 计算机是否有可能通过所谓的向后兼容性处理使用 16 位字大小的 short?或者它必须把16位的文件变成64位的文件,能做到这一点就说明系统是向后兼容的。
我不确定你在这里问什么。通常,64 位机器能够执行 32 位和 16 位可执行文件,因为那些较早的可执行文件使用了 64 位机器潜力的子集。
硬件指令集通常是向后兼容的,这意味着处理器设计人员倾向于添加功能,但很少删除功能。
4) 我们可以强制编译器将 int 设为 64 位吗?
所有编译器都有相当标准的扩展,允许您处理固定位大小的数据。比如头文件stdint.h
声明了int64_t
、uint64_t
等类型
5) 如何将ILP64整合到使用LP64的PC中?
https://software.intel.com/en-us/node/528682
6) 使用适用于其他编译器、OS 和体系结构(32 位处理器)的代码可能会出现哪些问题?
通常,编译器和系统足够聪明,可以弄清楚如何在任何给定系统上执行您的代码。然而,32 位处理器将不得不做额外的工作来处理 64 位数据。换句话说,正确性应该不是问题,但性能才是问题。
但通常情况下,如果性能对您来说真的很重要,那么无论如何您都需要针对特定的体系结构和平台进行编程。
澄清请求:非常感谢!我想澄清问题 no:1。你说这对记忆不好。让我们以 32 位 int 为例。当你把它发送到内存时,因为它是64位系统,对于一个想要的整数0xee ee ee ee,当我们发送它时它会不会变成0x ee ee ee ee + 32位其他?当字长为 64 位时,处理器如何发送 32 位? 32 位是想要的值,但它不会与 32 个未使用的位组合并以这种方式发送吗?如果我的假设是正确的,那么内存没有区别。
这里有两件事要讨论。
首先,您讨论的情况不会发生。处理器不需要 "promote" 将 32 位值转换为 64 位值即可正确使用它。这是因为现代处理器具有不同的访问模式,能够适当地处理不同大小的数据。
例如,64 位 Intel 处理器有一个名为 RAX 的 64 位寄存器。但是,通过将其称为 EAX,可以在 32 位模式下使用这个相同的寄存器,甚至可以在 16 位和 8 位模式下使用。我从这里偷了一张图:
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 位字。
结果是字长对内存带宽没有任何意义。但是,您可以将这些 32 位整数中的 16 个放入同一个 space 中,您可以将这些 64 位整数中的 8 个打包。或者您甚至可以在同一个 space 中容纳 32 个 16 位值或 64 个 8 位值。如果您的程序使用大量不同的数据值,您可以通过使用必要的最小数据类型来显着提高性能。