Transcript MPI
MPI: A Quick-Start Guide
Information Physics
Soon-Hyung Yook
Outlook
•
•
•
MPI
MPI functions
–
–
–
–
–
–
–
–
–
–
MPI_Init
MPI_Finalize
MPI_Comm_size
MPI_Comm_rank
MPI_Send
MPI_Receive
MPI_Broadcast
MPI_Scatter
MPI_Reduce
MPI_Gather
–
–
–
–
MPI_CHAR
MPI_INT
MPI_FLOAT
MPI_DOUBLE
MPI datatypes
MPI
• MPI
– Message-passing interface
– LAM
– MPICH2
MPI Functions
• MPI_Init(&argc, &argv)
– 프로그램 실행 인수들과 함께 mpi함수들을 초기화 해준다.
• MPI_Finalize()
– mpi함수들을 종료한다. 모든 mpi함수들은 MPI_Inti() 과 M
PI_Finalize() 사이에서 호출되어야 한다.
• int MPI_Comm_rank(MPI_Comm comm, int *rank)
– comm 커뮤니케이터에서 자신의 프로세스 id를 얻는다.
• int MPI_Comm_size(MPI_Comm comm, int *size)
– comm 커뮤니케이터에서 실행되는 프로세스의 개수를 얻
는다
MPI Functions
•
int MPI_Send(void *message, int count, MPI_Datatupe datatype, int d
est, int tag, MPI_Comm comm)
– dest로 메시지를 보낸다. 아래는 각 변수들의 설명이다.
message는 보내고자 하는 메시지를 저장하고 있는 버퍼
count 는 보낼 메시지 개수
datatype는 보낼 메시지 타입
dest는 보내고자 하는 프로세스 id
tag는 보내는 메시지에 대한 꼬리표
comm은 dest와 자신의 프로세스가 속해있는 커뮤니케이터
•
int MPI_Recv(void *message, int count, MPI_Datatype datatype, int so
urce, int tag, MPI_Comm comm, MPI_Status *status)
– source로부터 메시지를 받는다. 아래는 각 변수들의 설명이다.
message는 받은 메시지를 저장할 버퍼
count는 받을 메시지 개수(받는 메시지 개수보다 작으면 에러 발생)
datatype은 받을 메시지 타입
source는 메시지를 보내주는 프로세스 id
tag는 받은 메시지를 확인하기 위한 꼬리표(MPI_Recv에서의 tag와 MPI_Send에서의
tag가 같아야 한다)
comm은 source와 자신의 프로세스가 속해있는 커뮤니케이터
status는 실제받은 데이터에 대한 정보(source와 tag)
MPI Functions
•
Collective Communication을 위한 함수
–
•
int MPI_Bcast(void *message, int count, MPI_Datatype datatype, int root, MPI_Com
m comm)
–
•
comm으로 정의된 Communicator에 속한 모든 프로세스들에게 동일한 메시지를 전송한다.
root는 메시지를 뿌려주는 프로세스 id.
root의 message에만 보내고자 하는 데이터가 들어있고, 다른 프로세스의 message는 받은 메시
지를 저장할 공간이다.
int MPI_Reduce(void *operand, void *result, int count, MPI_Datatype datatype, MPI_
Op op, int root, MPI_Comm comm)
–
•
•
Communicator란 병렬 실행의 대상이 되는 프로세스 그룹으로서 정의된다. 기본적으로 MPI프로그
램에서는 MPI_COMM_WORLD라는 기본 Communicator가 생성되는데 이는 동시에 수행되는 모든
병렬 프로세스를 포함한다. 그러나 사용자는 필요에 따라 임의의 프로세스로 구성된 새로운 Comm
unicator를 생성할 수 있기도 하다. 이러한 Communicator에서 정의되는 프로세스간의 통신을 위한
함수들을 다음에 보였다.
모든 프로세스들이 MPI_Reduce를 수행하면 모든 operand가 op에 따라 연산을 수행하고 그 결과
가 root 프로세스의 result에 저장된다.
operand는 연산이 적용될 피연산자
result는 연산결과가 저장될 버퍼
op는 어떤 종료의 연산이 수행될 것인지를 알려주는 OP-CODE
root는 연산결과가 저장될 프로세스 id
int MPI_Barrier(MPI_Comm comm)
comm Communicator에 속한 모든 프로세스들이 MPI_Barrier를 호출할때까지 block시킴
으로서 동기화를 시킨다.
MPI Functions
•
Collective Communication을 위한 함수
•
int MPI_Gather(void *send_buf, int send_count, MPI_Datatype send_type, void *recv_
buf, int recv_count, MPI_Datatype recv_type, int root, MPI_comm comm)
–
•
int MPI_Scatter(void *send_buf, int send_count, MPI_Datatype send _type, void *recv
_buf, int recv_count, MPI_Datatyp recv_type, int root,MPI_Comm comm)
–
•
send_buf의 내용을 send_count의 크기로 잘라서 모든 프로세스들의 recv_buf로 순서대로 전송한다
. MPI_Gather와는 반대의 역할
int MPI_Allgather(void *send_buf, int send_count, MPI_Dataltype send_type, void *re
cv_buf, int recv_count, MPI_Datatype recv_type, MPI_comm comm)
–
•
root프로세스는 각 프로세스들이 보내준 자료(send_buf)를 프로세스 id 순으로 root의 recv_buf에
차곡차곡 쌓는다.
MPI_gather와 마찬가지로 모든 프로세스의 send_buf의 내용을 모으나, 모든 프로세스의 recv_buf
에 저장하는 것이 다르다.
int MPI_Allreduce(void *operand, void *result, int count, MPI_Datatype datatype, M
PI_Op, MPI_Comm comm)
–
MPI_Reduce와 마찬가지로 모든 프로세스의 operand에 op연산을 적용하지만 그 결과를 모든 프로
세스의 result가 가지게 된다
MPI Datatypes
• Basic datatypes
– MPI_CHAR (char)
– MPI_INT (int)
– MPI_FLOAT (float)
– MPI_DOUBLE (double)
MPI Programming Examples
#include <mpi.h>
#define WORKTAG 1
#define DIETAG 2
/* Local functions */
static void master(void);
static void slave(void);
static unit_of_work_t get_next_work_item(void);
static void process_results(unit_result_t result);
static unit_result_t do_work(unit_of_work_t work);
int main(int argc, char **argv)
{
int myrank;
/* Initialize MPI */
MPI_Init(&argc, &argv);
/* Find out my identity in the default communicator */
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
if (myrank == 0) {
master();
}
else{ slave(); }
/* Shut down MPI */
MPI_Finalize();
return 0;
}
MPI Programming Examples
static void master(void)
{
int ntasks, rank;
unit_of_work_t work;
unit_result_t result;
MPI_Status status;
/* Find out how many processes there are in the default communicator */
MPI_Comm_size(MPI_COMM_WORLD, &ntasks);
/* Seed the slaves; send one unit of work to each slave. */
for (rank = 1; rank < ntasks; ++rank) {
/* Find the next item of work to do */
work = get_next_work_item();
/* Send it to each rank */
MPI_Send(&work,
/* message buffer */
1,
/* one data item */
MPI_INT,
/* data item is an integer */
rank,
/* destination process rank */
WORKTAG,
/* user chosen message tag */
MPI_COMM_WORLD); /* default communicator */
}
/* Loop over getting new work requests until there is no more work to be done */
work = get_next_work_item();
while (work != NULL) {
/* Receive results from a slave */
MPI_Recv(&result, /* message buffer */
1, /* one data item */
MPI_DOUBLE, /* of type double real */
MPI_ANY_SOURCE, /* receive from any sender */
MPI_ANY_TAG, /* any type of message */
MPI_COMM_WORLD, /* default communicator */
&status); /* info about the received message */
/* Send the slave a new work unit */
MPI_Send(&work, /* message buffer */
1, /* one data item */
MPI_INT, /* data item is an integer */
status.MPI_SOURCE, /* to who we just received from */
WORKTAG, /* user chosen message tag */
MPI_COMM_WORLD); /* default communicator */
/* Get the next unit of work to be done */
work = get_next_work_item();
}
/* There's no more work to be done, so receive all the outstanding results from the
slaves. */
for (rank = 1; rank < ntasks; ++rank) {
MPI_Recv(&result, 1, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COM
M_WORLD, &status);
}
/* Tell all the slaves to exit by sending an empty message with the DIETAG. */
for (rank = 1; rank < ntasks; ++rank) {
MPI_Send(0, 0, MPI_INT, rank, DIETAG, MPI_COMM_WORLD);
}
}
static void slave(void)
{
unit_of_work_t work;
unit_result_t results;
MPI_Status status;
while (1) {
/* Receive a message from the master */
MPI_Recv(&work, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
/* Check the tag of the received message. */
if (status.MPI_TAG == DIETAG) { return; }
/* Do the work */
result = do_work(work);
/* Send the result back */
MPI_Send(&result, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
MPI Programming Examples
static unit_of_work_t get_next_work_item(void)
{
/* Fill in with whatever is relevant to obtain a new unit of work suitable to be given
to a slave. */
}
static void process_results(unit_result_t result)
{
/* Fill in with whatever is relevant to process the results returned by the slave */
}
static unit_result_t do_work(unit_of_work_t work)
{
/* Fill in with whatever is necessary to process the work and generate a result */
}