MUtilities development repository
修订版 | 37e7f72069e8cdc146257ba986d1a79e6d087e4e (tree) |
---|---|
时间 | 2016-12-24 06:14:25 |
作者 | ![]() |
Commiter | LoRd_MuldeR |
Clean up MUtils::CPUFetaures code.
@@ -19,6 +19,13 @@ | ||
19 | 19 | // http://www.gnu.org/licenses/lgpl-2.1.txt |
20 | 20 | ////////////////////////////////////////////////////////////////////////////////// |
21 | 21 | |
22 | +/** | |
23 | +* @file | |
24 | +* @brief This file contains function for detecting information about the CPU | |
25 | +* | |
26 | +* Call the MUtils::CPUFetaures::detect() to detect information about the processor, which will be returned in a `MUtils::CPUFetaures::cpu_info_t` struct. | |
27 | +*/ | |
28 | + | |
22 | 29 | #pragma once |
23 | 30 | |
24 | 31 | //MUtils |
@@ -29,33 +36,52 @@ | ||
29 | 36 | |
30 | 37 | namespace MUtils |
31 | 38 | { |
39 | + /** | |
40 | + * \brief This namespace contains functions and constants for detecting CPU information | |
41 | + * | |
42 | + * Call the detect() to detect information about the processor, which will be returned in a `cpu_info_t` struct. | |
43 | + */ | |
32 | 44 | namespace CPUFetaures |
33 | 45 | { |
34 | - //CPU flags | |
35 | - static const quint32 FLAG_MMX = 0x01; | |
36 | - static const quint32 FLAG_SSE = 0x02; | |
37 | - static const quint32 FLAG_SSE2 = 0x04; | |
38 | - static const quint32 FLAG_SSE3 = 0x08; | |
39 | - static const quint32 FLAG_SSSE3 = 0x10; | |
40 | - static const quint32 FLAG_SSE4 = 0x20; | |
41 | - static const quint32 FLAG_SSE42 = 0x40; | |
42 | - static const quint32 FLAG_AVX = 0x80; | |
43 | - | |
44 | - //CPU features | |
45 | - typedef struct _cpu_info_t | |
46 | + // CPU vendor flag | |
47 | + static const uint8_t VENDOR_INTEL = 0x01U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *Intel* | |
48 | + static const uint8_t VENDOR_AMD = 0x02U; ///< \brief CPU vendor flag \details Indicates that the CPU's vendor is *AMD* | |
49 | + | |
50 | + // CPU feature flag | |
51 | + static const quint32 FLAG_CMOV = 0x001U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *CMOV* instruction | |
52 | + static const quint32 FLAG_MMX = 0x002U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *MMX* instruction set extension | |
53 | + static const quint32 FLAG_SSE = 0x004U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE* instruction set extension | |
54 | + static const quint32 FLAG_SSE2 = 0x008U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE2* instruction set extension | |
55 | + static const quint32 FLAG_SSE3 = 0x010U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE3* instruction set extension | |
56 | + static const quint32 FLAG_SSSE3 = 0x020U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSSE3* instruction set extension | |
57 | + static const quint32 FLAG_SSE4 = 0x030U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.1* instruction set extension | |
58 | + static const quint32 FLAG_SSE42 = 0x080U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *SSE4.2* instruction set extension | |
59 | + static const quint32 FLAG_AVX = 0x100U; ///< \brief CPU feature flag \details Indicates that the CPU supports the *AVX* instruction set extension | |
60 | + | |
61 | + /** | |
62 | + * \brief Struct to hold information about the CPU | |
63 | + */ | |
64 | + typedef struct | |
46 | 65 | { |
47 | - quint32 family; | |
48 | - quint32 model; | |
49 | - quint32 stepping; | |
50 | - quint32 count; | |
51 | - quint32 features; | |
52 | - bool x64; | |
53 | - bool intel; | |
54 | - char vendor[0x40]; | |
55 | - char brand[0x40]; | |
66 | + quint32 family; ///< CPU *family* indicator, which specifies the processor "generation" to which the CPU belongs | |
67 | + quint32 model; ///< CPU *model* indicator, which is used to distinguish processor "variants" within a generation | |
68 | + quint32 stepping; ///< CPU *stepping* indicator, which is used to distinguish "revisions" of a certain processor model | |
69 | + quint32 count; ///< The number of available (logical) processors | |
70 | + quint32 features; ///< CPU *feature* flags, indicating suppoprt for extended instruction sets; all flags are OR-combined | |
71 | + bool x64; ///< Indicates that the processor and the operating system support 64-Bit (AMD64/EM64T) | |
72 | + uint8_t vendor; ///< CPU *vendor* flag; might be zero, if vendor is unknown | |
73 | + char idstr[13]; ///< CPU *identifier* string, exactly 12 characters (e.g. "GenuineIntel" or "AuthenticAMD") | |
74 | + char brand[48]; ///< CPU *brand* string, up to 48 characters (e.g. "Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz") | |
56 | 75 | } |
57 | 76 | cpu_info_t; |
58 | 77 | |
78 | + /** | |
79 | + * \brief Detect processor information | |
80 | + * | |
81 | + * 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. | |
82 | + * | |
83 | + * \return The function returns a `cpu_info_t` struct containing the detected information about the CPU. | |
84 | + */ | |
59 | 85 | MUTILS_API cpu_info_t detect(void); |
60 | 86 | } |
61 | 87 | } |
@@ -80,6 +80,9 @@ class QProcess; | ||
80 | 80 | |
81 | 81 | /////////////////////////////////////////////////////////////////////////////// |
82 | 82 | |
83 | +/** | |
84 | +* \brief Global MUtils namespace | |
85 | +*/ | |
83 | 86 | namespace MUtils |
84 | 87 | { |
85 | 88 | /** |
@@ -10,6 +10,7 @@ | ||
10 | 10 | * |
11 | 11 | * The public API of the *MUtilities* library is defined in the following header files (select file for details): |
12 | 12 | * - **Global.h** – miscellaneous useful functions |
13 | + * - **CPUFeatures.h** – functions for detection information about the CPU | |
13 | 14 | * |
14 | 15 | * |
15 | 16 | * # Example |
@@ -28,76 +28,73 @@ | ||
28 | 28 | #include <MUtils/OSSupport.h> |
29 | 29 | #include "Utils_Win32.h" |
30 | 30 | |
31 | +#define MY_CPUID(X,Y) __cpuid(((int*)(X)), ((int)(Y))) | |
32 | +#define CHECK_VENDOR(X,Y,Z) (_stricmp((X), (Y)) ? 0U : (Z)); | |
33 | +#define CHECK_FLAG(X,Y,Z) (((X) & (Y)) ? (Z) : 0U) | |
34 | + | |
31 | 35 | MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void) |
32 | 36 | { |
33 | 37 | const OS::ArgumentMap &args = OS::arguments(); |
34 | 38 | typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process); |
39 | + static const quint32 FLAGS_X64 = (FLAG_MMX | FLAG_SSE | FLAG_SSE2); | |
35 | 40 | |
36 | - cpu_info_t features; | |
41 | + cpu_info_t features; | |
37 | 42 | SYSTEM_INFO systemInfo; |
38 | - int CPUInfo[4] = {-1}; | |
39 | - char CPUIdentificationString[0x40]; | |
40 | - char CPUBrandString[0x40]; | |
43 | + uint32_t cpuInfo[4]; | |
41 | 44 | |
42 | - memset(&features, 0, sizeof(cpu_info_t)); | |
45 | + //Initialize variables to zero | |
46 | + memset(&features, 0, sizeof(cpu_info_t)); | |
43 | 47 | memset(&systemInfo, 0, sizeof(SYSTEM_INFO)); |
44 | - memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString)); | |
45 | - memset(CPUBrandString, 0, sizeof(CPUBrandString)); | |
46 | - | |
47 | - __cpuid(CPUInfo, 0); | |
48 | - memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int)); | |
49 | - memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int)); | |
50 | - memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int)); | |
51 | - features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0); | |
52 | - strncpy_s(features.vendor, 0x40, CPUIdentificationString, _TRUNCATE); | |
53 | - | |
54 | - if(CPUInfo[0] >= 1) | |
48 | + memset(cpuInfo, 0, sizeof(cpuInfo)); | |
49 | + | |
50 | + //Detect the CPU identifier string | |
51 | + MY_CPUID(&cpuInfo[0], 0); | |
52 | + memcpy(&features.idstr[0U * sizeof(uint32_t)], &cpuInfo[1], sizeof(uint32_t)); | |
53 | + memcpy(&features.idstr[1U * sizeof(uint32_t)], &cpuInfo[3], sizeof(uint32_t)); | |
54 | + memcpy(&features.idstr[2U * sizeof(uint32_t)], &cpuInfo[2], sizeof(uint32_t)); | |
55 | + features.idstr[3U * sizeof(uint32_t)] = '\0'; | |
56 | + features.vendor |= CHECK_VENDOR(features.idstr, "GenuineIntel", VENDOR_INTEL); | |
57 | + features.vendor |= CHECK_VENDOR(features.idstr, "AuthenticAMD", VENDOR_AMD); | |
58 | + | |
59 | + //Detect the CPU model and feature flags | |
60 | + if(cpuInfo[0] >= 1) | |
55 | 61 | { |
56 | - __cpuid(CPUInfo, 1); | |
57 | - if(CPUInfo[3] & 0x00800000) features.features |= FLAG_MMX; | |
58 | - if(CPUInfo[3] & 0x02000000) features.features |= FLAG_SSE; | |
59 | - if(CPUInfo[3] & 0x04000000) features.features |= FLAG_SSE2; | |
60 | - if(CPUInfo[2] & 0x00000001) features.features |= FLAG_SSE3; | |
61 | - if(CPUInfo[2] & 0x00000200) features.features |= FLAG_SSSE3; | |
62 | - if(CPUInfo[2] & 0x00080000) features.features |= FLAG_SSE4; | |
63 | - if(CPUInfo[2] & 0x00100000) features.features |= FLAG_SSE42; | |
64 | - if ((CPUInfo[2] & 0x18000000) == 0x18000000) | |
62 | + MY_CPUID(&cpuInfo[0], 1); | |
63 | + features.features |= CHECK_FLAG(cpuInfo[3], 0x00008000, FLAG_CMOV); | |
64 | + features.features |= CHECK_FLAG(cpuInfo[3], 0x00800000, FLAG_MMX); | |
65 | + features.features |= CHECK_FLAG(cpuInfo[3], 0x02000000, FLAG_SSE); | |
66 | + features.features |= CHECK_FLAG(cpuInfo[3], 0x04000000, FLAG_SSE2); | |
67 | + features.features |= CHECK_FLAG(cpuInfo[2], 0x00000001, FLAG_SSE3); | |
68 | + features.features |= CHECK_FLAG(cpuInfo[2], 0x00000200, FLAG_SSSE3); | |
69 | + features.features |= CHECK_FLAG(cpuInfo[2], 0x00080000, FLAG_SSE4); | |
70 | + features.features |= CHECK_FLAG(cpuInfo[2], 0x00100000, FLAG_SSE42); | |
71 | + | |
72 | + //Check for AVX | |
73 | + if ((cpuInfo[2] & 0x18000000) == 0x18000000) | |
65 | 74 | { |
66 | 75 | if((_xgetbv(0) & 0x6ui64) == 0x6ui64) /*AVX requires OS support!*/ |
67 | 76 | { |
68 | 77 | features.features |= FLAG_AVX; |
69 | 78 | } |
70 | 79 | } |
71 | - features.stepping = CPUInfo[0] & 0xf; | |
72 | - features.model = ((CPUInfo[0] >> 4) & 0xf) + (((CPUInfo[0] >> 16) & 0xf) << 4); | |
73 | - features.family = ((CPUInfo[0] >> 8) & 0xf) + ((CPUInfo[0] >> 20) & 0xff); | |
74 | - } | |
75 | 80 | |
76 | - __cpuid(CPUInfo, 0x80000000); | |
77 | - int nExIds = qMax<int>(qMin<int>(CPUInfo[0], 0x80000004), 0x80000000); | |
81 | + //Compute the CPU stepping, model and family | |
82 | + features.stepping = cpuInfo[0] & 0xf; | |
83 | + features.model = ((cpuInfo[0] >> 4) & 0xf) + (((cpuInfo[0] >> 16) & 0xf) << 4); | |
84 | + features.family = ((cpuInfo[0] >> 8) & 0xf) + ((cpuInfo[0] >> 20) & 0xff); | |
85 | + } | |
78 | 86 | |
79 | - for(int i = 0x80000002; i <= nExIds; ++i) | |
87 | + //Read the CPU "brand" string | |
88 | + MY_CPUID(&cpuInfo[0], 0x80000000); | |
89 | + const uint32_t nExIds = qBound(0x80000000, cpuInfo[0], 0x80000004); | |
90 | + for(uint32_t i = 0x80000002; i <= nExIds; ++i) | |
80 | 91 | { |
81 | - __cpuid(CPUInfo, i); | |
82 | - switch(i) | |
83 | - { | |
84 | - case 0x80000002: | |
85 | - memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); | |
86 | - break; | |
87 | - case 0x80000003: | |
88 | - memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); | |
89 | - break; | |
90 | - case 0x80000004: | |
91 | - memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); | |
92 | - break; | |
93 | - } | |
92 | + MY_CPUID(&cpuInfo[0], i); | |
93 | + memcpy(&features.brand[(i - 0x80000002) * sizeof(cpuInfo)], &cpuInfo[0], sizeof(cpuInfo)); | |
94 | 94 | } |
95 | + features.brand[sizeof(features.brand) - 1] = '\0'; | |
95 | 96 | |
96 | - strncpy_s(features.brand, 0x40, CPUBrandString, _TRUNCATE); | |
97 | - | |
98 | - if(strlen(features.brand) < 1) strncpy_s(features.brand, 0x40, "Unknown", _TRUNCATE); | |
99 | - if(strlen(features.vendor) < 1) strncpy_s(features.vendor, 0x40, "Unknown", _TRUNCATE); | |
100 | - | |
97 | + //Detect 64-Bit processors | |
101 | 98 | #if (!(defined(_M_X64) || defined(_M_IA64))) |
102 | 99 | const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process")); |
103 | 100 | if(isWow64ProcessPtr) |
@@ -105,24 +102,26 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void) | ||
105 | 102 | BOOL x64flag = FALSE; |
106 | 103 | if(isWow64ProcessPtr(GetCurrentProcess(), &x64flag)) |
107 | 104 | { |
108 | - if(x64flag) features.x64 = true; | |
105 | + if (x64flag) | |
106 | + { | |
107 | + features.x64 = true; | |
108 | + features.features |= FLAGS_X64; /*x86_64 implies SSE2*/ | |
109 | + } | |
109 | 110 | } |
110 | 111 | } |
111 | 112 | #else |
112 | 113 | features.x64 = true; |
114 | + features.features |= FLAGS_X64; | |
113 | 115 | #endif |
114 | 116 | |
115 | - if (features.x64) | |
116 | - { | |
117 | - features.features |= (FLAG_MMX | FLAG_SSE | FLAG_SSE2); /*x86_64 implies SSE2*/ | |
118 | - } | |
119 | - | |
117 | + //Make sure that (at least) the MMX flag has been set! | |
120 | 118 | if (!(features.features & FLAG_MMX)) |
121 | 119 | { |
122 | 120 | qWarning("Warning: CPU does not seem to support MMX. Take care!\n"); |
123 | 121 | features.features = 0; |
124 | 122 | } |
125 | 123 | |
124 | + //Count the number of available(!) CPU cores | |
126 | 125 | DWORD_PTR procAffinity, sysAffinity; |
127 | 126 | if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity)) |
128 | 127 | { |
@@ -137,11 +136,11 @@ MUtils::CPUFetaures::cpu_info_t MUtils::CPUFetaures::detect(void) | ||
137 | 136 | features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL); |
138 | 137 | } |
139 | 138 | |
139 | + //Apply manual CPU overwrites | |
140 | 140 | bool userFlag = false; |
141 | - if(args.contains("force-cpu-no-64bit")) { userFlag = true; features.x64 = false; } | |
142 | - if(args.contains("force-cpu-no-sse" )) { userFlag = true; features.features &= (~(FLAG_SSE | FLAG_SSE2 | FLAG_SSE3 | FLAG_SSSE3 | FLAG_SSE4 | FLAG_SSE42)); } | |
143 | - if(args.contains("force-cpu-no-intel")) { userFlag = true; features.intel = false; } | |
144 | - | |
141 | + if (args.contains(QLatin1String("cpu-no-simd"))) { userFlag = true; features.features = 0U; } | |
142 | + if (args.contains(QLatin1String("cpu-no-vendor"))) { userFlag = true; features.vendor = 0U; } | |
143 | + if (args.contains(QLatin1String("cpu-no-x64"))) { userFlag = true; features.x64 = 0U; } | |
145 | 144 | if(userFlag) |
146 | 145 | { |
147 | 146 | qWarning("CPU flags overwritten by user-defined parameters. Take care!\n"); |