이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.25 00:18
MPI_Allreduce는 MPI 표준에서 정의된 핵심적인 집합 통신 루틴 중 하나이다. 이 함수는 통신자에 포함된 모든 프로세스가 제공한 데이터에 대해 지정된 감소 연산을 수행하고, 그 연산 결과를 모든 프로세스에게 동일하게 반환하는 기능을 담당한다.
이 함수의 주요 목적은 병렬 계산에서 모든 프로세스가 공유해야 하는 전역적인 값을 효율적으로 계산하고 배포하는 것이다. 예를 들어, 모든 프로세스가 계산한 부분 합의 총합을 구하거나, 분산된 데이터에서 전역 최대값 또는 최소값을 찾아야 할 때 사용된다. 이는 MPI_Reduce 함수가 연산 결과를 단일 루트 프로세스에만 반환하는 것과 대비되는 특징으로, 이후 MPI_Bcast를 추가로 호출할 필요 없이 한 번의 연산으로 모든 프로세스가 결과를 얻을 수 있게 한다.
MPI_Allreduce는 MPI 포럼에 의해 표준화된 MPI 표준의 일부이며, Open MPI나 MS-MPI와 같은 주요 MPI 구현체에서 지원한다. 이 함수는 MPI_Scatter나 MPI_Gather와 같은 다른 집합 통신 함수들과 함께 병렬 알고리즘의 기본 구성 요소로 널리 활용된다.
MPI_Allreduce는 MPI의 집합 통신 루틴 중 핵심 함수이다. 이 함수의 주요 기능은 통신자(communicator)에 포함된 모든 프로세스가 제공한 데이터에 대해 지정된 감소(reduction) 연산을 수행하고, 그 연산 결과를 모든 프로세스에게 반환하는 것이다. 이는 모든 참여 프로세스가 연산의 최종 결과를 동시에 얻을 수 있게 함으로써, 병렬 계산에서 전역적인 상태 정보를 공유하는 데 필수적이다.
이 함수의 주요 목적은 병렬 알고리즘에서 모든 프로세스가 공통적으로 필요로 하는 전역 값을 효율적으로 계산하고 배포하는 것이다. 대표적인 사용 사례로는 모든 프로세스가 계산한 부분 합을 모아 전체 합계를 구하거나(MPI_SUM), 분산된 데이터에서 전역 최대값(MPI_MAX)이나 최소값(MPI_MIN)을 찾아 모든 프로세스가 이를 알게 하는 경우가 있다. 표준 편차 계산이나 벡터의 내적 연산과 같이 여러 단계의 감소 연산이 필요한 문제에서도 빈번히 사용된다.
MPI_Allreduce의 동작은 MPI_Reduce 함수와 밀접한 관련이 있지만, 한 가지 결정적인 차이가 있다. MPI_Reduce는 연산 결과를 단 하나의 루트 프로세스에게만 반환하는 반면, MPI_Allreduce는 결과를 통신 그룹 내의 모든 프로세스에게 브로드캐스트한다. 따라서 MPI_Allreduce는 MPI_Reduce 연산 후에 MPI_Bcast를 수행하는 것과 논리적으로 동등한 효과를 가지며, 이는 프로그램의 편의성과 명확성을 높여준다.
이 함수는 MPI 포럼에 의해 표준화된 MPI 표준의 일부이며, Open MPI나 MS-MPI와 같은 주요 MPI 구현체에서 지원한다. MPI_Allreduce와 함께 자주 사용되는 다른 집합 통신 함수로는 데이터를 분산하는 MPI_Scatter나 모으는 MPI_Gather 등이 있다.
MPI_Allreduce 함수의 구문은 C/C++과 Fortran에서 각각 정의된다. 이 함수는 모든 프로세스가 동일한 데이터 감소 연산의 결과를 받는 집합 통신 루틴이다.
C/C++ 언어에서의 함수 원형은 다음과 같다.
```c
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)
```
여기서 sendbuf는 각 프로세스가 제공하는 입력 데이터 버퍼의 시작 주소이며, recvbuf는 감소 연산의 결과가 모든 프로세스에 저장될 출력 버퍼의 시작 주소이다. count는 버퍼에 있는 원소의 개수, datatype은 각 원소의 MPI_Datatype을 지정한다. op는 수행할 감소 연산(MPI_Op)을 나타내며, comm은 통신이 이루어지는 커뮤니케이터이다. 함수는 성공 시 MPI_SUCCESS를 반환한다.
Fortran 언어에서의 구문은 아래와 같다.
```fortran
MPI_ALLREDUCE(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER COUNT, DATATYPE, OP, COMM, IERROR
```
Fortran에서는 입력 버퍼 SENDBUF, 출력 버퍼 RECVBUF, 원소 개수 COUNT, 데이터 타입 DATATYPE, 연산 OP, 커뮤니케이터 COMM을 인자로 받으며, 오류 코드는 IERROR에 저장된다.
두 언어 모두에서 인트라커뮤니케이터를 사용할 경우, MPI_IN_PLACE를 sendbuf(또는 SENDBUF) 인자로 지정할 수 있다. 이 경우 각 프로세스는 자신의 recvbuf에 있는 데이터를 입력값으로 사용하며, 연산 결과는 동일한 버퍼를 덮어쓴다. 이는 추가적인 메모리 사용을 줄이는 인플레이스 연산을 가능하게 한다.
MPI_Allreduce 함수는 총 6개의 매개변수를 사용한다. 각 매개변수는 연산에 참여하는 데이터, 연산의 종류, 통신의 범위를 정의하는 역할을 한다.
첫 번째 매개변수 sendbuf는 각 프로세스가 감소 연산에 제공할 입력 데이터 버퍼의 시작 주소이다. 두 번째 매개변수 recvbuf는 연산 결과를 저장할 출력 버퍼의 시작 주소이다. MPI_Allreduce는 모든 프로세스가 결과를 받기 때문에 recvbuf는 모든 참여 프로세스에서 유효한 버퍼를 가리켜야 한다. sendbuf와 recvbuf는 동일한 메모리 공간을 가리킬 수 없으며, 별도의 버퍼를 사용해야 한다. 단, 인트라커뮤니케이터 내에서 sendbuf 자리에 상수 MPI_IN_PLACE를 지정하는 인플레이스(in-place) 연산이 가능하다. 이 경우 입력 데이터는 각 프로세스의 recvbuf에서 읽히며, 동일한 버퍼에 결과가 덮어써진다.
세 번째 매개변수 count는 각 프로세스가 제공하는 데이터 요소의 개수를 지정한다. 네 번째 매개변수 datatype은 각 데이터 요소의 데이터 타입을 지정하며, MPI_INT, MPI_FLOAT, MPI_DOUBLE 등 MPI 표준에 정의된 타입 핸들을 사용한다. 다섯 번째 매개변수 op는 수행할 감소 연산을 지정한다. MPI_SUM(합), MPI_PROD(곱), MPI_MAX(최대값), MPI_MIN(최소값) 등 미리 정의된 연산이나 사용자 정의 연산 핸들을 사용할 수 있다. 여섯 번째 매개변수 comm은 연산에 참여하는 프로세스들의 그룹을 정의하는 커뮤니케이터 핸들이다.
매개변수 | 역할 | 비고 |
|---|---|---|
| 입력 데이터 버퍼 시작 주소 |
|
| 결과 수신 버퍼 시작 주소 | 모든 프로세스에서 유효해야 함 |
| 데이터 요소 개수 | 음이 아닌 정수 |
| 데이터 요소의 타입 |
|
| 수행할 감소 연산 |
|
| 통신 영역 커뮤니케이터 |
|
함수의 반환 값은 연산의 성공 또는 실패를 나타내는 정수 에러 코드이다. 일반적으로 MPI_SUCCESS가 반환되며, 그렇지 않을 경우 구현체별 에러 처리 루틴을 참조해야 한다.
MPI_Allreduce의 동작 방식은 모든 프로세스가 참여하는 집합 통신으로, 지정된 감소 연산을 수행한 결과를 모든 프로세스가 동시에 받는 것이다. 이 함수는 통신자 내의 모든 프로세스가 호출해야 하며, 각 프로세스는 sendbuf에 제공한 자신의 로컬 데이터를 입력값으로 제출한다. 그런 다음 시스템은 이 모든 입력값에 대해 사용자가 지정한 연산(예: MPI_SUM, MPI_MAX)을 적용하여 하나의 전역 결과값을 계산한다. 이 계산이 완료되면, 그 결과값이 모든 프로세스의 recvbuf에 동시에 반환된다.
내부적으로 이 동작은 MPI_Reduce 연산 후에 MPI_Bcast를 수행하는 것과 논리적으로 동등하다. 즉, 먼저 한 프로세스(루트)로 모든 데이터를 모아 연산한 후, 그 결과를 다시 모든 프로세스에 브로드캐스트하는 두 단계를 거치는 것이다. 그러나 실제 MPI 구현에서는 성능 최적화를 위해 이 두 단계를 하나의 효율적인 알고리즘으로 결합하여 통신 비용을 줄이는 경우가 많다. 이를 위해 이진 트리, 버터플라이 네트워크와 같은 다양한 통신 패턴이 사용될 수 있다.
이 함수는 병렬 계산에서 모든 프로세스가 공유해야 하는 전역 상태 정보, 예를 들어 분산된 데이터의 전체 합계, 평균, 최대값, 최소값 등을 효율적으로 계산하고 동기화하는 데 핵심적이다. 또한 MPI_IN_PLACE를 사용하는 인플레이스 옵션을 지원하여, 입력 데이터를 별도의 송신 버퍼가 아닌 수신 버퍼에서 직접 읽어 연산 결과로 덮어쓸 수 있어 메모리 사용량을 줄일 수 있다.
MPI_Allreduce의 사용 예제는 병렬 계산에서 모든 프로세스가 공유해야 하는 전역 값을 효율적으로 계산하는 일반적인 패턴을 보여준다. 가장 간단한 예는 모든 프로세스가 보유한 숫자들의 합계를 구하고 그 결과를 모든 프로세스가 동일하게 받는 것이다. 각 프로세스가 로컬 합계를 계산한 후, MPI_Allreduce를 MPI_SUM 연산과 함께 호출하면 전역 합계가 모든 프로세스의 recvbuf에 반환된다. 이는 이후 계산에서 모든 프로세스가 전역 합계를 필요로 할 때, 추가적인 MPI_Bcast 호출 없이 한 번의 집합 통신으로 해결할 수 있게 한다.
보다 복잡한 예로는 분산된 데이터 집합의 표준편차를 계산하는 경우가 있다. 이 계산은 평균과 제곱 편차의 합이라는 두 번의 감소 연산을 필요로 한다. 첫 번째 단계에서 MPI_Allreduce와 MPI_SUM을 사용해 모든 숫자의 총합을 구하고, 이를 바탕으로 전역 평균을 계산한다. 이 평균은 모든 프로세스에 필요하므로 MPI_Allreduce를 사용하는 것이 적합하다. 두 번째 단계에서는 각 프로세스가 자신의 데이터와 전역 평균의 차이의 제곱을 합산한 로컬 값을 생성한 후, 이 값들을 다시 MPI_Allreduce(또는 루트 프로세스만 결과를 필요로 한다면 MPI_Reduce)를 사용해 합산하여 최종 표준편차를 도출한다.
MPI_Allreduce는 MPI_IN_PLACE를 사용하여 인플레이스 연산으로도 활용할 수 있다. 이 경우 sendbuf 인자로 MPI_IN_PLACE를 지정하고, recvbuf에는 입력 데이터이자 출력 결과가 저장될 버퍼를 지정한다. 이는 통신 버퍼의 수를 줄이고 메모리 사용을 최적화하는 데 도움이 된다. 예를 들어, 각 프로세스가 배열의 일부를 계산한 후, 해당 배열 전체의 요소별 최대값을 모든 프로세스가 알아야 할 때, 인플레이스 방식을 사용하면 효율적으로 결과를 동기화할 수 있다.
MPI_Allreduce와 MPI_Reduce는 모두 MPI의 집합 통신 함수로, 여러 프로세스에 분산된 데이터에 대해 지정된 감소 연산을 수행한다는 공통점이 있다. 그러나 두 함수의 가장 근본적인 차이는 연산 결과를 받는 프로세스의 범위에 있다. MPI_Reduce는 연산의 결과를 단 하나의 지정된 루트 프로세스만이 수신 버퍼에서 받는다. 반면, MPI_Allreduce는 연산을 수행한 후 그 결과를 통신자 내의 *모든* 프로세스에게 동일하게 분배한다. 이는 MPI_Allreduce가 내부적으로 MPI_Reduce 연산을 수행한 후 그 결과를 MPI_Bcast를 사용하여 모든 프로세스에 브로드캐스트하는 것과 동등한 효과를 가진다.
이러한 차이로 인해 두 함수의 사용 목적이 구분된다. MPI_Reduce는 최종 결과를 한 곳에서 집계하거나 출력하는 데 적합하다. 예를 들어, 병렬 계산의 최종 합계를 랭크 0 프로세스에서만 파일로 저장할 때 사용할 수 있다. 반면, MPI_Allreduce는 모든 프로세스가 감소 연산의 결과를 동시에 필요로 하는 경우에 사용된다. 대표적인 예로, 모든 프로세스가 다음 계산 단계를 위해 전역 평균값을 알아야 하는 반복법 알고리즘이나, 표준편차 계산에서 첫 번째 합계(평균 계산)를 모든 프로세스가 공유해야 할 때 활용된다.
함수의 구문에서도 이 차이가 명확히 드러난다. MPI_Reduce는 호출 시 root 매개변수를 필요로 하지만, MPI_Allreduce는 이 매개변수가 존재하지 않는다. 성능 측면에서, MPI_Allreduce는 모든 프로세스에게 결과를 전달해야 하므로 일반적으로 MPI_Reduce 단일 호출보다는 더 많은 통신 비용이 소요될 수 있다. 그러나 최신 MPI 구현에서는 단일 브로드캐스트보다 더 효율적인 알고리즘(예: 이진 트리 또는 래더 네트워크 기반)을 사용하여 이 오버헤드를 최소화하려고 한다.
MPI_Allreduce의 성능은 사용되는 통신 패턴, 네트워크 토폴로지, 데이터 크기, 그리고 MPI 구현체의 알고리즘 선택에 크게 의존한다. 이 함수는 본질적으로 모든 프로세스가 결과를 받아야 하므로, 단순히 MPI_Reduce 후에 MPI_Bcast를 수행하는 것보다 더 효율적인 알고리즘을 사용할 수 있다. 대표적인 알고리즘으로는 이진 트리나 버터플라이(나비형) 알고리즘이 있으며, 이러한 방법들은 통신 단계를 최소화하고 대역폭을 효율적으로 사용함으로써 지연 시간과 전송 시간을 줄이는 데 목적이 있다.
성능을 최적화하기 위해서는 애플리케이션의 통신 커뮤니케이터 크기와 메시지 크기를 고려해야 한다. 작은 메시지를 많은 프로세스로 전송할 때는 지연 시간이 주요 병목 현상이 될 수 있는 반면, 큰 메시지를 처리할 때는 네트워크 대역폭이 성능을 좌우하는 경우가 많다. 또한, 인피니밴드나 옴니패스와 같은 고성능 인터커넥트를 사용하는 시스템에서는 MPI 구현체가 RDMA 기술을 활용하여 성능을 극대화할 수 있다.
프로그래머는 성능에 민감한 코드에서 MPI_Allreduce를 사용할 때, 불필요한 호출을 피하고 가능하면 큰 데이터 덩어리를 한 번에 처리하도록 루프를 구성하는 것이 좋다. 또한, 논블로킹 통신 함수인 MPI_Iallreduce를 사용하여 통신과 계산을 중첩시키는 방법도 성능 향상에 도움이 될 수 있다. 최종적으로 특정 하드웨어와 MPI 라이브러리에서 제공하는 튜닝 파라미터나 환경 변수를 활용하는 것이 실제 성능을 결정짓는 경우가 많다.
MPI_Allreduce 함수는 MPI_Op 매개변수를 통해 지정된 감소 연산을 모든 프로세스의 데이터에 적용한다. 이 연산은 결합 법칙이 성립해야 하며, MPI 표준에서 미리 정의된 내장 연산을 사용하는 것이 일반적이다. 주요 내장 연산으로는 모든 원소의 합을 구하는 MPI_SUM, 곱을 구하는 MPI_PROD, 최댓값을 찾는 MPI_MAX, 최솟값을 찾는 MPI_MIN 등이 있다.
논리 및 비트 연산도 지원된다. 예를 들어 MPI_LAND와 MPI_LOR는 각 프로세스의 데이터에 대해 논리 AND와 논리 OR 연산을 수행한다. MPI_BAND와 MPI_BOR는 비트 단위 AND 및 OR 연산을 수행한다. 또한 MPI_MAXLOC와 MPI_MINLOC은 최댓값 또는 최솟값과 그 값을 가진 프로세스의 랭크를 함께 반환하는 특수 연산이다.
사용자는 MPI_Op_create 함수를 이용해 자신만의 사용자 정의 감소 연산을 만들어 MPI_Allreduce에 사용할 수도 있다. 그러나 모든 내장 및 사용자 정의 연산은 데이터의 순서에 영향을 받지 않는 결합 법칙을 만족해야 하며, 커뮤니케이터 내의 모든 프로세스가 동일한 연산을 사용해야 정확한 결과를 보장한다. 이러한 연산들은 병렬 계산에서 전역 합계, 평균, 노름(norm) 계산 등에 광범위하게 활용된다.
MPI_Allreduce는 집합 통신 함수군에 속하며, 감소 연산을 수행하는 다른 함수들과 밀접한 관련이 있다. 가장 직접적인 관련 함수는 MPI_Reduce이다. 두 함수 모두 커뮤니케이터 내의 모든 프로세스가 제공한 데이터에 대해 지정된 감소 연산을 수행한다. 핵심 차이는 MPI_Reduce는 연산 결과를 단 하나의 루트 프로세스에만 반환하는 반면, MPI_Allreduce는 결과를 커뮤니케이터 내의 모든 프로세스에게 동시에 분배한다는 점이다. 따라서 MPI_Allreduce는 MPI_Reduce 연산 후에 MPI_Bcast를 수행하는 것과 논리적으로 동일한 효과를 가진다.
데이터 분배 및 수집 패턴과 관련된 다른 집합 통신 함수들도 있다. 예를 들어, MPI_Scatter는 한 프로세스의 데이터 배열을 여러 프로세스에 조각으로 나누어 보내는 반면, MPI_Gather는 여러 프로세스의 데이터를 한 프로세스로 모은다. MPI_Allgather는 MPI_Gather의 결과를 모든 프로세스가 받는 변형이다. 이와 유사하게, MPI_Alltoall은 모든 프로세스가 모든 프로세스에게 데이터를 보내는 완전한 교환 패턴을 구현한다.
성능 최적화나 비동기적 연산을 위한 고급 변형 함수들도 존재한다. MPI_Iallreduce는 MPI_Allreduce의 비동기(non-blocking) 버전으로, 연산이 백그라운드에서 수행되는 동안 프로그램이 다른 작업을 진행할 수 있게 한다. 또한 MPI_Reduce_scatter는 감소 연산을 수행한 후 그 결과를 참여 프로세스들에 분산시키는 복합 연산을 제공한다. 이러한 함수들은 MPI 표준의 일부로, 병렬 알고리즘을 설계할 때 문제의 통신 패턴에 맞게 선택하여 사용한다.
MPI_Allreduce는 MPI의 핵심적인 집합 통신 함수 중 하나로, 병렬 계산에서 모든 프로세스가 공통의 계산 결과를 필요로 할 때 널리 사용된다. 이 함수는 MPI_Reduce로 감소 연산을 수행한 후 그 결과를 루트 프로세스만이 아닌 통신자 내의 모든 프로세스에 브로드캐스트하는 두 단계의 작업을 하나의 효율적인 연산으로 결합한다는 점에서 실용적 가치가 높다.
이 함수의 존재 이유는 병렬 알고리즘의 설계를 단순화하는 데 있다. 예를 들어, 모든 프로세스가 전체 시스템의 평균이나 표준 편차, 전역 합계 등을 동시에 알아야 다음 계산 단계로 진행할 수 있는 경우가 많다. 만약 MPI_Reduce와 MPI_Bcast를 따로 호출한다면 프로그래머는 두 번의 집합 통신을 관리해야 하지만, MPI_Allreduce는 이를 한 번의 호출로 추상화하여 코드를 간결하게 하고 잠재적인 동기화 오류를 줄여준다.
성능 측면에서, MPI 구현체들은 이 함수를 단순한 '감소 후 브로드캐스트' 이상으로 최적화할 것을 권장한다. 네트워크 토폴로지와 시스템 규모에 맞춰 이진 트리나 버터플라이 네트워크 같은 효율적인 알고리즘을 내부적으로 사용함으로써 통신 비용과 대기 시간을 최소화한다. 따라서 사용자는 높은 수준의 편의성을 누리면서도, 하위 수준에서 최적화된 성능을 기대할 수 있다.