연습: Visual Studio 2010에서 MPI 클러스터 디버거 실행
이 연습에서는 로컬 컴퓨터 및 Microsoft Windows HPC Server 2008 클러스터에서 MPI 클러스터 디버거 세션을 설정하고 실행하는 방법에 대해 배웁니다. 이 연습에는 MPI(Message Passing Interface) 및 OpenMP(Open Multi-Processing) API(Application Programming Interface)를 사용하는 응용 프로그램을 작성할 때 필요한 단계 및 샘플 코드가 포함되어 있습니다.
이 가이드에서는 다음에 대해 설명합니다.
MPI 클러스터 디버거 사용 시 요구 사항
Visual Studio 2010에서 C++ MPI 샘플 프로젝트 작성
MPI 클러스터 디버거 구성 및 실행
부록: 응용 프로그램 이진 및 CRT(요청된 경우) 외에 Visual Studio가 배포하는 파일
MPI 클러스터 디버거 사용 시 요구 사항
Visual Studio 2010 Professional Edition 이상(원격 디버거 포함)이 개발 컴퓨터에 설치되어 있어야 합니다.
클러스터에서 관리 권한이 있어야 합니다.
Visual Studio가 디버깅 세션을 실행할 계산 노드에 액세스할 수 있어야 합니다. 다음 시나리오에서는 필요한 액세스를 제공합니다.
클러스터 헤드 노드 또는 전용 로그인 노드에서 응용 프로그램을 개발 중입니다.
계산 노드가 엔터프라이즈 네트워크(토폴로지 2, 4 또는 5)에 연결되어 있는 클러스터를 사용 중이고 개발 컴퓨터가 동일 도메인 또는 클러스터 도메인과 트러스트 관계인 도메인에 가입되어 있습니다.
응용 프로그램을 클라이언트 컴퓨터에서 HPC 클러스터로 제출하려면 Microsoft HPC Pack 2008이 설치되어 있어야 합니다.
MSMPI(Microsoft Message Passing Interface)를 사용하여 MPI 프로그램을 빌드하려면 Windows HPC Server 2008 SDK를 개발 컴퓨터에 설치해야 합니다.
Visual Studio 2010에서 C++ MPI 샘플 프로젝트 작성
이 섹션의 샘플 코드는 Monte Carlo 시뮬레이션을 사용하여 Pi 값의 근사치를 구하는 병렬 응용 프로그램용입니다.
샘플 코드는 각 MPI 프로세스에서 50,000,000회 반복 실행됩니다. 각 반복 시마다 샘플 코드는 x와 y 좌표 집합을 판별하기 위해 [0,1] 간격으로 임의 숫자를 생성합니다. 좌표 집합은 지점이 x2 + y2 = 1 선 아래인지 여부를 판별하기 위해 평가됩니다. 지점이 해당 선 아래이면 변수 count는 1이 증가됩니다. 각 MPI 프로세스의 count 값이 변수 result로 합산됩니다. 선(result) 아래에 있는 총 지점 수에 4를 곱한 후 총 반복 횟수로 나누어서 Pi 값의 근사치를 구합니다.
다음 절차에는 두 번의 Monte Carlo 시뮬레이션 구현이 포함되어 있습니다.
첫 번째 샘플은 MPI 및 OpenMP를 사용합니다. OpenMP에 대한 자세한 내용은 OpenMP in Visual C++(영문)을 참조하십시오.
두 번째 샘플은 MPI 및 PPL(Parallel Patterns Library)을 사용합니다. PPL에 대한 자세한 내용은 Parallel Patterns Library (PLL)(영문)을 참조하십시오.
샘플 프로젝트를 작성하려면
Visual Studio 2010을 실행합니다.
ParallelPI라는 새 C++ Win32 콘솔 응용 프로그램을 작성합니다. 미리 컴파일된 헤더 없이 프로젝트를 사용합니다.
파일 메뉴에서 새로 만들기를 가리킨 다음 프로젝트를 클릭합니다.
새 프로젝트 대화 상자에서 설치된 템플릿을 클릭한 다음 **Visual C++**를 선택합니다. (Visual Studio 설정 방법에 따라 **Visual C++**가 다른 언어 노드에 있을 수도 있습니다.)
템플릿 목록에서 Win32 콘솔 응용 프로그램을 클릭합니다.
프로젝트 이름에는 ParallelPI.
확인을 클릭합니다. 그러면 Win32 콘솔 응용 프로그램 마법사가 열립니다.
다음을 클릭합니다.
응용 프로그램 설정의 추가 옵션 아래에서 미리 컴파일된 헤더 확인란의 선택을 취소합니다.
마법사를 닫고 프로젝트를 작성하려면 마침을 클릭합니다.
프로젝트에 추가 속성을 지정합니다.
솔루션 탐색기에서 병렬 PI를 마우스 오른쪽 단추로 클릭한 다음 속성을 클릭합니다. 그러면 속성 페이지 대화 상자가 열립니다.
구성 속성을 확장한 다음 VC++ 디렉터리를 선택합니다.
포함 디렉터리에서 커서를 텍스트 상자에 나타나는 목록의 시작 부분에 놓고 세미콜론(;)이 뒤에 오는 MS MPI C 헤더 파일의 위치를 지정합니다. 예를 들면 다음과 같습니다.
C:\Program Files\Microsoft HPC Pack 2008 SDK\Include;
라이브러리 디렉터리에서 커서를 텍스트 상자에 나타나는 목록의 시작 부분에 놓고 세미콜론(;)이 뒤에 오는 Microsoft HPC Pack 2008 SDK 라이브러리 파일의 위치를 지정합니다.
예를 들어 32비트 응용 프로그램을 빌드 및 디버깅하기 원하는 경우:
C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\i386;
64비트 응용 프로그램을 빌드 및 디버깅하기 원하는 경우:
C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\amd64;
링커 아래에서 입력을 선택합니다.
추가 종속성에서 커서를 텍스트 상자에 나타나는 목록의 시작 부분에 놓고 다음과 같이 입력합니다.
msmpi.lib;
OpenMP와 함께 코드 샘플을 사용하는 경우:
구성 속성에서 **C/C++**를 확장한 다음 언어를 선택합니다.
Open MP 지원에서 Open MP에 대한 컴파일러 지원을 활성화하려면 **예(/openmp)**를 선택합니다.
확인을 클릭하여 속성 페이지를 닫습니다.
주 소스 파일에서 모든 코드를 선택한 다음 삭제합니다.
다음 코드 샘플 중 하나를 빈 소스 파일에 붙여넣습니다. 첫 번째 샘플은 MPI 및 OpenMP를 사용하고, 두 번째 샘플은 MPI 및 PPL(Parallel Patterns Library)을 사용합니다.
다음 코드 샘플은 MPI 및 OpenMP를 사용합니다.
ThrowDarts
함수는 사용 가능한 경우 다중 코어 하드웨어를 사용하기 위해 OpenMP 병렬for
루프를 사용합니다.// ParallelPI.cpp : Defines the entry point for the MPI application. // #include "mpi.h" #include "stdio.h" #include "stdlib.h" #include "limits.h" #include "omp.h" #include <random> int ThrowDarts(int iterations) { std::tr1::uniform_real<double> MyRandom; std::tr1::minstd_rand0 MyEngine; double RandMax = MyRandom.max(); int count = 0; omp_lock_t MyOmpLock; omp_init_lock(&MyOmpLock); //Compute approximation of pi on each node #pragma omp parallel for for(int i = 0; i < iterations; ++i) { double x, y; x = MyRandom(MyEngine)/RandMax; y = MyRandom(MyEngine)/RandMax; if(x*x + y*y < 1.0) { omp_set_lock(&MyOmpLock); count++; omp_unset_lock(&MyOmpLock); } } omp_destroy_lock(&MyOmpLock); return count; } int main(int argc, char* argv[]) { int rank; int size; int iterations; int count; int result; double time; MPI_Status s; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); MPI_Comm_size(MPI_COMM_WORLD,&size); if(rank == 0) { //Rank 0 asks the number of iterations from the user. iterations = 50000000; if(argc > 1) { iterations = atoi(argv[1]); } printf("Executing %d iterations.\n", iterations); fflush(stdout); } //Broadcast the number of iterations to execute. if(rank == 0) { for(int i = 1; i < size; ++i) { MPI_Ssend(&iterations, 1, MPI_INT, i, 0, MPI_COMM_WORLD); } } else { MPI_Recv(&iterations, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &s); } //MPI_Bcast(&iterations, 1, MPI_INT, 0, MPI_COMM_WORLD); count = ThrowDarts(iterations); //Gather and sum results if(rank != 0) { MPI_Ssend(&count, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); } else { for(int i = 1; i < size; ++i) { int TempCount = 0; MPI_Recv(&TempCount, 1, MPI_INT, i, 0, MPI_COMM_WORLD, &s); count += TempCount; } } result = count; //MPI_Reduce(&count, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); if(rank == 0) { printf("The value of PI is approximated to be: %16f", 4*((float)result/(float)(iterations*size))); } MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); return 0; }
다음 코드 샘플은 OpenMP 대신에 PPL(Parallel Patterns Library)을 사용하고 지점간 작업 대신에 MPI 일괄 작업을 사용합니다.
// ParallelPI.cpp : Defines the entry point for the MPI application. // #include "mpi.h" #include "stdio.h" #include "stdlib.h" #include "limits.h" #include <ppl.h> #include <random> #include <time.h> using namespace Concurrency; int ThrowDarts(int iterations) { combinable<int> count; int result = 0; parallel_for(0, iterations, [&](int i){ std::tr1::uniform_real<double> MyRandom; double RandMax = MyRandom.max(); std::tr1::minstd_rand0 MyEngine; double x, y; MyEngine.seed((unsigned int)time(NULL)); x = MyRandom(MyEngine)/RandMax; y = MyRandom(MyEngine)/RandMax; if(x*x + y*y < 1.0) { count.local() += 1; } }); result = count.combine([](int left, int right) { return left + right; }); return result; } void main(int argc, char* argv[]) { int rank; int size; int iterations; int count; int result; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); MPI_Comm_size(MPI_COMM_WORLD,&size); if(rank == 0) { //Rank 0 reads the number of iterations from the command line. //50M iterations is the default. iterations = 50000000; if(argc > 1) { iterations = atoi(argv[argc-1]); } printf("Executing %d iterations on %d nodes.\n", iterations, size); fflush(stdout); } //Broadcast the number of iterations to execute. MPI_Bcast(&iterations, 1, MPI_INT, 0, MPI_COMM_WORLD); count = ThrowDarts(iterations); //Gather and sum results MPI_Reduce(&count, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); if(rank == 0) { printf("The value of PI is approximated to be: %16f", 4*((double)result/(double)(iterations*size))); } MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); }
파일 메뉴에서 모두 저장을 클릭합니다.
빌드 메뉴에서 솔루션 다시 빌드를 클릭합니다.
MPI 클러스터 디버거 구성 및 실행
응용 프로그램을 빌드한 후 디버거를 구성하고 실행할 수 있습니다. 이 섹션에서는 디버깅을 위한 세 가지 옵션을 설명합니다.
로컬 컴퓨터에서 하나의 MPI 프로세스 디버그
로컬 컴퓨터에서 여러 MPI 프로세스 디버그
클러스터에서 하나 이상의 MPI 프로세스 디버그
참고
MPI 클러스터 디버거에서 디버깅하지 않고 시작할 수 없습니다. Ctrl+F5(또는 디버그 메뉴의 디버깅하지 않고 시작)를 눌러서도 디버깅을 시작할 수 있습니다.
로컬 컴퓨터에서 하나의 MPI 프로세스 디버그
로컬 컴퓨터에서 단 하나의 MPI 프로세스를 사용하여 디버그하려면 다른 응용 프로그램을 디버그할 때와 동일한 프로세스를 사용합니다. 프로그램에서 원하는 위치에 중단점을 설정하고 디버거를 시작하려면 F5를 누릅니다.
MPI 프로그램은 포트상에서 IP를 통해 통신합니다. MPI 프로그램을 처음 실행하면 포트가 열려 있음을 표시하는 방화벽의 보안 경고가 표시됩니다. 경고 메시지를 읽고 시스템에 수행하려는 변경 사항을 이해하고 있는지 확인합니다. 로컬 컴퓨터에서 디버깅을 계속하려면 방화벽을 차단 해제해야 합니다.
로컬 컴퓨터에서 여러 MPI 프로세스 디버그
다음 절차에는 ParallelPI를 위한 로컬 디버깅 세션을 시작하는 방법이 설명되어 있습니다.
로컬 컴퓨터에서 4개의 MPI 프로세스가 실행 중인 상태에서 MPI 클러스터 디버거를 시작하려면
솔루션 탐색기에서 병렬 PI를 마우스 오른쪽 단추로 클릭한 다음 속성을 클릭합니다. 그러면 속성 페이지 대화 상자가 열립니다.
구성 속성을 확장하고 디버깅을 선택합니다.
실행할 디버거 아래에서 MPI 클러스터 디버거를 선택합니다.
실행 환경의 드롭다운 목록에서 Hpc 노드 편집을 선택합니다. 그러면 노드 선택기 대화 상자가 열립니다.
헤드 노드 드롭다운 목록에서 localhost를 선택합니다.
프로세스 수에서 4를 선택합니다.
확인을 클릭하여 변경 내용을 저장하고 노드 선택기 대화 상자를 닫습니다.
ParallelPI는 실행할 반복 횟수를 결정하는 하나의 인수를 허용합니다. 기본값은 50,000,000으로 설정됩니다. 로컬 디버깅 세션의 경우 다음과 같이 반복 횟수를 5,000으로 줄이십시오.
응용 프로그램 인수에 5000을 입력합니다.
확인을 클릭하여 변경 내용을 저장하고 속성 페이지를 닫습니다.
병렬
for
루프의 본문 내에 중단점을 설정합니다.F5를 눌러 디버거를 실행합니다.
다섯 개의 콘솔 창이 나타납니다. 한 개의 cmd.exe 창과 4개의 ParallelPI.exe 창(실행한 각 프로세스당 하나씩)이 나타납니다. 0 순위 프로세스에 해당하는 콘솔 창은 반복 횟수 및 Pi의 계산된 근사치를 표시합니다.
디버그 메뉴에서 창을 클릭한 다음 프로세스를 클릭합니다.
프로세스 창에서 프로세스를 두 번 클릭하여 디버깅할 활성 프로세스를 설정합니다.
참고
여러 프로세스를 디버깅하는 경우에는 기본적으로 중단점이 디버깅 중인 모든 프로세스에 영향을 미칩니다. 의도치 않은 위치에서 프로세스가 중단되는 것을 피하려면 한 프로세스가 중단될 때 모든 프로세스 중단 옵션을 선택 취소합니다. (도구 메뉴에서 옵션을 클릭한 다음 디버깅 선택) 중단 동작을 변경하는 방법에 대한 자세한 내용은 방법: 실행 중단을 참조하십시오.
클러스터에서 하나 이상의 MPI 프로세스 디버그
클러스터에서 MPI 디버거를 실행하면 디버거는 응용 프로그램을 클러스터에 작업으로 제출합니다. 프로젝트(x86 또는 x64, 디버그 또는 릴리스)에 맞는 Visual C 런타임이 계산 노드의 작업 디렉토리에 있어야 합니다. 올바른 런타임이 계산 노드에 있지 않으면 추가로 배포할 파일 속성을 지정하여 디버거 배포에 해당 런타임을 포함해야 합니다. 다음 절차에는 OpenMP 디버그 런타임 DLL을 배포하는 단계가 포함되어 있습니다. 기본적으로 CRT(C 런타임) 라이브러리는 MPI 클러스터 디버거 실행 시 배포됩니다. 올바른 런타임이 없으면 응용 프로그램을 실행하려고 할 때 SxS(Side-by-Side) 오류가 표시됩니다. OpenMP 런타임이 포함되지 않은 경우에는 중단점이 맞지 않습니다.
클러스터에서 MPI 디버거 실행
솔루션 탐색기에서 병렬 PI를 마우스 오른쪽 단추로 클릭한 다음 속성을 클릭합니다. 그러면 속성 페이지 대화 상자가 열립니다.
구성 속성을 확장하고 디버깅을 선택합니다.
실행할 디버거 아래에서 MPI 클러스터 디버거를 선택합니다.
실행 환경의 드롭다운 목록에서 Hpc 노드 편집을 선택합니다. 그러면 노드 선택기 대화 상자가 열립니다.
헤드 노드 드롭다운 목록에서 사용할 클러스터의 헤드 노드 이름을 선택합니다.
헤드 노드 목록은 Active Directory 도메인 컨트롤러에서 채워집니다. 사용자 도메인에 있는 클러스터만 목록에 나타납니다. 헤드 노드가 표시되지 않으면 속성 필드에 헤드 노드의 이름이나 IPv4 주소를 입력합니다.
프로세스 수에서 4를 선택합니다.
단일 프로세스 예약 단위에서 프로세스 할당 방법을 선택합니다. 코어, 소켓 또는 노드당 한 개의 프로세스를 할당할 수 있습니다.
확인을 클릭하여 변경 내용을 저장하고 노드 선택기 대화 상자를 닫습니다.
배포 디렉토리에서 헤드 노드의 공유 디렉토리를 지정합니다. 배포 디렉터리가 존재하지 않고 지정된 루트 디렉터리에 쓰기 권한이 있으면 배포 디렉터리가 자동으로 생성됩니다.
CcpSpoolDir 디렉터리 공유 리소스는 HPC 팩 2008이 헤드 노드에 설치될 때 생성됩니다. 예를 들어, 다음을 입력합니다. 여기서 *<myHeadNode>*는 사용 중인 클러스터의 이름입니다.
\\<myHeadNode>\CcpSpoolDir\
작업 디렉터리에 각 계산 노드의 로컬 작업 디렉터리를 지정합니다. 예를 들어, 다음을 입력합니다. 여기서 *<myUserName>*은 사용자 이름입니다.
C:\Users\<myUserName>\ParallelPI
OpenMP와 함께 샘플 코드를 사용 중인 경우에는 OpenMP 디버그 런타임 DLL 파일(Microsoft.VC100.DebugOpenMP\vcomp100d.dll)을 추가합니다.
추가로 배포할 파일에서 **<파일 편집…>**을 선택합니다. 그러면 파일 및 폴더 선택기 대화 상자가 열립니다.
파일 추가를 클릭하고 Microsoft.VC100.DebugOpenMP\vcomp100d.dll을 탐색하여 파일을 선택한 다음 열기를 클릭합니다.
예를 들어 x86 기반 컴퓨터에서 64비트 버전 Windows Server 2008 운영 체제의 기본 위치는 다음과 같습니다.
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC100.DebugOpenMP\ vcomp100d.dll
확인을 클릭하여 파일을 추가하고 파일 및 폴더 선택기 대화 상자를 닫습니다.
확인을 클릭하여 변경 내용을 저장하고 속성 페이지를 닫습니다.
병렬
for
루프의 본문 내에 중단점을 설정합니다.F5를 눌러 디버거를 실행합니다.
작업을 클러스터에 제출 중이므로 클러스터에 연결하려면 암호를 입력하라는 메시지가 표시됩니다. 암호를 입력한 다음 Enter 키를 누릅니다.
디버거를 실행한 후 프로세스 창을 보고 프로세스의 배치를 확인합니다. 각 프로세스마다 전송 한정자 열을 보고 프로세스가 실행 중인 계산 노드를 확인합니다.
부록: 응용 프로그램 이진 및 CRT(요청된 경우) 외에 Visual Studio가 배포하는 파일
DebuggerProxy.dll
DebuggerProxy.dll.manifest
Delete_from_workdir.bat: 배포된 파일을 삭제하는 스크립트
Deploy_to_workdir.bat: 배포 디렉터리에서 작업 디렉터리로 파일을 복사하는 스크립트
dbghelp.dll
mcee.dll
Mpishim.bat: 원격 디버거를 시작하는 스크립트
Mpishim.exe: IDE와 Msvsmon.exe 간의 통신을 조정하는 프로그램
Msvsmon.exe: 원격 디버거
Msvsmon.exe.config
PfxTaskProvider.dll
symsrv.dll
symsrv.yes
vbdebug.dll
1042\msdbgui.dll
1042\vbdebugui.dll
참고 항목
개념
MPI 클러스터 디버거 구성 속성
HPC 클러스터에서 MPI 응용 프로그램 디버깅