C - OpenMP、MPI、串行程序

计算科学 流体动力学 并行计算 C mpi
2021-12-11 14:05:38

我是计算科学课程的一部分,并且来自非编程背景,所以请原谅我的无知。我正在编写一组 C 代码,以数值求解计算流体动力学课程的 Navier Stokes 方程。作为我们任务的一部分,我必须编写在单个处理器(串行)上运行的代码,并提供使用 OpenMP 和/或 MPI 并行化相同代码的选项(以利用多个处理器,具体取决于用户在运行时间的开始。

我希望社区对此提供建议,但我有几个问题:

我应该为我的程序考虑 if/else 结构吗?(程序需要转向串行代码,或启用 MPI 的代码,或 OpenMP 代码,或 MPI+OpenMP 的混合,这意味着大部分代码将被复制)。我是否应该构建将被复制为单独的函数的操作,并在需要它们的运行时调用这些函数?(但据我了解,函数调用通常会增加我的代码的开销,无论选择什么选择)有没有办法隐藏预处理器指令或与 MPI 相关的函数(例如 MPI_Init())所以它们会根据用户在开始或运行时所做的选择方便地出现或消失吗?最后,此任务的目的是衡量使用串行代码、对比 OpenMP、对比 MPI 的性能优势,与 MPI+OpenMP 相比。我试图避免代码重复是否有意义,因为组合代码(以提高可读性)会不必要地增加我的串行代码的开销?例如与 MPI 相关的函数调用,可能有许多 if/else 条件,这会扭曲我的串行代码的性能测量,而程序开头的 if/else 结构将其引导到完全独立的路径(如第一季度) )。

我希望我已经问了一个明确的问题并遵守了社区的标准。非常感谢你。

(我最初在我认为是 stackoverflow 的一般页面上问了这个问题,并被定向到这里。) https://stackoverflow.com/questions/24340121/c-openmp-mpi-serial-program

3个回答

我认为你的一些问题比其他问题更重要,你的一些重点是错误的。在追求开销时,您有使程序无法维护的危险。编写一个通用程序并将多余的精力放在更有趣的地方更容易。我为这样的自以为是而道歉。

如果语句。从严格的编程角度来看,在代码中包含大的 if-else 块是没有问题的。如果您的代码正确,那有什么关系?另一方面,如果您没有正确获取代码,则会出现问题。if-else 块的情况下,程序的执行可以采用大约条不同的路径。并行性支持(条路径)的选择不是您的程序将做出的唯一选择。这些路径可能有不同的结果/行为(如果你不这么认为,你可能无法确定),这意味着你必须确保这些路径中的每一个都正常工作,这需要大量的测试。n2n4

因此,典型的建议是尽量减少可以通过您的程序采用的不同路径的数量。数量越少,需要检查的就越少。

串行/OMP/MPI/OMP+MPI编写同一个程序的四个不同版本,它们都需要功能相同,这听起来像是一个愚蠢的想法。这并非不可能,但为什么呢?确保它们完全一样工作的额外努力浪费了。尝试最大化它们之间共享的代码量,尤其是数学复杂的代码。把额外的精力花在有趣的事情上。

开销在一个数值密集型程序中,调用几个函数的开销相对于计算工作来说绝对是微不足道的,而“不需要通信”的开销几乎为零。我不会担心一些函数调用。这在未优化的紧密内部循环中很重要,但对于 API 调用,这不是考虑因素。

MPI。我不知道如何轻松地编写一个程序,使完全相同的程序可以轻松地在 MPI 和没有任何 MPI 的情况下编译和工作。尝试使用 ifdef 隐藏所有 MPI 调用可能需要付出太多努力。

一种方法是拥有两个程序,MPI 和非 MPI,它们依赖于相同的核心数学例程。另一种方法是确保串行版本与在单个进程中运行的 MPI 版本相同。您需要注意的唯一开销是设置 MPI 环境的开销:您需要确保它不包含在计算、MPI 或非 MPI 的任何计时结果中。“通信字节”的开销应该为零。0

开放MP。考虑到 OpenMP 的并行性有多少是通过编译器编译指示访问的,而不是显式的 API 调用,许多 OpenMP 程序将在有或没有 OpenMP 支持的情况下进行编译。用 ifdefs 隐藏 OpenMP API 调用也可能不值得:只用一个线程运行程序。

当您说您需要四个代码时,您的描述中的某些内容听起来不对劲。你可能想从你的导师那里得到澄清。

为任何“OMP_NUM_THREADS=n mpiexec -np m”调用产生正确结果的 MPI+OpenMP 代码是否能满足您的任务?这将允许在运行时从单个二进制文件进行串行、并行、多线程和混合操作。

为简单起见,假设您有一个 1D 问题,并在 N 个网格点上对其进行离散化。您的代码将有一堆从 1 到 N 的循环来执行模板操作——假设您没有进行隐式时间步进,因为解决线性系统直接使故事变得相当复杂。

通过这种设置,串行、OpenMP 和 MPI 版本看起来非常相似。

  • 您的串行版本将具有从 1 到 N 的循环。
  • 您的 OpenMP 版本具有完全相同的循环,不同之处在于它们是并行的。
  • 有趣的是,MPI 版本仍然具有相同的循环,但现在您的 1..N 空间被划分为您的处理器

好的?因此,在所有三种情况下,您的大部分代码看起来都是一样的。现在对于 MPI 案例,这是一个困难的案例。你的大循环在进程中被划分,所以你会认为一个进程获得索引 100-200 例如。是的,但只是在比喻意义上。使用 MPI,每个进程都是独立的,因此它认为它的地址为 0-100,您需要根据需要将其转换回全局编号。

MPI 的真正问题是,当您应用模板时,您可能需要一些存在于另一个进程上的数组组件。上例中的元素 99 和 201。这就是您需要在执行模板代码之前执行一些发送和接收操作的地方。

所以你只有一个代码。你能用三种方式编译它吗?是的,只是通过它洒一些#ifdef指令。那部分很简单。