Два гиганта в одной программе — Nvidia CUDA и MPI

Здравствуйте хабровчане, в этой статье я хочу рассказать о взаимодействии двух технологий MPI(mpich2) и NVIDIA CUDA. Упор я хочу сделать именно на саму структуру программы и настройку вышеописанных технологий для работы в одной программе. И так поехали…


Для удобства, я написал небольшой план по которому мы будем двигаться:
1) Подготовка системы для работы.
2) Установка библиотеки mpich2.
3) Установка NVIDIA CUDA.
4) Написание программного кода (структура программы)
5) Настройка компилятора.
6) Компиляция и запуск исполняемого файла.

Пункт 1
Возможно стоило назвать его как-то по-другому, но тем не менее. Я использую операционную систему Ubuntu 12.04 и теоретически для всей настройки достаточно пакетного менеджера — synaptic
sudo apt-get install synaptic
Или же при желании вы можете поставить все прямо из терминала.

Пункт 2
И так приступим. Что же такое MPI? Немного перефразирую википедию — это некий API который позволяет обмениваться данными между процессами, выполняющими одну задачу. Проще говоря, это одна из нескольких технологий параллельного программирования. Более подробно можно почитать на википедии. Мы будем использовать MPICH2 — библиотека в которой реализован стандарт MPI, т. к. данная библиотека является самой распространенной. Для ее установки необходимо в терминале прописать:
sudo apt-get install mpi-default-dev
sudo apt-get install mpich2
sudo apt-get install libmpich2-dev

Либо Alt+f2:
gksu synaptic


и в поиске прописать:
mpi-default-dev
mpich2
libmpich2-dev



Выбрать найденный пакет и установить его. Так же при желании вы можете установить и другие с вашей точки зрения полезные пакеты связанные с mpi или mpich2. Желательно установить документацию. Далее проверим, что у нас получилось, создадим файл test.cpp и добавим в него следующий код:
#include <mpi.h>
#include <iostream> 

int main (int argc, char* argv[])
{
    int rank, size;
    MPI_Init (&argc, &argv);
    MPI_Comm_rank (MPI_COMM_WORLD, &rank);
    MPI_Comm_size (MPI_COMM_WORLD, &size);	
	std::cout<<"\nHello Habrahabr!!"<<std::endl;
    MPI_Finalize();
    return 0;
}

Скомпилируем его:
mpic++ test.cpp -o test

Запустим:
mpirun.mpich2 -l -n 8 ./test


В итоге должно получиться что-то вроде этого:


Пункт 3
Данный процесс я описывал в своем прошлом посте.

Пункт 4
Предположим, что у нас есть папка habr, создадим в ней следующие файлы:
main.cu
head.h // здесь будут содержаться хедерные файлы.
GPU.cu // код предназначенный для гпу.
CPU.cpp // код предназначенный для процессора.

main.cu — здесь напишем простейший mpi код, который послужит для запуска программы на нескольких ядрах. В функциях gpu и cpu происходит обычное умножение, с той лишь разницей, что в функции gpu умножение происходит на видео карте.
#include "head.h"

int main(int argc, char* argv[]){
	
	int rank, size;
	int x = 9999;
	int y = 9999;
	
	MPI_Init (&argc, &argv);
	MPI_Comm_rank (MPI_COMM_WORLD, &rank);//номер текущего процесса
    MPI_Comm_size (MPI_COMM_WORLD, &size);//число процессов
	
	int res_gpu = gpu(x, y);	
	int res_cpu = cpu(x, y);
	
	std::cout<<"res_gpu = "<<res_gpu<<std::endl;
	std::cout<<"res_cpu = "<<res_cpu<<std::endl;
	
	MPI_Finalize();
	return 0;
}


head.h — здесь опишем необходимые инклюды.
#include <iostream>
#include <mpi.h>
#include <cuda.h>

#include "CPU.cpp"
#include "GPU.cu"


GPU.cu — непосредственно код который умножает два числа на видео карточке.
#include <cuda.h>
#include <iostream>
#include <stdio.h>

__global__ void mult(int x, int y, int *res) {
	
	*res = x * y;
	
	}


int gpu(int x, int y){
	int *dev_res;	
	int res = 0;	
	cudaMalloc((void**)&dev_res, sizeof(int));	
	mult<<<1,1>>>(x, y, dev_res);	
	cudaMemcpy(&res, dev_res, sizeof(int), cudaMemcpyDeviceToHost);	
	cudaFree(dev_res);
	
	return res;
}



CPU.cpp — данный код, скорее для проверки умножения происходящего на гпу и в принципе больше полезности ни какой не несет.
int cpu(int x, int y){
        int res;
	res = x * y;
	
	return res;
}

Созданные файлы положить в папку ../src. В итоге должно получиться как-то так:


Пункт 5
Тут самое интересное, необходимо настроить компилятор nvcc для компиляции не только CUDA кода, но и MPI кода, для этого напишем небольшой make файл:
CXX        = nvcc
LD         = $(CXX)

LIBS_PATH = -L/usr/lib 
LIBS =  -lmpi -lopa -lmpl -lrt -lcr -lpthread
INCLUDE_PATH = -I/usr/lib/mpich2/include/
FLAGS = -g
TARGET = "/home/relaps/habr/src/main.cu"
OBIN = "/home/relaps/habr/bin/cuda&mpi"

all: $(TARGET)

$(TARGET):
	$(LD) $(INCLUDE_PATH) $(FLAGS) $(TARGET) -o $(OBIN) $(LIBS_PATH) $(LIBS)

И так теперь необходимо всего лишь перейти в папку с проектом и собрать его.

Пункт 6
Здесь я думаю комментарии излишни на скриншоте все видно.
Собираем:


Запускаем, получается что-то вроде этого:


Ну собственно на этом работа закончена, в итоге у нас получилась программа в которой задействованы и видео карточка, и несколько ядер процессора. Конечно пример с умножением двух чисел представленный в данном контексте, совершенно не актуален для данных технологий, но повторюсь — я ставил перед собой задачу показать, что mpi и cuda могут вполне сосуществовать в одной программе(считаю, что код элементарный за исключением директив cuda и mpi, поэтому особо его не пояснял). Естественно, если взять более сложную программу, то возникает сразу много нюансов, начиная от структуры кластера и т.п., продолжать тут можно долго, каждая такая программа требует индивидуального рассмотрения. Но в итоге оно того стоит.

p.s. В следующем посте, вероятнее всего, попытаюсь рассказать про основы nvidia cuda.

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 21

    0
    Это ведь не работает с ATI? А можете дать наводку?
      0
      Нет, с ATI работать не будет, технология CUDA только! для графических плат Nvidia. Если говорить про ATI я бы смотрел в сторону OpenCL, но как оно будет работать с mpi я без понятия.
        +1
        У ATI есть аналог CUD'ы: ATI Stream.

        Ну и OpenCL поддерживается и там и там.
          +2
          ATI Stream всё-таки достаточно устарел, сейчас развивается OpenCL, и лучше использовать именно его. Причём это будет работать практически везде: CPU, GPU разных производителей в отличие от CUDA и ATI Stream.
        0
        Интересно. По идее, используя MPI можно и нормальный вычислительный кластер на видеокартах сделать?
          +1
          Отчасти, гибридный MPI+CUDA код активно используется на современных кластерах, включающих в свой состав GPU.

          Кроме того, CUDA вполне адекватно взаимодействует и с OpenMP. А можно и с тем, и с другим. «Даёшь кентавров в программировании». Главное, не перестараться сильно с синхронизацией, а то потери на обмен данными превзойдут все ожидания.
            0
            Изначально планировалось запустить проект на tesla c2070 и какой-то кластер там был, но в конце-концов что-то там не срослось и доступа к нему нет, поэтому жду машину поинтересней. Ну а так вообще — да, в той же c2070 используются видюхи уровня gtx480.
              0
              Извините, промахнулся комментарием:( читайте ниже.
              0
              Хм… прочитав про MPI+CUDA подумал что далее будет про как минимум два компа, а тут опять всё локально…
              Как теория вполне сойдёт, а для практики маловато.
                +1
                Да, было бы интересно почитать опыт запуска на нескольких компах с разными карточками — там уже немного хитрее.
                  0
                  Тут скорее интереснее был бы mpi код, т.к. в cuda можно установить device который будет использоваться.
                +1
                > Упор я хочу сделать именно на саму структуру программы и настройку вышеописанных технологий для работы в одной программе.

                Как-то слабо. Статья в двух словах: сначала следуйте мануалу по вашей реализации MPI, затем мануалу по CUDA.

                Я ожидал рассказ про прямое копирование данных между памятью разных карт при помощи MPI и прочие классные штуки.
                  +2
                  Возможно расскажу ближе к новому году, когда у нас запустят гибридный суперкомпьютер, самому интересно.
                  0
                  А чем этот метод принципиально лучше OpenCL? Там ведь код для CPU и GPU пишется и запускается единообразно.
                    0
                    Кстати, несмотря на то, что OpenCL запускается и там, и там, ядра часто пишутся отдельные для CPU и GPU (если вообще используется CPU). Это связано с различными оптимизациями для этих устройств, код написанный для GPU будет значительно медленнее работать на CPU (по сравнению с оптимизированным для CPU). Ну а раз всё равно писать отдельный код, то можно воспользоваться и (субъективно) более удобными инструментами, чем OpenCL.
                      0
                      Я к тому, что используются одни и те же средства, а не 2 разные по идеологии и методам библиотеки. Оптимизации — это отдельная проблема, ведь часто приходится учитывать особенности каждой модели GPU и соответственно корректировать код.

                      Удобство — это когда как. Вот для научных вычислений лучше бывает архитектура shared memory. Например, для расчётов методом «частицы-в-ячейках» для каждого потока требуются параметры всех частиц, поэтому message-passing не очень катит.
                      0
                      Ну считается, что на cude проще писать, это конечно не настолько обычный «с», как ее везде пиарят, но по сравнению с OpenCL небо и земля. Конечно это лично мое мнение, т.к. я с OpenCL мало знаком, знаю что это такое и видел код, как-то даже хотел попробовать писать на нем, а потом вот на cuda натолкнулся и в принципе доволен, наверное одним из главных минусов можно считать — наличие данной технологии только на gpu nvidia. Пожалуй в следующей статье расскажу про cuda, в принципе для ее понимания достаточно неплохо разбираться в с/c++.
                        0
                        Мне лично кажется, что отсутствие кроссплатформенности — это ощутимый недостаток. Я пишу как раз на OpenCL из-за этого, а также потому, что OpenCL действительно весьма и весьма близок к С. Вплоть до того, что необходимо изучить только пару специфичных функций типа получения номера потока и иметь под рукой спецификацию.
                          0
                          Собственно OpenCL для этого и был создан Khronos Group (разрабатывающая OpenGL, и куда входят и Nvidia, и AMD, и Intel и прочие): чтобы унифицировать программный интерфейс использования видеокарт для параллельных вычислений.

                          OpenCL сложнее чем CUDA, требует больше кода, но если нужна кроссплатформенность — то лучше использовать именно его.
                      +2
                      Недавно начал осваивать эту технологию, так она связана с темой моей будущей диссертации. Вообще русскоязычной документации, по CUDA, CUDA+MPI, CUDA+MPI+OpenMP, как оказалось, валом. Кому интересно:
                      https://sites.google.com/site/cudacsmsusu/file-cabinet
                      http://tesla.parallel.ru/wordpress/?p=153 — здесь можно попросить логин на тестовый кластер с двумя сокетами и 8-ю теслами, без системы очередей.
                        +1
                        Уточню: для доступа на тестовый кластер необходимо зарегистрироваться (наличие конкретной задачи для расчета на GPU+MPI — обязательно).

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое