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

Переменные среды и вспомогательные функции

Перед запуском программы количество нитей, выполняющих параллельную область, можно задать, определив значение переменной среды OMP_NUM_THREADS. Значение по умолчанию переменной OMP_NUM_THREADS зависит от реализации. Из программы её можно изменить с помощью вызова функции omp_set_num_threads().

void omp_set_num_threads(int num)

Следующий пример демонстрирует применение функции omp_set_num_threads() и опции num_threads. Перед первой параллельной областью вызовом функции omp_set_num_threads(2) выставляется количество нитей, равное 2. Но к первой параллельной области применяется опция num_threads(3), которая указывает, что данную область следует выполнять тремя нитями. Следовательно, сообщение "Параллельная область 1"будет выведено тремя нитями. Ко второй параллельной области опция num_threads не применяется, поэтому действует значение, установленное функцией omp_set_num_threads(2), и сообщение "Параллельная область 2"будет выведено двумя нитями.

#include <stdio.h> 
#include <omp.h> 
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"); 
    } 
}

В некоторых случаях система может динамически изменять количество нитей, используемых для выполнения параллельной области, например, для оптимизации использования ресурсов системы. Это разрешено делать, если переменная среды OMP_DYNAMIC установлена в true. В системах с динамическим изменением количества нитей значение по умолчанию не определено, иначе значение по умолчанию: false. Переменную OMP_DYNAMIC можно установить с помощью функции omp_set_dynamic().

void omp_set_dynamic(int num)

В качестве значения параметра функции omp_set_dynamic() задаётся 0 или 1. Если система не поддерживает динамическое изменение количества нитей, то при вызове функции omp_set_dynamic() значение переменной OMP_DYNAMIC не изменится. Узнать значение переменной OMP_DYNAMIC можно при помощи функции omp_get_dynamic().

int omp_get_dynamic(void)

Следующий пример демонстрирует применение функций omp_set_dynamic() и omp_get_dynamic(). Сначала распечатывается значение, полученное функцией omp_get_dynamic() – это позволяет узнать значение переменной OMP_DYNAMIC по умолчанию. Затем при помощи функции omp_set_dynamic() переменная OMP_DYNAMIC устанавливается в true, что подтверждает выдача ещё один раз значения функции omp_get_dynamic(). Затем порождается параллельная область, выполняемая заданным количеством нитей (128). В параллельной области печатается реальное число выполняющих её нитей. Директива master позволяет обеспечить печать только процессом-мастером. В системах с динамическим изменением числа нитей выданное значение может отличаться от заданного (128).

#include <stdio.h> 
#include <omp.h> 
int main(int argc, char *argv[]) 
{ 
    printf("Значение OMP_DYNAMIC: %d\n", omp_get_dynamic()); 
    omp_set_dynamic(1); 
    printf("Значение OMP_DYNAMIC: %d\n", omp_get_dynamic()); 
    #pragma omp parallel num_threads(128) 
    { 
        #pragma omp master 
        { 
            printf("Параллельная область, %d нитей\n", 
            omp_get_num_threads()); 
        } 
    } 
}

Функция omp_get_max_threads() возвращает максимально допустимое число нитей для использования в следующей параллельной области.

int omp_get_max_threads(void)

Функция omp_get_num_procs() возвращает количество процессоров, доступных для использования программе пользователя на момент вызова. Нужно учитывать, что количество доступных процессоров может динамически изменяться.

int omp_get_num_procs(void)

Параллельные области могут быть вложенными; по умолчанию вложенная параллельная область выполняется одной нитью. Это управляется установкой переменной среды OMP_NESTED. Изменить значение переменной OMP_NESTED можно с помощью вызова функции omp_set_nested().

void omp_set_nested(int nested)

Функция omp_set_nested() разрешает или запрещает вложенный параллелизм. В качестве значения параметра задаётся 0 или 1.Если вложенный параллелизм разрешён, то каждая нить, в которой встретится описание параллельной области, породит для её выполнения новую группу нитей. Сама породившая нить станет в новой группе нитью-мастером. Если система не поддерживает вложенный параллелизм, данная функция не будет иметь эффекта.

Следующий пример демонстрирует использование вложенных параллельных областей и функции omp_set_nested(). Вызов функции omp_set_nested() перед первой частью разрешает использование вложенных параллельных областей. Для определения номера нити в текущей параллельной секции используются вызовы функции omp_get_thread_num(). Каждая нить внешней параллельной области породит новые нити, каждая из которых напечатает свой номер вместе с номером породившей нити. Далее вызов omp_set_nested() запрещает использование вложенных параллельных областей. Во второй части вложенная параллельная область будет выполняться без порождения новых нитей, что и видно по получаемой выдаче.

#include <stdio.h> 
#include <omp.h> 
int main(int argc, char *argv[]) 
{ 
    int n; 
    omp_set_nested(1); 
    #pragma omp parallel private(n) 
    { 
        n=omp_get_thread_num(); 
        #pragma omp parallel 
        { 
            printf("Часть 1, нить %d - %d\n", n, 
            omp_get_thread_num()); 
        } 
    } 
    omp_set_nested(0); 
    #pragma omp parallel private(n) 
    { 
        n=omp_get_thread_num(); 
        #pragma omp parallel 
        { 
            printf("Часть 2, нить %d - %d\n", n, 
            omp_get_thread_num()); 
        } 
    } 
}

Узнать значение переменной OMP_NESTED можно при помощи функции omp_get_nested().

int omp_get_nested(void)

Функция omp_in_parallel() возвращает 1, если она была вызвана из активной параллельной области программы.

int omp_in_parallel(void)

Следующий пример иллюстрирует применение функции omp_in_parallel(). Функция mode демонстрирует изменение функциональности в зависимости от того, вызвана она из последовательной или из параллельной области. В последовательной области будет напечатано "Последовательная область а в параллельной – "Параллельная область".

#include <stdio.h> 
#include <omp.h> 
void mode(void) 
{ 
    if(omp_in_parallel()) printf("Параллельная область\n"); 
    else printf("Последовательная область\n"); 
} 
int main(int argc, char *argv[]) 
{ 
    mode(); 
    #pragma omp parallel 
    { 
       #pragma omp master 
       { 
           mode(); 
       } 
    } 
}

Измерение времени

Возвращает затраченное время в секундах:

double omp_get_wtime(void);

Возвращает точность таймера, используемого предыдущей описанной функцией:

double omp_get_wtick(void);