Как определить количество физических процессоров/ядер в Windows, Mac и Linux

У меня есть многопоточное приложение c++, которое работает на Windows, Mac и нескольких версиях Linux.

Короче говоря: для того, чтобы он работал с максимальной эффективностью, я должен иметь возможность создавать отдельный поток для каждого физического процессора/ядра. Создание большего количества потоков, чем существует физических процессоров/ядер, значительно снижает производительность моей программы. Я уже могу правильно определить количество логических процессоров/ядер на всех трех этих платформах. Чтобы иметь возможность правильно определять количество физических процессоров/ядер, я должен определить, поддерживается ли гипертрединг И активен ли он.

Поэтому у меня вопрос: есть ли способ определить, поддерживается ли Hyper Threading и включен ли? Если да, то как именно.

Ответ 1

РЕДАКТИРОВАТЬ: это больше не соответствует 100% из-за продолжающегося искажения Intel.

Как я понимаю, вопрос заключается в том, что вы спрашиваете, как определить количество ядер процессора или потоков процессоров, которое отличается от определения количества логических и физических ядер в системе. Процессорные ядра часто не считаются физическими ядрами ОС, если у них нет собственного пакета или нет. Таким образом, ОС сообщит, что Core 2 Duo, например, имеет 1 физический и 2 логических процессора, а Intel P4 с гиперпотоками будет сообщаться точно так же, хотя 2 гиперпотока против 2 ядер процессора - это очень другой вещь производительность мудрый.

Я боролся с этим, пока я не объединил решение ниже, которое, я считаю, работает как для процессоров AMD, так и для процессоров Intel. Насколько мне известно, и я могу ошибаться, AMD еще не имеет потоков ЦП, но они предоставили возможность обнаружить их, которые, как я полагаю, будут работать на будущих процессорах AMD, которые могут иметь потоки ЦП.

Короче, это шаги с использованием инструкции CPUID:

  • Обнаружение процессора с использованием функции CPUID 0
  • Проверьте бит HTT 28 в функциях ЦП EDX от функции CPUID 1
  • Получить логический подсчет ядра из EBX [23:16] из функции CPUID 1
  • Получить фактическое количество неиспользуемых ядер процессора
    • Если поставщик == 'GenuineIntel', это 1 плюс EAX [31:26] из функции CPUID 4
    • Если поставщик == 'AuthenticAMD', это 1 плюс ECX [7: 0] из функции CPUID 0x80000008

Звучит сложно, но вот, надеюсь, независимая от платформы программа на С++, которая делает трюк:

#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, но он должен работать, поскольку инструкция CPUID действительна на машинах i686. Очевидно, что это не будет работать для PowerPC, но тогда у них также нет гиперпотоков.

Вот результат на нескольких разных компьютерах Intel:

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,66 ГГц:

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

Intel (R) Xeon (R) CPU E5520 @2,27 ГГц (с физическими пакетами процессора w/x2):

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

Intel (R) Pentium (R) 4 Процессор 3,00 ГГц:

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

Ответ 2

Обратите внимание, что это не дает количество физически ядер как предназначенных, а логических ядер.

Если вы можете использовать С++ 11 (благодаря комментарию alfC ниже):

#include <iostream>
#include <thread>

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

В противном случае, возможно, библиотека Boost является для вас вариантом. Один и тот же код, но другой, как указано выше. Включите <boost/thread.hpp> вместо <thread>.

Ответ 3

Решение для Windows только здесь:

GetLogicalProcessorInformation

для файла linux,/proc/cpuinfo. Я не использую linux теперь так не может дать вам больше деталей. Вы можете рассчитывать физических/логических процессоров. Если логическое число в два раза физически, тогда у вас включен HT (true только для x86).

Ответ 4

Текущий самый высокий голос с использованием CPUID, похоже, устарел. Он сообщает как о неправильном количестве логических, так и физических процессоров. Это подтверждается из этого ответа cpuid-on-intel-i7-processors.

В частности, использование CPUID.1.EBX [23:16] для получения логических процессоров или CPUID.4.EAX [31:26] +1 для получения физических данных с процессорами Intel не дает правильного результата на любом Процессор Intel у меня есть.

Для Intel CPUID.Bh следует использовать Intel_thread/Fcore и топологию кеша. Решение не кажется тривиальным. Для AMD необходимо другое решение.

Вот исходный код от Intel, который сообщает правильное количество физических и логических ядер, а также правильное количество сокетов https://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/. Я тестировал это на 80 логических ядрах, 40 физических ядрах, 4-х гнездовой системе Intel.

Вот исходный код для AMD http://developer.amd.com/resources/documentation-articles/articles-whitepapers/processor-and-core-enumeration-using-cpuid/. Это дало правильный результат в моей системе с одним гнездом Intel, но не в моей четырехсистеме сокета. У меня нет системы AMD для тестирования.

Я еще не расчленял исходный код, чтобы найти простой ответ (если он существует) с CPUID. Похоже, что если решение может измениться (как кажется), лучшим решением будет использование библиотеки или вызова ОС.

Edit:

Вот решение для процессоров Intel с листом 11 CPUID (Bh). Способ сделать это - это цикл над логическими процессорами и получить идентификатор x2APIC для каждого логического процессора из CPUID и подсчитать количество идентификаторов x2APIC, наименее значимый бит которого равен нулю. Для систем без гиперпоточности идентификатор x2APIC всегда будет четным. Для систем с гиперпотоком каждый 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-openmp.

Я протестировал это на своей 4-ядерной/8-HT-системе, и он вернулся 4 с и без гиперпотока в BIOS. Я также тестировал на 4-х гнездовую систему с каждым гнездом, имеющим 10 ядер /20 HT, и он возвратил 40 ядер.

Процессоры AMD или более старые процессоры Intel без листа CPUID 11 должны делать что-то другое.

Ответ 5

Чтобы перейти от математического ответа, начиная с 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;
    }

Ответ 6

Я знаю, что это старый поток, но никто не упоминал hwloc. Библиотека hwloc доступна в большинстве дистрибутивов 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
}

Ответ 7

Из сбора идей и концепций из некоторых из вышеперечисленных идей я придумал это решение. Пожалуйста, критикуйте.

//EDIT INCLUDES

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

Для почти каждой ОС стандартная функция "Получить основной счет" возвращает логический подсчет ядра. Но для того, чтобы получить счет физического ядра, мы должны сначала определить, имеет ли процессор гиперпоточность или нет.

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

Потому что нет процессора Intel с гиперпотоком, который будет только гиперпотоком по одному ядру (по крайней мере, не по тому, что я прочитал). Это позволяет нам найти, что это действительно безболезненный путь. Если доступна гиперпоточность, логические процессоры будут в два раза больше физических процессоров. В противном случае операционная система обнаружит логический процессор для каждого ядра. Значение логического и физического значений ядра будет идентичным.

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

fprintf (stdout, "LOGICAL: %i\n", logicalcpucount);
fprintf (stdout, "PHYSICAL: %i\n", physicalcpucount);

Ответ 8

В OS X вы можете прочитать эти значения из sysctl(3) (API C или утилиты командной строки с тем же именем). Страница руководства должна предоставить вам информацию об использовании. Интересны следующие клавиши:

$ 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

Ответ 9

Я не знаю, что все три отображают информацию таким же образом, но если вы можете с уверенностью предположить, что ядро ​​NT сообщит информацию об устройстве в соответствии со стандартом POSIX (на что, предположительно, поддерживает NT), вы можете отработайте этот стандарт.

Однако различие в управлении устройствами часто упоминается как один из камней преткновения для кросс-платформенной разработки. Я бы в лучшем случае реализовал это как три направления логики, я бы не стал писать один кусок кода для равномерного управления всеми платформами.

Хорошо, все, что предполагает С++. Для ASM, я полагаю, вы будете работать только на процессорах x86 или amd64? Вам все равно понадобятся два пути ветвления, по одному для каждой архитектуры, и вам нужно будет протестировать Intel отдельно от AMD (IIRC), но в целом вы просто проверяете CPUID. Это то, что вы пытаетесь найти? CPUID от ASM на процессорах семейства Intel/AMD?

Ответ 10

Это очень легко сделать в Python:

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

Возможно, вы можете посмотреть исходный код psutil, чтобы узнать, что происходит?

Ответ 11

В Windows имеются GetLogicalProcessorInformation и GetLogicalProcessorInformationEx доступные для Windows XP SP3 или более ранней версии и Windows 7+ соответственно. Разница в том, что GetLogicalProcessorInformation не поддерживает установки с более чем 64 логическими ядрами, что может быть важно для настроек сервера, но вы всегда можете вернуться к GetLogicalProcessorInformation если вы используете XP. Пример использования для 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;

В Linux может помочь синтаксический анализ /proc/cpuinfo.

Ответ 12

Вы можете использовать библиотеку libcpuid (также на GitHub - libcpuid).

Как можно увидеть на странице документации:

#include <stdio.h>
#include <libcpuid.h>

int main(void)
{
    if (!cpuid_present()) {                                                // check for CPUID presence
        printf("Sorry, your CPU does not support CPUID!\n");
        return -1;
    }

if (cpuid_get_raw_data(&raw) < 0) {                                    // obtain the raw CPUID data
        printf("Sorry, cannot get the CPUID raw data.\n");
        printf("Error: %s\n", cpuid_error());                          // cpuid_error() gives the last error description
        return -2;
}

if (cpu_identify(&raw, &data) < 0) {                                   // identify the CPU, using the given raw data.
        printf("Sorrry, CPU identification failed.\n");
        printf("Error: %s\n", cpuid_error());
        return -3;
}

printf("Found: %s CPU\n", data.vendor_str);                            // print out the vendor string (e.g. 'GenuineIntel')
    printf("Processor model is '%s'\n", data.cpu_codename);                // print out the CPU code name (e.g. 'Pentium 4 (Northwood)')
    printf("The full brand string is '%s'\n", data.brand_str);             // print out the CPU brand string
    printf("The processor has %dK L1 cache and %dK L2 cache\n",
        data.l1_data_cache, data.l2_cache);                            // print out cache size information
    printf("The processor has %d cores and %d logical processors\n",
        data.num_cores, data.num_logical_cpus);                        // print out CPU cores information

}

Как видно, data.num_cores содержит количество физических ядер ЦП.

Ответ 13

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

(вам также может потребоваться указать вашему компилятору библиотеку stdС++):

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

Насколько я знаю, OpenMP был разработан для решения таких проблем.