Молодогвардейцев 454015 Россия, Челябинская область, город Челябинск 89085842764
MindHalls logo

Настройка OpenMP в CLion и пример программы

Доброго времени суток! Продолжаем рассматривать варианты параллельного исполнения программ. Я уже рассказывал про библиотеку 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. А на сегодня у меня все, спасибо за внимание!