Дополнительные возможности OpenMP
Устаревшая конструкция передачи данных в директиве parallel do в OpenMP
В OpenMP имеется возможность передачи данных из главного потока в параллельные и назад через вызов функции в параллельном режиме. Эта конструкция считается устаревшей и в настоящее время редко используется на практике. Однако далее для полноты изложения все же рассмотрим возможность применения этой конструкции в программах с OpenMP.
Пример параллельной программы, написанной на языке Fortran с использованием вызова функции для передачи данных в параллельные потоки, приведен в примере 4.3.
program orphan integer, parameter :: M=8 integer, dimension(M) :: x integer :: myid, i common/global/ x, myid, i call omp_set_num_threads (4) !$omp parallel shared (x) call work ( ) !$omp end parallel write (6, *) x end program orphan subroutine work ( ) integer, parameter :: M=8 integer, dimension (M) :: x integer, external :: omp_get_thread_num common/global/ x, myid, i !$omp do private (i, myid) ordered do i = 1. M myid = omp_get_thread_num ( ) write (6, *) "T:", myid, " i=", i x (i) = myid enddo !$omp end do return end subroutine work4.3. Пример передачи данных в директиву parallel do в OpenMP с помощью вызова подпрограммы
В этом примере в управляющей программе orphan определяется число параллельных потоков OMP_NUM_THREADS=4. Затем в параллельном режиме осуществляется обращение к подпрограмме work. В этой подпрограмме цикл do i=1,M при M=8 выполняется в параллельном режиме так: 8 петель этого цикла распределяются в последовательном порядке согласно директиве orderedпо потокам и потоки выполняются в последовательном порядке. В конце программы в последовательном структурном блоке осуществляется вывод элементов массива x. Поэтому результат работы этой программы будет таким, как показано в примере 4.4.
Т: 0 i= 1 Т: 0 i= 2 Т: 1 i= 3 Т: 1 i= 4 Т: 2 i= 5 Т: 2 i= 6 Т: 3 i= 7 Т: 3 i= 8 0 0 1 1 2 2 3 34.4. Результат работы программы, приведенной в примере 4.3
Почему же такая конструкция редко применяется на практике? Дело в том, что здесь в параллельной области программы вызывается подпрограмма work, а обращение к подпрограмме имеет почти такую же трудоемкость, как и открытие параллельных потоков. Поэтому вместо вызова подпрограмм или функций желательно подставлять их текст в определенное место программы, т. е. делать inline -подстановку. В противном случае зачастую можно получить неэффективную параллельную программу.
Функции блокировки в OpenMP
Функции блокировки играют очень важную роль при написании параллельных программ. Для последовательных программ необходимость функций блокировки обусловлена в основном многопользовательским режимом, когда на одном процессоре одновременно могут выполняться несколько заданий различных пользователей. В такой ситуации очень важно обеспечить корректный доступ к данным и их корректную модификацию со стороны различных задач или пользователей. В параллельном программировании даже одно задание порождает несколько параллельных процессов, каждый из которых может оперировать с одними и теми же данными. Для обеспечения корректности доступа к данным и их корректной модификации в OpenMP существуют специальные функции блокировки, обеспечивающие решение задач корректности доступа, записи и обновления данных.
Сначала рассмотрим функции блокировки в OpenMP в программах, написанных на языке C/C++.
void omp_init_lock ( omp_lock_t *lock )
предназначена для инициализации блокировки объекта с указателем lock. Она не возвращает никаких значений.
void omp_destroy_lock ( omp_lock_t *lock )
гарантирует, что объект с указателем lock в данный момент не инициализирован.
С помощью функции
void omp_set_lock ( omp_lock_t *lock )
можно организовать блокировку выполнения потока до тех пор, пока объект открыт для операций чтения-записи. После завершения операций чтения-записи объект разблокируется и продолжается выполнение потока.
Для открытия заблокированного объекта с указателем lock можно воспользоваться функцией
void omp_unset_lock ( omp_lock_t *lock )
int omp_test_lock ( omp_lock_t *lock )
позволяет попытаться заблокировать объект, не прерывая выполнения потока. Она возвращает значение TRUE или 1, если попытка завершилась успешно. В противном случае возвращается значение FALSE или 0.
Напомним, что при обращении к функциям блокировки в заголовке текста программы должно содержаться включение описания этих функций:
#include < omp.h >
В примере 4.5 приведен пример фрагмента программы на языке C/C++, иллюстрирующий применение функций блокировки.
#include <omp.h> void main ( ) { omp_lock_t lock; int myid; omp_init_lock (&lock); #pragma omp parallel shared(lock) private(myid) { myid = omp_get_thread_num ( ); omp_set_lock (&lock); printf ("Hello from thread %d\n", myid); omp_unset_lock (&lock); while (! omp_test_lock (&lock)) { skip (myid); } do_work (myid); omp_unset_lock (&lock); } omp_destroy_lock (&lock); }4.5. Пример фрагмента программы на языке C/C++, иллюстрирующий применение функций блокировки
В программах, написанных на языке Fortran, переменная lock должна быть целочисленной типа KIND и иметь размерность, достаточную для записи адреса.
subroutine omp_init_lock ( omp_lock_t lock )
как и в языке C/C++, используется для инициализации блокировки объекта с указателем lock.
subroutine omp_destroy_lock ( omp_lock_t lock )
обеспечивает отсутствие блокировки объекта с указателем lock в данный момент времени.
subroutine omp_set_lock ( omp_lock_t lock )
устанавливает блокировку выполнения потока до тех пор, пока объект открыт для операций чтения-записи. После завершения операций чтениязаписи объект блокируется и продолжается выполнение потока.
С помощью подпрограммы можно открыть заблокированный объект с указателем lock:
subroutine omp_unset_lock ( omp_lock_t lock )
logical omp_test_lock ( omp_lock_t *lock )
как и в языке C/C++, позволяет попытаться заблокировать объект, не прерывая выполнения потока. Она возвращает значение TRUE, если попытка завершилась успешно. В противном случае возвращается значение FALSE.