MPI를 통한 성능 저하
나는 MPI를 배우고 있으며 아래의 간단한 구현에서 성능 향상이 거의 없다는 질문이 있습니다.
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
int main(int argc, char **argv)
{
int mpirank, mpisize;
int tabsize = atoi(*(argv + 1));
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &mpirank);
MPI_Comm_size(MPI_COMM_WORLD, &mpisize);
unsigned long int sum = 0;
int rcvsize = tabsize / mpisize;
int *rcvbuf = malloc(rcvsize * sizeof(int));
int *tab = malloc(tabsize * sizeof(int));
int totalsum = 0;
if(mpirank == 0){
for(int i=0; i < tabsize; i++){
*(tab + i) = 1;
}
}
MPI_Scatter(tab, tabsize/mpisize, MPI_INT, rcvbuf, tabsize/mpisize, MPI_INT, 0, MPI_COMM_WORLD);
for(int i=0; i < tabsize/mpisize; i++){
sum += *(rcvbuf + i);
}
MPI_Reduce(&sum, &totalsum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
if(mpirank == 0){
printf("The totalsum = %li\n", totalsum);
}
MPI_Finalize();
return 0;
}
위의 구현 실행 시간은 다음과 같습니다.
$ /usr/bin/time mpirun -np 1 test1 2000000000 The totalsum = 2000000000 13.76user 3.31system 0:17.30elapsed 98%CPU (0avgtext+0avgdata 15629824maxresident)k 0inputs+8outputs (0major+21720minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 1 test1 2000000000
The totalsum = 2000000000
13.78user 3.29system 0:17.31elapsed 98%CPU (0avgtext+0avgdata 15629824maxresident)k 0inputs+8outputs (0major+21717minor)pagefaults 0swaps
$ /usr/bin/time mpirun -np 1 test1 2000000000 The totalsum = 2000000000 13.78user 3.32system 0:17.33elapsed 98%CPU (0avgtext+0avgdata 15629828maxresident)k 0inputs+8outputs (0major+20697minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 20 test1 2000000000
The totalsum = 2000000000
218.42user 6.10system 0:12.99elapsed 1727%CPU (0avgtext+0avgdata 8209484maxresident)k 0inputs+17400outputs (118major+82587minor)pagefaults 0swaps
$ /usr/bin/time mpirun -np 20 test1 2000000000 The totalsum = 2000000000 216.17user 6.37system 0:12.89elapsed 1726%CPU (0avgtext+0avgdata 8209488maxresident)k 0inputs+17168outputs (126major+81092minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 20 test1 2000000000
The totalsum = 2000000000
216.16user 6.09system 0:12.88elapsed 1724%CPU (0avgtext+0avgdata 8209492maxresident)k 0inputs+17192outputs (111major+81665minor)pagefaults 0swaps
이는 약 25 %의 성능 향상만을 제공합니다. 내 생각에는 병목 현상이 메모리에 액세스하기 위해 경쟁하는 프로세스로 인해 발생할 수 있다는 것입니다. 그런 다음 똑같이 시도했지만 메모리를 사용하지 않고 데이터를 얻었습니다.
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
int main(int argc, char **argv)
{
int mpirank, mpisize;
int tabsize = atoi(*(argv + 1));
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &mpirank);
MPI_Comm_size(MPI_COMM_WORLD, &mpisize);
unsigned long int sum = 0;
for(int i=0; i < tabsize/mpisize; i++){
sum += 1;
}
MPI_Reduce(&sum, &totalsum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
if(mpirank == 0){
printf("The totalsum = %li\n", totalsum);
}
MPI_Finalize();
return 0;
}
결과는 다음과 같습니다.
$ /usr/bin/time mpirun -np 1 test2 2000000000 The totalsum = 2000000000 6.17user 0.11system 0:06.49elapsed 96%CPU (0avgtext+0avgdata 5660maxresident)k 0inputs+8outputs (0major+4005minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 1 test2 2000000000
The totalsum = 2000000000
6.16user 0.12system 0:06.49elapsed 96%CPU (0avgtext+0avgdata 5660maxresident)k 0inputs+8outputs (0major+4007minor)pagefaults 0swaps
$ /usr/bin/time mpirun -np 1 test2 2000000000 The totalsum = 2000000000 6.15user 0.11system 0:06.47elapsed 96%CPU (0avgtext+0avgdata 5664maxresident)k 0inputs+8outputs (0major+4005minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 20 test2 2000000000
The totalsum = 2000000000
8.67user 2.41system 0:01.06elapsed 1040%CPU (0avgtext+0avgdata 6020maxresident)k 0inputs+16824outputs (128major+49952minor)pagefaults 0swaps
$ /usr/bin/time mpirun -np 20 test2 2000000000 The totalsum = 2000000000 8.59user 2.74system 0:01.05elapsed 1076%CPU (0avgtext+0avgdata 6028maxresident)k 0inputs+16792outputs (131major+49960minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 20 test2 2000000000
The totalsum = 2000000000
8.65user 2.61system 0:01.06elapsed 1058%CPU (0avgtext+0avgdata 6024maxresident)k 0inputs+16792outputs (116major+50002minor)pagefaults 0swaps
이것은 약 83 %의 성능 향상을 보여 주며 내 추측을 확인시켜줍니다. 그런 다음 내 추측이 올바른지, 그렇다면 메모리 액세스로 첫 번째 구현을 개선 할 수있는 방법이 있습니까?
코드는 20 개의 물리적 코어가있는 머신에서 실행되었습니다.
EDIT1 : 2, 5 및 10 프로세스에 대한 첫 번째 구현의 추가 결과 :
$ /usr/bin/time mpirun -np 2 test1 2000000000 The totalsum = 2000000000 24.05user 3.40system 0:14.03elapsed 195%CPU (0avgtext+0avgdata 11724552maxresident)k 0inputs+960outputs (6major+23195minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 5 test1 2000000000
The totalsum = 2000000000
55.27user 3.54system 0:12.88elapsed 456%CPU (0avgtext+0avgdata 9381132maxresident)k 0inputs+4512outputs (26major+31614minor)pagefaults 0swaps
$ /usr/bin/time mpirun -np 10 test1 2000000000
The totalsum = 2000000000
106.43user 4.07system 0:12.44elapsed 887%CPU (0avgtext+0avgdata 8599952maxresident)k 0inputs+8720outputs (51major+50059minor)pagefaults 0swaps
EDIT2 :
다음과 같이 첫 번째 구현의 MPI_Scatter 부분을 측정하기 위해 MPI_Wtime ()을 넣었습니다.
...
for(int i=0; i < tabsize; i++){
*(tab + i) = 1;
}
}
MPI_Barrier(MPI_COMM_WORLD);
double start = MPI_Wtime();
MPI_Scatter(tab, tabsize/mpisize, MPI_INT, rcvbuf, tabsize/mpisize, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);
double end = MPI_Wtime();
for(int i=0; i < tabsize/mpisize; i++){
sum += *(rcvbuf + i);
...
다음과 같은 결과를 얻었습니다.
$ /usr/bin/time mpirun -np 1 test1 400000000
The MPI_Scatter time = 0.576 (14% of total)
3.13user 0.74system 0:04.08elapsed 95%CPU
$ /usr/bin/time mpirun -np 2 test1 400000000 The MPI_Scatter time = 0.580 (18% of total) 5.19user 0.79system 0:03.25elapsed 183%CPU $ /usr/bin/time mpirun -np 4 test1 400000000
The MPI_Scatter time = 0.693 (22.5% of total)
9.99user 1.05system 0:03.07elapsed 360%CPU
$ /usr/bin/time mpirun -np 5 test1 400000000 The MPI_Scatter time = 0.669 (22.3% of total) 12.41user 1.01system 0:03.00elapsed 446%CPU $ /usr/bin/time mpirun -np 8 test1 400000000
The MPI_Scatter time = 0.696 (23.7% of total)
19.67user 1.25system 0:02.95elapsed 709%CPU
$ /usr/bin/time mpirun -np 10 test1 400000000 The MPI_Scatter time = 0.701 (24% of total) 24.21user 1.45system 0:02.92elapsed 876%CPU $ /usr/bin/time mpirun -np 1 test1 1000000000
The MPI_Scatter time = 1.434 (15% of total)
7.64user 1.71system 0:09.57elapsed 97%CPU
$ /usr/bin/time mpirun -np 2 test1 1000000000 The MPI_Scatter time = 1.441 (19% of total) 12.72user 1.75system 0:07.52elapsed 192%CPU $ /usr/bin/time mpirun -np 4 test1 1000000000
The MPI_Scatter time = 1.710 (25% of total)
24.16user 1.93system 0:06.84elapsed 381%CPU
$ /usr/bin/time mpirun -np 5 test1 1000000000 The MPI_Scatter time = 1.675 (25% of total) 30.29user 2.10system 0:06.81elapsed 475%CPU $ /usr/bin/time mpirun -np 10 test1 1000000000
The MPI_Scatter time = 1.753 (26.6% of total)
59.89user 2.47system 0:06.60elapsed 943%CPU
$ /usr/bin/time mpirun -np 10 test1 100000000 The MPI_Scatter time = 0.182 (15.8% of total) 6.75user 1.07system 0:01.15elapsed 679%CPU $ /usr/bin/time mpirun -np 10 test1 200000000
The MPI_Scatter time = 0.354 (20% of total)
12.50user 1.12system 0:01.71elapsed 796%CPU
$ /usr/bin/time mpirun -np 10 test1 300000000 The MPI_Scatter time = 0.533 (22.8% of total) 18.54user 1.30system 0:02.33elapsed 849%CPU $ /usr/bin/time mpirun -np 10 test1 400000000
The MPI_Scatter time = 0.702 (23.95% of total)
24.38user 1.37system 0:02.93elapsed 879%CPU
$ /usr/bin/time mpirun -np 10 test1 1000000000
The MPI_Scatter time = 1.762 (26% of total)
60.17user 2.42system 0:06.62elapsed 944%CPU
답변
이는 약 25 %의 성능 향상만을 제공합니다. 내 생각에는 병목 현상이 메모리에 액세스하기 위해 경쟁하는 프로세스로 인해 발생할 수 있다는 것입니다. (..)
귀하의 코드는 대부분 통신 및 CPU 바운드입니다. 또한 2, 5 및 10 프로세스에 대한 결과에 따라 :
$ /usr/bin/time mpirun -np 2 test1 2000000000 The totalsum = 2000000000 24.05user 3.40system 0:14.03elapsed 195%CPU (0avgtext+0avgdata 11724552maxresident)k 0inputs+960outputs (6major+23195minor)pagefaults 0swaps $ /usr/bin/time mpirun -np 5 test1 2000000000
The totalsum = 2000000000
55.27user 3.54system 0:12.88elapsed 456%CPU (0avgtext+0avgdata 9381132maxresident)k 0inputs+4512outputs (26major+31614minor)pagefaults 0swaps
$ /usr/bin/time mpirun -np 10 test1 2000000000
The totalsum = 2000000000
106.43user 4.07system 0:12.44elapsed 887%CPU (0avgtext+0avgdata 8599952maxresident)k 0inputs+8720outputs (51major+50059minor)pagefaults 0swaps
이 코드는 메모리 바운드 너비가 포화 될 가능성이 거의없는 약 5 개의 프로세스에서 이미 확장을 중지합니다.
그런 다음 똑같이 시도했지만 메모리를 사용하지 않고 데이터를 얻었습니다. (..) 이것은 약 83 %의 성능 향상을 나타내며 내 추측을 확인합니다.
그러나 당신은 또한 MPI_Scatter
전화를 제거했습니다 . 결과적으로 통신 오버 헤드를 줄이면서 동시에 수행 할 작업량을 기본적으로 동일하게 유지합니다.
내 컴퓨터에서 코드를 프로파일 링했습니다 (물리적 코어 2 개, 논리적 4 개). 시간을 측정하기 위해 MPI_Wtime();
다음과 같이 사용 하고 있습니다.
int main(int argc, char **argv)
{
int mpirank, mpisize;
int tabsize = atoi(*(argv + 1));
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &mpirank);
MPI_Comm_size(MPI_COMM_WORLD, &mpisize);
MPI_Barrier(MPI_COMM_WORLD);
double start = MPI_Wtime();
...
if(mpirank == 0){
printf("The totalsum = %li\n", totalsum);
}
MPI_Barrier(MPI_COMM_WORLD);
double end = MPI_Wtime();
if(mpirank == 0)
printf("Time:%f\n",end-start);
}
귀하의 입력 ( 예 : 2000000000) 과 동일한 입력에 대한 결과는 다음과 같습니다.
1 process : 25.158740 seconds
2 processes : 19.116490 seconds
4 processes : 15.971734 seconds
약 40 %의 향상과 내 컴퓨터의 메모리 계층 구조는 물리적 코어가 20 개인 컴퓨터보다 훨씬 열등합니다.
이제 입력 크기를 크게 줄여 메모리 사용량을 2000000000 (8GB)에서 250000000 (1GB)으로 줄인 다음 다시 테스트 해 보겠습니다.
1 process : 1.312354 seconds
2 processes : 1.229174 seconds
4 processes : 1.232522 seconds
약 6 % 개선; 병목 현상이 메모리를 놓고 경쟁하는 프로세스 였다면 메모리 풋 프린트를 줄인 후 이러한 속도 향상을 기대하지 않을 것입니다. 그럼에도 불구하고 이러한 감소는 입력 크기를 줄임으로써 계산 당 통신 비율 을 증가 시켰다는 사실로 쉽게 설명 할 수 있습니다 .
2000000000 개의 요소를 사용한 테스트로 돌아가 보겠습니다. 이번에는 MPI_Scatter
통신 루틴 (제거한 것)에 소요 된 시간을 측정합니다 .
2 processes : 7.487354 seconds
4 processes : 8.728969 seconds
2 개 및 4 개의 프로세스에서 알 수 있듯이 애플리케이션 실행 시간의 약 40 % ( 예 : 7.487354 / 19.116490) 및 54 % ( 예 : 8.728969 / 15.971734)가 MPI_Scatter
각각 단독으로 사용되었습니다. 그렇기 때문에 해당 루틴을 제거했을 때 속도 향상이 향상되었음을 알 수 있습니다.
이제 입력 250000000 (1GB)에 대한 동일한 테스트 :
2 processes ::0.679913 seconds (55% of the time)
4 processes : 0.691987 seconds (56% of the time)
보시다시피, 메모리 풋 프린트가 더 작더라도 MPI_scatter
나머지 오버 헤드 는 거의 동일합니다 (4 개 프로세스의 경우). 결론은 더욱 공정 덜 계산이다 당 있어서, 결과적으로는, 더 높은 인 비 통신 당 프로세스 높은 번호 힘 팝업창이 동작하는 다른 오버 헤드 제외 - 계산이. 또한 코드에서 더 많은 프로세스를 사용하면 메모리 사용량이 선형 적으로 증가하지 않습니다. 단, 주 프로세스 (전체 데이터 포함)를 제외하고 리밍 프로세스는 데이터가 분산되어 있습니다.
통상적으로, 양호한 MPI_scatter
구현의 시간 복잡도 것이다 (N, P, 로그)를 O 와, n
입력의 크기 나되는 p
프로세스의 수. 따라서의 오버 헤드 MPI_scatter
는 입력 크기를 늘린 다음 해당 통신에 관련된 프로세스 수를 늘림으로써 더 빠르게 증가합니다. 그러나 입력 크기를 늘리면 병렬로 수행되는 프로세스 당 더 많은 계산 이 수행되는 반면 프로세스 수를 늘리면 수행되는 프로세스 당 계산이 줄어 듭니다 .
그러나 내가 수행 한 테스트는 내가 실행중인 환경으로 인해 MPI 구현이 사용자의 것과 다를 수 있으므로 지금까지 수행 한 테스트가 가장 정확하지 않다는 점을 명심하십시오. 그럼에도 불구하고 설정에서 동일한 테스트를 수행하면 동일한 결론을 도출 할 것이라고 확신합니다.