I\'m working on a cross platform profiling suite, and would like to add information about the machine\'s CPU (architecture/clock speed/cores) and RAM(total) to the report of
The most accepted answer by bsruth on this page using __cpuid loops unnecessarily through not needed extended functions. If all you need to know is the Processor Brand String then there is no need to query 0x80000000.
Wikipedia has a nice explanation with example code: https://en.wikipedia.org/wiki/CPUID#EAX=80000002h,80000003h,80000004h:_Processor_Brand_String
#include <cpuid.h> // GCC-provided
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint32_t brand[12];
if (!__get_cpuid_max(0x80000004, NULL)) {
fprintf(stderr, "Feature not implemented.");
return 2;
}
__get_cpuid(0x80000002, brand+0x0, brand+0x1, brand+0x2, brand+0x3);
__get_cpuid(0x80000003, brand+0x4, brand+0x5, brand+0x6, brand+0x7);
__get_cpuid(0x80000004, brand+0x8, brand+0x9, brand+0xa, brand+0xb);
printf("Brand: %s\n", brand);
}
and here is the version i came up with to directly convert it to a std::string in c++
std::string CPUBrandString;
CPUBrandString.resize(49);
uint *CPUInfo = reinterpret_cast<uint*>(CPUBrandString.data());
for (uint i=0; i<3; i++)
__cpuid(0x80000002+i, CPUInfo[i*4+0], CPUInfo[i*4+1], CPUInfo[i*4+2], CPUInfo[i*4+3]);
CPUBrandString.assign(CPUBrandString.data()); // correct null terminator
std::cout << CPUBrandString << std::endl;
this version is for linux, but it shouldn't be too hard to figure out for windows using __cpuid
For Windows and Win32 C++ Projects:
http://www.codeguru.com/cpp/w-p/system/hardwareinformation/article.php/c9087/Three-Ways-to-Retrieve-Processor-Information.htm
The above URL and contained article demonstrates 3 different ways to retrieve CPU info on Windows. The source code is at the bottom of the article, is well written, and has three useful classes that you can call from your Win32 C++ code.
The OP wants a CPU clock speed calculating routine portable between Windows and Linux. Here you go:
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef unsigned __int64 usCount;
static usCount GetUsCount()
{
static LARGE_INTEGER ticksPerSec;
static double scalefactor;
LARGE_INTEGER val;
if(!scalefactor)
{
if(QueryPerformanceFrequency(&ticksPerSec))
scalefactor=ticksPerSec.QuadPart/1000000000000.0;
else
scalefactor=1;
}
if(!QueryPerformanceCounter(&val))
return (usCount) GetTickCount() * 1000000000;
return (usCount) (val.QuadPart/scalefactor);
}
#else
#include <sys/time.h>
#include <time.h>
#include <sched.h>
typedef unsigned long long usCount;
static usCount GetUsCount()
{
#ifdef CLOCK_MONOTONIC
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ((usCount) ts.tv_sec*1000000000000LL)+ts.tv_nsec*1000LL;
#else
struct timeval tv;
gettimeofday(&tv, 0);
return ((usCount) tv.tv_sec*1000000000000LL)+tv.tv_usec*1000000LL;
#endif
}
#endif
static usCount usCountOverhead, CPUClockSpeed;
#ifdef __GNUC__
#include "x86intrin.h"
#define __rdtsc() __builtin_ia32_rdtsc()
#endif
static usCount GetClockSpeed()
{
int n;
usCount start, end, start_tsc, end_tsc;
if(!usCountOverhead)
{
usCount foo=0;
start=GetUsCount();
for(n=0; n<1000000; n++)
{
foo+=GetUsCount();
}
end=GetUsCount();
usCountOverhead=(end-start)/n;
}
start=GetUsCount();
start_tsc=__rdtsc();
for(n=0; n<1000; n++)
#ifdef WIN32
Sleep(0);
#else
sched_yield();
#endif
end_tsc=__rdtsc();
end=GetUsCount();
return (usCount)((1000000000000.0*(end_tsc-start_tsc))/(end-start-usCountOverhead));
}
Obviously this only works on x86/x64, and it relies on TSC counting at the same speed as the CPU. If you've done weird overclocking things e.g. on mine I overclock the FSB but downrate the multiplier to keep the core clock at spec, so TSC will count at FSB times the maximum multiplier which is too fast.
To get the best results, before running GetClockSpeed() I'd suggest you run an anti-SpeedStep loop e.g.
usCount start;
start=GetUsCount();
while(GetUsCount()-start<3000000000000ULL);
CPUClockSpeed=GetClockSpeed();
Niall
On Windows to determine CPU clock speed:
double CPUSpeed()
{
wchar_t Buffer[_MAX_PATH];
DWORD BufSize = _MAX_PATH;
DWORD dwMHz = _MAX_PATH;
HKEY hKey;
// open the key where the proc speed is hidden:
long lError = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
0,
KEY_READ,
&hKey);
if(lError != ERROR_SUCCESS)
{// if the key is not found, tell the user why:
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
lError,
0,
Buffer,
_MAX_PATH,
0);
wprintf(Buffer);
return 0;
}
// query the key:
RegQueryValueEx(hKey, L"~MHz", NULL, NULL, (LPBYTE) &dwMHz, &BufSize);
return (double)dwMHz;
}
For Linux with GCC you can use a very similar solution like windows. You need to include the <cpuid.h>
and you need to modify the input for the __cpuid()
method based on this.
#include <cpuid.h>
char CPUBrandString[0x40];
unsigned int CPUInfo[4] = {0,0,0,0};
__cpuid(0x80000000, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
unsigned int nExIds = CPUInfo[0];
memset(CPUBrandString, 0, sizeof(CPUBrandString));
for (unsigned int i = 0x80000000; i <= nExIds; ++i)
{
__cpuid(i, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
if (i == 0x80000002)
memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
else if (i == 0x80000003)
memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
else if (i == 0x80000004)
memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
}
cout << "CPU Type: " << CPUBrandString << endl;
I'm very late but here is my contribution. I tried to have a more modern C++ approach.
#include <intrin.h> // NOTE this header is MSVC specific!
#include <string>
#include <array>
std::string GetCpuInfo()
{
// 4 is essentially hardcoded due to the __cpuid function requirements.
// NOTE: Results are limited to whatever the sizeof(int) * 4 is...
std::array<int, 4> integerBuffer = {};
constexpr size_t sizeofIntegerBuffer = sizeof(int) * integerBuffer.size();
std::array<char, 64> charBuffer = {};
// The information you wanna query __cpuid for.
// https://docs.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex?view=vs-2019
constexpr std::array<int, 3> functionIds = {
// Manufacturer
// EX: "Intel(R) Core(TM"
0x8000'0002,
// Model
// EX: ") i7-8700K CPU @"
0x8000'0003,
// Clockspeed
// EX: " 3.70GHz"
0x8000'0004
};
std::string cpu;
for (int id : functionIds)
{
// Get the data for the current ID.
__cpuid(integerBuffer.data(), id);
// Copy the raw data from the integer buffer into the character buffer
std::memcpy(charBuffer.data(), integerBuffer.data(), sizeofIntegerBuffer);
// Copy that data into a std::string
cpu += std::string(charBuffer.data());
}
return cpu;
}
Here was the result of this function for myself: "Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz"
Honestly it's really annoying something like this hasn't been standardized...