MPI : 차단 vs 비 차단
MPI에서 통신 차단 및 비 차단 통신의 개념을 이해하는 데 어려움이 있습니다. 둘의 차이점은 무엇입니까? 장점과 단점은 무엇입니까?
통신 차단은 MPI_Send()
및을 사용하여 수행됩니다 MPI_Recv()
. 이러한 함수는 통신이 완료 될 때까지 반환되지 않습니다 (즉, 차단됨). 다소 단순화하면 MPI_Send()
MPI가 어딘가에 저장했거나 대상에서 수신했기 때문에 전달 된 버퍼를 재사용 할 수 있습니다. 마찬가지로 MPI_Recv()
수신 버퍼가 유효한 데이터로 채워지면 반환됩니다.
대조적으로, 비 - 블로킹 통신을 사용하여 수행 MPI_Isend()
하고 MPI_Irecv()
. 이러한 기능은 통신이 아직 완료되지 않은 경우에도 즉시 반환됩니다 (즉, 차단되지 않음). 통신이 완료되었는지 MPI_Wait()
또는 전화 MPI_Test()
를 걸어야합니다.
통신 차단은 사용하기가 다소 쉽기 때문에 충분할 때 사용됩니다. 비 차단 통신은 필요한 경우에 사용됩니다. 예를 들어를 호출 MPI_Isend()
하고 일부 계산을 수행 한 다음 수행 할 수 MPI_Wait()
있습니다. 이를 통해 계산과 통신이 겹치게되어 일반적으로 성능이 향상됩니다.
집합 적 통신 (예 : all-reduce)은 MPIv2까지의 차단 버전에서만 사용할 수 있습니다. IIRC, MPIv3는 비 차단 집단 통신을 도입합니다.
MPI의 전송 모드에 대한 간략한 개요는 여기에서 볼 수 있습니다 .
이 게시물은 약간 오래되었지만 수락 된 답변에 대해 논쟁합니다. "이러한 함수는 통신이 완료 될 때까지 반환되지 않습니다."라는 문장은 통신을 차단한다고해서 송수신 작업에 대한 핸드 셰이크가 보장되지 않기 때문에 약간 잘못된 것입니다.
먼저 알아야 할 사항 은 send에는 Standard, Buffered, Synchronous 및 Ready 의 네 가지 통신 모드가 있으며 각 모드 는 차단 및 비차 단일 수 있습니다.
보내기와 달리 수신에는 하나의 모드 만 있으며 차단 또는 비차 단일 수 있습니다 .
계속 진행하기 전에 어떤 것이 MPI_Send \ Recv 버퍼 이고 어떤 것이 시스템 버퍼 (MPI 라이브러리가 소유 한 각 프로세서의 로컬 버퍼로 통신 등급간에 데이터를 이동하는 데 사용되는 로컬 버퍼 ) 인지 명시 적으로 언급해야합니다. 그룹)
BLOCKING COMMUNICATION : 차단은 메시지가 수신자 / 대상으로 전달되었음을 의미하지 않습니다. 이는 단순히 (송신 또는 수신) 버퍼를 재사용 할 수 있음을 의미합니다. 버퍼를 재사용하려면 정보를 다른 메모리 영역에 복사하는 것으로 충분합니다. 즉, 라이브러리는 버퍼 데이터를 라이브러리의 자체 메모리 위치에 복사 한 다음 예를 들어 MPI_Send가 반환 할 수 있습니다.
MPI 표준은 전송 및 수신 작업에서 메시지 버퍼링을 분리하는 것을 매우 명확하게합니다. 일치하는 수신이 게시되지 않은 경우에도 메시지가 버퍼링되는 즉시 차단 전송이 완료 될 수 있습니다. 그러나 어떤 경우에는 메시지 버퍼링이 비용이 많이들 수 있으므로 송신 버퍼에서 수신 버퍼로 직접 복사하는 것이 효율적일 수 있습니다. 따라서 MPI 표준은 사용자가 자신의 애플리케이션에 적합한 전송 모드를 자유롭게 선택할 수 있도록 네 가지 전송 모드를 제공합니다. 각 통신 모드에서 어떤 일이 발생하는지 살펴 보겠습니다.
1. 표준 모드
에서 표준 모드, 그것은 보내는 메시지를 버퍼링 여부에 관계없이 MPI 라이브러리까지입니다. 라이브러리가 나가는 메시지를 버퍼링하기로 결정한 경우 일치하는 수신이 호출되기 전에도 전송이 완료 될 수 있습니다. 라이브러리가 버퍼링하지 않기로 결정한 경우 (성능상의 이유로 또는 버퍼 공간을 사용할 수 없기 때문에) 일치하는 수신이 게시되고 송신 버퍼의 데이터가 수신 버퍼로 이동 될 때까지 송신이 리턴되지 않습니다.
따라서 표준 모드의 MPI_Send 는 일치하는 수신이 게시되었는지 여부에 관계없이 표준 모드로 보내기가 시작될 수 있고 일치하는 수신의 발생에 따라 성공적으로 완료 될 수 있다는 점에서 로컬 이 아닙니다 (구현 된 사실로 인해 메시지가 버퍼링되는지 여부에 따라 다름).
표준 전송의 구문은 다음과 같습니다.
int MPI_Send(const void *buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm)
2. 버퍼 모드
표준 모드에서와 같이, 일치하는 수신이 게시되고 전송이 일치하는 수신이 게시되기 전에 완료 될 수 있다는 사실에 관계없이 버퍼링 된 모드에서 전송을 시작할 수 있습니다. 그러나 주된 차이점은 송신이 시작되고 일치하는 수신이 게시되지 않으면 송신 메시지 를 버퍼링 해야 한다는 사실에서 발생 합니다 . 일치하는 수신이 게시되면 버퍼링 된 전송이 수신을 시작한 프로세서와 기꺼이 랑데부 할 수 있지만, 수신이없는 경우 버퍼링 된 모드의 전송은 전송이 완료 될 수 있도록 나가는 메시지를 버퍼링해야합니다. 전체적으로 버퍼링 된 전송은 local 입니다. 이 경우 버퍼 할당은 사용자 정의이며 버퍼 공간이 부족할 경우 오류가 발생합니다.
버퍼 전송 구문 :
int MPI_Bsend(const void *buf, int count, MPI_Datatype datatype,
int dest, int tag, MPI_Comm comm)
3. 동기 모드
동기 전송 모드에서는 일치하는 수신이 게시되었는지 여부에 관계없이 전송을 시작할 수 있습니다. 그러나 전송은 일치하는 수신이 게시되고 수신자가 동기 전송으로 전송 된 메시지를 수신하기 시작한 경우에만 성공적으로 완료됩니다. 동기 전송이 완료되면 전송의 버퍼를 재사용 할 수있을뿐만 아니라 수신 프로세스가 데이터 수신을 시작했음을 나타냅니다. 송신과 수신이 모두 차단되면 통신 프로세서가 랑데부하기 전에 양쪽 끝에서 통신이 완료되지 않습니다.
동기 전송 구문 :
int MPI_Ssend(const void *buf, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm)
4. 준비 모드
이전의 세 가지 모드와 달리 준비 모드에서 보내기는 일치하는 수신이 이미 게시 된 경우에만 시작할 수 있습니다. 전송 완료는 일치하는 수신에 대해 아무것도 나타내지 않으며 단지 전송 버퍼를 재사용 할 수 있음을 나타냅니다. 준비 모드를 사용하는 송신은 일치하는 수신에 대한 추가 정보가있는 표준 모드 또는 동기 모드와 동일한 의미를 갖습니다. 통신 준비 모드가있는 올바른 프로그램은 성능 차이를 제외하고는 결과에 영향을주지 않고 동기식 전송 또는 표준 전송으로 대체 할 수 있습니다.
준비된 보내기 구문 :
int MPI_Rsend(const void *buf, int count, MPI_Datatype datatype, int dest,
int tag, MPI_Comm comm)
4 개의 차단 전송을 모두 거치면 원칙적으로 다르게 보일 수 있지만 구현에 따라 한 모드의 의미가 다른 모드와 유사 할 수 있습니다.
예를 들어 MPI_Send는 일반적으로 차단 모드이지만 구현에 따라 메시지 크기가 너무 크지 않으면 MPI_Send는 송신 버퍼에서 시스템 버퍼 ( '대부분 현대 시스템의 경우)로 나가는 메시지를 복사하고 즉시 반환합니다. 아래의 예를 살펴 보겠습니다.
//assume there are 4 processors numbered from 0 to 3
if(rank==0){
tag=2;
MPI_Send(&send_buff1, 1, MPI_DOUBLE, 1, tag, MPI_COMM_WORLD);
MPI_Send(&send_buff2, 1, MPI_DOUBLE, 2, tag, MPI_COMM_WORLD);
MPI_Recv(&recv_buff1, MPI_FLOAT, 3, 5, MPI_COMM_WORLD);
MPI_Recv(&recv_buff2, MPI_INT, 1, 10, MPI_COMM_WORLD);
}
else if(rank==1){
tag = 10;
//receive statement missing, nothing received from proc 0
MPI_Send(&send_buff3, 1, MPI_INT, 0, tag, MPI_COMM_WORLD);
MPI_Send(&send_buff3, 1, MPI_INT, 3, tag, MPI_COMM_WORLD);
}
else if(rank==2){
MPI_Recv(&recv_buff, 1, MPI_DOUBLE, 0, 2, MPI_COMM_WORLD);
//do something with receive buffer
}
else{ //if rank == 3
MPI_Send(send_buff, 1, MPI_FLOAT, 0, 5, MPI_COMM_WORLD);
MPI_Recv(recv_buff, 1, MPI_INT, 1, 10, MPI_COMM_WORLD);
}
위의 예에서 각 등급에서 어떤 일이 발생하는지 살펴 보겠습니다.
랭크 0 은 랭크 1과 랭크 2로 보내고 랭크 1과 3에서 수신하려고합니다.
랭크 1 은 랭크 0과 랭크 3으로 보내려고하고 다른 랭크에서는 아무것도받지 않습니다.
랭크 2 는 랭크 0에서 수신을 시도하고 나중에 recv_buff에서 수신 한 데이터로 일부 작업을 수행합니다.
랭크 3 은 랭크 0으로 보내고 랭크 1에서 수신하려고합니다.
Where beginners get confused is that rank 0 is sending to rank 1 but rank 1 hasn't started any receive operation hence the communication should block or stall and the second send statement in rank 0 should not be executed at all (and this is what MPI documentation stress that it is implementation defined whether or not the outgoing message will be buffered or not). In most of the modern system, such messages of small sizes (here size is 1) will easily be buffered and MPI_Send will return and execute it's next MPI_Send statement. Hence in above example, even if the receive in rank 1 is not started, 1st MPI_Send in rank 0 will return and it will execute its next statement.
In a hypothetical situation where rank 3 starts execution before rank 0, it will copy the outgoing message in the first send statement from the send buffer to a system buffer (in a modern system ;) ) and then start executing its receive statement. As soon as rank 0 finishes its two send statements and begins executing its receive statement, the data buffered in system by rank 3 is copied in the receive buffer in rank 0.
In case there's a receive operation started in a processor and no matching send is posted, the process will block until the receive buffer is filled with the data it is expecting. In this situation an computation or other MPI communication will be blocked/halted unless MPI_Recv has returned.
Having understood the buffering phenomena, one should return and think more about MPI_Ssend which has the true semantics of a blocking communication. Even if MPI_Ssend copies the outgoing message from send buffer to a system buffer (which again is implementation defined), one must note MPI_Ssend will not return unless some acknowledge (in low level format) from the receiving process has been received by the sending processor.
Fortunately MPI decided to keep things easer for the users in terms of receive and there is only one receive in Blocking communication : MPI_Recv, and can be used with any of the four send modes described above. For MPI_Recv, blocking means that receive returns only after it contains the data in its buffer. This implies that receive can complete only after a matching send has started but doesn't imply whether or not it can complete before the matching send completes.
What happens during such blocking calls is that the computations are halted until the blocked buffer is freed. This usually leads to wastage of computational resources as Send/Recv is usually copying data from one memory location to another memory location, while the registers in cpu remain idle.
NON-BLOCKING COMMUNICATION : For Non-Blocking Communication, the application creates a request for communication for send and / or receive and gets back a handle and then terminates. That's all that is needed to guarantee that the process is executed. I.e the MPI library is notified that the operation has to be executed.
For the sender side, this allows overlapping computation with communication.
For the receiver side, this allows overlapping a part of the communication overhead , i.e copying the message directly into the address space of the receiving side in the application.
In using blocking communication you must be care about send and receive calls for example look at this code
if(rank==0)
{
MPI_Send(x to process 1)
MPI_Recv(y from process 1)
}
if(rank==1)
{
MPI_Send(y to process 0);
MPI_Recv(x from process 0);
}
What happens in this case?
- Process 0 sends x to process 1 and blocks until process 1 receives x.
- Process 1 sends y to process 0 and blocks until process 0 receives y, but
- process 0 is blocked such that process 1 blocks for infinity until the two processes are killed.
It is easy.
Non-blocking means computation and transferring data can happen in the same time for a single process.
While Blocking means, hey buddy, you have to make sure that you have already finished transferring data then get back to finish the next command, which means if there is a transferring followed by a computation, computation must be after the success of transferring.
참고URL : https://stackoverflow.com/questions/10017301/mpi-blocking-vs-non-blocking
'Development Tip' 카테고리의 다른 글
흥미로운 '정확히 1 개의 인수 (2 개 제공)를 취함'Python 오류 (0) | 2020.12.07 |
---|---|
Scala 2.9의 "scala.sys.process"는 어떻게 작동합니까? (0) | 2020.12.07 |
MongoDB : 배열 내의 인덱스가 참조하는 배열의 단일 하위 요소를 어떻게 업데이트합니까? (0) | 2020.12.07 |
스토리 보드 UIViewcontroller, '사용자 정의 클래스'가 드롭 다운에 표시되지 않음 (0) | 2020.12.07 |
파이썬-부분 문자열을 기반으로 목록에서 색인 위치 찾기 (0) | 2020.12.07 |