mpi4py中异形NumPy数组的集合操作:gather与Gatherv详解


mpi4py中异形numpy数组的集合操作:gather与gatherv详解

本文深入探讨了在mpi4py中使用`comm.Gather`处理不同形状NumPy数组时遇到的挑战,并提供了两种有效的解决方案:利用`comm.gather`收集通用Python对象后进行拼接,以及使用`comm.Gatherv`直接将不同大小的数组高效地集合到一个预分配的NumPy缓冲区中。文章将详细阐述这两种方法的实现细节、适用场景及代码示例,帮助开发者优化并行程序的集合通信效率。

在并行计算中,经常需要在各个进程(或核心)上处理数据,然后将这些分散的结果收集到根进程上进行进一步的分析或整合。mpi4py库提供了强大的MPI(Message Passing Interface)绑定,使得Python程序能够方便地进行并行化。其中,comm.Gather是一个常用的集体通信操作,用于将所有进程的相同类型和形状的数据收集到根进程的一个连续缓冲区中。

然而,当每个进程需要发送的NumPy数组形状不一致时,直接使用comm.Gather会导致程序失败,因为它期望所有发送的数据都具有相同的维度和大小。本文将介绍两种在mpi4py中有效处理不同形状NumPy数组集合操作的方法:comm.gather(小写g)和comm.Gatherv(大写G,小写v)。

1. 问题背景:comm.Gather的局限性

comm.Gather操作的本质是将所有进程的相同类型数据按顺序收集到根进程的一个预定义缓冲区中。这意味着每个发送进程的数据必须是同构的,即具有相同的形状和数据类型。

考虑以下示例,其中不同进程生成了形状不同的NumPy数组:

from mpi4py import MPI
import numpy as np

comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()

# rank 1生成(2,3)的数组,其他进程生成(5,3)的数组
a = np.zeros((2 if rank == 1 else 5, 3), dtype=float) + rank
print(f"Rank {rank}: 数组形状 {a.shape}, 数据:\n{a}")

# 尝试使用comm.Gather,这通常会失败
# b = np.zeros((12, 3), dtype=float) - 1 # 假设一个足够大的接收缓冲区
# comm.Gather(a, b, root=0)
# if rank == 0:
#     print(f"Rank {rank}: 接收到的数据:\n{b}")

运行上述代码中被注释掉的comm.Gather部分,会因为数组形状不匹配而导致运行时错误。为了解决这个问题,我们需要采用更灵活的集合通信方法。

2. 解决方案一:使用 comm.gather 收集通用Python对象

comm.gather(注意是小写g)是mpi4py中一个更通用的集合操作。它不局限于NumPy数组,可以收集任何可序列化的Python对象。当每个进程发送的NumPy数组形状不同时,comm.gather会将其作为独立的Python对象进行收集,并在根进程上返回一个包含这些对象的列表或元组。随后,我们可以使用numpy.concatenate将这些数组拼接起来。

Decktopus AI Decktopus AI

AI在线生成高质量演示文稿

Decktopus AI 153 查看详情 Decktopus AI

2.1 实现细节

  1. 发送阶段: 每个进程将自己的NumPy数组a作为独立的Python对象发送。
  2. 接收阶段: 根进程接收所有发送的数组,并将它们存储在一个Python列表(或元组)中。
  3. 后处理: 根进程使用np.concatenate()函数将列表中的所有NumPy数组沿指定轴拼接成一个大的NumPy数组。

2.2 代码示例

import numpy as np
from mpi4py import MPI

comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()

# rank 1生成(2,3)的数组,其他进程生成(5,3)的数组
a = np.zeros((2 if rank == 1 else 5, 3), dtype=float) + rank
print(f"Rank {rank}: 数组形状 {a.shape}, 数据:\n{a}")

# 使用comm.gather收集不同形状的数组
# 根进程会收到一个包含所有数组的列表
gathered_arrays = comm.gather(a, root=0)

if rank == 0:
    print(f"\nRank {rank}: 原始收集到的数据 (列表形式):\n{gathered_arrays}")
    # 将收集到的数组列表拼接成一个大数组
    concatenated_array = np.concatenate(gathered_arrays, axis=0) # 沿0轴拼接
    print(f"\nRank {rank}: 拼接后的数据形状 {concatenated_array.shape}, 数据:\n{concatenated_array}")
else:
    # 非根进程的gathered_arrays为None
    print(f"Rank {rank}: 非根进程,gathered_arrays为 {gathered_arrays}")

2.3 适用场景与注意事项

  • 优点: 实现简单,代码直观,无需预先计算每个进程发送的数据大小和偏移量。适用于数据量不是特别巨大,或者需要灵活处理不同类型对象的场景。
  • 缺点: 涉及到Python对象的序列化和反序列化,以及后续的np.concatenate操作,可能会引入额外的性能开销,尤其是在数据量非常大时。此外,根进程需要足够的内存来存储所有单独的数组,然后再进行拼接。

3. 解决方案二:使用 comm.Gatherv 直接集合到NumPy数组

comm.Gatherv(注意是Gatherv)是comm.Gather的变体,专门设计用于处理每个进程发送数据大小不同的情况。它允许将来自不同进程的、大小不一的数据直接集合到根进程的一个预分配的NumPy数组中,而无需中间的Python对象列表和后续的拼接操作。这通常在性能要求较高的场景下更为高效。

3.1 comm.Gatherv的接收缓冲区参数

comm.Gatherv的接收缓冲区参数比comm.Gather复杂,它是一个元组,通常格式为 (recvbuf, recvcounts, displs, recvtype):

  • recvbuf:根进程上的目标NumPy数组,必须预先分配好,且大小足以容纳所有进程发送的数据。
  • recvcounts:一个列表或NumPy数组,长度等于进程总数。每个元素表示从对应进程接收的元素数量(不是字节数)。
  • displs:一个列表或NumPy数组,长度等于进程总数。每个元素表示从对应进程接收的数据在recvbuf中的起始元素偏移量(不是字节偏移量)。
  • recvtype:接收数据的MPI数据类型(例如MPI.DOUBLE对应float64,MPI.INT对应int32)。

3.2 实现细节

  1. 预计算: 在所有进程上(或至少在根进程上),需要预先计算好每个进程将发送的元素数量 (recvcounts) 以及这些数据在根进程的接收缓冲区中的起始偏移量 (displs)。
  2. 根进程预分配: 根进程需要预先分配一个足够大的NumPy数组作为recvbuf。
  3. 调用 comm.Gatherv: 所有进程调用comm.Gatherv,并由根进程提供接收缓冲区的详细信息。

3.3 代码示例

为了简化recvcounts和displs的计算,以下示例假设只有两个进程(size

import numpy as np
from mpi4py import MPI

comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()

# 示例限制为两个进程,以便手动设置recvcounts和displs
assert size <= 2, "此Gatherv示例仅适用于2个或更少的进程"

if rank == 0:
    a = np.zeros((5, 3), dtype=float) + rank
else: # rank == 1
    a = np.zeros((2, 3), dtype=float) + rank

print(f"Rank {rank}: 数组形状 {a.shape}, 数据:\n{a}")

# 定义全局总行数 (5来自rank 0, 2来自rank 1)
n_global_rows = 7
# 定义每行元素数
cols = a.shape[1]

# 根进程需要预分配接收缓冲区
if rank == 0:
    b = np.zeros((n_global_rows, cols), dtype=float)
    # 计算每个进程发送的元素数量
    # rank 0: 5行 * 3列 = 15个元素
    # rank 1: 2行 * 3列 = 6个元素
    recvcounts = [5 * cols, 2 * cols] # 对应每个进程的元素总数

    # 计算每个进程数据在b中的起始偏移量 (元素偏移量)
    # rank 0: 从b的0偏移量开始
    # rank 1: 从b的第15个元素 (即第5行3列后) 开始
    displs = [0, 5 * cols] 

    # 组合Gatherv的接收缓冲区参数
    recvbuf_params = (b, recvcounts, displs, MPI.DOUBLE)
else:
    b = None
    recvbuf_params = None # 非根进程不需要提供接收缓冲区参数

# 执行Gatherv操作
comm.Gatherv(a, recvbuf_params, root=0)

if rank == 0:
    print(f"\nRank {rank}: Gatherv接收到的数据形状 {b.shape}, 数据:\n{b}")
else:
    print(f"Rank {rank}: 非根进程,b为 {b}")

3.4 适用场景与注意事项

  • 优点: 效率高,数据直接传输到预分配的NumPy数组,避免了Python对象的序列化/反序列化和后续拼接的开销。适用于处理大规模NumPy数组,对性能要求高的场景。
  • 缺点: 实现相对复杂,需要精确计算recvcounts和displs。在实际应用中,通常需要先进行一次comm.gather或comm.allgather操作来收集所有进程的数组形状信息,然后根进程根据这些信息计算recvcounts和displs。
  • 数据类型匹配: recvtype参数必须与NumPy数组的dtype精确匹配。MPI.DOUBLE对应np.float64,MPI.FLOAT对应np.float32,MPI.INT对应np.int32等。
  • 元素计数与偏移: recvcounts和displs中的值都是元素数量,而不是字节数。例如,一个形状为(5, 3)的数组,其元素数量是15。

4. 总结与选择建议

当需要在mpi4py中将不同形状的NumPy数组收集到根进程时:

  • comm.gather (小写g):

    • 适用场景: 数据量相对较小,对代码简洁性要求更高,或者需要收集的不仅仅是NumPy数组,而是各种Python对象。
    • 优点: 简单易用,无需复杂的参数计算。
    • 缺点: 性能开销可能较高,需要额外的np.concatenate步骤。
  • comm.Gatherv (大写G,小写v):

    • 适用场景: 处理大规模NumPy数组,对性能有严格要求,且可以预先计算出每个进程发送的数据大小和偏移量。
    • 优点: 效率高,直接将数据写入预分配的缓冲区。
    • 缺点: 实现复杂,需要精确计算recvcounts和displs。

在实际开发中,应根据具体的应用需求(数据规模、性能要求、代码复杂度等)权衡选择合适的方法。对于大多数情况,如果性能不是极致瓶颈,comm.gather配合np.concatenate是一个简单有效的方案。而对于高性能计算场景,comm.Gatherv则是更专业的选择。

以上就是mpi4py中异形NumPy数组的集合操作:gather与Gatherv详解的详细内容,更多请关注其它相关文章!


# 几种  # seo主要学些什么  # 肇东网络推广网站  # 北京抖音营销推广咨询报价  # 安徽抖音seo是什么  # 虎门seo矩阵获客系统  # 医院网站建设标准数据表  # seo市场推广  # 网站建设团队意义  # 陕西网站建设680元  # 湖南郴州网站建设  # 自己的  # python  # 较高  # 两种  # 浮点  # 是一个  # 区中  # 序列化  # 适用于  # 偏移量  # red  # python程序  # 字节 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: WooCommerce购物车:强制显示所有交叉销售商品教程  支付宝网页版在线入口 支付宝官网电脑登录入口  《东方财富》条件单关闭方法  汽水音乐官方网站登录入口_汽水音乐网页版进入链接  疯狂小鸟微信小游戏入口 疯狂小鸟网页版秒玩  Flexbox布局:实现粘性导航与底部页脚的完美结合  B站怎么快速升级 B站用户等级提升攻略【详解】  《长生:天机降世》火塔小怪大全  ToDesk远程摄像头功能使用方法_ToDesk远程视频画面查看设置教程  Windows Audio服务启动失败怎么办_电脑没声音的终极服务修复法【修复】  《随手记》备份数据方法  在J*a中如何实现类的继承与方法重用_OOP继承方法重用技巧分享  CSS动画如何实现图标旋转并放大_transform rotate scale @keyframes实现  晓晓优选app支付宝绑定方法  手机雨课堂网页版入口免登录 雨课堂网页版可点击直接进入  《洛克王国:世界》国家队搭配攻略  windows server2019显卡驱动怎么安装_winserver2019显卡驱动安装与远程桌面优化  《小宇宙》标记不友善评论方法  猫眼电影app如何筛选支持退改签的影院_猫眼电影退改签影院筛选方法  123平台官方登录入口 123邮箱网页端在线沟通工具  德邦物流在线查询系统 德邦快递货物运输追踪  HTML中多图片上传与预览:解决ID冲突的专业指南  《豆瓣》私信用户方法  济南公交卡手机充值指南  WooCommerce 购物车:始终显示所有交叉销售商品  《procreate》绘制渐变效果教程  51漫画网实时入口 51漫画网页版官方免费漫画入口  Win11便笺在哪打开 Win11桌面便笺(Sticky Notes)使用方法【详解】  WooCommerce 新客户订单自动添加管理员备注教程  J*aScript模拟悬停与点击:自动化网页动态元素交互指南  路由器DNS怎么设置最快 优化DNS提升上网速度教程  126手机126邮箱登录_126邮箱手机登录入口官网  谷歌学术论文搜索引擎 谷歌学术官网入口论坛永久链接  纯CSS实现自适应宽度与响应式布局的水平按钮组  抖音猜你想搜能说明对方搜过吗  汽水音乐网页端访问 汽水音乐官方网页直达  c++如何链接Boost库_c++准标准库的集成与使用  《via浏览器》强制缩放网页设置方法  微信朋友圈怎么设置三天可见 微信朋友圈设置指定天数可见步骤【教程】  《百果园》充值余额方法  铁路12306官网入口 铁路12306中国铁路官网登录首页  荣耀magicv5怎么上手测评  优化2xN网格最大路径和的动态规划算法实践  青橙手机语音助手怎么唤醒_青橙手机语音助手设置与唤醒方法  VS Code的时间线(Timeline)视图:您的代码时光机  小红书网页版首页入口 小红书网页版电脑端官方登录链接  微信注销后银行卡解绑了吗_微信注销后银行卡解绑状态  Win11如何分屏操作_Win11多窗口分屏技巧  12306APP选座怎么选充电位置_12306APP带充电插座座位选择方法与技巧  PHP魔术方法__set与__isset:设计考量、性能权衡与静态分析的视角 

 2025-12-04

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.