Idenitfying CPU Brand & Model (C++)
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.
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.
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.
I posted this in another topic a while ago:
Processor.h
Processor.cpp
You can use hw::Processor class to query various features of the processor including its speed and supported instruction sets.
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.
And for completeness, here's my CPUID code - which works in x64 too, but is MSVC specific because it uses the __cpuid() intrinsic:
EDIT: Sample output:
#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?
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!
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
Popular Topics
Advertisement