2014-02-15 2 views
6

Я запустил программу OpenMP для выполнения метода Якоби, и он работал очень хорошо, 2 потока выполнялись чуть более 2х1 потока и 4 потока в 2 раза быстрее, чем 1 поток. Я чувствовал, что все работает отлично ... пока я не достиг ровно 20, 22 и 24 потоков. Я все разбив его, пока я не имел эту простой программуЯркое замедление OpenMP для определенного номера потока

#include <stdio.h> 
#include <omp.h> 

int main(int argc, char *argv[]) { 
    int i, n, maxiter, threads, nsquared, execs = 0; 
    double begin, end; 

    if (argc != 4) { 
     printf("4 args\n"); 
     return 1; 
    } 

    n = atoi(argv[1]); 
    threads = atoi(argv[2]); 
    maxiter = atoi(argv[3]); 
    omp_set_num_threads(threads); 
    nsquared = n * n; 

    begin = omp_get_wtime(); 
    while (execs < maxiter) { 

#pragma omp parallel for 
     for (i = 0; i < nsquared; i++) { 
      //do nothing 
     } 
     execs++; 
    } 
    end = omp_get_wtime(); 

    printf("%f seconds\n", end - begin); 

    return 0; 
} 

А вот некоторые выходы для различных чисел резьбы:

./a.out 500 1 1000 
    0.6765799 seconds 

./a.out 500 8 1000 
    0.0851808 seconds 

./a.out 500 20 1000 
    19.5467 seconds 

./a.out 500 22 1000 
    21.2296 seconds 

./a.out 500 24 1000 
    20.1268 seconds 

./a.out 500 26 1000 
    0.1363 seconds 

Я понял бы большой спад, если он по-прежнему для всех потоков после 20, потому что я бы понял, что это будет поток накладных расходов (хотя я чувствовал, что это немного экстремально). Но даже изменение n оставляет времена 20, 22 и 24, чтобы оставаться неизменным. Изменение maxter до 100 уменьшает его до примерно 1,9 секунды, 2,2 секунды, ..., что означает, что только создание потока вызывает замедление, а не внутреннюю итерацию.

Это как-то связано с ОС, пытающейся создать потоки, которых у нее нет? Если это что-то значит, omp_get_num_procs() возвращает 24, и это на процессорах Intel Xeon (поэтому 24 включает в себя гиперпотоки?)

Спасибо за помощь.

+0

Какой компилятор и опции вы используете? У вас есть оптимизация (например, -O3 с GCC или/O2 с MSVC)? Я не думаю, что это интересно, если оптимизация не используется. –

+0

@Zboson Я использовал GCC изначально без оптимизации (вычисление оптимизации портит мою урезанную версию), но теперь, когда я дал GCC-O3, проблема все еще возникает. Теперь я еще более смущен. –

+0

Все ли какие-либо задачи, выполняемые в системе все время? Ваша система имеет 12 физических ядер (и 24 логических ядра). Любая задача, которая составляет 100%, может сильно повлиять на два потока, а все остальные потоки должны ждать медленного завершения. –

ответ

1

Я подозреваю, что проблема связана с тем, что один поток работает на 100% на одном ядре. Из-за гиперпоточности это действительно потребляет два потока. Вам нужно найти ядро, которое вызывает это, и попытаться его исключить. Предположим, что это темы 20 и 21 (вы сказали, что начинается с 20 в вашем вопросе - вы уверены в этом?). Попробуйте что-то вроде этого

GOMP_CPU_AFFINITY = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 22 23 

Я никогда не использовал это раньше, поэтому вам, возможно, потребуется немного почитать об этом, чтобы понять это правильно. OpenMP and CPU affinity Возможно, вам потребуется сначала указать четные потоки, а затем нечетные (например, 0 2 4 ... 22 1 3 5 ...), и в этом случае я не уверен, что исключить (Edit: решение было: export GOMP_CPU_AFFINITY="0-17 20-24. См. Комментарии).

Что касается того, почему у 26 нитей не было бы проблем, я могу только догадываться. OpenMP может выбрать перенос потоков в разные ядра. Ваша система может запускать 24 логических потока. Я никогда не нашел причины устанавливать число потоков на значение больше, чем количество логических потоков (на самом деле в моем матричном коде умножения я устанавливал количество потоков в число физических ядер, поскольку гиперпоточность дает худший результат). Возможно, когда вы установите количество потоков на значение, большее, чем количество логических ядер, OpenMP решает, что при переносе потоков можно переносить. Если он переносит ваши потоки от ядра на 100%, проблема может исчезнуть. Возможно, вы сможете проверить это, отключив миграцию потоков с помощью OMP_PROC_BIND

+0

Ничего себе. Оказалось, что 18 тоже не работает, поэтому я экспортировал GOMP_CPU_AFFINITY = "0-17 20-24" '(From [here] (http: //gcc.gnu.org/onlinedocs/libgomp/GOMP_005fCPU_005fAFFINITY.html)), и он отлично работал. Так что, если я пытаюсь запустить как можно быстрее, я задаю количество потоков до 12 или 24? Большое вам спасибо, кстати. –

+1

@ RyanRossiter, я рад, что мы нашли решение. Хотелось бы, чтобы у меня был лучший ответ на 26 потоков. Можно подумать, что вы можете проверить [OMP_PROC_BIND] (http://gcc.gnu.org/onlinedocs/libgomp/GOMP_005fCPU_005fAFFINITY.html). Возможно, вы можете отключить миграцию потоков. Если вы отключите его, у 26 потоков может возникнуть такая же проблема. –

+1

@ RyanRossiter, чтобы ответить на ваш вопрос о 12 или 24 потоках (хотя в вашем случае я думаю, что это 11 или 22, поскольку одно ядро ​​уже потреблено), вы должны проверить и посмотреть. Скорее всего, ваш код будет соответствовать гиперпотоке (HT). HT работает хорошо, когда в вашем коде есть много киосков CPU (промахи кэшей, цепочки зависимостей ...), что обычно происходит (именно поэтому Intel создала гиперпоточность). Когда CPU останавливает задачи HT-переключателей, чтобы попытаться сделать что-то еще во время стойла. На самом деле сложно написать код, который не останавливает процессор. Легче использовать HT, чем оптимизировать код. –

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