Использование OpenMP при разработке приложений для QNX 6.5 и ЗОСРВ «Нейтрино»ВведениеСтандартный подход к разработке многопоточных приложений для операционных систем QNX 6.5 и ЗОСРВ «Нейтрино» подразумевает использование программного интерфейса POSIX Threads API и соответствующих механизмов синхронизации (mutex, condvar, rwlock и т.д.). Однако существуют и другие способы эффективно задействовать ресурсы современных многопроцессорных и многоядерных систем при решении определённых классов задач. В частности, одним из таких способов является использование инструментария OpenMP.
Интерфейс OpenMPOpenMP (Open Multi-Processing) является открытым программным интерфейсом, позволяющим осуществлять многопоточное выполнение кода приложений, разрабатываемых на языках C, C++ и Fortran, без явного использования каких-либо конструкций, предусмотренных стандартами POSIX или иными API, в т.ч. специфичными для используемой ОС. С точки зрения разработчика интерфейс OpenMP представляет собой совокупность директив компилятора, а также функций и переменных окружения. Ведущую роль при разработке приложений с использованием OpenMP играет компилятор, поскольку именно он осуществляет создание многопоточного программного кода для целевой программно-аппаратной платформы с учётом заданных директив.
Поддержка со стороны инструментария разработкиПоддержка OpenMP присутствует в различных средствах разработки приложений. В частности, при разработке приложений для QNX 6.5 и ЗОСРВ «Нейтрино» используются средства, входящие в состав компилятора
gcc начиная с версии
4.8.3 (стандарт
OpenMP версии
3.1, поддерживаются архитектуры x86, ARM, ARMv7, PowerPC, SHle).
Примечание: поддержка OpenMP в стандартной версии gcc, входящей в состав QNX SDP 6.5.0 (gcc 4.4.2), отсутствует.После установки gcc 4.8.3 необходимо создать дополнительный текстовый файл
libgomp.spec следующего содержания:
Файл
/opt/qnx650/target/qnx6/usr/lib/libgomp.spec (путь к файлу указан на примере Linux SDP)
# This spec file is read by gcc when linking. It is used to specify the
# standard libraries we need in order to link with -fopenmp.
*link_gomp: -lgomp %{static: }
Пример использованияВ качестве наглядного примера можно привести приложение из статьи
"Guide into OpenMP: Easy multithreading programming for C++" (автор: Joel Yliluoma), выполняющее расчёт фрактального множества Мандельброта и вывод его псевдографической интерпретации с использованием символов ASCII.
Исходный код #include <complex>
#include <cstdio>
typedef std::complex<double> complex;
int MandelbrotCalculate(complex c, int maxiter)
{
// iterates z = z + c until |z| >= 2 or maxiter is reached,
// returns the number of iterations.
complex z = c;
int n=0;
for(; n<maxiter; ++n)
{
if( std::abs(z) >= 2.0) break;
z = z*z + c;
}
return n;
}
int main()
{
const int width = 78, height = 44, num_pixels = width*height;
const complex center(-.7, 0), span(2.7, -(4/3.0)*2.7*height/width);
const complex begin = center-span/2.0;//, end = center+span/2.0;
const int maxiter = 100000;
#ifndef NO_OPENMP
#pragma omp parallel for ordered schedule(dynamic)
#endif
for(int pix=0; pix<num_pixels; ++pix)
{
const int x = pix%width, y = pix/width;
complex c = begin + complex(x * span.real() / (width +1.0),
y * span.imag() / (height+1.0));
int n = MandelbrotCalculate(c, maxiter);
if(n == maxiter) n = 0;
#ifndef NO_OPENMP
#pragma omp ordered
#endif
{
char c = ' ';
if(n > 0)
{
static const char charset[] = ".,c8M@jawrpogOQEPGJ";
c = charset[n % (sizeof(charset)-1)];
}
std::putchar(c);
if(x+1 == width) std::puts("|");
}
}
}
Примечание: код оригинального примера модифицирован - добавлена проверка макроса (#ifndef NO_OPENMP)За поддержку многопоточности в данном коде отвечают две программные конструкции:
#pragma omp parallel for ordered schedule(dynamic) и
#pragma omp ordered. Если исключить их из данной программы, результатом будет обычное однопоточное приложение.
Сборка приложения (многопоточной и однопоточной версии соответственно)За поддержку OpenMP при сборке приложения отвечает ключ
-fopenmp:
$ i486-pc-nto-qnx6.5.0-g++-4.8.3 -static -fopenmp openmp-fractals.cpp -o fractals-openmp-multithread
$ i486-pc-nto-qnx6.5.0-g++-4.8.3 -static -DNO_OPENMP openmp-fractals.cpp -o fractals-singlethread
Сравнение производительности (на 2-ядерной системе)$ time ./fractals-openmp-multithread
8.41s real 16.12s user 0.05s system
$ time ./fractals-singlethread
16.05s real 16.03s user 0.00s system
Таким образом, реальное время выполнения приложение уменьшилось в 2 раза за счёт использования многопоточности. При этом с точки зрения операционной системы данное приложение использовало два полноценных потока:
$ pidin -p fractals-openmp-multithread
pid tid name prio STATE Blocked
503843 1 ./fractals-openmp-multithread 10o READY
503843 2 ./fractals-openmp-multithread 10o RUNNING