Clean up MUtils::CPUFetaures code.

This commit is contained in:
LoRd_MuldeR 2016-12-23 22:14:25 +01:00
parent 1cff20a3ef
commit 37e7f72069
4 changed files with 108 additions and 79 deletions

View File

@ -19,6 +19,13 @@
// http://www.gnu.org/licenses/lgpl-2.1.txt // http://www.gnu.org/licenses/lgpl-2.1.txt
////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////
/**
* @file
* @brief This file contains function for detecting information about the CPU
*
* Call the MUtils::CPUFetaures::detect() to detect information about the processor, which will be returned in a `MUtils::CPUFetaures::cpu_info_t` struct.
*/
#pragma once #pragma once
//MUtils //MUtils
@ -29,33 +36,52 @@
namespace MUtils namespace MUtils
{ {
/**
* \brief This namespace contains functions and constants for detecting CPU information
*
* Call the detect() to detect information about the processor, which will be returned in a `cpu_info_t` struct.
*/
namespace CPUFetaures namespace CPUFetaures
{ {
//CPU flags // CPU vendor flag
static const quint32 FLAG_MMX = 0x01; static const uint8_t VENDOR_INTEL = 0x01U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *Intel*
static const quint32 FLAG_SSE = 0x02; static const uint8_t VENDOR_AMD = 0x02U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *AMD*
static const quint32 FLAG_SSE2 = 0x04;
static const quint32 FLAG_SSE3 = 0x08;
static const quint32 FLAG_SSSE3 = 0x10;
static const quint32 FLAG_SSE4 = 0x20;
static const quint32 FLAG_SSE42 = 0x40;
static const quint32 FLAG_AVX = 0x80;
//CPU features // CPU feature flag
typedef struct _cpu_info_t static const quint32 FLAG_CMOV = 0x001U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *CMOV* instruction
static const quint32 FLAG_MMX = 0x002U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *MMX* instruction set extension
static const quint32 FLAG_SSE = 0x004U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE* instruction set extension
static const quint32 FLAG_SSE2 = 0x008U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE2* instruction set extension
static const quint32 FLAG_SSE3 = 0x010U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE3* instruction set extension
static const quint32 FLAG_SSSE3 = 0x020U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSSE3* instruction set extension
static const quint32 FLAG_SSE4 = 0x030U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.1* instruction set extension
static const quint32 FLAG_SSE42 = 0x080U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.2* instruction set extension
static const quint32 FLAG_AVX = 0x100U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *AVX* instruction set extension
/**
* \brief Struct to hold information about the CPU
*/
typedef struct
{ {
quint32 family; quint32 family; ///< CPU *family* indicator, which specifies the processor "generation" to which the CPU belongs
quint32 model; quint32 model; ///< CPU *model* indicator, which is used to distinguish processor "variants" within a generation
quint32 stepping; quint32 stepping; ///< CPU *stepping* indicator, which is used to distinguish "revisions" of a certain processor model
quint32 count; quint32 count; ///< The number of available (logical) processors
quint32 features; quint32 features; ///< CPU *feature* flags, indicating suppoprt for extended instruction sets; all flags are OR-combined
bool x64; bool x64; ///< Indicates that the processor and the operating system support 64-Bit (AMD64/EM64T)
bool intel; uint8_t vendor; ///< CPU *vendor* flag; might be zero, if vendor is unknown
char vendor[0x40]; char idstr[13]; ///< CPU *identifier* string, exactly 12 characters (e.g. "GenuineIntel" or "AuthenticAMD")
char brand[0x40]; char brand[48]; ///< CPU *brand* string, up to 48 characters (e.g. "Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz")
} }
cpu_info_t; cpu_info_t;
/**
* \brief Detect processor information
*
* Detects information about the CPU on which the application is running, including CPU vendor, identifier string, feature flags (MMX, SSE, AVX, etc) as well as the CPU core count.
*
* \return The function returns a `cpu_info_t` struct containing the detected information about the CPU.
*/
MUTILS_API cpu_info_t detect(void); MUTILS_API cpu_info_t detect(void);
} }
} }

View File

@ -80,6 +80,9 @@ class QProcess;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/**
* \brief Global MUtils namespace
*/
namespace MUtils namespace MUtils
{ {
/** /**

View File

@ -10,6 +10,7 @@
* *
* The public API of the *MUtilities* library is defined in the following header files (select file for details): * The public API of the *MUtilities* library is defined in the following header files (select file for details):
* - **Global.h** &ndash; miscellaneous useful functions * - **Global.h** &ndash; miscellaneous useful functions
* - **CPUFeatures.h** &ndash; functions for detection information about the CPU
* *
* *
* # Example * # Example

View File

@ -28,76 +28,73 @@
#include <MUtils/OSSupport.h> #include <MUtils/OSSupport.h>
#include "Utils_Win32.h" #include "Utils_Win32.h"
#define MY_CPUID(X,Y) __cpuid(((int*)(X)), ((int)(Y)))
#define CHECK_VENDOR(X,Y,Z) (_stricmp((X), (Y)) ? 0U : (Z));
#define CHECK_FLAG(X,Y,Z) (((X) & (Y)) ? (Z) : 0U)
MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void) MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
{ {
const OS::ArgumentMap &args = OS::arguments(); const OS::ArgumentMap &args = OS::arguments();
typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process); typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
static const quint32 FLAGS_X64 = (FLAG_MMX | FLAG_SSE | FLAG_SSE2);
cpu_info_t features; cpu_info_t features;
SYSTEM_INFO systemInfo; SYSTEM_INFO systemInfo;
int CPUInfo[4] = {-1}; uint32_t cpuInfo[4];
char CPUIdentificationString[0x40];
char CPUBrandString[0x40];
//Initialize variables to zero
memset(&features, 0, sizeof(cpu_info_t)); memset(&features, 0, sizeof(cpu_info_t));
memset(&systemInfo, 0, sizeof(SYSTEM_INFO)); memset(&systemInfo, 0, sizeof(SYSTEM_INFO));
memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString)); memset(cpuInfo, 0, sizeof(cpuInfo));
memset(CPUBrandString, 0, sizeof(CPUBrandString));
__cpuid(CPUInfo, 0); //Detect the CPU identifier string
memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int)); MY_CPUID(&cpuInfo[0], 0);
memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int)); memcpy(&features.idstr[0U * sizeof(uint32_t)], &cpuInfo[1], sizeof(uint32_t));
memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int)); memcpy(&features.idstr[1U * sizeof(uint32_t)], &cpuInfo[3], sizeof(uint32_t));
features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0); memcpy(&features.idstr[2U * sizeof(uint32_t)], &cpuInfo[2], sizeof(uint32_t));
strncpy_s(features.vendor, 0x40, CPUIdentificationString, _TRUNCATE); features.idstr[3U * sizeof(uint32_t)] = '\0';
features.vendor |= CHECK_VENDOR(features.idstr, "GenuineIntel", VENDOR_INTEL);
features.vendor |= CHECK_VENDOR(features.idstr, "AuthenticAMD", VENDOR_AMD);
if(CPUInfo[0] >= 1) //Detect the CPU model and feature flags
if(cpuInfo[0] >= 1)
{ {
__cpuid(CPUInfo, 1); MY_CPUID(&cpuInfo[0], 1);
if(CPUInfo[3] & 0x00800000) features.features |= FLAG_MMX; features.features |= CHECK_FLAG(cpuInfo[3], 0x00008000, FLAG_CMOV);
if(CPUInfo[3] & 0x02000000) features.features |= FLAG_SSE; features.features |= CHECK_FLAG(cpuInfo[3], 0x00800000, FLAG_MMX);
if(CPUInfo[3] & 0x04000000) features.features |= FLAG_SSE2; features.features |= CHECK_FLAG(cpuInfo[3], 0x02000000, FLAG_SSE);
if(CPUInfo[2] & 0x00000001) features.features |= FLAG_SSE3; features.features |= CHECK_FLAG(cpuInfo[3], 0x04000000, FLAG_SSE2);
if(CPUInfo[2] & 0x00000200) features.features |= FLAG_SSSE3; features.features |= CHECK_FLAG(cpuInfo[2], 0x00000001, FLAG_SSE3);
if(CPUInfo[2] & 0x00080000) features.features |= FLAG_SSE4; features.features |= CHECK_FLAG(cpuInfo[2], 0x00000200, FLAG_SSSE3);
if(CPUInfo[2] & 0x00100000) features.features |= FLAG_SSE42; features.features |= CHECK_FLAG(cpuInfo[2], 0x00080000, FLAG_SSE4);
if ((CPUInfo[2] & 0x18000000) == 0x18000000) features.features |= CHECK_FLAG(cpuInfo[2], 0x00100000, FLAG_SSE42);
//Check for AVX
if ((cpuInfo[2] & 0x18000000) == 0x18000000)
{ {
if((_xgetbv(0) & 0x6ui64) == 0x6ui64) /*AVX requires OS support!*/ if((_xgetbv(0) & 0x6ui64) == 0x6ui64) /*AVX requires OS support!*/
{ {
features.features |= FLAG_AVX; features.features |= FLAG_AVX;
} }
} }
features.stepping = CPUInfo[0] & 0xf;
features.model = ((CPUInfo[0] >> 4) & 0xf) + (((CPUInfo[0] >> 16) & 0xf) << 4); //Compute the CPU stepping, model and family
features.family = ((CPUInfo[0] >> 8) & 0xf) + ((CPUInfo[0] >> 20) & 0xff); features.stepping = cpuInfo[0] & 0xf;
features.model = ((cpuInfo[0] >> 4) & 0xf) + (((cpuInfo[0] >> 16) & 0xf) << 4);
features.family = ((cpuInfo[0] >> 8) & 0xf) + ((cpuInfo[0] >> 20) & 0xff);
} }
__cpuid(CPUInfo, 0x80000000); //Read the CPU "brand" string
int nExIds = qMax<int>(qMin<int>(CPUInfo[0], 0x80000004), 0x80000000); MY_CPUID(&cpuInfo[0], 0x80000000);
const uint32_t nExIds = qBound(0x80000000, cpuInfo[0], 0x80000004);
for(int i = 0x80000002; i <= nExIds; ++i) for(uint32_t i = 0x80000002; i <= nExIds; ++i)
{ {
__cpuid(CPUInfo, i); MY_CPUID(&cpuInfo[0], i);
switch(i) memcpy(&features.brand[(i - 0x80000002) * sizeof(cpuInfo)], &cpuInfo[0], sizeof(cpuInfo));
{
case 0x80000002:
memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
break;
case 0x80000003:
memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
break;
case 0x80000004:
memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
break;
}
} }
features.brand[sizeof(features.brand) - 1] = '\0';
strncpy_s(features.brand, 0x40, CPUBrandString, _TRUNCATE); //Detect 64-Bit processors
if(strlen(features.brand) < 1) strncpy_s(features.brand, 0x40, "Unknown", _TRUNCATE);
if(strlen(features.vendor) < 1) strncpy_s(features.vendor, 0x40, "Unknown", _TRUNCATE);
#if (!(defined(_M_X64) || defined(_M_IA64))) #if (!(defined(_M_X64) || defined(_M_IA64)))
const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process")); const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process"));
if(isWow64ProcessPtr) if(isWow64ProcessPtr)
@ -105,24 +102,26 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
BOOL x64flag = FALSE; BOOL x64flag = FALSE;
if(isWow64ProcessPtr(GetCurrentProcess(), &x64flag)) if(isWow64ProcessPtr(GetCurrentProcess(), &x64flag))
{ {
if(x64flag) features.x64 = true; if (x64flag)
{
features.x64 = true;
features.features |= FLAGS_X64; /*x86_64 implies SSE2*/
}
} }
} }
#else #else
features.x64 = true; features.x64 = true;
features.features |= FLAGS_X64;
#endif #endif
if (features.x64) //Make sure that (at least) the MMX flag has been set!
{
features.features |= (FLAG_MMX | FLAG_SSE | FLAG_SSE2); /*x86_64 implies SSE2*/
}
if (!(features.features & FLAG_MMX)) if (!(features.features & FLAG_MMX))
{ {
qWarning("Warning: CPU does not seem to support MMX. Take care!\n"); qWarning("Warning: CPU does not seem to support MMX. Take care!\n");
features.features = 0; features.features = 0;
} }
//Count the number of available(!) CPU cores
DWORD_PTR procAffinity, sysAffinity; DWORD_PTR procAffinity, sysAffinity;
if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity)) if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity))
{ {
@ -137,11 +136,11 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void)
features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL); features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL);
} }
//Apply manual CPU overwrites
bool userFlag = false; bool userFlag = false;
if(args.contains("force-cpu-no-64bit")) { userFlag = true; features.x64 = false; } if (args.contains(QLatin1String("cpu-no-simd"))) { userFlag = true; features.features = 0U; }
if(args.contains("force-cpu-no-sse" )) { userFlag = true; features.features &= (~(FLAG_SSE | FLAG_SSE2 | FLAG_SSE3 | FLAG_SSSE3 | FLAG_SSE4 | FLAG_SSE42)); } if (args.contains(QLatin1String("cpu-no-vendor"))) { userFlag = true; features.vendor = 0U; }
if(args.contains("force-cpu-no-intel")) { userFlag = true; features.intel = false; } if (args.contains(QLatin1String("cpu-no-x64"))) { userFlag = true; features.x64 = 0U; }
if(userFlag) if(userFlag)
{ {
qWarning("CPU flags overwritten by user-defined parameters. Take care!\n"); qWarning("CPU flags overwritten by user-defined parameters. Take care!\n");