Skip to main content
GameDev.net gamedev.net
🔒 Locked

Idenitfying CPU Brand & Model (C++)

Started by Liam M Mar 10, 2007 at 12:37 AM 13 replies 25.2k views
Original Post
Liam M
Liam M
Hello, I'm working on a bit of rather complex math, where SSE optimisations would be rather useful. However, I am aiming to keep my application as cross CPU compatible as possible, and I realise that not all CPU's support all generations of the SSE instruction set. My question is this: Is there a way to identify the make and model of the CPU using C++? Or, more ideally, is there a way to obtain further processor information, such as what versions of SSE it supports, ect.? I was informed there was a way to do this using assembly language, but I am not very confident with it, and the desciption i was given was vague. I googled for it, but to no avail. Any example C++ code would be much appreciated. Thanks in advance, Liam M.
jflanglois
jflanglois
You can use the CPUID instruction. There might be some C++ code out there that abstracts it nicely, but I haven't looked. You will probably need to write some inline assembly at some point.


jfl.
Liam M
Liam M
Ah, thanks! That must have been the instruction I was told about. I'll look into making my own abstraction for it. Thanks for the help.
b2b3
b2b3
I posted this in another topic a while ago:

Processor.h
#ifndef PROCESSOR_INCLUDED#define PROCESSOR_INCLUDED#include <string>namespace hw{    class Processor    {    public:        Processor(void);        bool HasSSE(void) const        {            return has_sse;        }        bool HasSSE2(void) const        {            return has_sse2;        }        bool HasSSE3(void) const        {            return has_sse3;        }        bool HasMMX(void) const        {            return has_mmx;        }        bool HasMMXExt(void) const        {            return has_mmx_ext;        }        bool Has3DNow(void) const        {            return has_3dnow;        }        bool Has3DNowExt(void) const        {            return has_3dnow_ext;        }        bool IsHTT(void) const        {            return is_htt;        }        std::string GetName(void) const        {            return cpu_name;        }        std::string GetVendorID(void) const        {            return cpu_vendor;        }        std::string GetVendorName(void) const        {            if (cpu_vendor == "AuthenticAMD")            {                return std::string("AMD");            }            else if (cpu_vendor == "GenuineIntel")            {                return std::string("Intel");            }            else if (cpu_vendor == "CyrixInstead")            {                return std::string("Cyrix");            }            else if (cpu_vendor == "CentaurHauls")            {                return std::string("Centaur");            }            else if (cpu_vendor == "RiseRiseRise")            {                return std::string("Rise");            }            else if (cpu_vendor == "GenuineTMx86")            {                return std::string("Transmeta");            }            else if (cpu_vendor == "UMC UMC UMC ")            {                return std::string("UMC");            }            else            {                return cpu_vendor;            }        }        unsigned long GetSpeed(void) const        {            return cpu_speed;        }        int GetStepping(void) const        {            return stepping;        }        int GetModel(void) const        {            return model;        }        int GetFamily(void) const        {            return family;        }        int GetType(void) const        {            return type;        }        int GetExtModel(void) const        {            return ext_model;        }        int GetExtFamily(void) const        {            return ext_family;        }    private:        bool has_mmx;        bool has_mmx_ext;        bool has_3dnow;        bool has_3dnow_ext;        bool has_sse;        bool has_sse2;        bool has_sse3;        bool is_htt;        int stepping;        int model;        int family;        int type;        int ext_model;        int ext_family;        std::string cpu_name;        std::string cpu_vendor;        unsigned long cpu_speed;        unsigned __int64 GetTSC()        {            __asm             {	            rdtsc            }        }        void MeasureSpeed(void);        void DoCPUID(void);    };}#endif


Processor.cpp
#include <string>#include <windows.h>#include "Processor.h"using namespace std;namespace hw{    Processor::Processor(void) :        has_mmx(false),        has_mmx_ext(false),        has_3dnow(false),        has_3dnow_ext(false),        has_sse(false),        has_sse2(false),        has_sse3(false),        is_htt(false),        stepping(0),        model(0),        family(0),        type(0),        ext_model(0),        ext_family(0),        cpu_name("(unknown)"),        cpu_vendor("(unknown)"),        cpu_speed(0)    {        MeasureSpeed();        DoCPUID();    }    void Processor::MeasureSpeed(void)    {        LARGE_INTEGER    s, e, freq;        unsigned __int64 cs, ce;        // Determine timer frequency.        QueryPerformanceFrequency(&freq);        QueryPerformanceCounter(&s);        cs = GetTSC();        // Wait for a while...        for (volatile long i = 0; i < 1000000; ++i) ;        ce = GetTSC();         QueryPerformanceCounter(&e);         // Calculate frequency.        cpu_speed = (unsigned long) ((ce - cs) * freq.QuadPart / (e.QuadPart - s.QuadPart));    }    // this is the "core" method - it will cal CPUID instruction and parse output    void Processor::DoCPUID(void)    {        char cpu_name_string[49] = {0};  // max 48 chars + terminating 0        char cpu_vendor_id_string[13] = {0}; // max 12 chars + terminating 0        unsigned int cpu_feat_eax = 0;        unsigned int cpu_feat_edx = 0;        unsigned int cpu_feat_ecx = 0;        unsigned int cpu_feat_ext_edx = 0;        __asm        {            mov     eax, 0x00000000              // first CPUID function, always supported (on reasonable cpu)            cpuid                                // get info            mov     DWORD PTR [cpu_vendor_id_string + 0], ebx  // copy vendor id string            mov     DWORD PTR [cpu_vendor_id_string + 4], edx            mov     DWORD PTR [cpu_vendor_id_string + 8], ecx            test    eax, eax                                 jz      no_features                  // if eax is 0, no info will be available            mov     eax, 0x00000001              // get extended info about cpu            cpuid            mov     [cpu_feat_eax], eax          // store data for later processing            mov     [cpu_feat_edx], edx            mov     [cpu_feat_ecx], ecx            mov     eax, 0x80000000              // first extended function            cpuid            // now test which extended functions are supported            cmp     eax, 0x80000001              // is eax < 0x80000001            jb      no_features                  // yes -> jump to no_features label            cmp     eax, 0x80000004              // is eax < 0x80000004            jb      ext_feats_only               // yes -> jump to ext_feats_only label            // now get name of the cpu            mov     eax, 0x80000002            cpuid            mov     DWORD PTR [cpu_name_string + 0], eax            mov     DWORD PTR [cpu_name_string + 4], ebx            mov     DWORD PTR [cpu_name_string + 8], ecx            mov     DWORD PTR [cpu_name_string + 12], edx            mov     eax, 0x80000003            cpuid            mov     DWORD PTR [cpu_name_string + 16], eax            mov     DWORD PTR [cpu_name_string + 20], ebx            mov     DWORD PTR [cpu_name_string + 24], ecx            mov     DWORD PTR [cpu_name_string + 28], edx            mov     eax, 0x80000004            cpuid            mov     DWORD PTR [cpu_name_string + 32], eax            mov     DWORD PTR [cpu_name_string + 36], ebx            mov     DWORD PTR [cpu_name_string + 40], ecx            mov     DWORD PTR [cpu_name_string + 44], edx        ext_feats_only:            // get extended features            mov     eax, 0x80000001            cpuid            mov     [cpu_feat_ext_edx], edx        no_features:            // done        } // __asm        // now process data we got from cpu        cpu_name = string(cpu_name_string);        cpu_vendor = string(cpu_vendor_id_string);        stepping = cpu_feat_eax & 0xF;        model = (cpu_feat_eax >> 4) & 0xF;        family = (cpu_feat_eax >> 8) & 0xF;        type = (cpu_feat_eax >> 12) & 0x3;        ext_model = (cpu_feat_eax >> 16) & 0xF;        ext_family = (cpu_feat_eax >> 20) & 0xFF;        has_mmx = (cpu_feat_edx >> 23) & 0x1;        has_sse = (cpu_feat_edx >> 25) & 0x1;        has_sse2 = (cpu_feat_edx >> 26) & 0x1;        is_htt = (cpu_feat_edx >> 28) & 0x1;        has_sse3 = cpu_feat_ecx & 0x1;        has_mmx_ext = (cpu_feat_ext_edx >> 22) & 0x1;        has_3dnow = (cpu_feat_ext_edx >> 31) & 0x1;        has_3dnow_ext = (cpu_feat_ext_edx >> 30) & 0x1;    }}


You can use hw::Processor class to query various features of the processor including its speed and supported instruction sets.
nmi
nmi
If you need more information about different cpu types check out this site:
http://www.sandpile.org/
Evil Steve
Evil Steve
And for completeness, here's my CPUID code - which works in x64 too, but is MSVC specific because it uses the __cpuid() intrinsic:
#include <windows.h>#include <intrin.h>static const unsigned PROCESSOR_UNKNOWN = 0;static const unsigned PROCESSOR_AMD = 1;static const unsigned PROCESSOR_INTEL = 2;//============================================================================// Read the CPU speed from the registry//============================================================================DWORD ReadCPUSpeedFromRegistry(DWORD dwCPU){HKEY hKey;DWORD dwSpeed;	// Get the key name	wchar_t szKey[256];	_snwprintf(szKey, sizeof(szKey)/sizeof(wchar_t),		L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d\\", dwCPU);	// Open the key	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,szKey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)	{		return 0;	}	// Read the value	DWORD dwLen = 4;	if(RegQueryValueEx(hKey, L"~MHz", NULL, NULL, (LPBYTE)&dwSpeed, &dwLen) != ERROR_SUCCESS)	{		RegCloseKey(hKey);		return 0;	}	// Cleanup and return	RegCloseKey(hKey);    return dwSpeed;}//============================================================================// Calculate and log the CPU speed and features//============================================================================void LogCPU(){unsigned nHighestFeature;unsigned nHighestFeatureEx;int nBuff[4];char szMan[13];char szFeatures[256];unsigned nProcessorType;	// Get CPU manufacturer and highest CPUID	__cpuid(nBuff, 0);	nHighestFeature = (unsigned)nBuff[0];	*(int*)&szMan[0] = nBuff[1];	*(int*)&szMan[4] = nBuff[3];	*(int*)&szMan[8] = nBuff[2];	szMan[12] = 0;	if(strcmp(szMan, "AuthenticAMD") == 0)		nProcessorType = PROCESSOR_AMD;	else if(strcmp(szMan, "GenuineIntel") == 0)		nProcessorType = PROCESSOR_INTEL;	else		nProcessorType = PROCESSOR_UNKNOWN;	// Get highest extended feature	__cpuid(nBuff, 0x80000000);	nHighestFeatureEx = (unsigned)nBuff[0];	// Get processor brand name	if(nHighestFeatureEx >= 0x80000004)	{		char szCPUName[49];		szCPUName[0] = 0;		__cpuid((int*)&szCPUName[0], 0x80000002);		__cpuid((int*)&szCPUName[16], 0x80000003);		__cpuid((int*)&szCPUName[32], 0x80000004);		szCPUName[48] = 0;		for(int i=(int)strlen(szCPUName)-1; i>=0; --i)		{			if(szCPUName == ' ')				szCPUName = '\0';			else				break;		}		ELog::Get().SystemFormat(L"PERF    : CPU: %S (%S)\n", szCPUName, szMan);	}	else		ELog::Get().SystemFormat(L"PERF    : CPU: %S\n", szMan);	// Get CPU features	szFeatures[0] = 0;	if(nHighestFeature >= 1)	{		__cpuid(nBuff, 1);		if(nBuff[3] & 1<<0)			strcat(szFeatures, "FPU ");		if(nBuff[3] & 1<<23)			strcat(szFeatures, "MMX ");		if(nBuff[3] & 1<<25)			strcat(szFeatures, "SSE ");		if(nBuff[3] & 1<<26)			strcat(szFeatures, "SSE2 ");		if(nBuff[2] & 1<<0)			strcat(szFeatures, "SSE3 ");		// Intel specific:		if(nProcessorType == PROCESSOR_INTEL)		{			if(nBuff[2] & 1<<9)				strcat(szFeatures, "SSSE3 ");			if(nBuff[2] & 1<<7)				strcat(szFeatures, "EST ");		}		if(nBuff[3] & 1<<28)			strcat(szFeatures, "HTT ");	}	// AMD specific:	if(nProcessorType == PROCESSOR_AMD)	{		// Get extended features		__cpuid(nBuff, 0x80000000);		if(nHighestFeatureEx >= 0x80000001)		{			__cpuid(nBuff, 0x80000001);			if(nBuff[3] & 1<<31)				strcat(szFeatures, "3DNow! ");			if(nBuff[3] & 1<<30)				strcat(szFeatures, "Ex3DNow! ");			if(nBuff[3] & 1<<22)				strcat(szFeatures, "MmxExt ");		}		// Get level 1 cache size		if(nHighestFeatureEx >= 0x80000005)		{			__cpuid(nBuff, 0x80000005);			ELog::Get().SystemFormat(L"PERF    : L1 cache size: %dK\n", ((unsigned)nBuff[2])>>24);		}	}	// Get cache size	if(nHighestFeatureEx >= 0x80000006)	{		__cpuid(nBuff, 0x80000006);		ELog::Get().SystemFormat(L"PERF    : L2 cache size: %dK\n", ((unsigned)nBuff[2])>>16);	}	// Log features	ELog::Get().SystemFormat(L"PERF    : CPU Features: %S\n", szFeatures);	// Get misc system info	SYSTEM_INFO theInfo;	GetSystemInfo(&theInfo);	// Log number of CPUs and speeds	ELog::Get().SystemFormat(L"PERF    : Number of CPUs: %d\n", theInfo.dwNumberOfProcessors);	for(DWORD i=0; i<theInfo.dwNumberOfProcessors; ++i)	{		DWORD dwCPUSpeed = ReadCPUSpeedFromRegistry(i);		ELog::Get().SystemFormat(L"PERF    : * CPU %d speed: ~%dMHz\n", i, dwCPUSpeed);	}}


EDIT: Sample output:
PERF    : CPU: AMD Athlon(tm) 64 X2 Dual Core Processor 3800+ (AuthenticAMD)PERF    : L1 cache size: 64KPERF    : L2 cache size: 512KPERF    : CPU Features: FPU MMX SSE SSE2 SSE3 HTT 3DNow! Ex3DNow! MmxExt PERF    : Number of CPUs: 2PERF    : * CPU 0 speed: ~2010MHzPERF    : * CPU 1 speed: ~2010MHz

Liam M
Liam M
Wow, thank you all for your helpful replies! I'l go over all of em, and take a look at how they work (I'd still like to learn something), but this saves me so much time and irritation. Thanks guys!
b2b3
b2b3
Well, I tried porting my code to the x64.
It was much more complicated than I first thought since inline assembly cannot be used in 64-bit mode in Visual Studio (that sucks [sad]).

So, to make it work, I had to create asm file with CPUID wrapper function and add some preprocessor defines to enable/disable this wrapper based on the current compilation mode. I also had to add custom build step for said ASM file.
My build step uses following command-line:
ml64.exe "$(InputPath)" /c /Fo "cpuid_x64.obj" /Zi
Also, it has to be set up so that this build step is disabled in the Win32 mode and enabled only in x64 mode.

cpuid_x64.asm
; Calls CPUID with arguments in EAX and ECX registers; Output (EAX ~ EDX) is written to the regsPUBLIC cpuid_x64.CODE               ALIGN       8cpuid_x64           PROC FRAME        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       8cpuid_x64 ENDP_TEXT ENDSEND


Processor.h
#ifndef PROCESSOR_INCLUDED#define PROCESSOR_INCLUDED#include <string>namespace hw{    typedef unsigned __int32 UInt32;    struct Registers    {        explicit Registers(UInt32 eax_ = 0, UInt32 ebx_ = 0, UInt32 ecx_ = 0, UInt32 edx_ = 0) :        eax(eax_),        ebx(ebx_),        ecx(ecx_),        edx(edx_)        {        }        UInt32 eax;        UInt32 ebx;        UInt32 ecx;        UInt32 edx;    };    class Processor    {    public:        Processor(void);        bool HasSSE(void) const        {            return has_sse;        }        bool HasSSE2(void) const        {            return has_sse2;        }        bool HasSSE3(void) const        {            return has_sse3;        }        bool HasMMX(void) const        {            return has_mmx;        }        bool HasMMXExt(void) const        {            return has_mmx_ext;        }        bool Has3DNow(void) const        {            return has_3dnow;        }        bool Has3DNowExt(void) const        {            return has_3dnow_ext;        }        bool IsHTT(void) const        {            return is_htt;        }        std::string GetName(void) const        {            return cpu_name;        }        std::string GetVendorID(void) const        {            return cpu_vendor;        }        std::string GetVendorName(void) const        {            if (cpu_vendor == "AuthenticAMD")            {                return std::string("AMD");            }            else if (cpu_vendor == "GenuineIntel")            {                return std::string("Intel");            }            else if (cpu_vendor == "CyrixInstead")            {                return std::string("Cyrix");            }            else if (cpu_vendor == "CentaurHauls")            {                return std::string("Centaur");            }            else if (cpu_vendor == "RiseRiseRise")            {                return std::string("Rise");            }            else if (cpu_vendor == "GenuineTMx86")            {                return std::string("Transmeta");            }            else if (cpu_vendor == "UMC UMC UMC ")            {                return std::string("UMC");            }            else            {                return cpu_vendor;            }        }        unsigned long GetSpeed(void) const        {            return cpu_speed;        }        int GetStepping(void) const        {            return stepping;        }        int GetModel(void) const        {            return model;        }        int GetFamily(void) const        {            return family;        }        int GetType(void) const        {            return type;        }        int GetExtModel(void) const        {            return ext_model;        }        int GetExtFamily(void) const        {            return ext_family;        }        // Use this to query more data        static Registers CPUID(const Registers &regs);    private:        bool has_mmx;        bool has_mmx_ext;        bool has_3dnow;        bool has_3dnow_ext;        bool has_sse;        bool has_sse2;        bool has_sse3;        bool is_htt;        int stepping;        int model;        int family;        int type;        int ext_model;        int ext_family;        std::string cpu_name;        std::string cpu_vendor;        unsigned long cpu_speed;        void MeasureSpeed(void);        void DoCPUID(void);        static std::string RegString(UInt32 reg_data);        static std::string RegString(const Registers ®_data);    };}#endifProcessor.cpp#include <string>#include <windows.h>#include <intrin.h>#include "Processor.h"using namespace std;/* How long to wait when measuring cpu speed in milliseconds. Higher delay will increase precision. Delay should not   be shorter than the resolution of the GetTickCount (5ms on WinXP). */const DWORD DELTA_TIME = 100;#ifdef _M_X64 // 64-bit modeextern "C"{    void __fastcall cpuid_x64(hw::Registers *regs);}#endifnamespace hw{    Processor::Processor(void) :        has_mmx(false),        has_mmx_ext(false),        has_3dnow(false),        has_3dnow_ext(false),        has_sse(false),        has_sse2(false),        has_sse3(false),        is_htt(false),        stepping(0),        model(0),        family(0),        type(0),        ext_model(0),        ext_family(0),        cpu_name("(unknown)"),        cpu_vendor("(unknown)"),        cpu_speed(0)    {        MeasureSpeed();        DoCPUID();    }    void Processor::MeasureSpeed(void)    {        LARGE_INTEGER    s, e, freq;        unsigned __int64 cs, ce;        // Determine timer frequency.        QueryPerformanceFrequency(&freq);        volatile DWORD ticks_begin = GetTickCount();        QueryPerformanceCounter(&s);        cs = __rdtsc();        // Wait for a while...        while (GetTickCount() - ticks_begin < DELTA_TIME) ;        ce = __rdtsc();        QueryPerformanceCounter(&e);         // Calculate frequency.        cpu_speed = (unsigned long) ((ce - cs) * freq.QuadPart / (e.QuadPart - s.QuadPart));    }    // this is the "core" method - it will call CPUID instruction and parse output    void Processor::DoCPUID(void)    {        // Query basic info        Registers func0 = CPUID(Registers(0x00000000));        cpu_vendor = RegString(func0.ebx) + RegString(func0.edx) + RegString(func0.ecx);        if (func0.eax == 0)        {            // eax == 0 -> no extended info            return;        }        // Query extended info        Registers func1 = CPUID(Registers(0x00000001));        stepping = func1.eax & 0xF;        model = (func1.eax >> 4) & 0xF;        family = (func1.eax >> 8) & 0xF;        type = (func1.eax >> 12) & 0x3;        ext_model = (func1.eax >> 16) & 0xF;        ext_family = (func1.eax >> 20) & 0xFF;        has_mmx = (func1.edx >> 23) & 0x1;        has_sse = (func1.edx >> 25) & 0x1;        has_sse2 = (func1.edx >> 26) & 0x1;        is_htt = (func1.edx >> 28) & 0x1;        has_sse3 = func1.ecx & 0x1;                // First extended function - query info about extended functions        Registers ext_func0 = CPUID(Registers(0x80000000));                if (ext_func0.eax >= 0x80000001)        {            // First extended function - additional cpu info            Registers ext_func1 = CPUID(Registers(0x80000001));            has_mmx_ext = (ext_func1.edx >> 22) & 0x1;            has_3dnow = (ext_func1.edx >> 31) & 0x1;            has_3dnow_ext = (ext_func1.edx >> 30) & 0x1;        }        if (ext_func0.eax >= 0x80000004)        {            // Now query processor name            Registers part1 = CPUID(Registers(0x80000002));            Registers part2 = CPUID(Registers(0x80000003));            Registers part3 = CPUID(Registers(0x80000004));            cpu_name = RegString(part1) + RegString(part2) + RegString(part3);        }    }    Registers Processor::CPUID(const Registers &regs)    {        Registers local = regs;#ifdef _M_X64 // 64-bit version        cpuid_x64(&local);#else  // 32-bit version        __asm        {            mov     eax, local.eax            mov     ebx, local.ebx            mov     ecx, local.ecx            mov     edx, local.edx            cpuid            mov     local.eax, eax            mov     local.ebx, ebx            mov     local.ecx, ecx            mov     local.edx, edx        }#endif        return local;    }    // Convert "packed" string to "real" string    std::string Processor::RegString(UInt32 reg_data)    {        union        {            char    str[5];            UInt32  reg_data;        } data;        data.str[4] = 0;        data.reg_data = reg_data;        return string(data.str);    }    // Convert registry data to string    std::string Processor::RegString(const Registers ®_data)    {        return RegString(reg_data.eax) + RegString(reg_data.ebx) + RegString(reg_data.ecx) + RegString(reg_data.edx);    }}


Main.cpp
#include <iostream>#include "Processor.h"using namespace std;int main(int, char **){    hw::Processor processor;    cout << "CPU Info:" << endl;    cout << "Processor name:            " << processor.GetName() << endl;    cout << "Processor vendor:          " << processor.GetVendorName() << endl;    cout << "Procesor speed:            " << (processor.GetSpeed() / 1000000) << " MHz" << endl;    cout << "Processor family:          " << processor.GetFamily() << endl;    cout << "Processor model:           " << processor.GetModel() << endl;    cout << "Processor stepping:        " << processor.GetStepping() << endl;    cout << "Processor type:            " << processor.GetType() << endl;    cout << "Processor extended family: " << processor.GetExtFamily() << endl;    cout << "Processor extended model:  " << processor.GetExtModel() << endl;    cout << "Has MMX:                   " << (processor.HasMMX() ? "yes" : "no") << endl;    cout << "Has Ext MMX:               " << (processor.HasMMXExt() ? "yes" : "no") << endl;    cout << "Has SSE:                   " << (processor.HasSSE() ? "yes" : "no") << endl;    cout << "Has SSE2:                  " << (processor.HasSSE2() ? "yes" : "no") << endl;    cout << "Has SSE3:                  " << (processor.HasSSE3() ? "yes" : "no") << endl;    cout << "Has 3DNow!:                " << (processor.Has3DNow() ? "yes" : "no") << endl;    cout << "Has 3DNow! Ext:            " << (processor.Has3DNowExt() ? "yes" : "no") << endl;    cout << "Supports hyper-threading:  " << (processor.IsHTT() ? "yes" : "no") << endl;    cout << endl << "* done *" << endl;    getchar();    return 0;}


As you can see, I refactored away almost all assembly (except the cpuid invocation) and now its easier to see how various features are detected. Also you can use this class to detect features (that are not directly supported) via the static CPUID method.

I tested this code on my WinXP SP2 32-bit and on 64-bit Vista. CPUID works fine in both cases, but there is a problem with the processor speed detection on Vista - sometimes it returns odd values and I have no idea why (maybe its __rdtsc but I didn't have time to try to write my own). So handle with care [smile]. Also note that I tested this only on one computer with AMD CPU, so there is no guarantee it will work correctly on Intels...

HTH
Evil Steve
Evil Steve
Quote:
Original post by Anonymous Poster
@Steve: Your code only reports my Intel Core2 as having one CPU. Is this a Windows issue or is there a way to get the correct number of cores for my processor?

Also, it looks like with Intel chips you cannot get the L1/L2 cache sizes or am I just reading that incorrectly?
Hmm, what does the registry key "HARDWARE\DESCRIPTION\System\CentralProcessor" have in it? Is there one entry or two? Sounds like a Windows thing, but I don't really know. Does Task Manager show it up as two CPUs, or just the one (I.e. are there two CPU Usage History graphs or justy one)?

According to the Intel processor manuals, you can't get the level 1 cache size on intel processors (Not in a documented way anyway), so my code uses an AMD extension.


Quote:
Original post by Anonymous Poster
Also, is there a way to detect 64bit support? I know SYSTEM_INFO is suppose to tell you but it's returning PROCESSOR_ARCHITECTURE_INTEL (the wProcessorArchitecture variable). Also dwNumberOfProcessors is returning 1. Now in your code you don't modify this number at all when you print the number of processors but it looks like when you do your loop for the speed that you are indeed doing a +1 to the output from dwNumberOfProcessors.

One last request Steve, how do you get the Model, Type, Stepping, Family, Extended Model and Extended Family info? I see how b2b3 does it but that code won't work in 64bit.
That information should be the same as b2b3's code. The __cpuid() intrinsic puts eax into nBuff[0], ebx into nBuff[1], ecx into nBuff[2] and edx into nBuff[3], so it should be straightforwards to modify my code if that information is needed.
The processor architecture returned from GetSystemInfo returns the architecture the application is running in. So if you're running a 32-bit app, it'll never return a 64-bit architecture. I think you can use GetNativeSystemInfo, but I haven't tried it myself. That'll also only give you a 64-bit architecture if you're running a 64-bit OS, but your app is 32-bit. I don't know of any way to tell if it's a 64-bit processor running a 32-bit OS.

I used to measure the CPU speed manually, but since it's done at windows boot anyway, it's easier (and faster) to read it from the registry instead.
The "strange values" reported may be from QPC() switching CPU cores - you should only be calling QPC() or RDTSC from one core; using SetProcessAffinityMask. It's also a good idea to set your process and thread priorities to realtime (the highest) and do a Sleep(0) before starting your CPU timing to minimise the chance of another process or thread jumping in and causing a context switch and screwing up your results. Just remember to restore your priority levels after, or you'll screw things up fairly majorly [smile]


Quote:
Original post by b2b3
So, to make it work, I had to create asm file with CPUID wrapper function and add some preprocessor defines to enable/disable this wrapper based on the current compilation mode. I also had to add custom build step for said ASM file.
My build step uses following command-line:
ml64.exe "$(InputPath)" /c /Fo "cpuid_x64.obj" /Zi
Also, it has to be set up so that this build step is disabled in the Win32 mode and enabled only in x64 mode.
Why not use the __cupid() intrinsic? Your custom build step is compiler specific, so why not use __cpuid() instead?
b2b3
b2b3
Quote:
Original post by Evil Steve
I used to measure the CPU speed manually, but since it's done at windows boot anyway, it's easier (and faster) to read it from the registry instead.
The "strange values" reported may be from QPC() switching CPU cores - you should only be calling QPC() or RDTSC from one core; using SetProcessAffinityMask. It's also a good idea to set your process and thread priorities to realtime (the highest) and do a Sleep(0) before starting your CPU timing to minimise the chance of another process or thread jumping in and causing a context switch and screwing up your results. Just remember to restore your priority levels after, or you'll screw things up fairly majorly [smile]

Yeah, I know I can read CPU speed from registry. In fact, I would use performance counters or WMI to query such data. I just wanted to try my old code on x64 since I never wrote anything for 64-bit system.
And I'm quite sure that speed detection errors are not caused by the thread junping to differnt core, since I have only single core CPU. Also weird thing is that on the first run after recompile it detects correct speed. Incorrect speed is returned when I try to run program again without recompiling it. And on XP it works correctly always.

Quote:
Original post by Evil Steve
Why not use the __cupid() intrinsic? Your custom build step is compiler specific, so why not use __cpuid() instead?

I didn't use __cpuid since it only supports arguments in the EAX register. Newer processors cupport CPUID with parameters that can be stored in different registers. This can be used for example when detecting how many cores does processor have (set EAX to 4, ECX to 0 and EAX[31:26]+1 is the number of cores). __cpuid does not make any guarantees about values in registers other than EAX, so they may have random values which may screw up results. Of course if "classic" cpuid is sufficient, using intrinsics is much better (and less error prone) than writing custom one.

More details about recent CPUID features on Intels can be found in Intel Processor Identification and the CPUID Instruction.
For AMD processors more info can be found in CPUID Specification.
daviangel
daviangel
Quote:
Original post by b2b3
Well, I tried porting my code to the x64.
It was much more complicated than I first thought since inline assembly cannot be used in 64-bit mode in Visual Studio (that sucks [sad]).

So, to make it work, I had to create asm file with CPUID wrapper function and add some preprocessor defines to enable/disable this wrapper based on the current compilation mode. I also had to add custom build step for said ASM file.
My build step uses following command-line:
ml64.exe "$(InputPath)" /c /Fo "cpuid_x64.obj" /Zi
Also, it has to be set up so that this build step is disabled in the Win32 mode and enabled only in x64 mode.

cpuid_x64.asm
*** Source Snippet Removed ***

Processor.h
*** Source Snippet Removed ***

Main.cpp
*** Source Snippet Removed ***

As you can see, I refactored away almost all assembly (except the cpuid invocation) and now its easier to see how various features are detected. Also you can use this class to detect features (that are not directly supported) via the static CPUID method.

I tested this code on my WinXP SP2 32-bit and on 64-bit Vista. CPUID works fine in both cases, but there is a problem with the processor speed detection on Vista - sometimes it returns odd values and I have no idea why (maybe its __rdtsc but I didn't have time to try to write my own). So handle with care [smile]. Also note that I tested this only on one computer with AMD CPU, so there is no guarantee it will work correctly on Intels...

HTH

Do you have a speedstep processor?
That's probably why you are having issues.

Using the Processor Clocks

This timer is very accurate. On a system with a 3GHz processor,
this timer can measure events that last less than one nanosecond. The accuracy of this timer on a 3GHz system is +/- 0.333 nanoseconds. This timer, however, cannot be directly accessed using a high level language. It can only be called using the assembly instruction Read Time Stamp Counter (RDTSC). Depending on how the time values are stored, this timer can handle events that can last a very long time. For example, if the time value is stored as a 32-bit value, this timer can measure an event that only runs up to 1.432 seconds. However, if the time is returned as a 64-bit value, it can time the event that spans up to 194 years. There is a drawback with using the processor clock. For example, laptops using Intel® Pentium® II Processors and later have Intel Speedstep Technology built in it. While Speedstep technology is good for conserving power when laptops are running on batteries, it changes the processor frequency. If the frequency changes while the targeted code is running, the final reading will be redundant since the initial and final readings were not taken using the same clock frequency. The number of clock ticks that occurred during this time will be accurate, but the elapsed time will be an unknown.

[size="2"]Don't talk about writing games, don't write design docs, don't spend your time on web boards. Sit in your house write 20 games when you complete them you will either want to do it the rest of your life or not * Andre Lamothe
b2b3
b2b3
Quote:
Original post by daviangel
Do you have a speedstep processor?
That's probably why you are having issues.

Nope. I have desktop Athlon 64. My cpu frequency always stays on 1800 MHZ (+/- fractions of Mhz). And the problem shows only in Vista. Of course I will investigate it since it is bugging me greatly [smile].

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.