TL:DR:告诉您的编译器生成 64 位可执行文件,以便在大多数情况下获得最大性能。但值得针对 32 位构建进行基准测试,尤其是当您的代码使用大量指针密集型数据结构时。
理论上,更快的 64 位代码几乎总是可行的(一些传统的现实,例如不假设 32 位的 SSE2 和 32 位的传统调用约定,在实践中也支持 64 位),但有时让您的程序在 64 位模式下更快将涉及 ILP32 ABI 之类的东西,例如 Linux x32,或者当您想要至少 32 位的类型时,可能使用 int_least32_t 而不是 long。
Intel(和 AMD)CPU 在任何模式下都不会降低解码或执行效率1。
但是操作数大小的某些选择比其他选择差(例如,由于partial-register false dependencies or stalls,16 位很烂),并且 16 位代码需要前缀才能使用 32 位操作数大小和地址大小。 Intel CPU 在解码大量前缀时没有问题,但通常较大的代码大小是一件坏事,会降低 L1I 缓存中的代码密度,有时还会降低 uop 缓存中的代码密度。
脚注 1:除非您在 16 位模式下使用 32 位地址大小,例如“big unreal mode”,那么 Intel P6 系列 CPU(即在 Sandybridge 之前)将在每条此类指令上使用 16 位模式下的 32 位 ModRM 寻址模式停止 LCP,即使它实际上并没有改变长度,即 a false LCP stall .地址大小前缀在普通 32 位模式下没有用(除了填充),所以这个问题基本上与 32 位代码无关。
64 位代码具有更大的指令(因为 64 位操作数大小需要 REX 前缀)。通常这无关紧要,因为 uop 缓存和 L1I 缓存通常会完全隐藏代码大小对性能的影响。 32 位和 64 位操作数大小对于大多数指令来说都是相同的速度,并且 64 位代码仍然可以使用 32 位操作数大小,除非它确实需要宽类型,以避免 64 位整数除法的额外成本(以及 REX 前缀)。
场景是,我想写一个通用程序。我想知道哪种模式会更快,为什么?
这与您提出的问题不同。
长模式通常最快,因为完成相同的工作通常需要更少的指令,因为更好的调用约定和更多的寄存器(更少的溢出)。特别是如果您有任何 FP 计算或 SIMD 友好的循环,64 位模式可能是一个巨大的胜利,因为 FP 代码通常可以利用更多的寄存器。
但是 64 位代码中的重指针数据结构的缓存占用量是 32 位代码的两倍(可以在受保护/兼容模式下运行)。此外,具有 64 位对齐要求可能会导致更多的结构填充,因此指针 + int 结构在 64 位代码中将是 16 个字节,而不是 12 个字节。
因此,您可以在 64 位代码中获得更多的缓存未命中,这可能会使其比 32 位代码慢。 Linux's x32 ABI 试图两全其美(对于不需要大量虚拟地址空间的代码):长模式下的 32 位指针。
如果所有“指针”都位于您从中分配的同一个池中,则只需存储 32 位数组索引而不是指针即可。但是beware that it can result in worse load/use latency 因为您(或编译器)需要索引寻址模式,或者单独的添加指令。
在 64 位模式下,JVM(例如)使用一些技巧来“压缩”指针。 https://wiki.openjdk.java.net/display/HotSpot/CompressedOops - 某些类型的指针存储为 32 位,可以左移 3 位以供使用,因为它们指向 8 字节对齐的堆对象。这允许寻址 32GiB 的空间。