web-dev-qa-db-fra.com

Détecter par programme le nombre de processeurs / cœurs physiques ou si l'hyper-threading est actif sur Windows, Mac et Linux

J'ai une application c ++ multithread qui s'exécute sur Windows, Mac et quelques versions Linux.

Pour faire court: pour qu'il fonctionne à son efficacité maximale, je dois pouvoir instancier un seul thread par processeur/cœur physique. Créer plus de threads que 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 correctement sur ces trois plates-formes. Pour pouvoir détecter correctement le nombre de processeurs/cœurs physiques, je devrai détecter si l'hypertreading est pris en charge ET actif.

Ma question est donc de savoir s'il existe un moyen de détecter si l'hyperthreading est pris en charge et activé? Si oui, comment exactement.

42
HTASSCPP

EDIT: Ce n'est plus correct à 100% en raison de la confusion actuelle d'Intel.

La façon dont je comprends la question est que 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 de processeur ne sont souvent pas considérés comme des cœurs physiques par le système d'exploitation, sauf s'ils ont leur propre package ou meurent. Ainsi, un système d'exploitation signalera qu'un Core 2 Duo, par exemple, possède 1 processeur physique et 2 processeurs logiques et un Intel P4 avec des hyper-threads sera signalé exactement de la même manière, même si 2 hyper-threads contre 2 cœurs de processeur est un très chose différente performance sage.

Je me suis battu avec cela jusqu'à ce que je reconstitue la solution ci-dessous, qui je crois 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 fonctionnera sur les futurs processeurs AMD qui peuvent avoir des threads CPU.

En bref, voici les étapes à l'aide de l'instruction CPUID:

  1. Détecter le fournisseur de CPU à l'aide de la fonction CPUID 0
  2. Vérifiez le bit HTT 28 dans les fonctionnalités du processeur EDX à partir de la fonction CPUID 1
  3. Obtenez le nombre de cœurs logiques à partir d'EBX [23:16] à partir de la fonction CPUID 1
  4. Obtenir le nombre réel de cœurs de processeur non threadés
    1. Si le fournisseur == 'GenuineIntel', il s'agit de 1 plus EAX [31:26] de la fonction CPUID 4
    2. Si le fournisseur == 'AuthenticAMD', il s'agit de 1 plus ECX [7: 0] de la fonction CPUID 0x80000008

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

Voici la sortie sur quelques machines Intel différentes:

Processeur Intel (R) Core (TM) 2 Duo T7500 à 2,20 GHz:

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

Processeur Intel (R) Core (TM) 2 Quad Q8400 à 2,66 GHz:

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

Processeur Intel (R) Xeon (R) E5520 à 2,27 GHz (avec packages de processeur physique x2):

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

Processeur Intel (R) Pentium (R) 4 à 3,00 GHz:

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

Notez ceci, ne donne pas le nombre de cœurs physiques comme prévu, mais des 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 différent, comme ci-dessus. Comprendre <boost/thread.hpp> au lieu de <thread>.

17
math

Solution Windows uniquement décrite ici:

GetLogicalProcessorInformation

pour linux, fichier/proc/cpuinfo. Je n'utilise pas Linux maintenant, donc je ne peux pas vous donner plus de détails. Vous pouvez compter les instances de processeur physique/logique. Si le nombre logique est deux fois plus physique, vous avez activé HT (vrai uniquement pour x86).

14
rados

La réponse actuellement votée la plus élevée utilisant CPUID semble être obsolète. Il signale à la fois le mauvais nombre de processeurs logiques et physiques. Cela semble être confirmé par cette réponse cpuid-on-intel-i7-processors .

Plus précisément, l'utilisation de CPUID.1.EBX [23:16] pour obtenir les processeurs logiques ou CPUID.4.EAX [31:26] +1 pour obtenir les processeurs physiques avec les processeurs Intel ne donne le résultat correct sur aucun processeur Intel I avoir.

Pour Intel CPUID.Bh doit être utilisé Intel_thread/Fcore et topologie du cache . La solution ne semble pas anodine. 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 -énumération-topologie-processeur / . J'ai testé cela sur un système Intel à 80 cœurs logiques, 40 cœurs physiques et 4 sockets.

Voici le code source d'AMD http://developer.AMD.com/resources/documentation-articles/articles-whitepapers/processor-and-core-enumeration-using-cpuid/ . Il a donné le résultat correct sur mon système Intel à socket unique mais pas sur mon système à quatre connecteurs. 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 elle semble l'avoir fait), la meilleure solution est d'utiliser une bibliothèque ou un appel au système d'exploitation.

Éditer:

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 pair. Pour les systèmes avec hyper-threading, chaque ID x2APIC 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 threads doivent être liés pour que cela fonctionne. OpenMP par défaut ne lie pas les threads. Réglage export OMP_PROC_BIND=true les liera ou ils peuvent être liés dans le code comme indiqué dans thread-affinity-with-windows-msvc-and-openmp .

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

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

12
Z boson

Pour faire suite à la réponse mathématique, depuis le 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 renvoie pas le nombre de cœurs virtuels, mais il ne compte que les cœurs physiques.

Donc, un exemple serait

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

    int main()
    {
        std::cout << boost::thread::physical_concurrency();
        return 0;
    }
7
Harry

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 sous Windows. Le code suivant renverra le nombre de processeurs physiques. 4 dans le cas d'un processeur 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
}
6
pneveu

En rassemblant des idées et des concepts à partir de certaines des idées ci-dessus, j'ai trouvé cette solution. Veuillez critiquer.

//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 systèmes d'exploitation, la fonction standard "Get core count" renvoie le nombre de cœurs logiques. Mais pour obtenir le nombre de cœurs physiques, nous devons d'abord détecter si le CPU a un hyper threading ou non.

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 nombre de cœurs logiques, maintenant pour obtenir les résultats escomptés, nous devons d'abord vérifier si l'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 de CPU Intel avec hyper threading qui ne fera que hyper threader un cœur (du moins pas d'après ce que j'ai lu). Cela nous permet de trouver que c'est une façon vraiment indolore. Si l'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 cœur. Cela signifie que 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
Pnelego

Sous OS X, vous pouvez lire ces valeurs dans 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 d'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
2

Sous Windows, GetLogicalProcessorInformation et GetLogicalProcessorInformationEx sont disponibles pour Windows XP SP3 ou plus ancien et Windows 7+ respectivement. La différence est que GetLogicalProcessorInformation ne prend pas en charge les configurations avec plus de 64 cœurs logiques, ce qui peut être important pour les configurations de serveur, mais vous pouvez toujours revenir à GetLogicalProcessorInformation si vous êtes sous XP. Exemple d'utilisation de GetLogicalProcessorInformationEx ( source ):

PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL;
BOOL rc;
DWORD length = 0;
DWORD offset = 0;
DWORD ncpus = 0;
DWORD prev_processor_info_size = 0;
for (;;) {
    rc = psutil_GetLogicalProcessorInformationEx(
            RelationAll, buffer, &length);
    if (rc == FALSE) {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            if (buffer) {
                free(buffer);
            }
            buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length);
            if (NULL == buffer) {
                return NULL;
            }
        }
        else {
            goto return_none;
        }
    }
    else {
        break;
    }
}
ptr = buffer;
while (offset < length) {
    // Advance ptr by the size of the previous
    // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct.
    ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)\
        (((char*)ptr) + prev_processor_info_size);

    if (ptr->Relationship == RelationProcessorCore) {
        ncpus += 1;
    }

    // When offset == length, we've reached the last processor
    // info struct in the buffer.
    offset += ptr->Size;
    prev_processor_info_size = ptr->Size;
}

free(buffer);
if (ncpus != 0) {
    return ncpus;
}
else {
    return NULL;
}

return_none:
if (buffer != NULL)
    free(buffer);
return NULL;

Sous Linux, analyser /proc/cpuinfo pourrait aider.

0
Vladislav Toncharov

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
ostrokach

Je ne sais pas que les trois exposent les informations de la même manière, mais si vous pouvez supposer en toute sécurité que le noyau NT rapportera des informations sur le périphérique selon la norme POSIX (pour laquelle NT est censé prendre en charge), alors vous pouvez travailler la norme.

Cependant, la gestion différente des appareils est souvent citée comme l'un des obstacles au développement multiplateforme. Au mieux, j'implémenterais cela comme trois volets de logique, je n'essaierais pas d'écrire un morceau de code pour gérer toutes les plates-formes de manière uniforme.

Ok, tout cela suppose C++. Pour ASM, je suppose que vous ne fonctionnerez que sur des processeurs x86 ou AMD64? Vous aurez toujours besoin de deux chemins de branche, un pour chaque architecture, et vous devrez tester Intel séparément d'AMD (IIRC), mais dans l'ensemble, vous vérifiez simplement le CPUID. C'est ça que vous essayez de trouver? Le CPUID d'ASM sur les processeurs de la famille Intel/AMD?

0
jcolebrand