关于 cupynumeric-hdf5
cupynumeric-hdf5 是使用 `legate.io.hdf5` 在 cuPyNumeric 与 HDF5 文件间读写数组的技能,面向处理分布式数组和 HPC 数据流程的用户。它支持按块读取,并说明异步写入、GPU 读取及数据转换的注意事项。
主要功能
- 01并行写入单个 HDF5 文件
- 02读取 HDF5 数据集为分布式数组
- 03按块读取大型数据集
- 04LogicalArray 与 cuPyNumeric 数组转换
- 05说明 GPU 读取的 GDS 设置
适用场景
- 01将 cuPyNumeric 数组保存为 HDF5
- 02将 HDF5 数据集载入分布式数组
- 03分块读取大型 HDF5 文件
- 04为 GPU HDF5 读取配置 GDS
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ promptingcompany/ nv-skills --skill cupynumeric-hdf5安装后在 Claude Code 中输入/cupynumeric-hdf5确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
BENCHMARK.mdskill-card.mdskill.oms.sigassets/hdf5_batched_read.pyhdf5_roundtrip.pyevals/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_file | to_file(array, path, dataset_name) | 将 cuPyNumeric 数组/LogicalArray 写入一个 HDF5 文件,作为虚拟数据集(VDS)——每个 rank 写入自己的数据块。 |
from_file | from_file(path, dataset_name) -> LogicalArray | 将一个 HDF5 数据集读入分布式数组。 |
from_file_batched | from_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分块都会返回 LegateLogicalArray;将其包装为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 返回 ValueError | path 是一个目录 — 传入文件路径,例如 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 路径。