[МУЗЫКА]
[МУЗЫКА]
[МУЗЫКА] Здравствуйте!
Я рад, что вы добрались до этой лекции,
потому что сейчас мы уже начнем непосредственно изучать директивы
параллельного программирования и рассматривать различные версии программ.
А сначала я отвечу на те вопросы, которые задавал в предыдущей лекции.
Я считаю, что одними из самых главных директив и важных, которые были в
добавленной спецификации 4.0, является директива SMPD и директива Target.
Первая директива позволяет явно указать,
что нам нужно векторизовать код, а вторая директива позволяет нам
запускать дополнительные вычисления на сопроцессорах.
Эти директивы мы рассмотрим в последующих лекциях более подробно.
А сейчас давайте перейдем к изучению директивы,
которая позволяет создавать параллельную область.
Когда на прошлой лекции мы рассматривали модель параллельного выполнения, я говорил
о том, что в момент запуска программы порождается поток-мастер (основной поток),
и он, и только он, исполняет все последовательные области программы.
Для того, чтобы создать область кодов, в которых будут операторы выполняться сразу
несколькими потоками, необходимо объявить параллельную секцию.
Для объявления параллельной секции используется директива parallel.
Синтаксис данной директивы следующий: pragma omp parallel.
И дальше идут опции, которые влияют на выполнение этой директивы или на то,
какие переменные у нас какого типа будут.
Блок кода, который должен выполняться параллельно,
должен быть заключён в фигурные скобки.
При входе в параллельную область порождается новый N − 1-поток.
Каждый поток получает свой уникальный номер, причем поток, который породил
остальные, получает номер ноль и становится основным потоком данной группы.
Остальные потоки в качестве номера получают целые числа от 1 до N − 1.
Количество потоков, выполняющих данную параллельную область, остается неизменным
от входа в параллельную область до выхода из этой параллельной области.
При выходе из параллельной области производится неявная синхронизация всех
потоков, и дополнительные потоки уничтожаются или удаляются.
Все порожденные потоки исполняют один и тот же код,
соответствующий параллельной области.
Предполагается, что в SMP-системах потоки будут распределены по различным процессам.
Однако, это, как правило,
находится в ведении операционной системы и гарантировать,
что они находятся в разных ядрах или в разных процессорах, вы не можете.
Давайте рассмотрим простой пример,
демонстрирующий использование данной директивы.
В данном примере нет сложных вычислений, она работает достаточно быстро, но
для понимания того, как работает директива parallel, этой программы достаточно.
В начале выполнения программы существует только один поток.
И поэтому команду printf у нас выполнит только поток-мастер.
И вы на экране увидите сообщение «Последовательная область 1».
Далее поток добирается до директивы parallel,
соответственно, он порождает дополнительные потоки.
В общем случае мы не знаем, сколько будет создано потоков, но предположим,
что у нас один процессор и четыре ядра.
Тогда будет порождено три дополнительных потока.
Соответственно, параллельная область в общем выполнит четыре потока.
В параллельной области у нас имеется одна команда — это вызов функции printf.
И соответственно, вы тогда на экране увидите «Параллельная область»,
и это сообщение будет выведено четыре раза,
так как у нас четыре потока и каждый из них выполнил данную команду.
Далее параллельная область заканчивается,
дополнительные потоки удаляются, и остается только один поток — поток-мастер.
Он выполняет вызов функции printf,
которая выводит у нас на экран «Последовательная область 2».
И это сообщение будет выведено только один раз.
Вот так работает директива parallel.
То есть вы создаете дополнительные потоки, они дружно выполняют код,
который находится в параллельной секции, и завершают свое выполнение.
И поток-мастер заканчивает выполнение программы.
А теперь мы с вами рассмотрим опции данной директивы.
У этой директивы достаточно много опций, но мы рассмотрим только две из них,
которые влияют на ее выполнение, на то, как выполняется параллельная секция.
А остальные мы рассмотрим с вами позже,
когда будем рассматривать модели памяти и соответствующие директивы.
А теперь давайте рассмотрим опции, которые влияют на выполнение параллельной секции.
И первая опция, которая влияет, — это опция if.
Вы ее указываете сразу после директивы parallel и в круглых скобках пишете
логическое выражение.
Если логическое выражение — истина, тогда параллельная секция создается,
а если логическое выражение ложно, тогда участок кода, относящийся к
параллельной секции, выполняется в обычном последовательном режиме.
Я думаю, вы задались вопросом: «А зачем мне это?
Я уже хочу распараллелить этот участок кода, я знаю точно».
Предположим, вы пишете универсальную функцию в библиотеку,
которой будут пользоваться остальные люди.
И она решает достаточно простую задачу — перемножение матрицы на вектор.
Один человек может вызвать вашу функцию для
перемножения матрицы большой размерности — 10 тысяч на 10 тысяч.
И тогда вам действительно нужно провести распараллеливание,
чтобы задействовать все вычислительные ресурсы.
А другой пользователь будет вызывать вашу функцию для перемножения
маленькой матрицы, размерности два на два, допустим.
И тогда дополнительные накладные расходы, которые возникают при создании
параллельной секции и при удалении потоков в конце нее,
повлияют на эффективность вашей функции.
И в этом случае нужно и проще, и лучше, выполнить код последовательный.
Это будет быстрее.
Вот для таких случаев и предназначена данная опция.
И тогда вы можете учесть все условия, когда вы вычисляете большие матрицы,
и когда производите перемножение для маленьких матриц.
А теперь давайте рассмотрим следующую опцию.
Это опция num_threads.
И в круглых скобках вы пишете выражение, в результате которого получится целое число.
Благодаря этой опции вы можете указать, сколько потоков
должны выполнять параллельную секцию, относящуюся к директиве parallel.
В общем случае, если вы не укажете эту опцию,
создастся количество потоков по умолчанию, ну, либо,
если вы укажете количество потоков с использованием «переменное окружение».
А теперь давайте еще ответим на важный вопрос: какой участок кода,
заключаемый в фигурные скобки, может являться параллельной секцией?
И такой участок кода, на самом деле, должен иметь один вход и один выход.
Не допускаются условные переходы как
из параллельной секции в другую часть программы,
так и из других частей программы нельзя переходить внутрь параллельной секции.
Если вы будете соблюдать эти правила, то никаких ошибок у вас не будет.
В этой лекции мы с вами рассмотрели основную директиву parallel
(данная директива позволяет создавать параллельную секцию) и две опции,
которые влияют на то, как параллельная секция выполняется.
Ну и в конце лекции я хочу задать очередной вопрос: вы видите код простой
функции.
Предположим, вы ее вызываете своей основной программой.
Ответьте на следующие вопросы: будет ли скомпилирована программа и если нет,
то почему?
Попробуйте это сделать без реальной компиляции программы и
реального попытки выполнения данной функции.
А на следующей лекции я дам правильный ответ.
До встречи!