Разница между num_threads и omp_set_num_threads против OMP_NUM_THREADS

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

  • переменная среды OMP_NUM_THREADS
  • функция omp_set_num_threads (int)
  • num_threads (int) в #pragma omp parallel for num_threads(NB_OF_THREADS)

То, что я собрал до сих пор, первые два эквивалентны. Но как насчет третьего? Может кто-то предоставить более подробное изложение разницы, я не мог найти никакой информации в Интернете относительно разницы между 1/2 и 3.

Ответ 1

OMP_NUM_THREADS и omp_set_num_threads() не эквивалентны. Переменная окружения используется только для установки начального значения nthreads-var ICV (переменная внутреннего контроля), которая контролирует максимальное количество потоков в команде. omp_set_num_threads() может использоваться для изменения значения nthreads-var в любое время (конечно, вне каких-либо параллельных областей) и влияет на все последующие параллельные области. Поэтому установка значения, например, n, OMP_NUM_THREADS эквивалентно вызову omp_set_num_threads(n) до того, как будет обнаружена самая первая параллельная область.

Алгоритм определения количества потоков в параллельной области очень четко описан в спецификации OpenMP, которая свободно доступна на веб-сайте OpenMP:

if a num_threads clause exists

затем пусть ThreadsRequested будет значением выражения предложения num_threads;

else let ThreadsRequested = значение первого элемента nthreads-var;

Этот приоритет различных способов установки nthreads-var указан в части спецификации ICV Override Relationships:

Предложение num_threads и omp_set_num_threads() переопределяют значение переменной среды OMP_NUM_THREADS и начальное значение первого элемента ICV nthreads-var.

В переводе на человеческий язык, то есть:

  • OMP_NUM_THREADS (если присутствует) первоначально определяет количество потоков;
  • вызовы omp_set_num_threads() переопределяют значение OMP_NUM_THREADS;
  • Наличие предложения num_threads переопределяет оба других значения.

На фактическое количество используемых потоков также влияет то, включены ли динамические размеры команды (dyn-var ICV устанавливается через OMP_DYNAMIC и/или omp_set_dynamic()), и зависит от того, накладывается ли ограничение потока с помощью thread-limit-var (устанавливается через OMP_THREAD_LIMIT), а также от того, включен ли вложенный параллелизм (OMP_NESTED/omp_set_nested()) или нет.

Ответ 2

Думайте об этом как о масштабах. Вариант 3 (num_threads) устанавливает количество потоков только для текущей команды потоков. Другие параметры - глобальные/государственные настройки. Обычно я не устанавливаю количество потоков, и вместо этого я просто использую значения по умолчанию. Когда я изменяю количество потоков, это обычно только в особых случаях, поэтому я использую третий вариант, так что в следующий раз, когда я использую параллельную команду, он возвращается к глобальному (по умолчанию) настройке. См. Код ниже. После использования опции 3 следующая команда потоков возвращается к последней глобальной настройке.

#include <stdio.h>
#include <omp.h>

int main() {
    #pragma omp parallel
    {
        #pragma omp single
        {
            printf("%d\n", omp_get_num_threads());
        }
    }
    omp_set_num_threads(8);
    #pragma omp parallel
    {
        #pragma omp single
        {
            printf("%d\n", omp_get_num_threads());
        }
    }

    #pragma omp parallel num_threads(2)
    {
        #pragma omp single
        {
            printf("%d\n", omp_get_num_threads());
        }
    }

    #pragma omp parallel
    {
        #pragma omp single
        {
            printf("%d\n", omp_get_num_threads());
        }
    }       
}

4 8 2 8