• 其他语言



CPUID 在 x64 平台与 Microsoft Visual Studio* .NET 2005 上的应用
页面和feed选项
打印
收藏此页
Digg此页 | 添加到您的del.icio.us帐号

作者: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):

#pragma once

typedef struct cpuid_args_s {
	DWORD eax;
	DWORD ebx;
	DWORD ecx;
	DWORD edx;
} CPUID_ARGS;

#ifdef __cplusplus
extern "C" {
#endif

#ifdef _M_X64 // For 64-bit apps
unsigned __int64 __rdtsc(void);
#pragma intrinsic(__rdtsc)
#define _RDTSC __rdtsc

void cpuid64(CPUID_ARGS* p);
#define _CPUID cpuid64

#else // For 32-bit apps

#define _RDTSC_STACK(ts) \
	__asm rdtsc \
	__asm mov DWORD PTR [ts], eax \
	__asm mov DWORD PTR [ts+4], edx

__inline unsigned __int64 _inl_rdtsc32() {
	unsigned __int64 t;
	_RDTSC_STACK(t);
	return t;
}
#define _RDTSC _inl_rdtsc32

void cpuid32(CPUID_ARGS* p);
#define _CPUID cpuid32

#endif

// Our 32/64-bit example function
int GetCoresPerPackage();

#ifdef __cplusplus
}
#endif  

32/64 位 .c 文件(cpuid_32_64.c):

#include <windows.h>
#include “cpuid_32_64.h”

#ifndef _M_X64
void cpuid32(CPUID_ARGS* p) {
	__asm {
		mov	edi, p
		mov eax, [edi].eax
		mov ecx, [edi].ecx // for functions such as eax=4
		cpuid
		mov [edi].eax, eax
		mov [edi].ebx, ebx
		mov [edi].ecx, ecx
		mov [edi].edx, edx
	}
}
#endif

// Assumptions prior to calling:
// - CPUID instruction is available
// - We have already used CPUID to verify that this in an Intel® processor
int GetCoresPerPackage()
{
	// Is explicit cache info available?
	int nCaches=0;
	int coresPerPackage=1; // Assume 1 core per package if info not available 
	DWORD t;
	int cacheIndex;
	CPUID_ARGS ca;

	ca.eax = 0;
	_CPUID(&ca);
	t = ca.eax;
	if ((t > 3) && (t < 0x80000000)) { 
		for (cacheIndex=0; ; cacheIndex++) {
			ca.eax = 4;
			ca.ecx = cacheIndex;
			_CPUID(&ca);
			t = ca.eax;
			if ((t & 0x1F) == 0)
				break;
			nCaches++;
		}
	}

	if (nCaches > 0) {
		ca.eax = 4;
		ca.ecx = 0; // first explicit cache
		_CPUID(&ca);
		coresPerPackage = ((ca.eax >> 26) & 0x3F) + 1; // 31:26
	}
	return coresPerPackage;
}

void timeSomethingExample()
{
	ULONGLONG tStart, tElapsed;
	int i;

	tStart = _RDTSC();
	for (i=0; i < 1000; i++)
	{
		// Do something here 1000 times
	}
	tElapsed = _RDTSC() - tStart; // CPU timer ticks taken to do something 1000 times
}

64 位 .asm 文件 (cpuid64.asm):

; call cpuid with args in eax, ecx
; store eax, ebx, ecx, edx to p
PUBLIC cpuid64
.CODE
       ALIGN     8
cpuid64	PROC FRAME
; void cpuid64(CPUID_ARGS* p);
; rcx <= p
        sub			rsp, 32
        .allocstack 32
        push		rbx
        .pushreg	rbx
        .endprolog
        
		mov	r8, rcx
		mov eax, DWORD PTR [r8+0]
		mov ecx, DWORD PTR [r8+8]
		cpuid
		mov DWORD PTR [r8+0], eax
		mov DWORD PTR [r8+4], ebx
		mov DWORD PTR [r8+8], ecx
		mov DWORD PTR [r8+12], edx

        pop      rbx         
        add      rsp, 32     
        
        ret                  
        ALIGN     8
cpuid64 ENDP
_TEXT ENDS