操作实例:在 Visual Studio 2010 中启动 MPI 群集调试程序

在此操作实例中,您将了解如何在本地计算机和 Microsoft Windows HPC Server 2008 群集上设置和启动 MPI 群集调试程序会话。此操作实例包括使用消息传递界面 (MPI) 和打开多进程 (OpenMP) 应用程序编程界面 (API) 创建应用程序所需的步骤和示例代码。

本指南内容:

  • 使用 MPI 群集调试程序的要求

  • 在 Visual Studio 2010 中创建 C++ MPI 示例项目

  • 配置和启动 MPI 群集调试程序

  • 附录:除应用程序二进制外,Visual Studio 部署的文件和 CRT(如果要求)

使用 MPI 群集调试程序的要求

  • 您的开发计算机上必须安装了 Visual Studio 2010 Professional Edition 或更高版本(包括远程调试程序)。

  • 您必须在群集上拥有管理权限。

  • Visual Studio 必须能够访问要运行调试会话的计算节点。以下方案提供了所需的访问权限:

    • 您在群集头节点或专用登录节点上开发应用程序。

    • 所使用群集的计算节点被连接到企业网络(拓扑 2、4 或 5),并且您的开发计算机连接到了相同的域或是与群集域有信任关系的域。

  • 要从客户端计算机将应用程序提交到 HPC 群集,您必须安装了 Microsoft HPC Pack 2008。

  • 要使用 Microsoft 消息传递界面构建 MPI 程序,需要在开发计算机上安装 Windows HPC Server 2008 SDK。

在 Visual Studio 2010 中创建 C++ MPI 示例项目

本部分的示例代码用于一个使用 Monte Carlo 模拟估算 Pi 值的并行应用程序。

示例代码在每个 MPI 进程上运行 50,000,000 次迭代计算。在每次迭代计算中,示例代码都生成间隔为 [0,1] 的随机数字以确定一组 xy 坐标。然后评估坐标组以确定点是否位于行 x2 + y2 = 1 下。如果点位于该行下,则变量 count 将加一。每个 MPI 的值 count 的总和将成为变量 result。将位于行 (result) 下的总点数乘以四,然后再用所乘的结果除以总迭代计算次数以估算 Pi 值。

以下步骤包含 Monte Carlo 模拟的两个实现。

创建示例项目的步骤

  1. 运行 Visual Studio 2010。

  2. 创建一个名为 ParallelPI 的新 C++ Win32 控制台应用程序。使用一个无预编译头的项目。

    1. 在“文件”菜单上,指向“新建”****,然后单击“项目”。

    2. 在“新建项目”对话框中,单击“已安装的模板”,然后选择“Visual C++”。 (根据 Visual Studio 的设置方式,“Visual C++”可能位于“其他语言”****节点下。)

    3. 在模板列表中,单击“Win32 控制台应用程序”。

    4. 关于项目名称,请键入:ParallelPI.

    5. 单击“确定”。此操作将打开“Win32 控制台应用程序”向导。

    6. 单击“下一步”****。

    7. 在“应用程序设置”中的“其他选项”****下,清除“预编译头”复选框。

    8. 单击“结束”****以关闭向导并创建项目。

  3. 指定此项目的其他属性。

    1. 在“解决方案资源管理器”中,右键单击 Parallel PI,然后单击“属性”。此操作将打开“属性页”****对话框。

    2. 展开“配置属性”,然后选择“VC++ 目录”****。

      在“包含目录”中,将指针指到文本框中显示的列表的开头,然后指定 MS MPI C 标头文件的位置,随后添加英文分号 (;)。例如:

      C:\Program Files\Microsoft HPC Pack 2008 SDK\Include;
      
    3. 在“库目录”中,****将指针指到文本框中显示的列表的开头,然后指定 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;
      
    4. 在“链接器”下,选择“输入”****。

      在“附加依赖关系”中,将指针指到文本框中显示的列表开头,然后键入:

      msmpi.lib;

    5. 如果您使用的是 OpenMP 代码示例:

      在“配置属性”中展开 “C/C++”,然后选择“语言”

      在“打开 MP 支持”中,选择“是(/openmp)”****以启用编译器对 OpenMP 的支持。

    6. 单击“确定”关闭属性页。

  4. 在主要源文件中,选择全部代码,然后将其删除。

  5. 将以下示例代码之一粘贴到空的源文件中。第一个示例使用的是 MPI 和 OpenMP,第二个示例使用的是 MPI 和并行格式库 (PPL)。

    以下代码示例使用的是 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;
    }
    

     

    以下代码示例使用并行格式库 (PPL) 而非 OpenMP,使用 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();
    
    }
    
  6. 在“文件”****菜单上单击“全部保存”。

  7. 在“生成”****菜单上单击“重新生成解决方案”。

配置和启动 MPI 群集调试程序

在构建您的应用程序后,即可配置和启动调试程序。本节介绍了用于调试的三个选项:

  • 在本地计算机上调试一个 MPI 进程

  • 在本地计算机上调试多个 MPI 进程

  • 在群集上调试一个或多个 MPI 进程

提示

在 MPI 群集调试程序中,不可未经过调试即启动。按 Ctrl+F5(或在“调试”菜单上选择“启动时不要进行调试”)也可以启动调试。

在本地计算机上调试一个 MPI 进程

要在本地计算机上仅使用 MPI 进程进行调试,请使用与要调试任何其他应用程序相同的进程。在程序中希望的位置设置一个断点,然后按 F5 启动调试程序。

MPI 程序通过端口上的 IP 进行通讯。首次启动 MPI 程序时,可能会出现一条防火墙安全警告,显示已经打开一个端口。阅读警告消息并确保了解了您对系统所做的更改。您必须解除防火墙锁定以继续在本地计算机上调试。

在本地计算机上调试多个 MPI 进程

以下步骤描述了如何为 ParallelPI 启动本地调试会话。

在运行着四个 MPI 进程的本地计算机上启动 MPI 群集调试程序的步骤

  1. 在“解决方案资源管理器”中,右键单击 Parallel PI,然后单击“属性”。此操作将打开“属性页”****对话框。

  2. 展开“配置属性”,然后选择“调试”****。

  3. 在“要启动的调试程序”中,选择“MPI 群集调试程序”****。

  4. 在“运行环境”中,从下拉列表选择“编辑 Hpc 节点”****。此操作将打开“节点选择器”对话框。

  5. 在“头节点”****下拉列表中选择“localhost”。

  6. 在“进程数”****中选择“4”。

  7. 单击“确定”****保存更改并关闭“节点选择器”对话框。

  8. ParallelPI 将接受一个确定要运行的迭代计算次数的参数。默认次数为 50,000,000。对于本地调试会话,请根据下面的步骤将迭代次数减少为 5,000:

    在“应用程序参数”中键入 5000

  9. 单击“确定”保存更改并关闭“属性页”****。

  10. 在并行 for 循环代码中设置一个断点。

  11. 按 F5 启动调试程序。

  12. 将显示五个控制台窗口:一个 cmd.exe 窗口和四个 ParallelPI.exe 窗口(每个窗口用于您启动的每个进程)。与排名 0 进程对应的控制台窗口显示了迭代计算数和所计算的 Pi 估值。

  13. 在“调试”菜单上单击“窗口”****,然后单击“进程”。

  14. 通过双击“进程”****窗口中的一个进程以设置用于调试的活动进程。

提示

当调试多个进程时,默认情况下断点将会影响所有正在调试的进程。为避免在不希望的位置中断各个进程,请取消选择“一个进程中断时中断所有进程”选项。(在“工具”菜单上单击“选项”,然后选择调试)。有关如何更改中断行为的详细信息,请参阅如何:中断执行

在群集上调试一个或多个 MPI 进程

在群集上启动 MPI 调试程序时,调试程序会将您的应用程序作为作业提交给群集。与您的项目 (x86 或 x64、调试或发布)匹配的 Visual C 运行时必须位于计算节点的工作目录中。如果计算节点上没有这些正确的运行时,需通过指定“要部署的其他文件”属性将这些运行时纳入调试程序部署中。以下过程包含部署 OpenMP 调试运行时 DLL 的一个步骤。默认情况下,当启动 MPI 群集调试程序时,C 运行时 (CRT) 库将被部署。如果无正确的运行时,则尝试运行应用程序时将会出现并列错误。如果未包含 OpenMP 运行时,将不会调用断点。

在群集上启动 MPI 调试程序

  1. 在“解决方案资源管理器”****中,右键单击 Parallel PI,然后单击“属性”。此操作将打开“属性页”****对话框。

  2. 展开“配置属性”,然后选择“调试”****。

  3. 在“要启动的调试程序”中,选择“MPI 群集调试程序”****。

  4. 在“运行环境”中,从下拉列表选择“编辑 Hpc 节点”****。此操作将打开“节点选择器”对话框。

  5. 在“头节点”****下拉列表中,为群集选择要使用的头节点名称。

    头节点列表已从 Active Directory 域控制器填充。仅域中的群集会显示在列表中。如果看不到您的头节点,请在属性字段中键入头节点名称或 IPv4 地址。

  6. 在“进程数”中选择“4”****。

  7. 在“计划进程”中,选择如何分配您的进程。您可以选择每“核”、“套接字”或“节点”分配一个进程****。

  8. 单击“确定”保存更改并关闭“节点选择器”****对话框。

  9. 在“部署目录”中,在头节点上指定一个共享目录。如果部署目录不存在,并且您拥有指定根目录的写入权限,将自动创建部署目录。

    当 HPC Pack 2008 包安装到头节点上时,将创建 CcpSpoolDir 共享资源目录。例如,键入以下内容,其中 <myHeadNode> 是您使用的群集名称:

    \\<myHeadNode>\CcpSpoolDir\

  10. 在“工作目录”中,在每个计算节点上指定一个本地工作目录。例如,键入以下内容,其中 <myUserName> 是您的用户名:

    C:\Users\<myUserName>\ParallelPI

  11. 如果使用的是含 OpenMP 的示例代码,请添加 OpenMP 调试运行时 DLL 文件 (Microsoft.VC100.DebugOpenMP\vcomp100d.dll):

    1. 在“要部署的其他文件”中,选择<编辑文件…>****。此操作将打开“文件和文件夹选择器”对话框。

    2. 单击“添加文件”****,导航到 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

    3. 单击“确定”添加文件并关闭“文件和文件夹选择器”****对话框。

  12. 单击“确定”保存更改并关闭“属性页”****。

  13. 在并行 for 循环代码中设置一个断点。

  14. 按 F5 启动调试程序。

  15. 由于您向群集提交了一份作业,因此系统会提示您输入连接到群集的密码。键入密码,然后按 Enter。

  16. 启动调试程序后,请检查进程窗口以验证各个进程的位置。对于每个进程,可通过“传输限定符”列查看进程运行的计算节点。

附录:Visual Studio 部署了除应用程序二进制外的文件和 CRT(如果要求)

  • 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

  • 2052\msdbgui.dll

  • 2052\vbdebugui.dll

请参见

概念

为 MPI 群集调试程序配置属性
在 HPC 群集上调试 MPI 应用程序

其他资源

调试程序路线图
mpiexec Command Reference