Sign in to follow this  

Idenitfying CPU Brand & Model (C++)

This topic is 3931 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
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
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[i] == ' ')
szCPUName[i] = '\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
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 regs

PUBLIC cpuid_x64
.CODE

ALIGN 8
cpuid_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 8
cpuid_x64 ENDP
_TEXT ENDS

END



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 &reg_data);
};
}

#endif


Processor.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 mode
extern "C"
{
void __fastcall cpuid_x64(hw::Registers *regs);
}
#endif

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);

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 &reg_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

Share this post


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

Share this post


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

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites

This topic is 3931 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this