Используйте openMP только когда аргумент передается программе - PullRequest
0 голосов
/ 20 мая 2018

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

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

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

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

Ответы [ 3 ]

0 голосов
/ 20 мая 2018

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

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

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

и затем при компиляции вы можете передать флаг компилятору для определения макроса MP_ENABLED.В случае GCC ( и Clang ) вы должны передать -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 , который вы можете использовать вместо собственных пользовательских макросов.Это выглядит как превосходное решение, но разница в усилиях достаточно мала.

0 голосов
/ 23 мая 2018

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

Для сравнения кода 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
0 голосов
/ 20 мая 2018

Вы можете установить количество потоков во время выполнения с помощью вызова 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.

.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...