2010-05-25 2 views
38

У меня многопоточное приложение на C++, которое работает в Windows, Mac и нескольких Linux-вариантах.Программно определить количество физических процессоров/ядер или если гиперпоточность активна в Windows, Mac и Linux

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

Таким образом, мой вопрос заключается в том, есть ли способ определить, поддерживается ли поддержка гиперпотока И ВКЛЮЧЕНА? Если да, то как именно.

+1

Проверьте это - http://stackoverflow.com/questions/150355/programmatically-find-the-number-of-cores-on-a-machine – Secko

+1

Разве вы не задали один и тот же вопрос пару дней назад ? http://stackoverflow.com/questions/2904283/c-c-assembly-programatically-detect-if-hyper-threading-is-active-on-windows-ma –

+0

Вы отказались от этого вопроса? – jcoffland

ответ

0

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

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

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

11

только для Windows решение здесь мы проделали:

http://msdn.microsoft.com/en-us/library/ms683194

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

+0

Я поддержал это, потому что он работает. Для текстовой записи (и в случае, если ссылка в какой-то момент не работает), ссылка msdn в этом ответе использует GetLogicalProcessorInformation, которая работает нормально в большинстве * последних * версий Windows. (Источник говорит: «Windows Server 2003, Windows XP Professional x64 Edition и Windows XP с пакетом обновления 3 (SP3): в этом примере показано количество физических процессоров, а не количество активных процессорных ядер».) Эта ссылка msdn не следует путать с __cpuid, который, к сожалению, имеет не-woking-пример (на большинстве процессоров Intel после 2010 г.) – Fizz

16

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

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

#include <iostream> 
#include <thread> 

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

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

+4

Это очень простое решение, но оно не отличает аппаратные потоки, например гиперпотоки, от физических процессоров или ядер, которые, по моему мнению, являются точками этого вопроса. – jcoffland

+0

Да, вы правы, я пропустил эту деталь, так что я должен удалить свой пост? – math

+3

Не удаляйте свой пост, эта информация очень полезна. Спасибо, это помогло мне! – Salgat

24

EDIT: Это больше не на 100% правильное из-за продолжающегося befuddlement Intel.

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

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

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

  1. Detect поставщика CPU с помощью функции CPUID 0
  2. Проверить HTT укусил 28 в процессоре имеется EDX из функции CPUID 1
  3. Получить логическое основной отсчет от EBX [23:16] от функции CPUID 1
  4. Получить фактическое ядро ​​без резьбы CPU рассчитывать
    1. Если поставщик == «GenuineIn тел»это 1 плюс EAX [31:26] из функции CPUID 4
    2. Если поставщик == '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 (R) ядро ​​(TM) 2 Duo CPU T7500 @ 2.20GHz:

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

Intel (R) ядро ​​(TM) 2 Quad CPU Q8400 @ 2,66:

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

Intel (R) Xeon (R) CPU E5520 @ 2.27GHz (ж/x2 физических пакетов CPU):

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

Intel (R) Pentium (R) 4 CPU 3,00 ГГц:

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

Не хотите ли вы разработать? – jcoffland

+0

Спасибо за код jcoffland - однако, я не думаю, что код в вашем примере main() будет работать с процессорами AMD в его нынешнем виде, а __cpuid() изменился на __cpuidex() для Windows x64. Основная проблема с кодом заключается в том, что тест для поставщика == AMD находится в рамках тестирования блоков для поставщика == Intel. Часть AMD никогда не будет выполнена. Было бы здорово, если бы вы могли исправить код :) Спасибо и с уважением – 2011-10-07 16:46:29

+0

@John, хорошая точка. Кажется, я исправил это сейчас. – jcoffland

-1

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 на основе компилятора (* Никс, MacOS), вам необходимо скомпилировать с помощью:

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

(вы также должны сообщить ваш компилятор использовать STDC++ библиотека):

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

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

+0

Как это помогает при обнаружении гиперпотоков? – jcoffland

+2

Он дает только количество логических ядер. – timblechmann

+0

Этот ответ не хуже, чем пример форсирования. –

11

Нынешний самый высокий голос с использованием 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 and cache topology. Решение не кажется тривиальным. Для 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 будет иметь четную и нечетную версию.

// 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 НТ, и он возвратил 40 ядер.

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

+0

Ну, я поддержал вас, потому что это лучше, чем другие ответы, но вы не технически отвечаете на вопрос. Он только хочет знать, включен ли HT. Подсчет активных, не-HT-ядер не делает этого. Вам нужно изменить свой код, чтобы вернуть ответ «да/нет», т. Е. 'If (regs [3] & 1) ht_cores ++' и после сокращения 'ht_enabled = (ht_cores> 0)'. – Fizz

+0

Кроме того, код AMD не работает должным образом на текущих процессорах Intel в основном по той же причине, что и старое перечисление кода Intel не работает должным образом: он не допускает пробелов в пространстве идентификаторов APIC. Но это предположение [неверно для процессоров Intel, начиная с конца 2009 года или около того] (http://stackoverflow.com/a/24704156/3588161). – Fizz

+0

Спасибо за ваш ответ, это было очень полезно. У меня есть связанный с этим вопрос: если вы отключите HT в BIOS, каков тип уровня (например, бит 8:15 выходного значения в ECX), о котором сообщает лист CPUID 11 с ECX = 0? Это «SMT» или «core»? –

2

В 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 
4

Я знаю, что это старый нить, но никто не упомянул 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 
} 
+0

Пожалуйста, опишите, почему ваш код решит вопрос OP. – bcesars

+0

Я добавил дополнительную информацию. Это именно то, что искали ОП. Все другие предложения не являются многоплатформенными или работают только с определенным оборудованием. – pneveu

5

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

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

Теперь у нас есть логическое число ядер, теперь для того, чтобы получить желаемые результаты, мы должны сначала проверить, если используется Hyper-Threading, или если это даже доступно.

__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 с Hyper-Threading, которая будет только гипер нить один сердечник (по крайней мере, не от того, что я читал). Это позволяет нам найти, что это действительно безболезненный путь. Если доступна гиперпоточность, логические процессоры будут в два раза больше физических процессоров. В противном случае операционная система обнаружит логический процессор для каждого ядра. Значение логического и физического значений ядра будет одинаковым.

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

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

Я бы предложил более подробное объяснение кода. –

+0

Я могу это сделать, спасибо за отзыв, просто некоторые комментарии или еще немного? – Pnelego

+0

Я внес некоторые изменения, объяснение лучше? – Pnelego

5

Чтобы следовать дальше от ответа математика, поскольку наддув 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; 
    } 
0

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

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

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

+0

он спрашивает C++. – user5280911

Смежные вопросы