如何检测Windows、Mac和Linux上的物理处理器/内核数量 我有一个多线程的C++应用程序,它运行在Windows、MAC和一些Linux的口味上。p>
长话短说:为了让它以最高效率运行,我必须能够为每个物理处理器/核心实例化一个线程。创建比物理处理器/内核更多的线程会大大降低程序的性能。我已经能够在这三个平台上正确地检测到逻辑处理器/内核的数量。为了能够正确检测物理处理器/内核的数量,我必须检测是否支持并激活了hyper-treading如何检测Windows、Mac和Linux上的物理处理器/内核数量 我有一个多线程的C++应用程序,它运行在Windows、MAC和一些Linux的口味上。p>,c++,windows,macos,assembly,hyperthreading,C++,Windows,Macos,Assembly,Hyperthreading,长话短说:为了让它以最高效率运行,我必须能够为每个物理处理器/核心实例化一个线程。创建比物理处理器/内核更多的线程会大大降低程序的性能。我已经能够在这三个平台上正确地检测到逻辑处理器/内核的数量。为了能够正确检测物理处理器/内核的数量,我必须检测是否支持并激活了hyper-treading 因此,我的问题是,是否有一种方法可以检测超线程是否被支持和启用?如果是这样的话,具体是怎样的。我不知道这三种方法都是以相同的方式公开信息的,但是如果您可以安全地假设NT内核将根据POSIX标准报告设备信息(据
因此,我的问题是,是否有一种方法可以检测超线程是否被支持和启用?如果是这样的话,具体是怎样的。我不知道这三种方法都是以相同的方式公开信息的,但是如果您可以安全地假设NT内核将根据POSIX标准报告设备信息(据推测NT支持POSIX标准),那么您可以取消该标准 然而,设备管理的不同常常被认为是跨平台开发的绊脚石之一。我最多将其实现为三条逻辑,我不会试图编写一段代码来均匀处理所有平台
<>当然,所有的假设都是C++。对于ASM,我想您将只在x86或amd64 CPU上运行?您仍然需要两个分支路径,每个架构一个,您需要单独测试Intel和AMD(IIRC),但大体上您只需要检查CPUID。这就是你想要找到的吗?英特尔/AMD系列CPU上ASM的CPUID?Windows专用解决方案描述如下: 对于linux,/proc/cpuinfo文件。我不是在运行linux 现在,我不能给你更多的细节。你可以数数 物理/逻辑处理器实例。如果逻辑计数 是物理的两倍,则您已启用HT
(仅适用于x86)。请注意,这并没有给出预期的物理核数,而是逻辑核数。 如果您可以使用C++11(感谢下面alfC的评论):
#包括
#包括
int main(){
标准::cout编辑:由于英特尔一直在胡思乱想,这不再是100%正确。
我对这个问题的理解是,你问的是如何检测CPU内核的数量与CPU线程的数量,这与检测系统中逻辑和物理内核的数量不同。CPU内核通常不被操作系统视为物理内核,除非它们有自己的包或死。因此,操作系统会报告Core 2 Duo,for例如,具有1个物理CPU和2个逻辑CPU,并且具有超线程的Intel P4将以完全相同的方式报告,尽管2个超线程与2个CPU内核在性能方面是完全不同的
我一直在努力解决这个问题,直到我拼凑出下面的解决方案,我相信它对AMD和Intel处理器都有效。据我所知,我可能是错的,AMD还没有CPU线程,但他们提供了一种方法来检测它们,我认为这将在未来可能有CPU线程的AMD处理器上工作
简而言之,以下是使用CPUID指令的步骤:
使用CPUID函数0检测CPU供应商
从CPUID函数1检查CPU功能EDX中的HTT位28
从CPUID函数1中的EBX[23:16]获取逻辑内核计数
获取实际的非线程CPU内核计数
如果vendor=='GenuineIntel',这是1加上CPUID函数4中的EAX[31:26]
如果vendor=='AuthenticAMD',这是1加上CPUID函数0x8000008中的ECX[7:0]
听起来很困难,但这里有一个希望与平台无关的C++程序,它的诀窍是:
#include <iostream>
#include <string>
using namespace std;
void cpuID(unsigned i, unsigned regs[4]) {
#ifdef _WIN32
__cpuid((int *)regs, (int)i);
#else
asm volatile
("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
: "a" (i), "c" (0));
// ECX is set to zero for CPUID function 4
#endif
}
int main(int argc, char *argv[]) {
unsigned regs[4];
// Get vendor
char vendor[12];
cpuID(0, regs);
((unsigned *)vendor)[0] = regs[1]; // EBX
((unsigned *)vendor)[1] = regs[3]; // EDX
((unsigned *)vendor)[2] = regs[2]; // ECX
string cpuVendor = string(vendor, 12);
// Get CPU features
cpuID(1, regs);
unsigned cpuFeatures = regs[3]; // EDX
// Logical core count per CPU
cpuID(1, regs);
unsigned logical = (regs[1] >> 16) & 0xff; // EBX[23:16]
cout << " logical cpus: " << logical << endl;
unsigned cores = logical;
if (cpuVendor == "GenuineIntel") {
// Get DCP cache info
cpuID(4, regs);
cores = ((regs[0] >> 26) & 0x3f) + 1; // EAX[31:26] + 1
} else if (cpuVendor == "AuthenticAMD") {
// Get NC: Number of CPU cores - 1
cpuID(0x80000008, regs);
cores = ((unsigned)(regs[2] & 0xff)) + 1; // ECX[7:0] + 1
}
cout << " cpu cores: " << cores << endl;
// Detect hyper-threads
bool hyperThreads = cpuFeatures & (1 << 28) && cores < logical;
cout << "hyper-threads: " << (hyperThreads ? "true" : "false") << endl;
return 0;
}
// test.cpp
#include <omp.h>
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int nThreads = omp_get_max_threads();
cout << "Can run as many as: " << nThreads << " threads." << endl;
}
英特尔(R)核心(TM)2四处理器Q8400@2.66GHz:
logical cpus: 4
cpu cores: 4
hyper-threads: false
英特尔(R)至强(R)CPU E5520@2.27GHz(带x2个物理CPU包):
英特尔(R)奔腾(R)4 CPU 3.00GHz:
logical cpus: 2
cpu cores: 1
hyper-threads: true
OpenMP应该做到这一点:
#include <iostream>
#include <string>
using namespace std;
void cpuID(unsigned i, unsigned regs[4]) {
#ifdef _WIN32
__cpuid((int *)regs, (int)i);
#else
asm volatile
("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
: "a" (i), "c" (0));
// ECX is set to zero for CPUID function 4
#endif
}
int main(int argc, char *argv[]) {
unsigned regs[4];
// Get vendor
char vendor[12];
cpuID(0, regs);
((unsigned *)vendor)[0] = regs[1]; // EBX
((unsigned *)vendor)[1] = regs[3]; // EDX
((unsigned *)vendor)[2] = regs[2]; // ECX
string cpuVendor = string(vendor, 12);
// Get CPU features
cpuID(1, regs);
unsigned cpuFeatures = regs[3]; // EDX
// Logical core count per CPU
cpuID(1, regs);
unsigned logical = (regs[1] >> 16) & 0xff; // EBX[23:16]
cout << " logical cpus: " << logical << endl;
unsigned cores = logical;
if (cpuVendor == "GenuineIntel") {
// Get DCP cache info
cpuID(4, regs);
cores = ((regs[0] >> 26) & 0x3f) + 1; // EAX[31:26] + 1
} else if (cpuVendor == "AuthenticAMD") {
// Get NC: Number of CPU cores - 1
cpuID(0x80000008, regs);
cores = ((unsigned)(regs[2] & 0xff)) + 1; // ECX[7:0] + 1
}
cout << " cpu cores: " << cores << endl;
// Detect hyper-threads
bool hyperThreads = cpuFeatures & (1 << 28) && cores < logical;
cout << "hyper-threads: " << (hyperThreads ? "true" : "false") << endl;
return 0;
}
// test.cpp
#include <omp.h>
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int nThreads = omp_get_max_threads();
cout << "Can run as many as: " << nThreads << " threads." << endl;
}
(您可能还需要告诉编译器使用stdc++库):
据我所知,OpenMP旨在解决这类问题。当前使用CPUID的投票率最高的答案似乎已过时。它报告了错误的逻辑和物理处理器数量。这似乎从该答案中得到了证实
具体来说,使用CPUID.1.EBX[23:16]获取逻辑处理器,或使用CPUID.4.EAX[31:26]+1获取具有英特尔处理器的物理处理器,在我拥有的任何英特尔处理器上都不会给出正确的结果
对于Intel CPUID,应使用.Bh。该解决方案看起来并不简单。对于AMD,需要使用不同的解决方案
这是Intel的源代码,它报告了正确的物理和逻辑内核数量以及正确的套接字数量。我在80逻辑内核、40物理内核、4套接字Intel系统上测试了这一点
这是AMD的源代码。它在我的单插槽Intel系统上给出了正确的结果,但在我的四插槽系统上没有。我没有AMD系统要测试
我还没有仔细分析源代码,以找到一个简单的CPUID答案(如果有的话)。似乎如果解决方案可以改变(似乎已经改变),那么最好的解决方案就是使用库或操作系统调用
编辑:
这是一个针对使用CPUID leaf 11(Bh)的英特尔处理器的解决方案。执行此操作的方法是在逻辑处理器上循环,并从CPUID获取每个逻辑处理器的x2APIC ID,并计算最低有效位为零的x2APIC ID数。对于没有超线程的系统,x2APIC ID将始终为偶数。对于具有超线程的系统,每个x2APIC ID将具有偶数和奇数版本
// input: eax = functionnumber, ecx = 0
// output: eax = output[0], ebx = output[1], ecx = output[2], edx = output[3]
//static inline void cpuid (int output[4], int functionnumber)
int getNumCores(void) {
//Assuming an Intel processor with CPUID leaf 11
int cores = 0;
#pragma omp parallel reduction(+:cores)
{
int regs[4];
cpuid(regs,11);
if(!(regs[3]&1)) cores++;
}
return cores;
}
必须绑定线程才能工作。OpenMP默认情况下不绑定线程。设置export-OMP\u-PROC\u-bind=true
将绑定线程,或者可以在代码中绑定线程,如中所示
我在我的4核/8 HT系统上测试了这个,它返回了
$ g++ -fopenmp -o test.o test.cpp
$ g++ -fopenmp -o test.o -lstdc++ test.cpp
// input: eax = functionnumber, ecx = 0
// output: eax = output[0], ebx = output[1], ecx = output[2], edx = output[3]
//static inline void cpuid (int output[4], int functionnumber)
int getNumCores(void) {
//Assuming an Intel processor with CPUID leaf 11
int cores = 0;
#pragma omp parallel reduction(+:cores)
{
int regs[4];
cpuid(regs,11);
if(!(regs[3]&1)) cores++;
}
return cores;
}
$ sysctl hw
hw.ncpu: 24
hw.activecpu: 24
hw.physicalcpu: 12 <-- number of cores
hw.physicalcpu_max: 12
hw.logicalcpu: 24 <-- number of cores including hyper-threaded cores
hw.logicalcpu_max: 24
hw.packages: 2 <-- number of CPU packages
hw.ncpu = 24
hw.availcpu = 24
#include <hwloc.h>
int nPhysicalProcessorCount = 0;
hwloc_topology_t sTopology;
if (hwloc_topology_init(&sTopology) == 0 &&
hwloc_topology_load(sTopology) == 0)
{
nPhysicalProcessorCount =
hwloc_get_nbobjs_by_type(sTopology, HWLOC_OBJ_CORE);
hwloc_topology_destroy(sTopology);
}
if (nPhysicalProcessorCount < 1)
{
#ifdef _OPENMP
nPhysicalProcessorCount = omp_get_num_procs();
#else
nPhysicalProcessorCount = 1;
#endif
}
//EDIT INCLUDES
#ifdef _WIN32
#include <windows.h>
#elif MACOS
#include <sys/param.h>
#include <sys/sysctl.h>
#else
#include <unistd.h>
#endif
uint32_t registers[4];
unsigned logicalcpucount;
unsigned physicalcpucount;
#ifdef _WIN32
SYSTEM_INFO systeminfo;
GetSystemInfo( &systeminfo );
logicalcpucount = systeminfo.dwNumberOfProcessors;
#else
logicalcpucount = sysconf( _SC_NPROCESSORS_ONLN );
#endif
__asm__ __volatile__ ("cpuid " :
"=a" (registers[0]),
"=b" (registers[1]),
"=c" (registers[2]),
"=d" (registers[3])
: "a" (1), "c" (0));
unsigned CPUFeatureSet = registers[3];
bool hyperthreading = CPUFeatureSet & (1 << 28);
if (hyperthreading){
physicalcpucount = logicalcpucount / 2;
} else {
physicalcpucount = logicalcpucount;
}
fprintf (stdout, "LOGICAL: %i\n", logicalcpucount);
fprintf (stdout, "PHYSICAL: %i\n", physicalcpucount);
#include <iostream>
#include <boost/thread.hpp>
int main()
{
std::cout << boost::thread::physical_concurrency();
return 0;
}
$ python -c "import psutil; psutil.cpu_count(logical=False)"
4
#include <stdio.h>
#include <libcpuid.h>
int main(void)
{
if (!cpuid_present()) { // check for CPUID presence
printf("Sorry, your CPU doesn't support CPUID!\n");
return -1;
}
if (cpuid_get_raw_data(&raw) < 0) { // obtain the raw CPUID data
printf("Sorry, cannot get the CPUID raw data.\n");
printf("Error: %s\n", cpuid_error()); // cpuid_error() gives the last error description
return -2;
}
if (cpu_identify(&raw, &data) < 0) { // identify the CPU, using the given raw data.
printf("Sorrry, CPU identification failed.\n");
printf("Error: %s\n", cpuid_error());
return -3;
}
printf("Found: %s CPU\n", data.vendor_str); // print out the vendor string (e.g. `GenuineIntel')
printf("Processor model is `%s'\n", data.cpu_codename); // print out the CPU code name (e.g. `Pentium 4 (Northwood)')
printf("The full brand string is `%s'\n", data.brand_str); // print out the CPU brand string
printf("The processor has %dK L1 cache and %dK L2 cache\n",
data.l1_data_cache, data.l2_cache); // print out cache size information
printf("The processor has %d cores and %d logical processors\n",
data.num_cores, data.num_logical_cpus); // print out CPU cores information
}