64 位英特尔® 架构处理器拓扑枚举

 

处理器拓扑信息对于许多处理器资源管理实践都非常重要,包括任务/线程调度、许可政策执行、相似性控制/迁移等。高速缓存结构的拓扑信息对于优化软件性能有着重要意义。本白皮书介绍了适用于使用 64 位和 32 位英特尔架构处理器的单路到多路平台的拓扑枚举算法。使用初始 APIC ID 的拓扑枚举算法(处理器和高速缓存)已被扩展为使用 x2APIC ID,后者已成为在一个相关域内超支持过 256 个逻辑处理器的未来平台必须满足的要求。


下载“64 位英特尔® 架构处理器拓扑枚举” [PDF 153KB]

下载代码包:topo-09272010.tar

 

 


简介

微处理器中的硬件多线程在近年激增。当今销售的大部分英特尔® 架构处理器提供了一种或多种形式的硬件多线程支持(多核和/或同步多线程 (SMT),后者于 2002 年以“超线程技术”这一名称进入市场)。从处理器硬件的角度看,64 位英特尔架构处理器的物理包装能支持 SMT 和多核。因此,物理处理器实际上是一个带有某些共享系统资源(例如内存、总线/系统链接、高速缓存)的分层排序逻辑处理器集合。从平台硬件的角度看,多处理器系统中存在的硬件多线程可能包含两个或更多被组织成相对于内存子系统统一或非统一配置的物理处理器。

使用硬件线程功能的应用程序编程必须遵守由基础操作系统提供的编程模型和软件结构。例如,操作系统调度程序一般会指派队列中的软件任务使用逻辑处理器级别的硬件资源;操作系统可能定义自己的数据结构并为应用程序提供服务,允许它们通过多线程应用程序的相似性定制在任务和和逻辑处理器之间的关联。操作系统和应用程序(BIOS、操作系统加载程序)下的软件堆栈对于启用硬件多线程特性和配置由操作系统定义的软件结构非常很重要。

英特尔 64 位架构中的 CPUID 指令定义了一系列丰富的信息,用来帮助 BIOS、操作系统和应用程序查询软件堆栈各自成员实现有效操作所需的处理器拓扑。一般而言,BIOS 需要收集物理处理器的拓扑信息,确定系统中存在多少个物理处理器;准备和系统拓扑有关的必要软件结构,并将系统拓扑信息传递到接管系统控制权的下一层软件堆栈。操作系统和应用程序层会对拓扑信息进行广泛使用。本文档包含了操作系统和应用程序的几种常见软件用法,以支持使用 CPUID 分析单路或多路系统中的处理器拓扑。

处理器拓扑枚举的主要软件用法在于查询和标识单路或多路系统中的逻辑处理器、处理器内核和物理包装之间的层级关系。我们将这种用法称为系统拓扑枚举。操作系统或某些应用程序可能需要使用系统拓扑枚举根据物理处理器实施许可策略。操作系统使用这一信息来实施有效的任务调度、最小化线程迁移、配置应用程序线程管理界面和配置适合于处理器/内存拓扑的内存分配服务。多线程应用程序使用系统拓扑信息来确认最佳线程绑定、管理内存分配以确保最佳位置和改善多炉系统中的性能扩展工作。

英特尔 64 位处理器可满足系统拓扑枚举要求:

  • 支持相关内存的 64 位或 32 位英特尔架构平台内的每个逻辑处理器都会在相关域内被指派一个独特 ID (APIC ID)。多节点集群安装可能在每个相关域中采用厂商特定 BIOS(保存着在处理器重置过程中分配的 APIC ID),并采用 ID 进行扩展,从而形成集群系统内一个独特 ID 超集。本文档仅介绍了在相关域内提供独特 ID 的 CPUID 界面。
  • 在相关 64 位或 32 位英特尔架构平台内指派的独特 ID 值遵循基于分解为三个子字段的 APIC ID 位字段的算法。三个子字段集合对应被定义为“SMT、敁处理器内核(或称“内核”)和敁物理包装(或称“包装”)的三个等级。”这允许每个等级映射到 APIC ID 中的一个子字段(子 ID)。

 

在概念上,拓扑枚举算法就是根据派生两个定义 APIC ID 内位子集的参数,提取对应于 APIC ID 中给定结构等级的子 ID。相关的参数有:(a) 可用于屏蔽 APIC ID 内无用位的掩码宽度,(b) 相对于 APIC ID 位 0 的偏移量。

“SMT”等级对应于处理器拓扑的最内部成分。所以它位于 APIC ID 的最不重要的部分。如果“SMT”的相应宽度为 0,就表示结构的下一个外部等级内只有 1 个逻辑处理器。例如 英特尔® 酷睿™2 双核处理器一般会产生为 0 的 “SMT_Mask_Width”。如果相应宽度为 1 位宽,则表示结构的下一个外部等级内可能有两个逻辑处理器。

如果“内核”的相应宽度为 0,就表示物理处理器内只有 1 个处理内核。如果“内核”的相应宽度为 1,物理处理器内可能有 2 个处理内核。

请注意,系统内所有逻辑处理器中指派的 APIC ID 值不需要相同。但对应于三个结构等级的位字段子集在位边界上是相同的。鉴于这一要求,用于提取给定子 ID 的掩码位偏移量可根据内部结构等级的“掩码宽度”派生。


多路系统中的独特 APIC ID

尽管旧版 32 位英特尔架构多路系统为系统内每个逻辑处理器指派独特的 APIC ID,编程界面已在过去发生了多次演变。对于英特尔奔腾 Pro 处理器和奔腾 III 至强处理器,只能从本地 APIC 寄存器(本地APIC 寄存器使用内存映射的 IO 界面,并且由操作系统管理)访问 APIC ID。在第一代英特尔奔腾 4 和英特尔至强处理器(2000 和 2001 年)中,CPUID 指令提供了在处理器重置过程中指派的初始 APIC ID 的相关信息。第一代支持超线程技术(2002 年)的英特尔至强 MP 处理器和英特尔奔腾 4 处理器中的 CPUID 指令提供了额外信息,允许软件将初始 APIC ID 分解为一个两级拓扑枚举。随着于 2005 年推出双核 64 位英特尔架构处理器,使用 CPUID 的系统拓扑枚举演变成一个基于 8 位宽初始 APIC ID 的三级算法。未来的 64 位英特尔架构平台或许能够支持大量超过 8 位初始 APIC ID 字段容量的逻辑处理器。64 位英特尔架构中的 x2APIC 扩展定义了一个 32 位的 x2APIC ID。未来 64 位英特尔架构处理器中的 CPUID 指令将允许软件枚举使用 x2APIC ID 的系统拓扑。CPUID 的扩展拓扑枚举叶(叶 11)是用于未来 64 位英特尔架构处理器系统拓扑枚举的首选界面。

未来 64 位英特尔架构处理器中的 CPUID 指令可能支持独立于叶 11 的 x2APIC 硬件。对于多种未来 64 位英特尔架构平台,可使用 CPUID 叶 11 或旧版初始 APIC ID(通过 CPUID 叶 11 和叶 4)执行系统拓扑枚举。图 1 显示了软件如何选择用于系统拓扑枚举的 CPUID 叶信息的示例。

图 1 选择用于系统拓扑枚举的 CPUID 叶信息的示例

 

可通过设置 EAX = 0 确定受支持的 CPUID 叶的最大值,执行 CPUID 并检查 EAX 中返回的值,即 CPUID.0:EAX。如果 CPUID.0:EAX >= 11,软件可通过设置 EAX=11、ECX=0确定 CPUID 叶 11是否存在,执行 CPUID 以检查 EBX 中返回的非零值,即 CPUID. (EAX=11, ECX=0):EBX != 0。

功能完善的硬件多线程要求对 CPUID 叶进行完全报告。

如果软件发现较新的 64 位或 32 位英特尔架构处理器(2004 年以后的产品)上 CPUID.0:EAX < 4,它就必须检查 MSR IA32_MISC_ENABLES[bit 22]。

如果 IA32_MISC_ENABLES[bit 22] 被(BIOS 或通过其他方式)设置为 1,用户可以通过将 IA32_MISC_ENABLES[bit 22] 设置为“0”(修改 BIOS CMO S 设置或使用 WRMSR)还原 CPUID 叶函数完全报告。

对于只支持两级拓扑的较旧版 32 位英特尔架构处理器,三级系统拓扑枚举算法(使用 CPUID 叶 1 和叶 4)与支持两级拓扑(SMT 和物理包装)的较旧版处理器完全兼容。对于那些将 CPUID.1:EBX[23:16] 报告为保留(即 0)的处理器,处理器仅支持一个拓扑等级。

表 A-1 显示了处理三个处理器硬件类别中 CPUID 叶函数的代码示例。


使用 CPUID 扩展拓扑叶的系统拓扑枚举

系统拓扑枚举的算法可以总结为三个操作阶段:

  • 派生“掩码宽度”常数,可用来提取每个子 ID。
  • 收集系统中每个逻辑处理器的独特 APIC ID,并提取/分解每个 APIC ID 为三个子 ID 集。
  • 分析分层子 ID 的关系,从而根据处理器拓扑的三个结构等级确立操作系统线程管理服务之间的映射表。

 

表 A-2 显示了应用于处理器拓扑和缓存拓扑的系统范围拓扑三个阶段的基本结构示例。

图 2 概述了为 x2APIC ID 查询 CPUID 叶 11 和根据“SMT”、“内核”、“物理包装”结构等级提取子 ID 的步骤。

图 2 从每个逻辑处理器的 x2APIC ID 中提取子 ID 的步骤

表 A-3 列出了包含 APIC ID、不同子 ID 和顺序编号方案结构集合的数据结构,用于枚举处理器拓扑和/或系统的缓存拓扑中的每个实体。

使用 CPUID 的应用程序级别的系统拓扑枚举包含在系统内每个逻辑处理器上执行 CPUID 指令。这表示使用操作系统提供的服务切换上下文。根据用户代码进行按需上下文切换一般依赖操作系统提供的线程相似性管理 API。线程相似性 API 的容量和限制根据不同操作系统而有所差异。例如,在一些操作系统中,线程相似性 API 具有 32 或 64 个逻辑处理器的限制。预计未来版本中将提供用于管理较大数量逻辑处理器的线程相似性 API 的增强功能。


使用 CPUID 叶 1 和叶 4 的系统拓扑枚举

图 3 概述了通过 CPUID 叶 11 查询初始 APIC ID 和使用 CPUID 叶 1 和叶 4 根据 SMT、内核、物理包装结构等级提取子 ID 的步骤。从初始 APIC ID 提取子 ID 是以查询 CPUID 叶 1 和 4 为基础,从而派生形成 8 位初始 APIC ID 字段的三个选择掩码(SMT 掩码、内核掩码、包装掩码)的位宽度。选择掩码允许软件根据“SMT”、“内核”、“包装装”从每个逻辑处理器的初始 APIC ID 提取子 ID。

图 3 从每个逻辑处理器的初始 APIC ID 中提取子 ID 的步骤

表 A-4 显示了为系统内每个逻辑处理器查询 APID ID 的示例,以及将每个 APIC ID 解析为相应子 ID 用于之后的拓扑学构造分析的示例。

表 A-5 列出了用于从逻辑处理器(我们已绑定了当前的执行上下文)的每个 APIC ID 提取不同子 ID 的支持例程。

表 A-6 显示了操作系统特定的包装程序函数。


高速缓存拓扑枚举

64 英特尔架构处理器的物理包装具有一个高速缓存结构。高速缓存结构的指定级别可由一个或多个逻辑处理器共享。一些软件可能希望通过利用特定高速缓存结构等级的共享高速缓存优化性能。使用高速缓存拓扑的性能调谐可通过合并系统拓扑信息和增加高速缓存拓扑信息来实现。图 4 概述了分解子 ID 以枚举共享目标高速缓存级别和枚举系统内可见目标级别高速缓存的程序。Cache_ID 可从报告 32 位 x2APIC ID 的处理器的 x2APIC ID 或从不报告 x2APIC ID 的处理器的初始 APIC ID 中提取。“Cache_ID”阵列可用来列举不同高速缓存连同其他从处理器拓扑中派生的子 ID,以便实施代码调谐技术。

图 4 提取每个逻辑处理器目标高速缓存级别的 Cache_ID 的步骤

三级子 ID SMT_ID[k]、Core_ID[k]、Pkg_ID[k]、k = 0、..、N-1 可被软件以多种特定于应用程序的方法使用。最常见的用法包括:

  • 确定物理处理器的数量以实施每包装许可策略。Pkg_ID[] 阵列中的每个独特值都代表一个物理处理器。
  • 线程绑定策略可选择支持将每个新任务与系统内单独的内核绑定。这可能需要软件了解每个逻辑处理器相对于每个独特处理器内核的相似性掩码之间的关系。
  • MP 扩展优化策略可能希望根据大型上一级别高速缓存的大小将其数据工作集分区,并允许多个线程处理驻留在每个上一级别高速缓存中的数据块。这要求软件管理相对于系统内每个 Cache_ID 和 APIC ID 的相似性掩码和线程绑定。

 


拓扑子 ID 的数据处理

每个子 ID 分层都代表一个 APIC ID 子集(x2APIC ID 或初始 APIC ID)。它允许软件为父结构等级内每个独特实体寻址。对于处理器拓扑枚举:

  • SMT_ID:每个独特的 SMT_ID 都允许软件区分处理器内核内的不同逻辑处理器,
  • Core_ID:每个独特的 Core_ID 都允许软件区分物理包装内的不同处理器内核,
  • Pkg_ID:每个独特的 Core_ID 都允许软件区分多处理器系统内的不同物理包装。

对于缓存拓扑枚举:

  • CacheSMT_ID:每个独特的 CacheSMT_ID 都允许软件区分共享相同目标高速缓存级别的不同逻辑处理器。
  • Cache_ID:每个独特的 Cache_ID 都允许软件区分系统内不同目标级别高速缓存。

 

从 APIC ID 提取子 ID 可以利用从 CPUID 指令中派生的常数参数。从平台硬件角度看,64 位和 32 位英特尔架构多路系统要求每个物理处理器支持相同的硬件多线程功能。因此,系统拓扑枚举可以在一个逻辑处理上执行相关 CPUID 叶函数,以便派生系统范围的子 ID 提取参数。但 APIC ID 必须通过在系统内每个逻辑处理器上执行 CPUID 指令进行查询。


用于 x2APIC ID 的子 ID 提取参数

从 x2APIC ID 提取子 ID 是以查询有效子叶指数 n 的 CPUID.(EAX=11, ECX=n):EAX[4:0] 值为基础,以便在 CPUID.(EAX=1,ECX=0):EDX[31:0] 查询 x2APIC ID 时获取位宽度参数派生提取掩码。提取掩码允许软件从 x2APIC ID 提取一个位的子集,作为相应结构等级的子 ID。为了枚举子 ID,以 1 递增增加子叶指数 (ECX=n),直到 CPUID.(ECX=11,ECX=n).EBX[15:0] == 0

  • SMT_ID:CPUID.(EAX=11, ECX=0):EAX[4:0] 提供宽度参数以派生 SMT 选择掩码,从而提取相同处理器内核内逻辑处理器的 SMT_ID。子叶指数 (ECX=0) 获得结构上的定义,并与 MT?级别类型 (CPUID.(EAX=11, ECX=0):ECX[15:8] == 1) 关联。
    • SMT_Mask_Width = CPUID.(EAX=11, ECX=0):EAX[4:0],如果 CPUID.(EAX=11, ECX=0):ECX[15:8] 为 1
    • SMT_Select_Mask = ~((-1) << SMT_Mask_Width )
    • SMT_ID = x2APIC_ID & SMT_Select_Mask
  • Core_ID:与子叶指数 (ECX=1) 相关的级别类型在拥有不同硬件多线程功能的处理器间可能有所不同。如果 CPUID.(EAX=11, ECX=1):ECX[15:8] 为 2,它就与“处理器内核”级别类型相关。随后,CPUID.(EAX=11,ECX=1):EAX[4:0] 提供宽度参数以派生选择掩码,从而提取相同处理器内核内所有逻辑处理器的选择掩码。“处理器内核”包括这种情况内的“SMT”,从而通过将自此派生的包含掩码的 SMT 部分归零来完成枚举包装内的不同内核。
    • CorePlus_Mask_Width = CPUID.(EAX=11,ECX=1):EAX[4:0] if CPUID.(EAX=11, ECX=1):ECX[15:8] is 2
    • CoreOnly_Select_Mask = (~((-1) << CorePlus_Mask_Width ) ) ^ SMT_Select_Mask.
    • Core_ID = (x2APIC_ID & CoreOnly_Select_Mask) >> SMT_Mask_Width
  • Pkg_ID:在三级拓扑的相关域内,APIC_ID 的高位(除了较低的 orePlus_Mask_Width?位)可以枚举系统内的不同物理包装。在集群安装中,软件可能需要参考特定供应商的文档才能区分关于指定节点内有多少物理包装得到组织的拓扑。
    • Pkg_Select_Mask = (-1) < ;< CorePlus_Mask_Width
    • Pkg_ID = (x2APIC_ID & Pkg_Select_Mask) >> CorePlus_Mask_Width

 

可以在《附录》内的支持函数 PUTopologyLeafBConstants()?中找到为 x2APIC ID 派生提取参数的示例。

表 A-7 列出了用于从 CPUID 叶 0BH 派生位掩码提取参数以便从 x2APIC ID 提取子 ID 的支持函数。


用于初始 APIC ID 的子 ID 提取参数

从 INITIAL_APIC_ID (CPUID.1:EBX[31:24]) 提取拓扑学子 ID 会使用从 CPUID.1:EBX[23:16] 和 CPUID.(EAX=04H, ECX=0):EAX[31:26] 派生的参数。CPUID.1:EBX[23:16] 代表可为物理包装内逻辑处理器指派的可寻址 ID(初始 APIC ID)的最大数量。该值可能不与物理包装硬件中存在的逻辑处理器数量相同。(1 + (CPUID.(EAX=4, ECX=0):EAX[31:26] )) 的值代表可用于枚举物理包装内不同处理器内核的可寻址 ID (Core_ID) 的最大数量。该值可与数理包装硬件中存在的处理器内核的实际数量不同。

  • SMT_ID:可将可寻址初始 APIC ID 的最大数量除以可寻址内核 ID 的最大数量派生得到等效的 MT_Mask_Width。
    • SMT_Mask_Width = Log2[1]( RoundToNearestPof2(CPUID.1:EBX[23:16]) / ((CPUID.(EAX=4, ECX=0):EAX[31:26] ) + 1)), 其中 Log2 是基于 2 的对数,RoundToNearestPof2() 运算是将输入整数四舍五入到不小于输入值、最接近的 2 的幂整数。
    • SMT_Select_Mask = ~((-1) << SMT_Mask_Width )
    • SMT_ID = INITIAL_APIC_ID & SMT_Select_Mask
  • Core_ID:(1 + (CPUID.(EAX=04H, ECX=0):EAX[31:26] ))的值也可用于派生一个等效的 oreOnly_Mask_Width。
    • CoreOnly_Mask_Width = Log2(1 + (CPUID.(EAX=4, ECX=0):EAX[31:26] ))
    • CoreOnly_Select_Mask = (~((-1) << (CoreOnly_Mask_Width + SMT_Mask_Width) ) ) ^ SMT_Select_Mask.
    • Core_ID = (INITIAL_APIC_ID & CoreOnly_Select_Mask) >> SMT_Mask_Width
  • Pkg_ID:可按如下所示派生而得:
    • CorePlus_Mask_Width = CoreOnly_Mask_Width + SMT_Mask_Width
    • Pkg_Select_Mask = ((-1) << CorePlus_Mask_Width)
    • Pkg_ID = (INITIAL_APIC_ID & Pkg_Select_Mask) >> CorePlus_Mask_Width

 

表 A-8 列出了用于从 CPUID 叶 01H 和叶 04H 派生位掩码提取参数以便从初始 APIC ID 提取子 ID 的支持函数。表 A-9 列出了用于从系统范围的提取函数派生掩码宽度的支持函数。


高速缓存 ID 提取参数

高速缓存 ID 特定于高速缓存结构的目标级别高速缓存。软件必须定义一个先验,即它希望根据处理器拓扑进行优化的目标缓高速存级别(与 CPUID 叶 4 关联的子级别指数 n)。在它选择了子叶指数 ECX=n 后,则 Log2(RoundToNearestPof2( (1 + CPUID.(EAX=4, ECX=n):EAX[25:14])) 就是等效的“Cache_Mask_Width参数。”“Cache_Mask_Width”参数构成了构造选择掩码(用于提取共享目标高速缓存级别的逻辑处理器的子 ID )或者互补掩码(用于从 APID 选择用于标识系统中指定目标级别的不同高速缓存实体的高位)的基础。要构造掩码(用于提取共享高速缓存的不同逻辑处理器的子 ID),只需 ~((-1) << Cache_Mask_Width )。

派生缓存拓扑的位掩码提取参数与表 A-8 中所示类似。软件可以选择集中处理高速缓存结构内某个特定高速缓存级别。在与本白皮书分开发行的配套完整源代码包装中,读者能找到为每个高速缓存级别派生位掩码提取参数的相关示例以及相应的缓存拓扑示例。出于空间考虑,高速缓存拓扑代码的完整源代码没有列在《附录》中。


分析拓扑枚举结果和自定义

软件如何使用(分层子 ID 形式的)拓扑学信息?这实际上取决于每个应用程序的特定需要和情况。它可能因为不同操作系统提供的不同 API 而需要调整。为了便于说明,我们考虑通过一些使用子 ID 的示例来确定以分层方式管理相似性。

了解每个拓扑分层的子 ID 可在多个方面体现用处,例如:

  • 计算系统内指定结构等级中的实体数量;
  • 在添加拓扑学洞察(每内核、每包装、每目标级别高速缓存)时使用操作系统线程管理服务(相似性掩码)以优化应用程序性能。

 

相似性掩码是一个在特定操作系统内定义的数据结构,不同操作系统可能会使用相同概念,但提供不同形式的应用程序编程界面。例如,Microsoft Windows* 提供相似性掩码作为可由相似性控制类应用程序通过位字段直接控制的数据类型。Linux 在内部实施相似的数据结构,但对其进行了抽象,所以应用程序可通过一个将以零为基的数字指派给每个逻辑处理器的迭代界面控制相似性。

由操作系统提供的相似性掩码或等效编号方式不能携带可存储系统拓扑的分层属性的属性。我们将在本章节中概括地使用“相似性掩码”这一术语(因为这项技术可以简单地概括为相似性控制的编号界面)。

在引用代码示例中,我们使用子 ID 为每个结构等级创建序号编号方案(以零为基)。系统拓扑(包装、内核)中的不同实体可以被使用一系列分层序号的应用程序引用。通过将分层序号方案和查找表用于相应的相似性掩码,软件可以轻松控制线程绑定、并优化高速缓存使用。

图 4 描述了系统中 Pkg_ID 和 Core_ID 数据处理的基本示例,以获得有关系统内软件可见物理包装、处理器内核的信息。这一基本技巧也可以调整,以便获取系统内的相似性映射、分层细目和不对称信息。

表 A-10 的 a、b 和 c 部分列出了一种算法,它用于分析系统内所有逻辑处理器的子 ID 以及派生一个三联的以零为基编码方案,用于为每个拓扑学级别内独特实体编索引。

表 A-11 列出了用来组织代码示例剩余部分中使用的杂项全局变量、阵列、工作区项目的数据结构。全套的源代码在单独的包装内提供。全部源代码可在 32 位和 64 位 Windows 和 Linux 操作系统下编译。我们对有限的操作系统和编译器工具集进行了测试。


拓扑枚举的动态软件可见性

当应用程序软件检查/使用拓扑信息时,它就必须牢记软件可见性的动态属性。平台级呈现的硬件容量可通过 BIOS 设置、操作系统引导选项、操作系统支持的用户界面得到不同呈现。例如,64 位和 32 位英特尔架构多路系统要求每个物理处理器支持相同的硬件多线程功能。平台硬件级存在的这一硬件对称性在应用程序级别可能呈现得不同。系统拓扑枚举可以发现动态软件可见的不对称性,无论这一不对称是由 BIOS 设置、操作系统引导选项还是 UI 配置导致。

附录列出了批量的支持函数,这些函数用于将系统的处理器拓扑枚举为对于当前软件过程可见。完整的源代码单独提供下载。参考代码可在 32 位或 64 位 Windows* 环境中编译。在 64 位环境中,提供用于查询子叶的增强固有函数需要 cpuid64.asm 文件。同时我们也提供了适用于为 32 位或 64 位 Linux 环境的参考代码。


词汇表

物理处理器:能够同时执行一个或多个软件线程的微处理器物理包装。每个物理包装都插入物理插座中。每个物理包装都可能包含一个或多个处理器内核,这些处理器内核也被称为物理包装。

处理器内核:提供用于解码、执行指令以及在物理包装内特定子系统间传输数据的专用功能的电路。处理器内核可能包含一个或多个逻辑处理器。

逻辑处理器:允许软件执行人员 (OS) 调度任务或执行线程上下文的处理器硬件资源的基本模块性。每个逻辑处理器每次只可以执行一个线程上下文。

超线程技术:32 位英特尔架构处理器家族内的一项功能,其中每个处理器内核能提供多个逻辑处理器的功能。

SMT:Simultaneous Multi-Threading(“同时多线程”)的缩写名称。这是一种通过在逻辑处理器之间共享执行资源和高速缓存结构,在相同处理器内核内提供多个逻辑处理器功能的有效方式。

多核处理器:包括多个处理器内核的物理处理器。

多路平台:包含两个或多个物理插座的计算机系统。

硬件多线程:指允许系统运行多线程软件的任何硬件支持组合。用于多线程的硬件支持形式有:SMT、多核和多处理器。

处理器拓扑:物理包装内处理器实体(逻辑处理器、处理器内核)相对于物理处理器内硬件资源共享结构的分层关系。

高速缓存结构:在处理器实体和物理内存子系统之间缓冲数据传输的高速缓存等级的物理排列。

高速缓存拓扑:高速缓存等级相对于物理处理器内逻辑处理器的结构关系。


附录

表 A-1 系统范围 CPU 拓扑常数确定

 

// Derive parameters used to extract/decompose APIC ID for CPU topology
// The algorithm assumes CPUID feature symmetry across all physical packages.
// Since CPUID reporting by each logical processor in a physical package are
// identical, we only execute CPUID on one logical processor to derive these
// system-wide parameters

// return 0 if successful, non-zero if error occurred
static int CPUTopologyParams()
{
DWORD maxCPUID; // highest CPUID leaf index this processor supports
CPUIDinfo info; // data structure to store register data reported by CPUID
_CPUID(&info, 0, 0);
maxCPUID = info.EAX;
// cpuid leaf B detection
if (maxCPUID >= 0xB)
{
CPUIDinfo CPUInfoB;
_CPUID(&CPUInfoB,0xB, 0);
//glbl_ptr points t
o assortment of global data, workspace, etc
glbl_ptr->hasLeafB = (CPUInfoB.EBX != 0);
}
_CPUID(&info, 1, 0);
// Use HWMT feature flag CPUID.01:EDX[28] to treat three configurations:
if (getBitsFromDWORD(info.EDX,28,28))
{
// #1, Processors that support CPUID leaf 0BH
if (glbl_ptr->hasLeafB)
{
// use CPUID leaf B to derive extraction parameters
CPUTopologyLeafBConstants();
}
else
//#2, Processors that support legacy parameters
// using CPUID leaf 1 and leaf 4
{
CPUTopologyLegacyConstants(&info, maxCPUID);
}
}
else
//#3, Prior to HT, there is only one logical
//processor in a physical package
{

glbl_ptr->CoreSelectMask = 0;
glbl_ptr->SMTMaskWidth = 0;
glbl_ptr->PkgSelectMask = (-1);
glbl_ptr->PkgSelectMaskShift = 0;
glbl_ptr->SMTSelectMask = 0;
}

if( glbl_ptr->error)return -1;
else return 0;
}

 

表 A-2 衍生系统拓扑枚举信息模块结构

 

/*
* BuildSystemTopologyTables
*
* Construct the processor topology tables and values necessary to
* support the external functions that display CPU topology and/or
* cache topology derived from system topology enumeration.
* Arguments: None
* Return: None, sets glbl_ptr->error if tables or values can not be calculated.
*/
static void
BuildSystemTopologyTables()
{
unsigned lcl_OSProcessorCount, subleaf;
int numMappings = 0;
// call OS-specific service to find out how many logical processors
// are supported by the OS
glbl_ptr->OSProcessorCount = lcl_OSProcessorCount = GetMaxCPUSupportedByOS();

// allocated the memory buffers within the global pointer
AllocArrays(lcl_OSProcessorCount);

// Gather all the system-wide constant parameters needed to
// derive topology information
if (CPUTopologyParams() ) return ;

if (CacheTopologyParams() ) return ;

// For each logical processor, collect APIC ID and
// parse sub IDs for each APIC ID
numMappings = QueryParseSubIDs();
if ( numMappings < 0 ) return ;
// Derived separate numbering schemes for each level of the cpu topology
if( AnalyzeCPUHierarchy(numMappings) < 0 ) {
glbl_ptr->error |= _MSGTYP_TOPOLOGY_NOTANALYZED;
}
// an example of building cache topology info for each cache level
if(glbl_ptr->maxCacheSubleaf != -1) {
for(subleaf=0; subleaf <= glbl_ptr->maxCacheSubleaf; subleaf++) {
if( glbl_ptr->EachCacheMaskWidth[subleaf] != 0xffffffff) {
// ensure there is at least one core in the target level cache
if (AnalyzeEachCHierarchy(subleaf, numMappings) < 0) {
glbl_ptr->error |= _MSGTYP_TOPOLOGY_NOTANALYZED;

}
}
}
}
}

 

表 A-3 APIC ID、子 ID 数据结构,顺序编号方案映射

typedef struct {
unsigned int32 APICID;
// the full x2APIC ID or initial APIC ID of a logical
//processor assigned by HW
unsigned __int32 OrdIndexOAMsk;
// An ordinal index (zero-based) for each logical
// processor in the system, 1:1 with "APICID"
// Next three members are the sub IDs for processor topology enumeration
unsigned __int32 pkg_IDAPIC;
// Pkg_ID field, subset of APICID bits
// to distinguish different packages
unsigned __int32 Core_IDAPIC;
// Core_ID field, subset of APICID bits to
// distinguish different cores in a package
unsigned __int32 SMT_IDAPIC;

// SMT_ID field, subset of APICID bits to
// distinguish different logical processors in a core
// the next three members stores a numbering scheme of ordinal index
// for each level of the processor topology.
unsigned __int32 packageORD;
// a zero-based numbering scheme for each physical
// package in the system
unsigned __int32 coreORD;
// a zero-based numbering scheme for each core in the
// same package
unsigned __int32 threadORD;
// a zero-based numbering scheme for each thread in
// the same core
// Next two members are the sub IDs for cache topology enumeration
unsigned __int32 EaCacheSMTIDAPIC[MAX_CACHE_SUBLEAFS];
// SMT_ID field, subset of
// APICID bits to distinguish different logical processors
// sharing the same cache level
unsigned __int32 EaCacheIDAPIC[MAX_CACHE_SUBLEAFS];
// sub ID to enumerate
// different cache entities of the cache level corresponding
// to the array index/cpuid leaf 4 subleaf index
// the next three members stores a numbering scheme of ordinal index
// for enumerating different cache entities of a cache level, and enumerating
// logical processors sharing the same cache entity.
unsigned __int32 EachCacheORD[MAX_CACHE_SUBLEAFS];
// a zero-based numbering
// scheme for each cache entity of the specified cache level in the system
unsigned __int32 threadPerEaCacheORD[MAX_CACHE_SUBLEAFS];
// a zero-based
// numbering scheme for each logical processor sharing the same cache of the
// specified cache level

} IdAffMskOrdMapping;


/* Alternate technique for ring 3 code to infer the effect of CMOS setting in BIOS
* that restricted CPUID instruction to report highest leaf index
is 2, i.e.
* MSR IA32_MISC_ENABLES[22] was set to 1; This situation
* will prevent software from using CPUID to conduct topology enumeration
* RDMSR instruction is privileged, this alternate routine can run in ring 3.
*/
Int InferBIOSCPUIDLimitSetting()
{ DWORD maxleaf, max8xleaf;
CPUIDinfo info; // data structure to store register data reported by CPUID
// check CPUID leaf reporting capability is intact
CPUID(&info, 0);
maxleaf = info.EAX;
CPUID(&info, 0x80000000);
max8xleaf = info.EAX;
// Earlier Pentium 4 and Intel Xeon processor (prior to 90nm Intel Pentium 4
// processor)support extended with max extended leaf index 0x80000004,
// 90nm Intel Pentium 4 processor and later processors supports higher extended
// leaf index greater than 0x80000004.
If ( maxleaf <= 4 && max8xleaf > 0x80000004) return 1;
else return 0;
}

 

 

表 A-4 查询 APIC ID 以及将 APIC ID 解析为子 ID

/*
* QueryParseSubIDs
*
* Use OS specific service to find out how many logical processors can be accessed
* by this application.
* Querying CPUID on each logical processor requires using OS-specific API to
* bind current context to each logical processor first.
* After gathering the APIC ID's for each logical processor,
* we can parse APIC ID into sub IDs for each topological levels
* The thread affnity API to bind the current context limits us
* in dealing with the limit of specific OS
* The loop to iterate each logical processor managed by the OS can be done
* in a manner that abstract the OS-specific affinity mask data structure.
* Here, we construct a generic affinity mask that can handle arbitrary number
* of logical processors.
* Return: 0 is no error
*/
long QueryParseSubIDs(void)
{ unsigned i;
//DWORD_PTR processAffinity;
//DWORD_PTR systemAffinity;
unsigned long numMappings = 0, lcl_OSProcessorCount;
unsigned long APICID;
// we already queried OS how many logical processor it sees.
lcl_OSProcessorCount = glbl_ptr->OSProcessorCount;
// we will use our generic affinity bitmap that can be generalized from
// OS specific affinity mask constructs or the bitmap representation of an OS
AllocateGenericAffinityMask(&glbl_ptr->cpuid_values_processAffinity, lcl_OSProcessorCount);
AllocateGenericAffinityMask(&glbl_ptr->cpuid_values_systemAffinity, lcl_OSProcessorCount);
// Set the affinity bits of our generic affinity bitmap according to
// the system affinity mask and process affinity mask
SetChkProcessAffinityConsistency(lcl_OSProcessorCount);
if (glbl_ptr->error) return -1;

for (i=0; i < glbl_ptr->OSProcessorCount;i++) {
// can't asume OS affinity bit mask is contiguous,
// but we are using our generic bitmap representation for affinity
if(TestGenericAffinityBit(&glbl_ptr->cpuid_values_processAffinity, i) == 1) {
// bind the execution context to the ith logical processor
// using OS-specifi API

if( BindContext(i, glbl_ptr->cpuid_values_OSProcessorCount) ) {
glbl_ptr->error |= _MSGTYP_UNKNOWNERR_OS;
break;
}
// now the execution context is on the i'th cpu, call the parsing routine
ParseIDS4EachThread(i, numMappings);
numMappings++;
}
}
glbl_ptr->EnumeratedThreadCount = numMappings;
if( glbl_ptr->error)return -1;
else return numMappings;
};

 

 

表 A-5 将 APIC ID 解析为子 ID 的支持例程

/*
* ParseIDS4EachThread
* after execution context has already bound to the target logical processor
* Query the 32-bit x2APIC ID if the processor supports it, or
* Query the 8-bit initial APIC ID for older processors. Apply various
* system-wide topology constant to parse the APIC ID into various sub IDs
* Arguments:
* i : the ordinal index to reference a logical processo
* r in the system
* numMappings : running count ot how many processors we've parsed
* Return: 0 is no error
*/
unsigned ParseIDS4EachThread(unsigned i, unsigned numMappings)
{ unsigned APICID;
unsigned subleaf;

APICID = glbl_ptr->PApicAffOrdMapping[numMappings].APICID = GetApicID(i);
glbl_ptr->PApicAffOrdMapping[numMappings].OrdIndexOAMsk = i;
// this an ordinal number that can relate to generic affinitymask
glbl_ptr->PApicAffOrdMapping[numMappings].pkg_IDAPIC = ((APICID & glbl_ptr->PkgSelectMask)
>> glbl_ptr->PkgSelectMaskShift);
glbl_ptr->PApicAffOrdMapping[numMappings].Core_IDAPIC = ((APICID & glbl_ptr->CoreSelectMask)
>> glbl_ptr->SMTMaskWidth);
glbl_ptr->PApicAffOrdMapping[numMappings].SMT_IDAPIC = (APICID & glbl_ptr->SMTSelectMask);
if(glbl_ptr->maxCacheSubleaf != -1) {
for(subleaf=0; subleaf <= glbl_ptr->maxCacheSubleaf; subleaf++) {
glbl_ptr->PApicAffOrdMapping[numMappings].EaCacheSMTIDAPIC[subleaf]
= (APICID & glbl_ptr->EachCacheSelectMask[subleaf]);

 

如需更全面地了解编译器优化,请参阅优化注意事项