Détection programmatique du nombre de processeurs/cœurs physiques ou si hyper-threading est actif sur Windows, Mac et Linux

j'ai une application c++ multithread qui tourne sur Windows, Mac et quelques saveurs de Linux.

pour faire une histoire courte: pour qu'il fonctionne au maximum d'efficacité, je dois pouvoir instancier un seul thread par processeur/cœur physique. Créer plus de threads qu'il n'y a de processeurs/cœurs physiques dégrade considérablement les performances de mon programme. Je peux déjà détecter correctement le nombre de processeurs/noyaux logiques correctement sur les trois ces plates-formes. Pour être capable de détecter correctement le nombre de processeurs/cœurs physiques, je vais devoir détecter si hyper-treading est supporté et actif.

ma question est donc de savoir s'il y a un moyen de détecter si l'hyperthreading est supportée et activée? Si oui, comment exactement.

40
demandé sur Cœur 2010-05-25 06:57:07

11 réponses

EDIT: Ce n'est plus correct à 100% en raison de l'Intel en cours befuddlement.

la façon dont je comprends la question Est que vous demandez comment détecter le nombre de cœurs CPU par rapport aux fils CPU qui est différent de la détection du nombre de cœurs logiques et physiques dans un système. Les cœurs CPU ne sont souvent pas considérés comme des cœurs physiques par L'OS à moins qu'ils aient leur propre paquet ou matrice. Donc un OS signalera qu'un Core 2 Duo, par exemple, a 1 CPU physique et 2 logique et un Intel P4 avec Hyper-threads sera rapporté exactement de la même façon, même si 2 Hyper-threads par rapport à 2 cœurs CPU est une chose très différente du point de vue de la performance.

j'ai lutté avec cela jusqu'à ce que j'ai assemblé la solution ci-dessous, qui je crois fonctionne à la fois pour les processeurs AMD et Intel. Pour autant que je sache, et je pourrais me tromper, AMD n'a pas encore de fils CPU mais ils ont fourni un moyen de les détecter que je suppose va travailler sur Futur Les processeurs AMD qui peuvent avoir des threads CPU.

en bref voici les étapes en utilisant L'instruction CPUID:

  1. Détecter CPU fournisseur à l'aide de CPUID fonction 0
  2. vérifier pour le bit htt 28 dans les fonctionnalités CPU EDX à partir de la fonction CPUID 1
  3. Obtenir la logique nombre de cœurs de EBX[23:16] à partir de CPUID fonction 1
  4. obtenir le nombre réel de cœurs CPU non filetés
    1. If fournisseur == 'GenuineIntel" c'est 1 plus EAX[31:26] de CPUID fonction 4
    2. Si le vendeur == 'AuthenticAMD" c'est 1 plus ECX[7:0] à partir de CPUID fonction 0x80000008

semble difficile, mais voici, espérons-le, UN programme C++ indépendant de la plate-forme qui fait l'affaire:

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

Je ne l'ai pas encore testé sur Windows ou OSX mais cela devrait fonctionner car L'instruction CPUID est valide sur les machines i686. Évidemment, cela ne marchera pas pour PowerPC, mais ils n'ont pas non plus d'hyperbreaks.

Voici la sortie sur quelques machines Intel différentes:

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

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

Intel(R) Core (TM)2 Quad CPU Q8400 @ 2.66 GHz:

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

Intel (R) Xeon (R) CPU E5520 @ 2.27 GHz (W/ x2 paquets CPU physiques):

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

Intel (R) Pentium (r) 4 CPU 3.00 GHz:

 logical cpus: 2
    cpu cores: 1
hyper-threads: true
25
répondu jcoffland 2014-08-13 19:39:54

notez ceci, ne donne pas le nombre de noyaux physiques tel que prévu, mais des noyaux logiques.

si vous pouvez utiliser C++11 (grâce au commentaire d'alfC ci-dessous):

#include <iostream>
#include <thread>

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

sinon peut-être que la bibliothèque Boost est une option pour vous. Même code mais différent inclure comme ci-dessus. Inclure <boost/thread.hpp> au lieu de <thread> .

17
répondu math 2015-07-17 09:40:29

Windows only solution described here:

GetLogicalProcessorInformation

pour linux, fichier/proc / cpuinfo. Je ne suis pas sous linux maintenant, ne peut donc pas vous donner plus de détails. Vous pouvez compter les instances physiques / logiques du processeur. Si le compte logique est deux fois plus physique, alors vous avez HT activé (vrai seulement pour x86).

12
répondu rados 2018-03-13 03:22:11

la réponse la plus élevée actuellement utilisée avec CPUID semble obsolète. Il signale à la fois le mauvais nombre de processeurs logiques et physiques. Cela semble être confirmé à partir de cette réponse cpuid-sur-intel i7-processeurs .

spécifiquement, en utilisant CPUID.1.EBX [23: 16] pour obtenir les processeurs logiques ou CPUID.4.EAX [31: 26]+1 pour obtenir les physiques avec des processeurs Intel ne donne pas le bon résultat sur N'importe quel processeur Intel que J'ai.

pour un processeur Intel.Bh doit être utilisé Intel_thread/Fcore et la topologie de cache . La solution ne semble pas anodine. Pour la DMA, une solution différente est nécessaire.

voici le code source par Intel qui rapporte le nombre correct de noyaux physiques et logiques ainsi que le nombre correct de sockets https://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration / . Je testé sur un noyau logique de 80, un noyau physique de 40, un système Intel de 4 sockets.

voici le code source pour AMD http://developer.amd.com/resources/documentation-articles/articles-whitepapers/processor-and-core-enumeration-using-cpuid / . Il a donné le bon résultat sur mon système Intel à une seule prise, mais pas sur mon système à quatre prises. Je n'ai pas de système de DMLA à tester.

Je n'ai pas encore disséqué le code source pour trouver un simple réponse (s'il y en a une) avec CPUID. Il semble que si la solution peut changer (il semble) que la meilleure solution est d'utiliser une bibliothèque ou d'OS d'appel.

Edit:

Voici une solution pour les processeurs Intel avec CPUID leaf 11 (Bh). Pour ce faire, bouclez les processeurs logiques et obtenez l'ID x2APIC pour chaque processeur logique à partir de CPUID et comptez le nombre d'ID x2APIC où le bit le moins significatif est zéro. Pour les systèmes sans Hyper-threading L'ID x2APIC sera toujours égal. Pour les systèmes avec Hyper-threading chaque ID x2APIC aura une version Pair et Impair.

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

les fils doivent être liés pour que cela fonctionne. OpenMP par défaut ne lie pas les threads. Paramètre export OMP_PROC_BIND=true lier ou ils peuvent être liées dans le code comme indiqué au thread-affinity-avec-windows-msvc-et-openmp .

j'ai testé cela sur mon système 4 core/8 HT et il est retourné 4 avec et sans Hyper-filetage désactivé dans le BIOS. J'ai également testé sur un système à 4 prises avec chaque prise ayant 10 cœurs / 20 HT et il a retourné 40 cœurs.

Les processeurs

AMD ou les processeurs Intel plus anciens sans CPUID leaf 11 doivent faire quelque chose de différent.

11
répondu Z boson 2014-10-06 12:48:13

de la collecte des idées et des concepts à partir de certaines des idées ci-dessus, j'ai trouvé cette solution. Merci de la critique.

//EDIT INCLUDES

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

pour presque tous les OS, la fonctionnalité standard "Get core count" renvoie le nombre de noyaux logique. Mais pour obtenir le nombre de noyau physique, nous devons d'abord détecter si le CPU a un filetage hyper ou pas.

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

nous avons maintenant le compte de noyau logique, maintenant afin d'obtenir les résultats attendus, nous d'abord doit vérifier si hyper threading est utilisé ou s'il est même disponible.

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

parce qu'il n'y a pas D'unité centrale Intel avec un filetage hyper qui ne fera que fileter un noyau (du moins pas à partir de ce que j'ai lu). Cela nous permet de trouver, c'est vraiment indolore. Si hyper threading est disponible,les processeurs logiques seront exactement le double des processeurs physiques. Sinon, le système d'exploitation détectera un processeur logique pour chaque core. Sens le nombre de noyaux logique et physique sera identique.

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

fprintf (stdout, "LOGICAL: %i\n", logicalcpucount);
fprintf (stdout, "PHYSICAL: %i\n", physicalcpucount);
6
répondu Pnelego 2015-04-21 03:09:49

pour suivre la réponse de maths, à partir de boost 1.56 il existe l'attribut physical_concurrency qui fait exactement ce que vous voulez.

de la documentation - http://www.boost.org/doc/libs/1_56_0/doc/html/thread/thread_management.html#thread.thread_management.thread.physical_concurrency

Le nombre de cœurs physiques disponibles sur le système actuel. Contrairement à hardware_concurrency() il ne retourne pas le nombre de cœurs virtuels, mais il ne compte qu'cœurs physiques.

donc un exemple serait

    #include <iostream>
    #include <boost/thread.hpp>

    int main()
    {
        std::cout << boost::thread::physical_concurrency();
        return 0;
    }
6
répondu Harry 2015-06-25 17:43:40

je sais que c'est un vieux fil, mais personne n'a mentionné hwloc . La bibliothèque hwloc est disponible sur la plupart des distributions Linux et peut également être compilée sur Windows. Le code suivant retourne le nombre de processeurs physiques. 4 dans le cas d'un CPU i7.

#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
}
5
répondu pneveu 2015-04-06 17:43:41

sur OS X, vous pouvez lire ces valeurs à partir de sysctl(3) (L'API C, ou l'utilitaire en ligne de commande du même nom). La page de manuel devrait vous donner des informations sur l'utilisation. Les clés suivantes peuvent présenter un intérêt:

$ 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
2
répondu Variable Length Coder 2014-08-13 20:32:13

Je ne sais pas si les trois exposent les informations de la même manière, mais si vous pouvez supposer en toute sécurité que le noyau NT signalera les informations de périphérique selon le standard POSIX (pour lequel NT est supposé avoir un support), alors vous pourriez travailler hors de ce standard.

toutefois, les différences dans la gestion des appareils sont souvent citées comme l'une des pierres d'achoppement du développement de plates-formes croisées. Je voudrais au mieux mettre en œuvre cela comme trois volets de logique, Je ne voudrais pas essayer d'écrire un morceau de code pour manipuler toutes les plates-formes également.

Ok, Tout ce qui suppose C++. Pour L'ASM, je présume que vous ne vous lancerez que sur les processeurs x86 ou amd64? Vous aurez toujours besoin de deux chemins de branche, un pour chaque architecture, et vous aurez besoin de tester Intel séparément D'AMD (IIRC), mais dans l'ensemble, vous n'avez qu'à vérifier pour le CPUID. Est-ce que vous essayez de trouver? Le CPUID D'ASM sur les CPU de la famille Intel/AMD?

0
répondu jcolebrand 2010-05-25 03:07:29

c'est très facile à faire en Python:

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

peut-être pourriez-vous regarder le code source psutil pour voir ce qui se passe?

0
répondu ostrokach 2016-06-03 16:26:26

OpenMP devrait faire l'affaire:

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

la plupart des compilateurs prennent en charge OpenMP. Si vous utilisez un compilateur basé sur gcc (*nix, MacOS), vous devez compiler en utilisant:

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

(vous pourriez aussi avoir besoin de dire à votre compilateur d'utiliser la bibliothèque stdc++):

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

autant que je sache, OpenMP a été conçu pour résoudre ce genre de problèmes.

-1
répondu chahuistle 2012-09-27 22:05:49