коммунистические вычисления ALEK SILVERSTONE Спецвыпуск: Хакер, номер #065, стр. 065-070-4 if rank=0 then begin // главная ветвь end else begin // остальные ветви end; MPI_Finalize(); end. В MPI существует огромное множество функций для приема и отправки сообщений. Одни пересылают сообщение «один-одному», другие — «все-каждому» и т.д. Кроме того, существуют блокирующие и буферизированные (не блокирующие) варианты всех функций. Для начала рассмотрим две самые простые функции MPI_Send и MPI_Recv, выполняющие передачу по модели «один-одному» с автоматическим выбором типа блокировки. Так зачем нужны остальные, если, к примеру, схему передачи «один-всем» можно реализовать циклическим вызовом MPI_Send? Да, можно, но этот путь неэффективен: такие коллективные функции в MPI передают данные используя реальную архитектуру кластера, то есть используют широковещательные адреса, разделяемую память и т.д. Перечислю параметры MPI_Send по порядку: адрес буфера, в котором хранятся данные для передачи; количество данных (не размер буфера!); тип данных; ранг получателя сообщения; идентификатор сообщения; коммуникатор. Идентификатор сообщения назначается программистом и служит для удобства, так как получающий поток может фильтровать сообщения по этому полю. Тип данных нужен для их корректного преобразования, так как теоретически MPI может связывать потоки на разных платформах, имеющих разные внутренние представления данных. По этой же причине все функции приема и передачи оперируют не количеством передаваемых байт, а количеством ячеек нужного типа. MPI_Recv имеет еще один параметр, имеющий тип MPI_Status — структура, в которую помещаются свойства полученного сообщения. Кроме того, параметры «Идентификатор сообщения» и «Ранг потока» могут иметь значения MPI_ANY_TAG и MPI_ANY_SOURCE соответственно. Как нетрудно догадаться, в этих случаях функция получает пакет с любым идентификатором и от любого потока. Рекомендую делать именно так, а потом фильтровать сообщения по параметру статуса, если, конечно, не требуется четкий порядок получения сообщений. В принципе, любую программу можно написать используя только описанные функции. Однако мы стремимся к удобству, поэтому рассмотрим еще несколько. Первая — это MPI_Barrier (в качестве параметра передается коммуникатор). Функция останавливает выполнение всех потоков в этом коммуникаторе до тех пор, пока ВСЕ они не подойдут к барьеру. Очень удобно для синхронизации работы потоков. Вторая функция — MPI_Abort. Прекращает выполнение всех потоков в коммуникаторе. Параметры — коммуникатор и код ошибки. Следующая функция будет посложнее :). MPI_Bcast имеет параметры: адрес буфера; количество ячеек памяти; тип данных; корневой процесс; коммуникатор. Часто слышу от кого-нибудь, что MPI_Bcast рассылает содержимое буфера всем потокам. На самом деле эта функция синхронизирует содержимое буфера, делая его равным содержимому корневого процесса. Разница в том, что сообщение Bcast'а в другом процессе ловится не Recv'ом, а Bcact'ом! До этого момента я говорил только о передаче сообщений, содержащих лишь один тип. А как передать разнотипную структуру? Если все используемые компьютеры имеют одинаковую архитектуру, то можно писать что-то наподобие MPI_Send(&buf, sizeof(s), MPI_BYTE, ...). Если архитектуры компьютеров различаются, то предварительно данные должны быть упакованы — вместе с самими данными в буфер записывается информация об их типе. Это делается функцией MPI_Pack: |