53 votes

Comment détecter le nombre de processeurs physiques / cœurs sur Windows, Mac et Linux

J'ai une application c++ multi-filière qui fonctionne sous Windows, Mac et quelques versions de Linux.

Pour faire court : pour que le système fonctionne avec une efficacité maximale, je dois pouvoir instancier un seul thread par processeur physique/cœur. 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/cœurs logiques sur ces trois plateformes. Pour pouvoir détecter correctement le nombre de processeurs/cœurs physiques, je dois détecter si l'hyper-treading est supporté ET actif.

Ma question est donc de savoir s'il existe un moyen de détecter si l'Hyper Threading est supporté. et activé ? Si oui, comment exactement.

1 votes

1 votes

N'avez-vous pas posé la même question il y a deux jours ? stackoverflow.com/questions/2904283/

0 votes

Avez-vous abandonné cette question ?

31voto

jcoffland Points 1506

EDIT : Ce n'est plus correct à 100% en raison de l'incompréhension permanente d'Intel.

Si je comprends bien votre question, vous demandez comment détecter le nombre de cœurs de processeur par rapport aux threads de processeur, ce qui est différent de la détection du nombre de cœurs logiques et physiques dans un système. Les cœurs du processeur ne sont souvent pas considérés comme des cœurs physiques par le système d'exploitation, à moins qu'ils n'aient leur propre paquet ou matrice. Ainsi, un système d'exploitation indiquera qu'un Core 2 Duo, par exemple, possède 1 processeur physique et 2 processeurs logiques, et un Intel P4 avec hyper-threads sera indiqué exactement de la même manière, même si 2 hyper-threads et 2 cœurs de processeur sont très différents en termes de performances.

Je me suis débattu avec ce problème jusqu'à ce que je trouve la solution ci-dessous, qui, je pense, fonctionne pour les processeurs AMD et Intel. Pour autant que je sache, et je peux me tromper, AMD n'a pas encore de threads CPU mais ils ont fourni un moyen de les détecter qui, je suppose, fonctionnera sur les futurs processeurs AMD qui pourraient avoir des threads CPU.

En résumé, voici les étapes à suivre pour utiliser l'instruction CPUID :

  1. Détecter le fournisseur du CPU en utilisant la fonction CPUID 0
  2. Vérification du bit HTT 28 dans les caractéristiques du CPU EDX de la fonction CPUID 1
  3. Obtenez le nombre de noyaux logiques à partir de EBX[23:16] de la fonction CPUID 1.
  4. Obtenir le nombre réel de cœurs de processeurs non-threadés
    1. Si le vendeur == 'GenuineIntel', c'est 1 plus EAX[31:26] de la fonction 4 du CPUID.
    2. Si le vendeur == 'AuthenticAMD', c'est 1 plus ECX[7:0] de la fonction CPUID 0x80000008.

Cela semble difficile, mais voici 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 n'ai pas encore testé cela sous Windows ou OSX mais cela devrait fonctionner puisque l'instruction CPUID est valide sur les machines i686. Évidemment, cela ne fonctionnera pas pour les PowerPC mais ils n'ont pas d'hyper-threads non plus.

Voici le résultat sur quelques machines Intel différentes :

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

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

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

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

Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (avec x2 paquets de CPU physiques) :

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

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

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

7 votes

Pouvez-vous développer ?

0 votes

Merci pour le code jcoffland - cependant, je ne pense pas que le code dans votre exemple main() fonctionnera avec les processeurs AMD en l'état actuel des choses et le __cpuid() a changé en __cpuidex() pour Windows x64. Le principal problème du code est que le test pour vendor == AMD se trouve dans un bloc if testant vendor == Intel. La partie AMD ne sera jamais exécutée. Ce serait formidable si vous pouviez corriger le code :) Merci et salutations

0 votes

D'après cet article : msdn.microsoft.com/fr/us/library/hskdteyh.aspx , __cpuid() est toujours valide sous Windows x64.

25voto

math Points 2127

Notez que cela ne donne pas le nombre de cœurs physiques comme prévu, mais les cœurs 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, la bibliothèque Boost est peut-être une option pour vous. Même code mais un include différent de celui ci-dessus. Include <boost/thread.hpp> au lieu de <thread> .

7 votes

Il s'agit d'une solution très simple, mais qui ne fait pas la différence entre les threads matériels, aussi appelés hyper-threads, et les processeurs ou cœurs physiques, ce qui, je pense, est le but de cette question.

1 votes

Oui, vous avez raison, ce détail m'a échappé. Dois-je supprimer mon message ?

10 votes

Ne supprimez pas votre message, ces informations sont très utiles. Merci, cela m'a aidé !

18voto

rados Points 51

La solution Windows est décrite ici :

GetLogicalProcessorInformation

pour linux, le fichier /proc/cpuinfo. Je ne suis pas sous linux donc je ne peux pas vous donner plus de détails. Vous pouvez compter les instances de processeurs physiques/logiques. Si le nombre d'instances logiques est deux fois plus élevé que le physique, alors vous avez activé HT (vrai seulement pour x86).

1 votes

J'ai upvoted ceci parce qu'il fonctionne. Pour l'enregistrement textuel (et au cas où le lien disparaîtrait à un moment donné), le lien msdn dans cette réponse utilise GetLogicalProcessorInformation, qui fonctionne bien sur la plupart des systèmes d'exploitation. récent versions de Windows. (La source dit : "Windows Server 2003, Windows XP Professional x64 Edition, et Windows XP avec SP3 : Cet exemple rapporte le nombre de processeurs physiques plutôt que le nombre de cœurs de processeurs actifs.") Ce lien msdn ne doit pas être confondu avec celui pour __cpuid, qui malheureusement a un exemple non fumeur (sur la plupart des CPU Intel post-2010).

15voto

Z boson Points 4670

La réponse actuelle la plus votée utilisant CPUID semble être obsolète. Elle indique le mauvais nombre de processeurs logiques et physiques. Ceci semble être confirmé par cette réponse cpuid-on-intel-i7-processeurs .

Plus précisément, l'utilisation de CPUID.1.EBX[23:16] pour obtenir les processeurs logiques ou de CPUID.4.EAX[31:26]+1 pour obtenir les processeurs physiques avec des processeurs Intel ne donne pas le résultat correct sur tous les processeurs Intel que je possède.

Pour Intel CPUID.Bh devrait être utilisé Intel_thread/Fcore et topologie du cache . La solution ne semble pas être triviale. Pour AMD, une solution différente est nécessaire.

Voici le code source d'Intel qui indique le nombre correct de cœurs physiques et logiques ainsi que le nombre correct de sockets. https://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ . Je l'ai testé sur un système Intel à 4 sockets de 80 cœurs logiques et 40 cœurs physiques.

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 AMD à tester.

Je n'ai pas encore disséqué le code source pour trouver une réponse simple (si elle existe) avec CPUID. Il semble que si la solution peut changer (comme cela semble être le cas), la meilleure solution est d'utiliser une bibliothèque ou un appel au système d'exploitation.

Edit :

Voici une solution pour les processeurs Intel avec CPUID leaf 11 (Bh). La façon de faire est de boucler sur les processeurs logiques et d'obtenir l'ID x2APIC pour chaque processeur logique à partir de CPUID et de compter 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 pair. Pour les systèmes avec hyper-threading, chaque x2APIC ID aura une version paire et impaire.

// 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. Par défaut, OpenMP ne lie pas les threads. Configuration de export OMP_PROC_BIND=true les liera ou ils peuvent être liés dans le code comme indiqué dans l'exemple suivant thread-affinity-with-Windows-msvc-and-openmp .

Je l'ai testé sur mon système 4 cœurs/8 HT et il a donné 4 avec et sans hyper-threading désactivé dans le BIOS. Je l'ai également testé sur un système à 4 sockets avec chaque socket ayant 10 cœurs / 20 HT et il a retourné 40 cœurs.

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

0 votes

J'ai voté pour vous parce que votre réponse est meilleure que les autres, mais vous ne répondez pas techniquement à la question. Il veut seulement savoir si la HT est activée. Compter les cœurs actifs, non HT, ne le fait pas. Vous devez modifier votre code pour renvoyer une réponse oui/non, c'est à dire if(regs[3]&1) ht_cores++ et après la réduction ht_enabled = (ht_cores > 0) .

0 votes

De plus, le code AMD ne fonctionne pas correctement sur les processeurs Intel actuels pour la même raison que l'ancienne énumération de code Intel ne fonctionne pas correctement : elle suppose qu'il n'y a pas de vide dans l'espace d'identification APIC. Mais cette hypothèse est incorrect pour les processeurs Intel à partir de la fin 2009 environ .

0 votes

Merci pour votre réponse, c'était très utile. J'ai une question connexe : si vous désactivez la HT dans le BIOS, quel est le type de niveau (par exemple, les bits 8:15 de la valeur de sortie dans ECX) signalé par la feuille CPUID 11 avec ECX = 0 ? Est-ce "SMT" ou "core" ?

2voto

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

$ 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X