С. антоновпараллельноепрограммированиесиспользованиемтехнологииopenMP
Скачать 0.55 Mb.
|
!$OMP , C$OMP или *$OMP , а в языке Си — указаниями препроцессору, начинающимися с #pragma omp . Ключевое слово omp ис- пользуется для того, чтобы исключить случайные совпадения имён директив OpenMP с другими именами. Формат директивы на Си/Си++: #pragma omp directive-name [ опция [[,] опция ]...] Формат директивы на Фортране: !$OMP directive-name [ опция [[,] опция ]...] С $OMP directive-name [ опция [[,] опция ]...] *$OMP directive-name [ опция [[,] опция ]...] Объектом действия большинства директив является один оператор или блок, перед которым расположена директива в исходном тексте программы. В OpenMP такие операторы или блоки называются ассоциированными с дирек- тивой. Ассоциированный блок должен иметь одну точку входа в начале и од- ну точку выхода в конце. Порядок опций в описании директивы несуществе- нен, в одной директиве большинство опций может встречаться несколько раз. После некоторых опций может следовать список переменных (для Фор- трана также и имён COMMON-блоков, заключённых в слеши), разделяемых запятыми. Все директивы OpenMP можно разделить на 3 категории: определение па- раллельной области, распределение работы, синхронизация. Каждая дирек- тива может иметь несколько дополнительных атрибутов – опций(clause). От- дельно специфицируются опции для назначения классов переменных, которые могут быть атрибутами различных директив. Чтобы задействовать функциибиблиотеки OpenMP периодавыполнения (ис- полняющей среды), в программу нужно включить заголовочный файл omp.h (для программ на языке Фортран – файл omp_lib.h или модуль omp_lib ). Если вы используете в приложении только OpenMP-директивы, включать этот файл не требуется. Функции назначения параметров имеют приоритет над соответствующими переменными окружения. 11 Все функции, используемые в OpenMP, начинаются с префикса omp_ . Если пользователь не будет использовать в программе имён, начинающихся с та- кого префикса, то конфликтов с объектами OpenMP заведомо не будет. В языке Си, кроме того, является существенным регистр символов в названиях функций. Названия функций OpenMP записываются строчными буквами. Для того чтобы программа, использующая функции OpenMP, могла оста- ваться корректной для обычного компилятора, можно прилинковать специ- альную библиотеку, которая определит для каждой функции соответствую- щую «заглушку» (stub). Например, в компиляторе Intel соответствующая библиотека подключается заданием ключа –openmp-stubs Выполнение программы После получения выполняемого файла необходимо запустить его на требуе- мом количестве процессоров. Для этого обычно нужно задать количество ни- тей, выполняющих параллельные области программы, определив значение переменной среды OMP_NUM_THREADS . Например, в Linux в командной обо- лочке bash это можно сделать при помощи следующей команды: export OMP_NUM_THREADS=n После запуска начинает работать одна нить, а внутри параллельных областей одна и та же программа будет выполняться всем набором нитей. Стандарт- ный вывод программы в зависимости от системы будет выдаваться на терми- нал или записываться в файл с предопределенным именем. Замер времени В OpenMP предусмотрены функции для работы с системным таймером. Функция omp_get_wtime() возвращает в вызвавшей нити астрономическое время в секундах (вещественное число двойной точности), прошедшее с не- которого момента в прошлом. Си: double omp_get_wtime(void); Фортран: double precision function omp_get_wtime() Если некоторый участок программы окружить вызовами данной функции, то разность возвращаемых значений покажет время работы данного участка. Гарантируется, что момент времени, используемый в качестве точки отсчета, 12 не будет изменён за время существования процесса. Таймеры разных нитей могут быть не синхронизированы и выдавать различные значения. Функция omp_get_wtick() возвращает в вызвавшей нити разрешение тайме- ра в секундах. Это время можно рассматривать как меру точности таймера. Си: double omp_get_wtick(void); Фортран: double precision function omp_get_wtick() Пример 2 иллюстрирует применение функций omp_get_wtime() и omp_get_wtick() для работы с таймерами в OpenMP. В данном примере производится замер начального времени, затем сразу замер конечного време- ни. Разность времён даёт время на замер времени. Кроме того, измеряется точность системного таймера. #include #include int main(int argc, char *argv[]) { double start_time, end_time, tick; start_time = omp_get_wtime(); end_time = omp_get_wtime(); tick = omp_get_wtick(); printf(" Время на замер времени %lf\n", end_time-start_time); printf(" Точность таймера %lf\n", tick); } Пример 2a. РаботассистемнымитаймераминаязыкеСи. program example2b include "omp_lib.h" double precision start_time, end_time, tick start_time = omp_get_wtime() end_time = omp_get_wtime() tick = omp_get_wtick() print *, " Время на замер времени ", end_time-start_time print *, " Точность таймера ", tick end Пример 2b. РаботассистемнымитаймераминаязыкеФортран. Задания • Определите, какую версию стандарта OpenMP поддерживает компиля- тор на доступной системе. • Откомпилируйте любую последовательную программу с включением опций поддержки технологии OpenMP и запустите с использованием 13 нескольких нитей. Сколько нитей будет реально исполнять операторы данной программы? • Может ли программа на OpenMP состоять только из параллельных об- ластей? Только из последовательных областей? • Чем отличается нить-мастер от всех остальных нитей? • При помощи функций OpenMP попробуйте определить время, необхо- димое для работы функции omp_get_wtick() . Хватает ли для этого точности системного таймера? 14 Параллельные и последовательные области В момент запуска программы порождается единственная нить-мастер или «основная» нить, которая начинает выполнение программы с первого опера- тора. Основная нить и только она исполняет все последовательные области программы. При входе в параллельную область порождаются дополнитель- ные нити. Директива parallel Параллельная область задаётся при помощи директивы parallel ( parallel ... end parallel ). Си: #pragma omp parallel [ опция [[,] опция ]...] Фортран: !$omp parallel [ опция [[,] опция ]...] < код параллельной области > !$omp end parallel Возможные опции: • if( условие ) – выполнение параллельной области по условию. Вхож- дение в параллельную область осуществляется только при выполнении некоторого условия. Если условие не выполнено, то директива не сра- батывает и продолжается обработка программы в прежнем режиме; • num_threads ( целочисленное выражение ) – явное задание количест- ва нитей, которые будут выполнять параллельную область; по умолча- нию выбирается последнее значение, установленное с помощью функ- ции omp_set_num_threads() , или значение переменной OMP_NUM_THREADS ; • default(private|firstprivate|shared|none) – всем переменным в параллельной области, которым явно не назначен класс, будет назначен класс private , firstprivate или shared соответственно; none озна- чает, что всем переменным в параллельной области класс должен быть назначен явно; в языке Си задаются только варианты shared или none ; • private( список ) – задаёт список переменных, для которых порожда- ется локальная копия в каждой нити; начальное значение локальных копий переменных из списка не определено; 15 • firstprivate( список ) – задаёт список переменных, для которых по- рождается локальная копия в каждой нити; локальные копии перемен- ных инициализируются значениями этих переменных в нити-мастере; • shared( список ) – задаёт список переменных, общих для всех нитей; • copyin( список ) – задаёт список переменных, объявленных как threadprivate , которые при входе в параллельную область инициали- зируются значениями соответствующих переменных в нити-мастере; • reduction( оператор : список ) – задаёт оператор и список общих пе- ременных; для каждой переменной создаются локальные копии в каж- дой нити; локальные копии инициализируются соответственно типу оператора (для аддитивных операций – 0 или его аналоги, для мульти- пликативных операций – 1 или её аналоги); над локальными копиями переменных после выполнения всех операторов параллельной области выполняется заданный оператор; оператор это: для языка Си – + , * , - , & , | , ^ , && , || , для языка Фортран – + , * , - , .and. , .or. , .eqv. , .neqv. , max , min , iand , ior , ieor ; порядок выполнения операторов не опреде- лён, поэтому результат может отличаться от запуска к запуску. При входе в параллельную область порождаются новые OMP_NUM_THREADS-1 нитей, каждая нить получает свой уникальный номер, причём порождающая нить получает номер 0 и становится основной нитью группы («мастером»). Остальные нити получают в качестве номера целые числа с 1 до OMP_NUM_THREADS-1 . Количество нитей, выполняющих данную параллель- ную область, остаётся неизменным до момента выхода из области. При вы- ходе из параллельной области производится неявная синхронизация и унич- тожаются все нити, кроме породившей. Все порождённые нити исполняют один и тот же код, соответствующий па- раллельной области. Предполагается, что в SMP-системе нити будут распре- делены по различным процессорам (однако это, как правило, находится в ве- дении операционной системы). Пример 3 демонстрирует использование директивы parallel . В результате выполнения нить-мастер напечатает текст " Последовательная область 1" , затем по директиве parallel порождаются новые нити, каждая из которых напечатает текст " Параллельная область " , затем порождённые нити завер- шаются и оставшаяся нить-мастер напечатает текст " Последовательная об - ласть 2" 16 #include int main(int argc, char *argv[]) { printf(" Последовательная область 1\n"); #pragma omp parallel { printf(" Параллельная область \n"); } printf(" Последовательная область 2\n"); } Пример 3a. ПараллельнаяобластьнаязыкеСи. program example3b print *, " Последовательная область 1" !$omp parallel print *, " Параллельная область " !$omp end parallel print *, " Последовательная область 2" end Пример 3b. ПараллельнаяобластьнаязыкеФортран. Пример 4 демонстрирует применение опции reduction . В данном примере производится подсчет общего количества порождённых нитей. Каждая нить инициализирует локальную копию переменной count значением 0 . Далее, каждая нить увеличивает значение собственной копии переменной count на единицу и выводит полученное число. На выходе из параллельной области происходит суммирование значений переменных count по всем нитям, и по- лученная величина становится новым значением переменной count в после- довательной области. #include int main(int argc, char *argv[]) { int count = 0; #pragma omp parallel reduction (+: count) { count++; printf(" Текущее значение count: %d\n", count); } printf(" Число нитей : %d\n", count); } Пример 4a. Опция reduction наязыкеСи. 17 program example4b integer count count=0 !$omp parallel reduction (+: count) count=count+1 print *, " Текущее значение count: ", count !$omp end parallel print *, " Число нитей : ", count end Пример 4b. Опция reduction наязыкеФортран. Сокращённая запись Если внутри параллельной области содержится только один параллельный цикл, одна конструкция sections или одна конструкция workshare , то мож- но использовать укороченную запись: parallel for ( parallel do для язы- ка Фортран), parallel sections или parallel workshare . При этом до- пустимо указание всех опций этих директив, за исключением опции nowait Переменные среды и вспомогательные функции Перед запуском программы количество нитей, выполняющих параллельную область, можно задать, определив значение переменной среды OMP_NUM_THREADS . Например, в Linux в командной оболочке bash это можно сделать при помощи следующей команды: export OMP_NUM_THREADS=n Значение по умолчанию переменной OMP_NUM_THREADS зависит от реализа- ции. Из программы её можно изменить с помощью вызова функции omp_set_num_threads() Си: void omp_set_num_threads(int num); Фортран: subroutine omp_set_num_threads(num) integer num Пример 5 демонстрирует применение функции omp_set_num_threads() и опции num_threads . Перед первой параллельной областью вызовом функции omp_set_num_threads(2) выставляется количество нитей, равное 2 . Но к первой параллельной области применяется опция num_threads(3) , которая указывает, что данную область следует выполнять тремя нитями. Следова- тельно, сообщение " Параллельная область 1" будет выведено тремя нитя- ми. Ко второй параллельной области опция num_threads не применяется, по- 18 этому действует значение, установленное функцией omp_set_num_threads(2) , и сообщение " Параллельная область 2" будет выведено двумя нитями. #include #include int main(int argc, char *argv[]) { omp_set_num_threads(2); #pragma omp parallel num_threads(3) { printf(" Параллельная область 1\n"); } #pragma omp parallel { printf(" Параллельная область 2\n"); } } Пример 5a. Функция omp_set_num_threads() иопция num_threads наязыкеСи. program example5b include "omp_lib.h" call omp_set_num_threads(2); !$omp parallel num_threads(3) print *, " Параллельная область 1" !$omp end parallel !$omp parallel print *, " Параллельная область 2" !$omp end parallel end Пример 5b. Функция omp_set_num_threads() иопция num_threads наязыке Фортран. В некоторых случаях система может динамически изменять количество ни- тей, используемых для выполнения параллельной области, например, для оп- тимизации использования ресурсов системы. Это разрешено делать, если пе- ременная среды OMP_DYNAMIC установлена в true . Например, в Linux в командной оболочке bash её можно установить при помощи следующей ко- манды: export OMP_DYNAMIC=true В системах с динамическим изменением количества нитей значение по умол- чанию не определено, иначе значение по умолчанию: false Переменную OMP_DYNAMIC можно установить с помощью функции omp_set_dynamic() 19 Си: void omp_set_dynamic(int num); Фортран: subroutine omp_set_dynamic(num) logical num На языке Си в качестве значения параметра функции omp_set_dynamic() за- даётся 0 или 1 , а на языке Фортран – .FALSE. или .TRUE. Если система не поддерживает динамическое изменение количества нитей, то при вызове функции omp_set_dynamic() значение переменной OMP_DYNAMIC не изме- нится. Узнать значение переменной OMP_DYNAMIC можно при помощи функции omp_get_dynamic() Си: int omp_get_dynamic(void); Фортран: logical function omp_get_dynamic() Пример 6 демонстрирует применение функций omp_set_dynamic() и |