Idenitfying CPU Brand & Model (C++)

Started by
13 comments, last by b2b3 17 years, 1 month ago
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.
Advertisement
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.
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.
Check out the Intel developers website. It has all the code you need and for 32bit you will use some assembly but not for 64bit.
[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
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.
If you need more information about different cpu types check out this site:
http://www.sandpile.org/
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

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!
@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?
Well it gets L2 cache size on Intel but not L1.

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.

Thanks greatly!

This topic is closed to new replies.

Advertisement