Используйте openMP только тогда, когда программе передается аргумент

Есть ли хороший способ использовать OpenMP для распараллеливания цикла for, только если в программу передается аргумент -omp?

Это кажется невозможным, поскольку #pragma omp parallel for является директивой препроцессора и, таким образом, оценивается даже до времени компиляции, и, конечно, он достоверен только в том случае, если аргумент передается программе во время выполнения.

На данный момент я использую очень уродливое решение для достижения этой цели, которое приводит к огромному дублированию кода.

if (ompDefined) {
#pragma omp parallel for
  for(...)
    ...
}
else {
  for(...)
    ...
}

Этот stackoverflow.com/questions/4085595/conditional-pragma-omp может представлять интерес.

High Performance Mark 20.05.2018 11:02
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
611
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете установить количество потоков во время выполнения с помощью звонящийomp_set_num_threads:

#include <omp.h>

int main() 
{
    int threads = 1;

    #ifdef _OPENMP
    omp_set_num_threads(threads);
    #endif

    #pragma omp parallel for
    for(...) 
    {
        ...
    }
}

Это не совсем то же самое, что отключение OpenMP, но это остановит параллельное выполнение вычислений. Я обнаружил, что всегда рекомендуется устанавливать это с помощью переключателя командной строки (вы можете реализовать это с помощью GNU Getopt или Boost.ProgramOptions). Это позволяет легко запускать однопоточные и многопоточные тесты на одном и том же коде.

Как Владимир Ф отметил в комментариях, вы также можете установить количество потоков, установив переменную среды OMP_NUM_THREADS перед выполнением вашей программы:

gcc -Wall -Werror -pedantic -O3 -fopenmp -o test test.c 
OMP_NUM_THREADS=1
./test
unset OMP_NUM_THREADS

Наконец, вы можете отключить OpenMP во время компиляции, не предоставив GCC опцию -fopenmp. Однако вам нужно будет поставить защиту препроцессора вокруг любых строк в вашем коде, которые требуют включения OpenMP (см. Выше). Если вы хотите использовать некоторые функции, включенные в библиотеку OpenMP, без фактического включения прагм OpenMP, вы можете просто связать их с библиотекой OpenMP, заменив опцию -fopenmp на -lgomp.

Спасибо, это намного лучшее решение, чем у меня сейчас ... Тем не менее, я провожу несколько тестов, и есть довольно заметное влияние на скорость, если запустить код с использованием одного потока с OpenMP и запустить код без OpenMP .... К сожалению, это будет дать мне немного неправильные результаты.

freeDom- 20.05.2018 04:01

Разве вы не можете просто не предоставить компилятору переключатель OpenMP? Прошло некоторое время с тех пор, как я использовал OpenMP, но я думаю, что GCC включал OpenMP только в том случае, если вы передали ему параметр -fopenmp.

andypea 20.05.2018 04:10

К сожалению, GCC выдает кучу ошибок, если вы не предоставите -fopenmp для любого использования #pragma omp.

freeDom- 20.05.2018 04:23

Вы уверены, что это ошибки, а не просто предупреждения?

andypea 20.05.2018 04:28

Да, по крайней мере, с моей версией GCC и omp он тогда не компилируется.

freeDom- 20.05.2018 04:32

Кстати: Спасибо, что рассказали мне о getopt! Пока я делал все это сам, включая всю обработку ошибок ...

freeDom- 20.05.2018 04:35

Я думаю, вам нужно защитить заголовок и omp_set_num_threads(threads); с помощью макроса препроцессора _OPENMP.

jww 20.05.2018 05:09

Ах да, везде работает охрана! Большое спасибо!

freeDom- 20.05.2018 05:21

Единственный сложный случай, с которым я столкнулся, - это вопрос о том, как использовать omp_get_wtime, когда openmp отключен путем удаления -fopenmp. Таймер omp обычно лучший портативный для Windows. Я видел таймер MPI, используемый в приложениях без MPI.

tim18 20.05.2018 14:15

Гораздо более простой вариант - заменить -fopenmp на -lgomp, если вы не хотите, чтобы использовались прагмы OpenMP, но все же хотите, чтобы функции библиотеки OpenMP распознавались. Этот метод является эквивалентом заглушки переключателя компилятора OpenMP.

Gilles 20.05.2018 14:51

OMP_NUM_THREADS проще, в вашем коде вообще ничего не нужно.

Vladimir F 20.05.2018 19:14

Одним из решений было бы использование препроцессора для игнорирования инструкции прагмы, если вы не передаете компилятору дополнительный флаг.

Например, в вашем коде может быть:

#ifdef MP_ENABLED
#pragma omp parallel for
#endif
for(...)
  ...

а затем при компиляции вы можете передать компилятору флаг для определения макроса MP_ENABLED. В случае GCC (и лязг) вы должны передать -DMP_ENABLED.

Затем вы можете скомпилировать с помощью gcc как

gcc SOME_SOURCE.c -I SOME_INCLUDE.h -lomp -DMP_ENABLED -o SOME_OUTPUT

затем, когда вы хотите отключить параллелизм, вы можете внести небольшие изменения в команду компиляции, отбросив -DMP_ENABLED.

gcc SOME_SOURCE.c -I SOME_INCLUDE.h -lomp -DMP_ENABLED -o SOME_OUTPUT

Это приводит к тому, что макрос не определен, что приводит к тому, что препроцессор игнорирует прагму.

Вы также можете использовать аналогичное решение, используя вместо этого ifndef, в зависимости от того, считаете ли вы параллельное поведение по умолчанию или нет.

Обновлено: как отмечалось в некоторых комментариях, включение OMP lib определяет некоторые макросы, такие как _OPENMP, которые вы можете использовать вместо ваших собственных пользовательских макросов. Это выглядит превосходным решением, но разница в усилиях достаточно мала.

Я пробовал что-то похожее, но не подумал об использовании компилятора / Makefile для передачи флага ... Мне это кажется хорошим вариантом!

freeDom- 20.05.2018 04:57

Зачем вам добавлять требование для пользователя в -DMP_ENABLED, если -fopenmp определяет _OPENMP в соответствующих компиляторах?

jww 20.05.2018 05:07

Хороший отзыв о __OPENMP. Не уверен, что OP использует соответствующий компилятор.

Annoth 20.05.2018 17:03
Ответ принят как подходящий

Я думаю, что то, что вы ищете, можно решить с помощью Метод диспетчера ЦП.

Для сравнения кода OpenMP с кодом, отличным от OpenMP, вы можете создавать разные объектные файлы из одного и того же исходного кода, как это

//foo.c
#ifdef _OPENMP
double foo_omp() {
#else
double foo() {
#endif
  double sum = 0;
  #pragma omp parallel for reduction(+:sum)
  for(int i=0; i<1000000000; i++) sum += i%10;
  return sum;
}

Скомпилировать так

gcc -O3 -c foo.c
gcc -O3 -fopenmp -c foo.c -o foo_omp.o

Это создает два объектных файла foo.o и foo_omp.o. Затем вы можете вызвать одну из этих функций следующим образом

//bar.c
#include <stdio.h>

double foo();
double foo_omp();
double (*fp)();

int main(int argc, char *argv[]) {
  if (argc>1) {
    fp = foo_omp;
  }
  else {
    fp = foo;
  }
  double sum = fp();
  printf("sum %e\n", sum);
}

Скомпилируйте и свяжите вот так

gcc -O3 -fopenmp bar.c foo.o foo_omp.o

Затем я рассчитываю код следующим образом

time ./a.out -omp
time ./a.out

и первый случай занимает около 0,4 с, а второй - около 1,2 с в моей системе с 4 ядрами / 8 аппаратными потоками.


Вот решение, для которого нужен только один исходный файл

#include <stdio.h>

typedef double foo_type();

foo_type foo, foo_omp, *fp;

#ifdef _OPENMP
#define FUNCNAME foo_omp
#else
#define FUNCNAME foo
#endif

double FUNCNAME () {
  double sum = 0;
  #pragma omp parallel for reduction(+:sum)
  for(int i=0; i<1000000000; i++) sum += i%10;
  return sum;
}

#ifdef _OPENMP
int main(int argc, char *argv[]) {
  if (argc>1) {
    fp = foo_omp;
  }
  else {
    fp = foo;
  }
  double sum = fp();
  printf("sum %e\n", sum);
}
#endif

Скомпилировать так

gcc -O3 -c foo.c
gcc -O3 -fopenmp foo.c foo.o

Другие вопросы по теме