• Выбор языка



Инструкции CPUID для среды Microsoft Visual Studio* .NET 2005 и 64-разрядных платформ
Опции страницы
Распечатать | Отправить другу | Поддержка
Сделать закладку
Digg this | Добавить в вашей del.icio.us учетную запись
Проголосовать
теги сообщества

Поиск тегов
 

Автор: Эрик Палмер (Eric Palmer)

При разработке для платформ x64 в Visual Studio .NET* 2005 программисты уже не могут использовать встроенный ассемблерный код, как в случае 32-разрядного кода. Это вынуждает программистов либо полагаться на код C/C++, используя встроенные средства, либо использовать трудоемкое создание 64-разрядной MASM (.asm) версии функции. К сожалению, реализация встроенного средства VS .Net 2005 для CPUID (__cpuid) распознает только входные параметры в регистре eax, и не распознает определенные позже входные параметры в ecx, которые требуются для запросов, связанных с параметрами кэша и определенными многоядерными характеристиками. Таким образом, для использования функции CPUID в полном объеме требуется 64-разрядный .asm листинг.

Следующие примеры кодов демонстрируют, как использовать команды CUPID и RDSTC с VS.NET 2005 для 64-разрядных (x64) платформ. Команда CPUID обычно применяется для получения подробной информации о ЦП системы, а RDTSC используется для считывания внутреннего счетчика системного времени ЦП для целей синхронизации и измерения производительности. Встроенное средство RDTSC (__rdtsc) должно работать, как и предполагается, и может использоваться вместо встроенного ассемблера.

Для компоновки 64-разрядного файла .asm создайте пользовательский шаг компоновки, который вызывает 64-разрядный MASM, "ml64.exe", как показано на приведенном ниже моментальном снимке экрана. Для 32-разрядной конфигурации файл cpuid64.asm компоновать не следует, поэтому для платформы Win32 установите значение Да для опции Общие -> Исключить из компоновки.


(Для увеличения нажмите на изображение)

Показанный ниже файл заголовка (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() - вызывается _CPUID с eax=4 и ecx=0 для считывания детерминированных параметров кэша первого набора, представленных ЦП, и извлечения поля, указывающего число ядер процессора в процессорном пакете. (Например, эта функция будет возвращать значение 1 в случае одноядерного процессора Intel® Pentium® 4 и значение 2 в случае двухъядерного процессора Intel® Pentium® D.) Если в этой функции на платформе x64 использовалось бы встроенное средство __cpuid вместо функции cpuid64, входящее значение ecx было бы недетерминированным, и значение вывода было бы недостоверным. Во втором примере функция timeSomethingExample() дважды вызывает _RDTSC и вычисляет число тактов счетчика в цикле. Пример _CPUID показывает, как можно использовать одно и то же определение для вызова либо 64-разрядного кода .asm, либо 32-разрядного встроенного ассемблера, а в примере _RDTSC показано, как с помощью одного определения можно вызывать либо 64--разрядное встроенное средство, либо 32-разрядный встроенный ассемблер.

И в примере _CPUID, и в примере _RDTSC показано создание служебных функций, явно портируемых с платформ Win32 на платформы x64 в тех случаях, когда для каждой платформы требуется свой базовый код. Кроме того, функция 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

We invite you to post a comment (not monitored by customer support) on this page or send a question directly to our support team.