Организация параллельных потоков. Часть 2 | страница 18



Мы запустили задачу на двух параллельных потоках. Однако, операционная система старается равномерно распределить вычислительную нагрузку между всеми ядрами.


Рис. 4.27. Частичная загрузка всех ядер


Задание. Проведите эксперимент с частичной загрузкой всех ядер процессора, задав число потоков меньше, чем число ядер.

4.3.3. Привязка к ядрам

Привяжем параллельные потоки к конкретным ядрам процессора. Добавим в нашу программу с большой задачей несколько строк (рис. 4.28).

Вставляем дополнительные команды в начале параллельной области, когда локальная копия S получает нулевое значение (строка 11).

Определяем номер текущего потока:

omp_get_num_thread ().

Затем формируем маску для привязки потока к ядру (строка 13):

Mask = pow (2, TNum).

Возводим два в степень, равную номеру потока. Номера потоков начинаются от нуля. Маска привязки к вычислительному ядру состоит из степеней двойки. Таким образом, мы привязываем каждый поток к своему ядру.

Например, нулевой поток привязываем к нулевому ядру. Маска равна

2>0 = 1.

Первый поток привязываем к первому ядру. Маска равна

2>1 = 2.

И так далее.

Затем в строке 14 задаём привязку к ядру с помощью следующей функции:

SetThreadAffinityMask.

Для контроля выводим данные на экран (строка 15).

В нашей программе появился новый параметр директивы parallel:

private (TNum, Mask).

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


Рис. 4.28. Привязка потоков к ядрам


Компилируем.

Переходим в командное окно. Задаём два потока и запускаем программу.

Рассмотрим загрузку в Диспетчере задач (рис. 4.29).

Теперь нулевое и первое ядра загружены на 100%. Остальные ядра не участвуют в нашей работе.


Рис. 4.29. Два потока — два ядра


Задание. Составьте программу (рис. 4.28) и запускайте её с разным количеством потоков. Обратите внимание на загрузку отдельных ядер.


Рассмотрим значения маски для разного количества потоков. Убедимся, что значения маски вычисляются правильно (рис. 4.30).


Рис. 4.30. Вычисление маски


Задание. Установите разные значения количества потоков и проверьте правильность вычисления маски.

4.4. Время вычислений

Мы рассмотрели организацию параллельных вычислений. Настало время заняться временем. Нас интересует время вычислений. По нему мы сможем оценить эффективность распараллеливания.