以编程方式检测多个物理处理器/核心,或者在Windows,Mac和Linux上是否处于活动状态

HTA*_*CPP 45 c++ windows macos assembly hyperthreading

我有一个多线程的c ++应用程序,可以在Windows,Mac和一些Linux风格上运行.

简而言之:为了使它以最高效率运行,我必须能够为每个物理处理器/核心实例化一个线程.创建比物理处理器/内核更多的线程会大大降低程序的性能.我已经可以在所有这三个平台上正确检测逻辑处理器/核心的数量.为了能够正确检测物理处理器/内核的数量,我必须检测是否支持超级交叉和活动.

因此,我的问题是,是否有办法检测是否支持超线程并启用?如果是这样,究竟如何.

jco*_*and 25

编辑:由于英特尔正在进行的讨论,这已不再是100%正确.

我理解这个问题的方式是,您正在询问如何检测CPU核心数与CPU线程数,这与检测系统中的逻辑和物理核心数不同.CPU核心通常不被操作系统视为物理核心,除非它们有自己的封装或裸片.因此,操作系统将报告Core 2 Duo具有1个物理CPU和2个逻辑CPU,并且具有超线程的Intel P4将以完全相同的方式报告,即使2个超线程与2个CPU核心非常相同不同的表现明智.

我一直在努力解决这个问题,直到我将下面的解决方案拼凑在一起,我认为这对AMD和Intel处理器都有效.据我所知,我可能错了,AMD还没有CPU线程,但是它们提供了一种检测它们的方法,我认为它可以用于未来可能具有CPU线程的AMD处理器.

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

  1. 使用CPUID功能0检测CPU供应商
  2. 从CPUID功能1检查CPU功能EDX中的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;
}
Run Code Online (Sandbox Code Playgroud)

我还没有在Windows或OSX上测试过这个,但它应该可以工作,因为CPUID指令在i686机器上有效.显然,这对PowerPC不起作用,但它们也没有超线程.

以下是几台不同Intel机器的输出:

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

 logical cpus: 2
    cpu cores: 2
hyper-threads: false
Run Code Online (Sandbox Code Playgroud)

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

 logical cpus: 4
    cpu cores: 4
hyper-threads: false
Run Code Online (Sandbox Code Playgroud)

英特尔(R)Xeon(R)CPU E5520 @ 2.27GHz(带有x2物理CPU封装):

 logical cpus: 16
    cpu cores: 8
hyper-threads: true
Run Code Online (Sandbox Code Playgroud)

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

 logical cpus: 2
    cpu cores: 1
hyper-threads: true
Run Code Online (Sandbox Code Playgroud)

  • 你愿意详细说明吗? (7认同)
  • @Alex,上例中的Xeon E5520来自一台带有两个处理器软件包的机器,所以数字是正确的.但是,i7确实会导致此代码出现问题.当我发布该解决方案时,没有多少i7.它似乎在当时有效.随着i7的推出,英特尔制作了自己的CPID指令几乎无法用于此目的. (4认同)
  • 代码仍然无法正常工作.它在i5-3317U cpu上报告了16个逻辑核心. (4认同)
  • 这段代码如果有用,现在已经过时了. (3认同)
  • 代码确实在某一点上起作用,但即使对于2010年制造的i3 5x0处理器也是如此,因为在那个时候,英特尔决定在APIC id空间中增加空白.有关详细信息,请参阅[我在另一个主题中的答案](http://stackoverflow.com/a/24704156/3588161). (3认同)

mat*_*ath 18

请注意,并未按预期提供物理内核的数量,而是提供逻辑内核.

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

#include <iostream>
#include <thread>

int main() {
    std::cout << std::thread::hardware_concurrency() << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 不要删除你的帖子,这个信息非常有帮助.谢谢你帮助了我! (5认同)
  • 这是一个非常简单的解决方案,但它不区分硬件线程,即超线程,与物理CPU或内核,我认为是这个问题的重点. (4认同)
  • 在C++ 11中:http://en.cppreference.com/w/cpp/thread/thread/hardware_concurrency (2认同)

小智 15

这里描述的Windows解决方案:

GetLogicalProcessorInformation

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


Z b*_*son 12

使用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和缓存拓扑.解决方案似乎并不简单.对于AMD,需要一个不同的解决方案.

以下是英特尔的源代码,它报告正确数量的物理和逻辑内核以及正确的套接字数量https://software.intel.com/en-us/articles/intel-64-architecture-processor-topology - 枚举/.我在80逻辑核心,40个物理核心,4插槽Intel系统上测试了这个.

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

我还没有解析源代码,找到一个带有CPUID的简单答案(如果存在).似乎如果解决方案可以改变(似乎有)最佳解决方案是使用库或OS调用.

编辑:

以下是具有CPUID叶11(Bh)的Intel处理器的解决方案.执行此操作的方法是循环逻辑处理器并从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;
}
Run Code Online (Sandbox Code Playgroud)

必须绑定线程才能使其正常工作.默认情况下,OpenMP不绑定线程.设置export OMP_PROC_BIND=true将绑定它们,或者它们可以绑定在代码中,如thread-affinity-with-windows-msvc-and-openmp所示.

我在我的4核/ 8 HT系统上进行了测试,并在BIOS中禁用超线程返回4.我还测试了一个4插槽系统,每个插槽有10个内核/ 20 HT,它返回40个内核.

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


Pne*_*ego 7

通过从上述一些想法中收集想法和概念,我提出了这个解决方案.请批评.

//EDIT INCLUDES

#ifdef _WIN32
    #include <windows.h>
#elif MACOS
    #include <sys/param.h>
    #include <sys/sysctl.h>
#else
    #include <unistd.h>
#endif
Run Code Online (Sandbox Code Playgroud)

对于几乎所有操作系统,标准的"获取核心数"功能都会返回逻辑核心数.但是为了获得物理核心数,我们必须首先检测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
Run Code Online (Sandbox Code Playgroud)

我们现在拥有逻辑核心数,现在为了获得预期的结果,我们首先必须检查是否正在使用超线程或者它是否可用.

__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);
Run Code Online (Sandbox Code Playgroud)

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

if (hyperthreading){
    physicalcpucount = logicalcpucount / 2;
} else {
    physicalcpucount = logicalcpucount;
}

fprintf (stdout, "LOGICAL: %i\n", logicalcpucount);
fprintf (stdout, "PHYSICAL: %i\n", physicalcpucount);
Run Code Online (Sandbox Code Playgroud)

  • 现在有了英特尔的性能核心和超线程核心,我们不能再将总数除以/2了吗? (2认同)

小智 7

从数学的答案开始,从boost 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;
    }
Run Code Online (Sandbox Code Playgroud)


pne*_*veu 6

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

#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
}
Run Code Online (Sandbox Code Playgroud)


A F*_*Fog 5

仅测试 Intel CPU 是否具有超线程是不够的,还需要测试超线程是否启用或禁用。没有记录的方法来检查这一点。一位 Intel 人员想出了这个技巧来检查是否启用了超线程:使用 CPUID[0xa].eax[15:8] 检查可编程性能计数器的数量,并假设如果该值为 8,则 HT 被禁用,如果值为 4,HT 已启用 ( https://software.intel.com/en-us/forums/intel-isa-extensions/topic/831551 )。

AMD 芯片上没有问题:CPUID 报告每个核心 1 或 2 个线程,具体取决于同时多线程是禁用还是启用。

您还必须将 CPUID 中的线程计数与操作系统报告的线程计数进行比较,以查看是否存在多个 CPU 芯片。

我创建了一个函数来实现这一切。它报告物理处理器的数量和逻辑处理器的数量。我已经在 Windows 和 Linux 中的 Intel 和 AMD 处理器上进行了测试。它应该也可以在 Mac 上运行。我已在https://github.com/vectorclass/add-on/tree/master/physical_processors发布了此代码


jco*_*and 0

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

然而,设备管理的不同经常被认为是跨平台开发的绊脚石之一。我最多将其实现为三股逻辑,我不会尝试编写一段代码来均匀地处理所有平台。

好吧,所有这些都是假设 C++ 的。对于 ASM,我猜你只会在 x86 或 amd64 CPU 上运行?您仍然需要两条分支路径,每个架构各一条,并且您需要将 Intel 与 AMD (IIRC) 分开进行测试,但总的来说,您只需检查 CPUID。这就是你想要寻找的吗?Intel/AMD 系列 CPU 上 ASM 的 CPUID?