Доброго времени суток! Продолжаем рассматривать варианты параллельного исполнения программ. Я уже рассказывал про библиотеку MPI, которая позволяет создавать несколько параллельно исполняемых процессов в системе. Рассказал о базовой установке MPI, интеграции его в CLion и даже поделился своей реализацией алгоритма Флойда-Уоршелла.
Но это все о процессах, каждый из них имеет свой стек, свою область памяти, свое процессорное время, а из этого следует, что обмениваться данными они могут только с помощью функций пересылок(по сети или иным способом).
А в этой статье я расскажу о потоках(Threads). Потоки создаются внутри процесса, они имеют доступ к стеку своего процесса, могут читать и писать в область памяти процесса. Благодаря этому увеличивается эффективность параллельных алгоритмов, уменьшаются затраты ресурсов на пересылку данных. Кроме того, упрощается программирование алгоритмов, проще следить за синхронизацией данных и прочее.
Что такое OpenMP
Признанным открытым стандартном параллельного программирования на языках C/C++ и Fortran является OpenMP. Он включает в себя множество директив препроцессора, библиотечных функций и переменных окружения для реализации многопоточных программ. Более подробную информацию вы сможете найти на вики и прочих источниках, а я перейду непосредственно к настройке.
Настройка OpenMP в CLion
По аналогии с настройкой MPI, дело решается всего одной строкой в CMakeLists.txt. Дело в том, что специально устанавливать OpenMP не нужно, он по умолчанию поддерживается очень многими компиляторами, в том числе и gcc версии 4.7 и выше. На моей Ubuntu 16.04 из коробки установлен gcc версии 5.4.0, поэтому никакой головной боли я не испытал. Поэтому смело обновляйте gcc и в бой. Гордым обладателям Visual Studio стоит покопаться в настройках проекта и включить поддержку OpenMP.
Дать компилятору понять, что нужно работать с потоками можно флагом -fopenmp. Именно это мы и пропишем в CMakeLists.txt
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fopenmp")
Мы установили флаг для компилятора, по умолчанию для gcc. С такой же легкостью мы можем скрестить MPI и OpenMP, прописав правила из инструкции, ссылку на которую я давал выше. В итоге у нас получится такая настройка.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fopenmp") include_directories(/usr/include/mpi/) set(CMAKE_C_COMPILER "/usr/bin/mpicc") set(CMAKE_CXX_COMPILER "/usr/bin/mpic++")
Пример программы с MPI + OpenMP
Для демонстрации работы связки MPI и OpenMP я предлагаю написать простейшую программу. Задача: создать два процесса, в каждом по три потока так, чтобы каждый поток выводил свое случайное число. Таким образом мы убедимся, что потоки создаются именно в рамках процесса, и что все потоки имеют доступ к общей памяти процесса.
#include <iostream> #include <omp.h> #include "mpi.h" using namespace std; int main(int argc, char** argv) { MPI_Init(&argc, &argv); int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); srand(time(NULL) * rank); //Установить количество потоков omp_set_num_threads(3); //Код внутри этой директивы будет распараллелен на потоки #pragma omp parallel { //отсюда cout << "proc: " << rank << " number: " << rand() << endl; } //и до сюда MPI_Finalize(); return 0; }
Заключение
Поздравляю друзья, нам удалось распараллелить еще один «Hello world». Вообще по ощущениям, писать с помощью OpenMP гораздо приятнее. Не нужно париться над пересылками данных, синхронизациями и прочим. Но максимально эффективной все таки является связка MPI+OpenMP. А на сегодня у меня все, спасибо за внимание!