Опубликован: 15.04.2008 | Доступ: свободный | Студентов: 1558 / 544 | Оценка: 4.36 / 4.02 | Длительность: 06:55:00
ISBN: 978-5-94774-857-4
Специальности: Программист
Лекция 4:

Дополнительные возможности OpenMP

< Лекция 3 || Лекция 4: 12 || Лекция 5 >

Устаревшая конструкция передачи данных в директиве 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 work
4.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   3
4.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.

< Лекция 3 || Лекция 4: 12 || Лекция 5 >