Программно определять количество физических процессоров / ядер или, если гиперпоточность активна в Windows, Mac и Linux - PullRequest
41 голосов
/ 25 мая 2010

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

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

Поэтому мой вопрос заключается в том, существует ли способ определить, поддерживается ли ГИПЕРТРИПРОДАЖА И РАЗРЕШЕНО? Если да, то как именно.

Ответы [ 11 ]

25 голосов
/ 21 июня 2010

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

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

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

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

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

Звучит сложно, но есть, надеюсь, независимая от платформы программа 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;
}

На самом деле я еще не проверял это на Windows или OSX, но оно должно работать, так как инструкция CPUID действительна на машинах i686. Очевидно, что это не будет работать для PowerPC, но у них также нет гиперпотоков.

Вот вывод на нескольких разных машинах Intel:

Процессор Intel® Core ™ 2 Duo T7500 @ 2,20 ГГц:

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

Процессор Intel® R Core 2 TM Q8400 @ 2,66 ГГц:

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

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

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

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

 logical cpus: 2
    cpu cores: 1
hyper-threads: true
17 голосов
/ 27 мая 2010

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

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

#include <iostream>
#include <thread>

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

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

14 голосов
/ 26 мая 2010

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

GetLogicalProcessorInformation

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

12 голосов
/ 18 июля 2014

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

В частности, использование 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 с листом CPUID 11 (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 leaf 11 должны делать что-то другое.

7 голосов
/ 25 июня 2015

Для продолжения математического ответа, начиная с буста 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 голосов
/ 21 апреля 2015

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

//EDIT INCLUDES

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

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

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);
6 голосов
/ 02 апреля 2015

Я знаю, что это старая ветка, но никто не упомянул hwloc . Библиотека hwloc доступна в большинстве дистрибутивов Linux, а также может быть скомпилирована в Windows. Следующий код вернет количество физических процессоров. 4 для процессора 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
}
2 голосов
/ 14 августа 2014

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

$ 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
0 голосов
/ 03 июня 2016

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

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

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

0 голосов
/ 28 сентября 2012

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...