【问题标题】:Intel MPI distributed memory: building a wall out of M*N blocks using q<M processors英特尔 MPI 分布式内存:使用 q<M 个处理器用 M*N 块构建一堵墙
【发布时间】:2026-01-18 14:10:01
【问题描述】:

假设我有 M 个独立的工作,每个工作有 N 个步骤。作业彼此独立,但每个作业的步骤应该是连续的。换句话说,J(i,j) 应该在 J(i,j-1) 完成之后才开始(i 表示作业索引,j 表示步骤)。这与建造一堵宽 M 高 N 块的墙是同构的。

每个作业块只能执行一次。使用一个CPU做一个block的工作(也是同一个顺序)所花费的时间对于不同的block来说是不同的,并且是事先不知道的。

使用 MPI 执行此操作的简单方法是将工作块分配给处理器,并等到它们全部完成它们的块后再进行下一次分配。这样我们可以确保执行优先级,但会有很多等待时间。

有没有更有效的方法来做到这一点?我的意思是当一个处理器完成它的工作时,使用某种环境变量或共享内存,它可以决定它接下来应该执行哪个工作块,而无需等待其他处理器完成它们的工作并使用通信做出集体决定。

【问题讨论】:

  • 这听起来类似于 h.265(视频编解码器)对 Wavefront Parallel Processing 所做的事情,其中​​每个视频块都依赖于其上方和左侧的块。将依赖项限制为该模式允许比任意依赖项更多的并行性。您可能想看看该系统是如何设计为您的想法的,除了您显然在工作之间没有任何依赖关系,但您仍然希望它们相互等待。
  • 为什么所有 M 个工作都处于相似的进度阶段很重要?如果 M 中的某些作业在其他作业完成之前才开始(即将所有 N 个步骤放入单个作业调度器作业中),是否可以?或者这会导致您只剩下几个串行作业,因此您无法利用所有 CPU?根据每个步骤的大小,缓存可能很重要,因此在同一台机器(甚至同一集群节点的相同 CPU)上对相同数据执行多个步骤可能很重要。
  • 另一种简单的方法可能是将一个 CPU(例如 p0)分配为调度器/仲裁器,这样每个 CPU 都需要在空闲时进行注册。 p0 可以自适应地对作业/块进行排序并将它们分配给 CPU。这将引入一些通信开销的想法。类似的事情可以用共享内存来做,谁有空,谁就拿下一个块,而创建“订单”以方便挑选的任务是共享的
  • @peterCordes 工作是否处于相似阶段并不重要,如果 M = integer * num_cpu,那会起作用(即使没有优化),
  • @makadev 共享内存的解决方案很有趣,如果我可以有一些共享变量,它们会在所有处理器中立即更新(如果它们被特定的 cpu 更改)我认为有可能找到一个好的解决方案。不确定英特尔 MPI 是否可能有这样的变量。

标签: mpi intel


【解决方案1】:

您有 M 个作业,每个作业有 N 个步骤。您还有一组大小为 W 的工作进程,介于 2 和 M 之间。

如果 W 接近于 M,您能做的最好的事情就是简单地按 1:1 分配它们。如果一名工人提前完成,那很好。

如果 W 比 M 小很多,而 N 也相当大,这里有个思路:

  1. 估计完成一个步骤的一些平均或典型时间。将此称为 T。您可以随时调整此估算值,以防一开始的估算值很差。
  2. 在工人之间平均分配 M 个工作,然后启动它们。告诉工人在超时之前尽可能多地运行分配的作业,比如 T*N/K。允许稍微超过超时以完成当前作业,以确保前进。
  3. 让工作人员相互交流他们完成了哪些步骤。
  4. 重复,再次平均分配作业,同时考虑到每个作业的完成程度(例如,两个 50% 完成的作业与一个 0% 完成的作业相同)。

这个想法是给所有工人足够的时间来完成每次大约 1/K 的总工作。如果没有工作比 K*T 花费更多,这将是非常有效的。

你可以自己找一个合理的 K。也许试试 10。

【讨论】:

  • 在某些方面与我的回答类似,我在其中提出了一个不要让最不完整的任务步骤计数的想法。我敢肯定,关于它们在行为上的不同之处有一些有趣的说法,但我现在是在画一个空白。
  • @JohnZwinck 我喜欢这个想法,如果在 MPI 中没有使用某种共享变量的解决方案,那么统计最小化确实是唯一的解决方案。
  • @AmirHajibabaei:MPI 用于消息传递,而不是共享内存。例如,如果您想要后者,您可以阅读 RDMA。
  • @AmirHajibabaei,MPI 提供对共享内存的可移植访问和远程内存访问。前者仅适用于共享单个节点的进程,而后者效率不是很高,除非有少数 HPC 供应商 MPI 实现 + 相应的硬件。指定一个职级担任工作调度员通常是最简单的解决方案。该等级还可以在单​​独的线程中执行工作或使用非阻塞机制,如MPI_Iprobe
【解决方案2】:

这是一个想法,IDK,如果它好的话:

维护一个共享变量:n = 最落后任务的进度。即任何 M 个任务已完成的最低步数。它从 0 开始,因为所有任务都从第一步开始。它保持为 0,直到所有任务都完成了至少 1 个步骤。

当处理器完成作业的一个步骤时,对照n 检查它当前正在处理的步骤的进度。如果n &lt; current_job_step - 4,请切换任务,因为我们正在处理的任务比最落后的任务领先太多。

我选择4 是为了在切换过多与仅在几个任务中进行过多串行工作之间取得平衡。根据需要进行调整,并可能在接近尾声时使其自适应。

在没有两个线程同时占用同一个工作单元的情况下切换任务并非易事,除非您有一个调度程序线程来做出所有决定。如果这是在单个共享内存机器上,您可以使用锁定来保护优先级队列。

【讨论】:

  • 这个想法很吸引人,但我不确定你将如何使用 MPI 来实现它。
  • @JohnZwinck:我已经有好几年没有研究 MPI 了,但我从来没有真正用它做过什么。如果每个工作人员都维护自己的每个工作的进度表,他们可以在每个步骤完成时进行广播,以便其他工作人员可以更新他们的表格。但是,如果没有两个工人要求相同的任务,您仍然会遇到切换任务的问题。对于共享内存来说,这是一个已解决的问题,如果 MPI 没有办法解决,我会感到震惊。
  • @AmirHajibabaei:仅在缓存一致的共享内存系统中,就像单个 SMP 机器上的多个线程一样。那么是的,您可以拥有原子无锁共享变量。 MPI 是为消息传递而设计的,而不是共享内存,所以即使你所有的 MPI 工作人员都在同一台机器上,AFAIK 也不会真正帮助你做到这一点。
  • @PeterCordes 是否可以在 MPI 中拥有共享变量,这些变量会在所有处理器中立即更新(如果它们被特定的 cpu 更改)?那将改变游戏规则。在调度器的情况下,通信会导致作业同步,除非我们指定一个主控器只做调度,如果我们有很多处理器,这可能是有效的。
  • @AmirHajibabaei:我刚刚回答了这个问题。您是否在单个多处理器机器上运行 MPI 作业?好的。还是您在集群上运行?那就不要。如果你分配一个线程来做调度,它可能大部分时间都在休眠,所以你应该相应地选择你的 MPI 作业的大小(可用内核数 + 1)。