以编程方式检测物理处理器/内核的数量,或者如果Windows,Mac和Linux上的超线程处于活动状态

我有一个multithreading的c + +应用程序,运行在Windows,Mac和一些Linux的风味。

长话短说:为了以最高效率运行,我必须能够实例化每个物理处理器/内核的单个线程。 创build比物理处理器/内核更多的线程会大大降低程序的性能。 我已经可以在所有这三个平台上正确检测到逻辑处理器/内核的数量。 为了能够正确检测物理处理器/内核的数量,我们必须检测是否支持超级执行并且处于活动状态。

因此,我的问题是,如果有一种方法来检测是否支持超线程和启用? 如果是这样,究竟如何。

编辑:这不再是100%正确由于英特尔正在进行的迷惑。

我理解这个问题的方法是,你问如何检测CPU核心数量与CPU线程数量,这与检测系统中逻辑和物理核心的数量是不同的。 CPU内核通常不被操作系统视为物理内核,除非它们拥有自己的封装或裸片。 因此,一个操作系统会报告说,例如,Core 2 Duo有1个物理CPU和2个逻辑CPU,具有超线程的Intel P4将以完全相同的方式报告,尽pipe2个超线程与2个CPU核心是非常相似的不同的事情performance明智。

我一直在努力,直到我将下面的解决scheme拼凑在一起,我认为这个解决scheme适用于AMD和Intel处理器。 据我所知,我可能是错的,AMD还没有CPU线程,但他们已经提供了一种方法来检测他们,我认为将在未来可能有CPU线程的AMD处理器。

简而言之,这里是使用CPUID指令的步骤:

  1. 使用CPUIDfunction0检测CPU供应商
  2. 检查来自CPUIDfunction1的CPUfunctionEDX中的HTT位28
  3. 从CPUID函数1获取EBX [23:16]的逻辑核心数
  4. 获取实际的非线程CPU核心数
    1. 如果供应商=='GenuineIntel'这是从CPUID函数4加1 EAX [31:26]
    2. 如果供应商=='AuthenticAMD'这是从CPUID函数0x80000008加1 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; } 

我还没有真正在Windows或OSX上testing过,但它应该工作,因为CPUID指令在i686机器上是有效的。 显然,这不会为PowerPC工作,但他们也没有超线程。

以下是一些不同的英特尔机器上的输出:

Intel(R)Core(TM)2 Duo CPU T7500 @ 2.20GHz:

  logical cpus: 2 cpu cores: 2 hyper-threads: false 

英特尔(R)酷睿™2四核CPU Q8400 @ 2.66GHz:

  logical cpus: 4 cpu cores: 4 hyper-threads: false 

Intel(R)Xeon(R)CPU E5520 @ 2.27GHz(w / x2物理CPU包):

  logical cpus: 16 cpu cores: 8 hyper-threads: true 

英特尔(R)奔腾(R)4 CPU 3.00GHz:

  logical cpus: 2 cpu cores: 1 hyper-threads: true 

请注意,这并不能提供物理内核的数量,而是逻辑内核。

如果你可以使用C ++ 11(感谢alfC的评论):

 #include <iostream> #include <thread> int main() { std::cout << std::thread::hardware_concurrency() << std::endl; return 0; } 

否则,Boost库可能是你的select。 相同的代码,但不同的包括如上。 包括<boost/thread.hpp>而不是<thread>

目前使用CPUID的最高投票答案似乎已经过时。 它报告了逻辑和物理处理器的错误数量。 这似乎从这个答案cpuid-on-intel-i7-处理器证实。

具体来说,使用CPUID.1.EBX [23:16]来获取逻辑处理器或CPUID.4.EAX [31:26] +1以获得具有Intel处理器的物理处理器不会在任何Intel处理器I上提供正确的结果有。

对于Intel CPUID.Bh应该使用Intel_thread / Fcore和caching拓扑结构 。 解决scheme似乎并不重要。 对AMD而言,不同的解决scheme是必要的

以下是英特尔的源代码,它报告了物理和逻辑内核的正确数量以及正确的套接字数量https://software.intel.com/zh-cn/articles/intel-64-architecture-processor-topology -enumeration / 。 我testing了一个80逻辑核心,40物理核心,4sockets英特尔系统。

这里是AMD的源代码http://developer.amd.com/resources/documentation-articles/articles-whitepapers/processor-and-core-enumeration-using-cpuid/ 。 它在我的单插槽Intel系统上给出了正确的结果,但在我的四插槽系统上却没有。 我没有AMD系统来testing。

我还没有解剖源代码,find一个简单的答案(如果存在)与CPUID。 看来,如果解决scheme可以改变(因为它似乎有),最好的解决scheme是使用库或操作系统调用。

编辑:

以下是CPUID 11(Bh)的英特尔处理器的解决scheme。 执行此操作的方法是在逻辑处理器上循环,并从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_PROC_BIND=true将绑定它们,或者可以像在thread-affinity-with-windows-msvc-and- export OMP_PROC_BIND=true中所显示的那样绑定它们。

我在我的4核/ 8 HT系统上testing了这个function,并在BIOS中禁用和禁止超线程的情况下返回了4。 我还testing了一个4sockets系统,每个sockets有10个核心/ 20 HT,它返回了40个核心。

AMD处理器或没有CPUID 11的较老的Intel处理器必须做一些不同的事情。

Windows只解决scheme在这里描述:

http://msdn.microsoft.com/en-us/library/ms683194

对于linux,/ proc / cpuinfo文件。 我现在没有运行linux,所以不能给你更多的细节。 您可以计算物理/逻辑处理器实例。 如果逻辑计数是物理的两倍,则启用HT(仅适用于x86)。

从上述观点收集想法和概念,我提出了这个解决scheme。 请批评。

 //EDIT INCLUDES #ifdef _WIN32 #include <windows.h> #elif MACOS #include <sys/param.h> #include <sys/sysctl.h> #else #include <unistd.h> #endif 

对于几乎每个操作系统,标准的“获取核心数”function都会返回逻辑核心数。 但为了获得物理核心数量,我们必须首先检测CPU是否超线程。

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

因为没有超线程的英特尔CPU只会超线程一个内核(至less不是我读过的)。 这让我们发现这是一个非常无痛的方式。 如果超线程可用,则逻辑处理器将是物理处理器的两倍。 否则,操作系统将检测每个单核的逻辑处理器。 这意味着逻辑和物理核心数量将是相同的。

 if (hyperthreading){ physicalcpucount = logicalcpucount / 2; } else { physicalcpucount = logicalcpucount; } fprintf (stdout, "LOGICAL: %i\n", logicalcpucount); fprintf (stdout, "PHYSICAL: %i\n", physicalcpucount); 

从math的答案继续,作为提升1.56存在physical_concurrency属性正是你想要的。

从文档 – http://www.boost.org/doc/libs/1_56_0/doc/html/thread/thread_management.html#thread.thread_management.thread.physical_concurrency

当前系统上可用的物理内核数量。 与hardware_concurrency()相反,它不会返回虚拟内核的数量,但它只计算物理内核。

所以就是一个例子

  #include <iostream> #include <boost/thread.hpp> int main() { std::cout << boost::thread::physical_concurrency(); return 0; } 

我知道这是一个古老的线程,但没有人提到hwloc 。 hlloc库在大多数Linux发行版上都可用,也可以在Windows上编译。 以下代码将返回物理处理器的数量。 4在i7 CPU的情况下。

 #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 } 

在OS X上,可以从sysctl(3) (C API或同名的命令行实用程序sysctl(3)读取这些值。 手册页应该给你使用信息。 以下按键可能是有用的:

 $ 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 

我不知道所有这三个都以同样的方式公开信息,但是如果你可以安全地假定NT内核将根据POSIX标准报告设备信息(NT可以支持这个标准),那么你可以解决这个问题标准。

但是,设备pipe理的不同往往被认为是跨平台开发的绊脚石之一。 我至多将这个作为三个逻辑链来实现,我不会试图写一块代码来均匀地处理所有的平台。

好吧,所有这一切都假设C ++。 对于ASM,我认为你只能在x86或AMD64 CPU上运行。 你仍然需要两个分支path,每个架构一个,你需要testing英特尔从AMD(IIRC)分开,但总的来说,你只是检查CPUID。 那是你想要find的吗? Intel / AMD系列CPU上ASM的CPUID?

这在Python中很容易实现:

 $ python -c "import psutil; psutil.cpu_count(logical=False)" 4 

也许你可以看看psutil源代码来看看发生了什么?

OpenMP应该这样做:

 // 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; } 

大多数编译器都支持OpenMP。 如果您使用的是基于gcc的编译器(* nix,MacOS),则需要使用以下代码进行编译:

 $ g++ -fopenmp -o test.o test.cpp 

(您可能还需要告诉您的编译器使用stdc ++库):

 $ g++ -fopenmp -o test.o -lstdc++ test.cpp 

据我所知,OpenMP旨在解决这类问题。