В OpenMP (параллельный для) в g ++ 4.7 не очень эффективен? 2,5x при 5x процессоре, Я определил, что производительность моей программы варьируется между 11 и 13 секундами (в основном всегда выше 12 секунд, а иногда даже медленнее, чем 13,4 с), примерно на 500% CPU при использовании по умолчанию #pragma omp parallel for
и ускорение OpenMP составляет всего 2,5x при 5x CPU с g++-4.7 -O3 -fopenmp
На 4-х ядерном 8-ниточном Xeon.
Я пытался с помощью schedule(static) num_threads(4)
и заметил, что моя программа всегда завершает работу с 11,5 с до 11,7 с (всегда ниже 12 с) при примерно 320% ЦП, например, работает более согласованно и использует меньше ресурсов (даже если лучший прогон на полсекунды медленнее, чем редкий выброс с гиперпоточностью).
Есть ли какой-нибудь простой OpenMP-способ для обнаружения гиперпоточности и уменьшения num_threads()
к фактическому количеству ядер процессора?
(Есть похожий вопрос, Низкая производительность из-за гиперпоточности с OpenMP: как привязать потоки к ядрам, но в моем тестировании я обнаружил, что простое сокращение с 8 до 4 потоков каким-то образом уже выполняет эту работу с g / g ++ — 4.7 на Debian 7 wheezy и Xeon E3-1240v3, так что этот вопрос касается лишь сокращения num_threads()
на количество ядер.)
Если вы работали под Linux [также предполагая x86 arch], вы можете посмотреть на /proc/cpuinfo
, Есть два поля cpu cores
а также siblings
, Первый — это количество [реальных] ядер, а второй — количество гипер-нитей. (например, в моей системе их 4 и 8 соответственно для моей четырехъядерной гиперпоточной машины).
Поскольку Linux может обнаружить это [и по ссылке в комментарии Зулана], информация также доступна из x86 cpuid
инструкция.
В любом случае для этого есть переменная окружения: OMP_NUM_THREADS
который может быть проще использовать в сочетании со скриптом запуска / оболочки
Вы можете рассмотреть одну вещь: помимо определенного количества потоков, вы можете насыщать шину памяти, и никакое увеличение потоков [или ядер] не улучшит производительность и, фактически, может снизить производительность.
Из этого вопроса: Увеличивать два целых числа с помощью CAS есть ссылка на видео-разговор с CppCon 2015, который состоит из двух частей: https://www.youtube.com/watch?v=lVBvHbJsg5Y а также https://www.youtube.com/watch?v=1obZeHnAwz4
Они около 1,5 часов каждый, но, IMO, это того стоит.
В своем выступлении докладчик [который провел большую многопоточную / многоядерную оптимизацию] говорит, что из его опыта шина / система памяти имеет тенденцию насыщаться примерно после четырех потоков.
Hyper-Threading — это реализация Intel одновременная многопоточность (SMT). Современные процессоры AMD не поддерживают SMT (в семействе микроархитектур Bulldozer есть еще что-то, что AMD называет кластерной многопоточностью, но в микроархитектуре Zen предполагается наличие SMT). OpenMP не имеет встроенной поддержки для обнаружения SMT.
Если вам нужна общая функция для обнаружения Hyper-Threading, вам необходимо поддерживать процессоры разных поколений и убедиться, что процессор является процессором Intel, а не AMD. Для этого лучше всего использовать библиотеку.
Но вы можете создать функцию с использованием OpenMP, которая работает для многих современных процессоров Intel, как я описал Вот.
Следующий код будет подсчитывать количество физических ядер на современных процессорах Intel (он работал на каждом процессоре Intel, на котором я его пробовал). Вы должны связать нити, чтобы заставить это работать. С GCC вы можете использовать
export OMP_PROC_BIND=true
в противном случае вы можете связать с кодом (что я и делаю).
Обратите внимание, что я не уверен, что этот метод надежен с VirtualBox. В VirtualBox на 4-ядерном / 8-процессорном процессоре с логическим процессором и Windows в качестве хоста и Linux в качестве догадки устанавливают количество ядер для виртуальной машины на 4, этот код сообщает о 2 ядрах, а / proc / cpuinfo показывает, что два ядра фактически являются логическими процессорами.
#include <stdio.h>
//cpuid function defined in instrset_detect.cpp by Agner Fog (2014 GNU General Public License)
//http://www.agner.org/optimize/vectorclass.zip
// Define interface to cpuid instruction.
// 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) {
#if defined (_MSC_VER) || defined (__INTEL_COMPILER) // Microsoft or Intel compiler, intrin.h included
__cpuidex(output, functionnumber, 0); // intrinsic function for CPUID
#elif defined(__GNUC__) || defined(__clang__) // use inline assembly, Gnu/AT&T syntax
int a, b, c, d;
__asm("cpuid" : "=a"(a),"=b"(b),"=c"(c),"=d"(d) : "a"(functionnumber),"c"(0) : );
output[0] = a;
output[1] = b;
output[2] = c;
output[3] = d;
#else // unknown platform. try inline assembly with masm/intel syntax
__asm {
mov eax, functionnumber
xor ecx, ecx
cpuid;
mov esi, output
mov [esi], eax
mov [esi+4], ebx
mov [esi+8], ecx
mov [esi+12], edx
}
#endif
}
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;
}
int main(void) {
printf("cores %d\n", getNumCores());
}