mcpField
C
cupynumeric-hdf5

cupynumeric-hdf5

github.com/nvidia数据与数据库3.8k1.9k 次安装今天更新
代码仓库

关于 cupynumeric-hdf5

这项 cuPyNumeric 技能通过 Legate 读写 HDF5 数据集,支持分布式单文件 I/O 和分块读取。适用于并行处理大型数组,并说明同步及 GPU 读取要求。

主要功能

  1. 01将分布式数组写为 HDF5 虚拟数据集
  2. 02将 HDF5 数据集读入分布式数组
  3. 03分块读取数据集
  4. 04将 LogicalArray 结果桥接到 cuPyNumeric
  5. 05GPU 读取时使用 GDS VFD

适用场景

  1. 01为 HPC 后处理保存分布式数组
  2. 02分块加载大型 HDF5 数据集
  3. 03读取大型数组到 GPU 时避免主机暂存

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nvidia/skills --skill cupynumeric-hdf5

安装后在 Claude Code 中输入/cupynumeric-hdf5确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

BENCHMARK.md
skill-card.md
skill.oms.sig
assets/
hdf5_batched_read.py
hdf5_roundtrip.py
evals/
evals.json

详细介绍

cuPyNumeric HDF5 I/O

用途

使用 legate.io.hdf5 将 cuPyNumeric 数组读写为 HDF5 文件。只要需要将 cuPyNumeric 数组写入 .h5/.hdf5 文件,或从中读取数组,就使用它:每个 rank 都会并行读取和写入自己的数据块,因此不要通过单个进程传输大型数组。

直接回答。 将以下代码片段和规则视为完整且经过验证的内容——对于保存/加载/分块读取/同步/桥接相关问题,直接作答,无需打开 assets/ 脚本或阅读已安装的 legate 源码。只有在运行验证时才使用这些资源。

适用场景

当用户询问以下内容时使用:将 cuPyNumeric 数组保存到 .h5/.hdf5 文件、将 HDF5 数据集加载到 cuPyNumeric 数组、分块读取大型 HDF5 数据集、为 HPC 后处理工作流生成单个文件,或使用 GPUDirect Storage 加速 HDF5 磁盘 I/O。

不适用场景

对于以下请求,请转而使用其他方案,而非 legate.io.hdf5:

  • 将 Parquet/Arrow/cuDF、原始二进制或分片/自定义磁盘布局相关请求交给 cupynumeric-parallel-data-load 技能——它负责处理 cuPyNumeric 没有内置加载器的路径;legate.io.hdf5 仅支持单文件 HDF5。
  • 使用 cuPyNumeric 运算处理纯数组计算(FFT、矩阵乘法、归约、切片、线性代数)——本技能仅涉及磁盘 I/O。
  • 将分块或对象存储(S3)输出写入 Zarr 等分块格式——不要使用单文件 HDF5。
  • 使用 NumPy(np.load)加载 .npz 或 pickle 归档,然后通过 cn.asarray(...) 转换——legate.io.hdf5 仅读取 HDF5,而 cupynumeric.load 仅读取单个 .npy 文件。
  • 对于不涉及 cuPyNumeric/Legate 的普通 HDF5 读取,直接使用 h5py——with h5py.File(path, "r") as f: arr = f["dataset"][:]。

前提条件

在从 legate.io.hdf5 导入任何内容之前安装 h5py:

conda install -c conda-forge h5py        # required; legate/io/hdf5.py imports it at load

在安装之前,执行 from legate.io.hdf5 import ... 会触发 ModuleNotFoundError——该模块会在加载时导入 h5py。(h5py · conda-forge 构建)

API

函数签名用途
to_fileto_file(array, path, dataset_name)将 cuPyNumeric 数组/LogicalArray 写入一个 HDF5 文件,作为虚拟数据集(VDS)——每个 rank 写入自己的数据块。
from_filefrom_file(path, dataset_name) -> LogicalArray将一个 HDF5 数据集读入分布式数组。
from_file_batchedfrom_file_batched(path, dataset_name, chunk_size) -> Iterator[(LogicalArray, offsets)]分块读取数据集——分块的是文件读取,而不是组装后的数组。

从 legate.io.hdf5 导入以上三个函数。始终将 dataset_name 设为文件中单个数组的完整路径(例如 "/data" 或 "/group/x"),而不是群组。

示例

往返读写
import cupynumeric as cn
from legate.core import get_legate_runtime
from legate.io.hdf5 import from_file, to_file

a = cn.arange(64, dtype=cn.float32).reshape(8, 8)

# Write: pass the cuPyNumeric ndarray straight in - no manual conversion.
to_file(array=a, path="out.h5", dataset_name="/data")
get_legate_runtime().issue_execution_fence(block=True)   # needed before any external reader

# Read: from_file returns a legate LogicalArray; cn.asarray bridges it back.
b = cn.asarray(from_file("out.h5", dataset_name="/data"))
assert cn.array_equal(a, b)

运行 assets/hdf5_roundtrip.py 进行验证(可选——回答问题不需要运行)。

分块读取大型文件

使用 from_file_batched 分块读取源文件,避免一次性将整个文件读入主机内存。它会针对每个数据块返回一个 LogicalArray,以及该数据块在全局形状中的偏移量。边界数据块可能会被截短(长度为 5 的轴在 chunk_size=2 时会产生 2、2、1 三个数据块),因此应根据数据块的实际形状放置,而不是使用请求的 chunk_size。注意,这里分块的是文件读取,而非结果——组装后的数组(out)仍必须能放入分布式内存:

import h5py
import cupynumeric as cn
from legate.core import get_legate_runtime
from legate.io.hdf5 import from_file_batched

with h5py.File("big.h5", "r") as f:          # read shape/dtype without loading data
    shape, dtype = f["data"].shape, f["data"].dtype

out = cn.empty(shape, dtype=dtype)
for chunk, (r0, c0) in from_file_batched("big.h5", "data", chunk_size=(4096, 4096)):
    out[r0:r0 + chunk.shape[0], c0:c0 + chunk.shape[1]] = cn.asarray(chunk)
get_legate_runtime().issue_execution_fence(block=True)

确保每个 chunk_size 值都为正数,且其长度与数据集的秩相同,否则 from_file_batched 会引发 ValueError。运行 assets/hdf5_batched_read.py 进行验证(可选)。

使用说明

  • 直接将 cuPyNumeric ndarray 传给 to_file——它实现了 __legate_data_interface__,可作为 LogicalArrayLike 传给 to_file。请跳过任何 np.array(...) 往返转换。
  • 使用 cn.asarray(...) 将结果转换回来。 from_file 和每个 from_file_batched 分块都会返回 Legate LogicalArray;将其包装为 cn.asarray(la),即可得到 cuPyNumeric ndarray(零拷贝,不经过主机)。
  • 在任何外部读取器打开文件前设置栅栏。 Legate I/O 是异步的:to_file 只会将写入操作加入队列。在 h5py、子进程或其他工具打开文件前,插入 get_legate_runtime().issue_execution_fence(block=True)。对于同一 Legate 程序中稍后发出的 from_file,无需设置栅栏——运行时会保留操作顺序。
  • 从 cuPyNumeric 源代码树之外运行(例如,执行 cd /tmp)。Python 会将当前工作目录放在 sys.path 的首位,因此树内的 cupynumeric/ 目录会遮蔽已安装的包(ModuleNotFoundError: cupynumeric.install_info)。
  • 为所有 rank 指定相同的 path。 程序会在每个 rank 上运行(SPMD),因此传给 to_file/from_file 的 path 必须在每个 rank 上完全相同——每个 rank 使用各自的 tempfile.mkstemp() 文件名会破坏集体 I/O。如果程序自行创建文件,请使用集体 to_file 写入,而不要使用每个 rank 各自的 h5py 写入。

to_file 的行为注意事项

  • 预期会生成 HDF5 虚拟数据集(VDS):每个 rank 写入自己的数据块,文件会将它们呈现为一个逻辑数据集。
  • 将 to_file 视为破坏性操作——如果 path 已存在,它会覆盖该文件,因此请保护好任何不得覆盖的文件。
  • 让 to_file 创建缺失的父目录;不要预先创建这些目录。
  • 为 path 指定文件名(/path/to/file.h5),不能指定目录——否则会引发 ValueError。传入有界数组(即形状已知的数组);对于无界数组,to_file 会引发 ValueError。无界数组是指创建时未指定形状、由生产任务稍后填入范围的 Legate 数组(例如 create_array(dtype, ndim=n))。cuPyNumeric ndarray 始终是有界的——即使是惰性/延迟数组也如此,因此这只影响原始 LogicalArray。

GPUDirect Storage(GDS)

读取 HDF5 并将数据载入 GPU 内存的运行,始终设置 LEGATE_IO_USE_VFD_GDS=1——无论集群是否具有支持 GPUDirect 的存储:

export LEGATE_IO_USE_VFD_GDS=1          # set before launching
# or, with the legate driver:
legate --io-use-vfd-gds my_script.py
  • 通过 GDS VFD 将数据读入 GPU,而不要使用默认路径。 默认的(POSIX)VFD 会通过零拷贝内存(ZCMEM)暂存每次 GPU 读取,而 Legate 仅预留 128 MB——因此,读取大于约 128 MB 的数组时会中止。GDS VFD 会移除该暂存缓冲区。
  • 读入主机(CPU)内存时不要设置该变量——此时不需要 GDS VFD 插件,设置它只会增加开销。
  • 即使没有支持 GPUDirect 的存储,也要保留 =1——cuFile 会自动回退到兼容模式(如果尚未启用,请设置 export CUFILE_ALLOW_COMPAT_MODE=true),而 =1 仍可避免 ZCMEM 中止。
  • **正确说明其归属:**GDS VFD 是基于 NVIDIA cuFile 的 nv-legate/vfd-gds 插件,不是 KvikIO(KvikIO 支持 Legate 的 Zarr/分块 I/O,而非 HDF5)。可在运行日志中搜索 H5FD__gds_open: Successfully opened file w/GDS VFD,确认其已启用。

故障排除

症状原因和修复方法
导入时出现 ModuleNotFoundError: No module named 'h5py'缺少 h5py — 执行 conda install -c conda-forge h5py。
to_file 后,文件对 h5py 来说看起来是空的或被截断异步写入尚未完成 — 在外部读取之前添加 get_legate_runtime().issue_execution_fence(block=True)。
to_file 返回 ValueErrorpath 是一个目录 — 传入文件路径,例如 results/data.h5。
出现 ModuleNotFoundError: No module named 'cupynumeric.install_info'在源代码树中运行 — cd /tmp(切换到仓库以外的任意目录)。
读取 ≳128 MB 的 GPU 数组时中止或崩溃默认的 128 MB ZCMEM 暂存缓冲区 — 对 GPU 读取设置 LEGATE_IO_USE_VFD_GDS=1。
from_file 返回了 LogicalArray(...)这是预期行为 — 用 cn.asarray(...) 包装它。

限制和版本说明

  • 从 legate.io.hdf5 导入(Legate 26.01+);将 25.03 系列遗留的任何 legate.core.io.hdf5 导入路径改写为新路径(例如,25.03 发布博文仍显示旧路径)。
  • 显式安装 h5py — cuPyNumeric 的默认环境中不包含它。
  • 将 dataset_name 指向单个数组,绝不能指向组;先用 h5py 遍历组,以查找数据集路径。
  • 在 GPU 上,始终使用 LEGATE_IO_USE_VFD_GDS=1 读取(参见 GPUDirect Storage(GDS))— 默认路径在 GPU 数组大于 128 MB ZCMEM 缓冲区时会中止。CPU 读取时不要设置该变量。

验证

cd /tmp                                  # outside the cupynumeric source tree
conda install -c conda-forge h5py        # one-time, if not already present
LEGATE_CONFIG="--cpus 4" LEGATE_AUTO_CONFIG=0 python <skill>/assets/hdf5_roundtrip.py
LEGATE_CONFIG="--cpus 4" LEGATE_AUTO_CONFIG=0 python <skill>/assets/hdf5_batched_read.py

预期会显示 HDF5 ROUND TRIP OK 和 HDF5 BATCHED READ OK。添加 --gpus 1(以及 LEGATE_IO_USE_VFD_GDS=1)以测试 GPU / GDS 路径。

评论

支持 Markdown