确定我的处理器的字长

38

我如何确定我的CPU的字长?如果我理解正确,int应该是一个字长对吧?但我不确定自己是否正确。

所以,只需打印sizeof(int)就足以确定处理器的字长了吗?


特定于某个操作系统,还是跨平台? - Chris H
请注意,sizeof 返回字符数,标准仅指定每个字符至少为8位。 - Pascal Cuoq
sizeof(int) 在我所工作的每个平台上都有效。 - kenny
阅读答案,我认为对于“字长”这个术语的含义缺乏明确的解释——是指寄存器的大小,总线的大小,还是针对哪些操作? - Soren
@Soren 我认为“词大小”是指处理器在一个CPU周期内可以读取的字节数。通常,一个具有32位处理器的系统在一个CPU周期内读取4个字节,而一个64位处理器在一个CPU周期内读取8个字节。 - meispi
10个回答

24

你对于 int 类型所占的字节数的假设是不正确的,请参考这里

由于在编译时必须知道处理器、操作系统和编译器,因此可以使用编译器提供的预定义的架构/操作系统/编译器宏来推断字长。

然而,在更简单和大多数 RISC 处理器上,字长、总线宽度、寄存器大小和内存组织通常是一致的,但是对于具有不同浮点寄存器、累加器、总线宽度、缓存宽度、通用寄存器等大小的更复杂的 CISC 和 DSP 架构,情况可能并非如此。

当然,这也引出了一个问题,你为什么需要知道这个?通常,你会使用适合应用程序的类型,并相信编译器提供任何优化。如果你认为你需要这些信息进行优化,那么最好使用C99 的 “快速” 类型。如果你需要优化特定算法,则需要针对多种类型实现并进行分析。


2
我修复了损坏/不正确的链接,如果之前有任何困惑的话! - Clifford
1
在x86-64 Linux上,int_fast16_tint_fast32_t都是int64_t,这可能不是一个理想的选择。对于某些情况来说,32位是最快的,并且可以生成更小的代码(更少的REX前缀)。如果您将它们存储在内存中,特别是在数组中,绝对不要使用占用两倍缓存的64位类型。 - Peter Cordes
1
@PeterCordes:同意 - 回答中使用“通常”和“可能”这些词是非常有意义的。与任何“手动优化”一样,它应该经过测试并与直接解决方案进行比较。很遗憾,OP没有选择澄清他需要知道什么,或者询问这个Y问题的X。 - Clifford

14

一个int应该是一个单词,对吗?

据我所知,这取决于数据大小模型。关于 UNIX 系统的说明,请参阅64 位和数据大小中立性。例如,Linux 32 位是 ILP32,而 Linux 64 位是 LP64。我不确定在 Windows 系统和版本之间的区别,除了我相信所有 32 位的 Windows 系统都是 ILP32。

如何确定我的 CPU 的字长?

这取决于你采用哪个 C 标准版本和我们谈论的平台是什么。这是一个编译时还是运行时的判断?

C 头文件<limits.h>可能定义了WORD_BIT和/或__WORDSIZE


这些东西是由编译器决定的,与CPU字长的实际大小没有直接关系。换句话说,这些东西是由编译器开发人员定义的,而不是由CPU本身的物理特性确定的。 - Foredecker
例如交叉编译器环境,没错。我应该澄清一下,如果有人想编写能够意识到目标主机字长的代码,他们可以使用 limits.h 头文件中的某些内容。 - mctylr
1
当然,这是针对目标环境而不是目标CPU的_能力_。例如,任何来自英特尔或AMD的晚期x86处理器都可以用作32位或64位处理器。根据正在运行的操作系统,CPU可以被用作32位或64位处理器。在32位模式下,即使CPU具有64位寄存器,也无法访问寄存器作为64位(8字节)_字_寄存器。 - mctylr
__WORDSIZEx86-64 Linux x32 ABI 中是32位(在64位模式下使用寄存器调用ABI的ILP32),所以也不是它。 __SYSCALL_WORDSIZE-mx32-m64 下是64位。但是,在 -m32 下根本没有定义,因此也无法使用,并且可能是Linux或glibc特定的东西。 - Peter Cordes

8

sizeof(int)并不总是您的CPU的“字”大小。这里最重要的问题是为什么您想知道字大小……您是在尝试做某种运行时和CPU特定的优化吗?

话虽如此,在Windows上使用Intel处理器,名义上的字大小将是32位或64位,并且您可以轻松找出:

  • 如果您的程序编译为32位,则名义上的字大小为32位
  • 如果您已编译为64位程序,则名义上的字大小为64位。

这个答案听起来平凡,但对于第一顺序来说是正确的。但是有一些重要的微妙之处。即使现代Intel或AMD处理器上的x86寄存器宽度为64位;您只能(容易地)在32位程序中使用它们的32位宽度-即使您正在运行64位操作系统。这在Linux和OSX上也是如此。

此外,在大多数现代CPU上,数据总线宽度比标准ALU寄存器(EAX,EBX,ECX等)宽。这个总线宽度可能会有所变化,有些系统具有128位甚至192位的宽总线。

如果您担心性能,则还需要了解L1和L2数据缓存的工作原理。请注意,一些现代CPU具有L3缓存。缓存包括一个称为写缓冲区的单元。


sizeof(int)不是在编译时完成的吗?这意味着它编译时的大小,而不是运行它的计算机的大小,对吗? - FryGuy
32位x86代码无法使用全宽64位寄存器。没有操作数大小前缀或其他任何东西。此外,您确定任何CPU中都有192位宽的数据路径吗?那听起来像是GPU中的内存总线宽度。英特尔从L1到执行单元的路径从128b增加到256b(在撰写本答案多年后发布的Haswell中)。 - Peter Cordes

3
创建一个程序,执行一些整数操作,例如整数版本的SAXPY算法,并多次运行该程序。使用不同的字长大小进行运行,从8比特到64比特(即从char到long long)。

测量每个版本在运行算法时花费的时间。如果有一个特定版本的运行时间明显比其他版本更短,则用于该版本的字长大小可能是计算机的本机字长大小。另一方面,如果有几个版本的运行时间相差不大,请选择具有更大字长的版本。

请注意,即使使用此技术也可能会得到虚假数据:使用Turbo C编译并在DOS上通过80386处理器运行的基准测试将报告字长为16位,仅因为编译器不使用32位寄存器执行整数算术,而是调用内部函数来执行每个算术运算的32位版本。


我想不出有什么用例会需要通过这种方法在运行时确定处理器字长。处理器是固定的,因此应该在编译时确定或者如果需要运行时,则使用特定于平台/操作系统的方法确定。 - Conor Patrick
编译时测试仅限于使用sizeof运算符等,该运算符可为多种数据类型提供以字节为单位的大小,但不一定与本机字长相同。这种(经验)方法不需要特定的平台/操作系统支持。它依赖于使用本机字长进行整数运算的速度更快的基础。32位处理器可以使用8、16、32和64位数据,并且对于8、16和32位数据,它们将花费大约相同的时间,但是算术运算将需要更多的周期来完成64位操作的相同工作,因此可以得出本机字长为32位的结论。 - mcleod_ideafix
我认为没有实际应用场景来确定字长。平台通常会在编译时提供宏定义。 - Conor Patrick
请确保考虑缓存效应(更宽的类型可能会因为增加了内存带宽需求而变慢,而不是因为执行多个指令)。例如,重复地在相同的小缓冲区上循环,以确保它被缓存。或者像 a += b; b += a;(斐波那契数列)这样做几百万次(使用 unsigned 类型,因为它 溢出)。它不太可能被优化掉,并且不依赖于内存。 - Peter Cordes
我认为在Turbo C中将字长设置为16位是正确的结果。如果编译器不使用它们,那么机器有没有这些都无所谓。你正在测量编译器目标的字长,这会影响使用uint16_t vs. uint32_t vs. uint64_t vs. __uint128_t的决策。 - Peter Cordes
@ConorPatrick:使用多个函数实现构建二进制文件并在启动时对它们进行基准测试是一种有效的策略。也许只能作为 CPP 宏无法处理的平台的后备,或者用于没有可靠宏的平台。Linux 在软件 RAID5/RAID6 中使用此策略:有几个 SSE/AVX 实现都会编译到 x86 中,并在引导时(或模块加载时)选择最快的一个。 - Peter Cordes

2
此外,C 语言中的 long 类型的大小等于机器字长,而 int 类型的大小有时小于字长。例如,Alpha 处理器的字长为 64 位。因此,寄存器、指针和 long 类型的长度都是 64 位。
请参考以下链接了解更多信息:http://books.msspace.net/mirrorbooks/kerneldevelopment/0672327201/ch19lev1sec2.html 记住上述内容后,可以运行以下程序来查找当前机器的字长大小。
#include <stdio.h>

int main ()

{

    long l;
    
    short s = (8 * sizeof(l));
    
    printf("Word size of this machine is %hi bits\n", s);
    
    return 0;
}

1
简而言之:没有好的方法。C数据类型最初的想法是int是最快(本地)整数类型,long是最大的等等。
然后出现了在一种CPU上起源并被移植到不同CPU的操作系统,其本机字长不同。为了保持源代码兼容性,一些操作系统打破了这个定义,保留了旧大小的数据类型,并添加了新的、非标准的数据类型。
话虽如此,根据您实际需要,您可能会发现stdint.h中有一些有用的数据类型,或者编译器特定或平台特定的宏用于各种目的。

0
使用于编译时:sizeof(void*)

9
在像360和PS3这样的平台上,指针是32位的(ABI怪异以节省空间),因此这不正确。 - Maister
那会给指针的大小,这是另一回事。 - Soren

0
许多人认为内存是一个字节数组。但是CPU有另一种看待内存的方式,即内存粒度。根据架构,内存粒度可以为2、4、8、16甚至32字节。内存粒度和地址对齐对软件的性能、稳定性和正确性有很大的影响。考虑一个4字节的粒度和一个非对齐的内存访问以读取4字节。在这种情况下,每次读取,如果地址增加了1个字节则需要两个额外的读取指令加上两个移位操作,最后还需要进行位运算来得到最终结果,这会降低性能。进一步地,原子操作可能会受到影响,因为它们必须是不可分割的。其他的副作用包括缓存、同步协议、CPU内部总线流量、CPU写缓冲区等等。可以在循环缓冲区上进行实际测试,以查看结果可能会有什么不同。基于型号,不同制造商的CPU具有不同的寄存器,在通用和特定操作中将使用这些寄存器。例如,现代CPU具有128位寄存器的扩展。因此,字长不仅与操作类型有关,而且与内存粒度有关。字长和地址对齐是必须注意的问题。市场上有一些CPU不关心地址对齐,如果提供了,则会被简单忽略。你能猜到会发生什么吗?

0

无论出于何种原因了解处理器的大小,都不重要。

处理器的大小是指单个CPU核心算术逻辑单元(ALU)可以在单个时间点上处理的数据量。 CPU核心的ALU将始终在累加器寄存器上工作。因此,CPU的位数大小是累加器寄存器的位数大小。

您可以从处理器的数据表中或通过编写小型汇编语言程序来查找累加器的大小。

请注意,某些处理器(如ARM)中累加器寄存器的有效可用大小可能会根据操作模式(Thumb和ARM模式)而变化。这意味着处理器的大小也会根据该处理器的模式而变化。

在许多体系结构中,虚拟地址指针大小和整数大小与累加器大小相同。这仅是为了利用不同处理器操作中的累加器寄存器,但这不是硬性规定。


0

正如其他人所指出的,你对计算这个值感兴趣的方式是怎样的?有很多变量需要考虑。

sizeof(int) != sizeof(word)。byte、word、double word 的大小自它们被创建以来至少在 Windows API 世界中为了 API 兼容性而从未改变。即使处理器的字长大小是指令可以操作的自然大小。例如,在 msvc/cpp/c# 中,sizeof(int) 是四个字节。即使在 64 位编译模式下也是如此。Msvc/cpp 有 __int64,而 c# 有 Int64/UInt64(非 CLS 兼容)的 ValueType。在 win32 API 中还有 WORD DWORD 和 QWORD 的类型定义,分别从未改变过两个字节、四个字节和八个字节。在 Win32 上还有 UINT/INT_PTR 和在 c# 上有 UIntPtr/IntPtr,它们保证足够大来表示一个内存地址和一个引用类型。据我所知,如果架构仍然存在,我可能会错,我认为没有人需要处理,也不存在 near/far 指针,因此,如果你使用 c/cpp/c#,sizeof(void*) 和 Unsafe.SizeOf{IntPtr}() 将足以以符合标准的跨平台方式确定您的最大“字”大小。如果有人能纠正这一点,请这样做!此外,在 c/cpp 中,内在类型的大小定义不明确。

C数据类型大小 - 维基百科


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