Jump to content
  • Advertisement
Sign in to follow this  
Liam M

Idenitfying CPU Brand & Model (C++)

This topic is 4296 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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: 64K
PERF : L2 cache size: 512K
PERF : CPU Features: FPU MMX SSE SSE2 SSE3 HTT 3DNow! Ex3DNow! MmxExt
PERF : Number of CPUs: 2
PERF : * CPU 0 speed: ~2010MHz
PERF : * CPU 1 speed: ~2010MHz

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
Guest 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?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!