作者:Eric Palmer针对 Visual Studio .NET* 2005中的x64平台,程序员不能像对32位代码那样使用内联汇编代码(inline assembly)。这迫使他们或求助使用内联函数(intrinsics)的 C/C++ 代码,或不嫌麻烦地创建函数的 64 位 MASM(.asm)版本。不幸的是,VS .Net 2005 在实施 CPUID(__cpuid)的内联函数时,只能识别 eax 寄存器中的输入自变量,而不能识别 ecx 中最近定义的输入,这些输入用于有关高速缓存参数和某些多核特性的查询。这样,CPUID 指令的充分使用就需要一份 64 位 .asm 清单。
以下代码示例显示了如何与面向 64 位(x64)平台的 VS .Net 2005 配合使用 PUID 和 RDTSC 指令。CPUID 指令被广泛用于获取系统 CPU 的详细信息,而 RDTSC 则被用来读取 CPU 用于计时和性能测量目的的内部时间戳计数器。RDTSC 内联数据(__rdtsc)的确可实现预期的功用,从而替代内联汇编代码。
要连编(build)64 位 .asm 文件,你需要先创建一个定制的连编步骤来调用 64 位 MASM“ml64.exe”,如下面抓图所示。32 位配置无需连编 cpuid64.asm 文件,因此对于 Win32* 平台,将 General -> Excluded From Build 设为 Yes。

头文件(cpuid_32_64.h)创建了 _CPUID 和 _RDTSC 函数的单一定义,在 32 位和 64 位连编中均可使用。对于 64 位连编,_CPUID 使用 .asm 函数 cpuid64,而 _RDTSC 使用内联函数 __rdtsc。对于 32 位连编,_CPUID使用内联汇编语言函数 cpuid32,而_RDTSC 使用内联汇编函数 _inl_rdtsc32。
下面我们来看下文 C 文件(cpuid_32_64.c)中的两个例子。第一个是 GetCoresPerPackage(),它使用 eax=4 和 ecx=0 来调用 _CPUID,以读取 CPU 报告的第一组具有确定性的高速缓存参数,并提取指示每个处理器封装中处理器内核数量的字段。(例如,单核英特尔® 奔腾® 4 处理器上这个函数的返回值是 1,双核英特尔® 奔腾® D 处理器上这个函数的返回值是 2。)如果这个函数在 x64 平台上使用内联函数 __cpuid 而不是 cpuid64 函数,ecx 的输入值将不具确定性,输出也将不可靠。第二个例子是 timeSomethingExample(),它通过两次调用 _RDTSC 来计算循环中消耗的时间。_CPUID 的例子显示了如何使用一个定义来调用 64 位 .asm 代码或 32 位内联汇编代码,而 _RDTSC 的例子则显示了如何使用一个定义来调用 64 位内联函数或 32 位内联汇编函数。
_CPUID 的例子和 _RDTSC 的例子都显示了在各平台要求不同的底层代码的情况下,如何创建可透明地从 Win32 移植到 x64 平台的功能函数(utility function)。此外,cpuid64 函数也有助于弥补 __cpuid 内联函数的不足,使得 32 位和 64 位应用可充分利用 CPUID 指令的全部功能。
头文件(cpuid_32_64.h):