Есть ли хороший способ использовать OpenMP для распараллеливания цикла for, только если в программу передается аргумент -omp?
Это кажется невозможным, поскольку #pragma omp parallel for является директивой препроцессора и, таким образом, оценивается даже до времени компиляции, и, конечно, он достоверен только в том случае, если аргумент передается программе во время выполнения.
На данный момент я использую очень уродливое решение для достижения этой цели, которое приводит к огромному дублированию кода.
if (ompDefined) {
#pragma omp parallel for
for(...)
...
}
else {
for(...)
...
}





Вы можете установить количество потоков во время выполнения с помощью звонящий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 .... К сожалению, это будет дать мне немного неправильные результаты.
Разве вы не можете просто не предоставить компилятору переключатель OpenMP? Прошло некоторое время с тех пор, как я использовал OpenMP, но я думаю, что GCC включал OpenMP только в том случае, если вы передали ему параметр -fopenmp.
К сожалению, GCC выдает кучу ошибок, если вы не предоставите -fopenmp для любого использования #pragma omp.
Вы уверены, что это ошибки, а не просто предупреждения?
Да, по крайней мере, с моей версией GCC и omp он тогда не компилируется.
Кстати: Спасибо, что рассказали мне о getopt! Пока я делал все это сам, включая всю обработку ошибок ...
Я думаю, вам нужно защитить заголовок и omp_set_num_threads(threads); с помощью макроса препроцессора _OPENMP.
Ах да, везде работает охрана! Большое спасибо!
Единственный сложный случай, с которым я столкнулся, - это вопрос о том, как использовать omp_get_wtime, когда openmp отключен путем удаления -fopenmp. Таймер omp обычно лучший портативный для Windows. Я видел таймер MPI, используемый в приложениях без MPI.
Гораздо более простой вариант - заменить -fopenmp на -lgomp, если вы не хотите, чтобы использовались прагмы OpenMP, но все же хотите, чтобы функции библиотеки OpenMP распознавались. Этот метод является эквивалентом заглушки переключателя компилятора OpenMP.
OMP_NUM_THREADS проще, в вашем коде вообще ничего не нужно.
Одним из решений было бы использование препроцессора для игнорирования инструкции прагмы, если вы не передаете компилятору дополнительный флаг.
Например, в вашем коде может быть:
#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 для передачи флага ... Мне это кажется хорошим вариантом!
Зачем вам добавлять требование для пользователя в -DMP_ENABLED, если -fopenmp определяет _OPENMP в соответствующих компиляторах?
Хороший отзыв о __OPENMP. Не уверен, что OP использует соответствующий компилятор.
Я думаю, что то, что вы ищете, можно решить с помощью Метод диспетчера ЦП.
Для сравнения кода 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
Этот stackoverflow.com/questions/4085595/conditional-pragma-omp может представлять интерес.