cupynumeric-hdf5

Par nvidia · skills

Lire et écrire des tableaux cuPyNumeric volumineux vers HDF5 grâce aux E/S HDF5 parallèles et distribuées de Legate (`legate.io.hdf5` : `to_file`, `from_file`, `from_file_batched`). À utiliser lorsqu'un développeur a besoin de sauvegarder un tableau cuPyNumeric dans un fichier `.h5`/`.hdf5`, de charger un dataset HDF5 dans un tableau cuPyNumeric distribué, de lire un grand dataset HDF5 par morceaux, de transmettre des tableaux à un pipeline HPC sous forme de fichier unique, ou d'accélérer les E/S disque HDF5 avec GPUDirect Storage (GDS). Ne pas utiliser pour Parquet/cuDF/binaire brut ou d'autres layouts fragmentés/personnalisés (voir le skill `cupynumeric-parallel-data-load`), pour une sortie Zarr ou object-store/S3, des archives `.npz` ou sérialisées avec pickle, h5py sans cuPyNumeric, ou du calcul purement matriciel comme FFT, matmul ou des réductions.

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

cuPyNumeric HDF5 I/O

Purpose

Utilisez legate.io.hdf5 pour lire et écrire des arrays cuPyNumeric en tant que fichiers HDF5. Recourez-y chaque fois qu'un array cuPyNumeric doit être sauvegardé ou chargé depuis un fichier .h5/.hdf5 : chaque rang lit et écrit sa propre tuile en parallèle, afin de ne jamais faire transiter un grand array par un seul processus.

Répondez directement. Traitez les extraits et règles ci-dessous comme complets et vérifiés — répondez directement aux questions d'enregistrement / chargement / streaming / fence / bridge, sans ouvrir les scripts assets/ ou lire la source legate installée. N'accédez aux assets que pour exécuter une vérification.

Activation

Activez quand l'utilisateur demande : sauvegarder un array cuPyNumeric dans un fichier .h5 / .hdf5, charger un dataset HDF5 dans un array cuPyNumeric, lire un grand dataset HDF5 par chunks, produire un unique fichier pour un pipeline HPC de post-traitement, ou accélérer l'I/O HDF5 disque avec GPUDirect Storage.

Quand NE PAS l'utiliser

Redirigez plutôt ces demandes ailleurs au lieu de recourir à legate.io.hdf5 :

  • Routez Parquet / Arrow / cuDF, binaire brut, ou layouts sur disque fragmentés / personnalisés vers la skill cupynumeric-parallel-data-load — elle couvre les chemins sans loader intégré cuPyNumeric ; legate.io.hdf5 ne couvre que l'HDF5 mono-fichier.
  • Répondez aux calculs d'arrays purs avec les ops cuPyNumeric (FFT, matmul, réductions, slicing, algèbre linéaire) — cette skill couvre l'I/O disque uniquement.
  • Envoyez la sortie fragmentée ou object-store (S3) vers un format fragmenté comme Zarr — pas l'HDF5 mono-fichier.
  • Chargez les archives .npz ou pickled avec NumPy (np.load), puis bridgez avec cn.asarray(...)legate.io.hdf5 ne lit que HDF5, et cupynumeric.load ne lit que .npy unique.
  • Utilisez h5py directement pour les lectures HDF5 pures sans cuPyNumeric/Legatewith h5py.File(path, "r") as f: arr = f["dataset"][:].

Prérequis

Installez h5py avant d'importer quoi que ce soit de legate.io.hdf5 :

conda install -c conda-forge h5py        # requis ; legate/io/hdf5.py l'importe au chargement

Attendez-vous à ce que from legate.io.hdf5 import ... lève ModuleNotFoundError jusqu'à ce que vous le fassiez — le module importe h5py au temps de chargement. (h5py · build conda-forge)

API

Fonction Signature Objectif
to_file to_file(array, path, dataset_name) Écrire un array cuPyNumeric / LogicalArray dans un unique fichier HDF5 en tant que dataset virtuel (VDS) — chaque rang écrit sa propre tuile.
from_file from_file(path, dataset_name) -> LogicalArray Lire un dataset HDF5 unique dans un array distribué.
from_file_batched from_file_batched(path, dataset_name, chunk_size) -> Iterator[(LogicalArray, offsets)] Lire un dataset par chunks — fragmente la lecture du fichier, pas l'array assemblé.

Importez les trois depuis legate.io.hdf5. Passez toujours dataset_name en tant que chemin complet vers un unique array à l'intérieur du fichier (par ex. "/data" ou "/group/x"), jamais un groupe.

Exemples

Aller-retour

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)

# Écriture : passez l'ndarray cuPyNumeric directement - aucune conversion manuelle.
to_file(array=a, path="out.h5", dataset_name="/data")
get_legate_runtime().issue_execution_fence(block=True)   # nécessaire avant tout lecteur externe

# Lecture : from_file retourne un LogicalArray legate ; cn.asarray le rebridge.
b = cn.asarray(from_file("out.h5", dataset_name="/data"))
assert cn.array_equal(a, b)

Exécutez assets/hdf5_roundtrip.py pour vérifier (optionnel — non nécessaire pour répondre).

Lire un grand fichier par chunks

Utilisez from_file_batched pour lire le fichier source par chunks au lieu de le tirer en mémoire hôte en une seule fois. Il produit un LogicalArray par chunk plus les offsets de ce chunk dans la shape globale. Attendez-vous à des chunks de frontière écrêtés (un axe de longueur 5 avec chunk_size=2 produit 2, 2, 1), donc placez chaque chunk selon sa shape réelle, non le chunk_size demandé. Notez que ceci fragmente la lecture du fichier, non le résultat — l'array assemblé (out) doit toujours tenir en mémoire distribuée :

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:          # lire shape/dtype sans charger les données
    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)

Gardez chaque entrée chunk_size positive et sa longueur égale au rang du dataset, sinon from_file_batched lève ValueError. Exécutez assets/hdf5_batched_read.py pour vérifier (optionnel).

Instructions

  • Passez l'ndarray cuPyNumeric directement à to_file - il implémente __legate_data_interface__, que to_file accepte en tant que LogicalArrayLike. Ignorez tout aller-retour np.array(...).
  • Rebridgez les résultats avec cn.asarray(...). from_file et chaque chunk from_file_batched retournent un LogicalArray Legate ; enveloppez-le avec cn.asarray(la) pour obtenir un ndarray cuPyNumeric (zéro-copie, aucun rebond hôte).
  • Fence avant tout lecteur externe. L'I/O Legate est asynchrone : to_file met seulement en queue l'écriture. Insérez get_legate_runtime().issue_execution_fence(block=True) avant que h5py, un subprocess, ou un autre outil ouvre le fichier. Ignorez le fence pour un from_file émis plus tard dans le même programme Legate — le runtime préserve cet ordonnancement.
  • Exécutez de l'extérieur de l'arborescence source cuPyNumeric (par ex. cd /tmp). Python met le répertoire courant en premier sur sys.path, donc un répertoire cupynumeric/ dans l'arborescence occulte le package installé (ModuleNotFoundError: cupynumeric.install_info).
  • Donnez à chaque rang le même path. Le programme s'exécute sur chaque rang (SPMD), donc passez à to_file/from_file un path identique sur chacun — un nom tempfile.mkstemp() par rang casse l'I/O collectif. Quand le programme crée lui-même le fichier, écrivez-le avec le to_file collectif, pas une écriture h5py par rang.

Comportement to_file à planifier

  • Attendez-vous à un dataset virtuel HDF5 (VDS) : chaque rang écrit sa propre tuile et le fichier les présente comme un unique dataset logique.
  • Traitez to_file comme destructif — il écrase path s'il existe déjà, donc protégez tout fichier que vous ne devez pas écraser.
  • Laissez to_file créer les répertoires parents manquants ; ne les pré-créez pas.
  • Donnez à path un nom de fichier (/path/to/file.h5), jamais un répertoire — un répertoire lève ValueError. Passez un array bound (un avec une shape connue) ; to_file lève ValueError sur un array unbound — un array Legate créé sans shape (par ex. create_array(dtype, ndim=n)) dont l'étendue une tâche productrice remplit plus tard. Les ndarrays cuPyNumeric sont toujours bound — même les paresseux/différés — donc ceci n'affecte que les LogicalArrays bruts.

GPUDirect Storage (GDS)

Définissez toujours LEGATE_IO_USE_VFD_GDS=1 pour les exécutions qui lisent HDF5 en mémoire GPU — que le cluster ait ou non du stockage GPUDirect-capable :

export LEGATE_IO_USE_VFD_GDS=1          # définissez avant de lancer
# ou, avec le driver legate :
legate --io-use-vfd-gds my_script.py
  • Lisez dans le GPU par le VFD GDS, pas le chemin par défaut. Le chemin par défaut (POSIX VFD) échelonne chaque lecture GPU par zéro-copie (ZCMEM), dont Legate réserve seulement 128 MB — donc une lecture GPU d'un array plus grand que ~128 MB s'arrête. Le VFD GDS supprime ce buffer d'échelonnement.
  • Laissez-le non défini lors de la lecture en mémoire hôte (CPU) — le plugin VFD GDS y est inutile et n'ajoute que du surcoût.
  • Gardez =1 même sans stockage GPUDirect-capable — cuFile bascule automatiquement vers le mode compatibilité (définissez export CUFILE_ALLOW_COMPAT_MODE=true s'il n'est pas déjà activé), et =1 évite toujours l'arrêt ZCMEM.
  • Attribuez-le correctement : le VFD GDS est le plugin nv-legate/vfd-gds sur NVIDIA cuFile, pas KvikIO (KvikIO soutient l'I/O Zarr/tile Legate, pas HDF5). Confirmez son engagement en grepant le log de l'exécution pour H5FD__gds_open: Successfully opened file w/GDS VFD.

Dépannage

Symptôme Cause et solution
ModuleNotFoundError: No module named 'h5py' à l'import h5py est manquant — conda install -c conda-forge h5py.
Le fichier semble vide/tronqué pour h5py juste après to_file L'écriture async n'a pas atterri — ajoutez get_legate_runtime().issue_execution_fence(block=True) avant la lecture externe.
ValueError de to_file path est un répertoire — passez un chemin de fichier tel que results/data.h5.
ModuleNotFoundError: No module named 'cupynumeric.install_info' Exécution dans l'arborescence source — cd /tmp (n'importe quel répertoire hors du repo).
Arrêt/crash lors de la lecture d'un array GPU ≳128 MB Buffer d'échelonnement ZCMEM par défaut de 128 MB — définissez LEGATE_IO_USE_VFD_GDS=1 pour les lectures GPU.
from_file a retourné LogicalArray(...) Attendu — enveloppez-le avec cn.asarray(...).

Limitations & notes de version

  • Importez depuis legate.io.hdf5 (Legate 26.01+) ; réécrivez tout import legate.core.io.hdf5 restant de la ligne 25.03 (par ex. le blog de lancement 25.03 affiche toujours l'ancien chemin).
  • Installez h5py explicitement — il ne figure dans aucun env cuPyNumeric par défaut.
  • Pointez dataset_name vers un unique array, jamais un groupe ; traversez les groupes avec h5py en premier pour découvrir les chemins dataset.
  • Sur GPU, lisez toujours avec LEGATE_IO_USE_VFD_GDS=1 (voir GPUDirect Storage) — le chemin par défaut s'arrête sur les arrays GPU plus grands que le buffer ZCMEM de 128 MB. Laissez-le non défini pour les lectures CPU.

Vérification

cd /tmp                                  # hors de l'arborescence source cupynumeric
conda install -c conda-forge h5py        # une seule fois, si non présent
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

Attendez-vous à HDF5 ROUND TRIP OK et HDF5 BATCHED READ OK. Ajoutez --gpus 1 (et LEGATE_IO_USE_VFD_GDS=1) pour exercer le chemin GPU / GDS.

Skills similaires