MPI load balancing

제목을 이렇게 정하니 좀 거창한데 사실 내용은 간단하다.

대개 MPI를 처음 사용하면 수행 속도를 올리는 방법이란 게 동일한 작업을 random seed라든가 실행 조건을 다르게 맞춰놓고 여러 개의 thread, machine에 일을 벌려놓고 종료될 때 모든 결과를 합산한다거나 최대/최소값을 구하는 방법 정도가 된다.

문제는 여러 대의 머신에서 작업한다고 할 때 각각의 machine의 성능에 차이가 있을 때, 혹은 누군가 작업을 돌려놓고 있어서 최대 성능이 나지 않을 때 등등의 경우다.

어차피 작업자가 이걸 전부 알아서 수동으로 최적화하면 좋겠지만 보통 노가다가 아니기 때문에 내 경우는 큰 일을 돌려놓기 전에 모든 머신들을 체크해서 CPU core를 점유하는 작업의 개수를 찾아내서 그만큼 가용 자원에서 제한뒤에 host file을 만들고, 그 다음엔 dynamic load balancing을 한다.

이 방법을 요약하면 목표 작업량을 미리 설정해놓고 모든 머신들이 십시일반으로 일을 하게 해놓고 그 총합이 목표량에 도달했는지만 수시로 체크하고 도달하면 모든 작업들을 종료시키는 것이다. 그러니까 앞서 말한 방법과의 차이는 이를테면 rank가 0인 프로세스 (대개 master라고 한다)에서 나머지 프로세스들에서 날아오는 작업 처리 완료 여부를 체크해서 목표량에 도달했는지 체크하게 하면 된다.

대개 일반적으로 보여지는 예에서는 master에서 전적으로 작업 관리를 하고 worker process들에게 종료신호를 알려주는 식으로 작성되어있는데, 이럴 필요가 없다. 어차피 작업을 관리하는데 들어가는 연산량이라는 것은 고작 덧셈에 불과하고 MPI를 통해 받아야 할 데이터가 조금 더 많아질 뿐이다. MPI에서는 이 데이터를 주고 받는 과정에서 non-blocking한 방법을 지원해서 현재 수신된 packet이 있는지 여부만을 체크하는 기능이 있다. 즉, 받은 게 있으면 어떤 통신작업을 수행하고 아니면 그냥 지나치도록 할 수 있단 말이다. 이를테면 master가 다른 worker들의 작업 여부를 관리하려고 할 때 특정 프로세스에서 데이터가 날아올 때까지 기다릴 필요가 없단 것이다.

결국 모든 프로세스가 동일한 일을 수행하고 단지 rank가 0인 프로세스만 약간의 관리 비슷한 작업을 수행하면 그뿐이다. 그 관리 작업이란 게 별 개 아니라 목표작업량에 도달했다고 판단되면 나머지 프로세스들에게 작업 종료를 알려주는 정도다. 나머지 프로세스들은 종료 메시지를 받으면 작업을 종료한다 마찬가지로.

문제는 mpirun 옵션에 좀 신경을 써야 한다는 거다. 무슨 말이냐면 MPI로 사용하는 통신 매체가 엉뚱하게 설정되어있으면 MPI message가 전달이 제대로 되지 않아 엉뚱한 동작을 할 수 있다는 거다.

내 경우, arch linux에 openmpi를 얹어서 사용하는데 docker를 올려두면 오작동을 한다든지 내부 VM들과의 관계 때문에 이런 저런 virtual network device들이 설정되어있으면 오작동을 한다. 일부의 default option에는 뭔가 문제가 있는데 대부분 조립 PC들을 잔뜩 가져다놓고 MPI를 한다고 하면 잘해봐야 tcp,self를 쓰는 거다. 이 정도만 해도 충분히 좋은 성능이 나온다. 로드 밸런싱을 못하면서 수동작업으로 끙끙대는 것 보단 분명히 로드 밸런싱이 잘 되는 경우가 훨씬 좋은 성능을 낸다. MPI를 써서 일을 하겠다는 자체가 작업량이 어마어마하게 많으니까 어쩔 수 없이 기계의 힘을 빌어하겠다는 것인데, 로드 밸런싱이 잘 되서 일이 조금이라도 빨리 끝나면 전체작업도 순조롭게 마무리할 수 있다. 더구나 로드 밸런싱이 잘 되면 기종에 불문하고 내가 가진 거의 모든 컴퓨팅 자원들을 투입할 수 있다.