Présentation de NVIDIA Warp
NVIDIA Warp est un framework Python dédié à l’écriture de kernels GPU hautes performances. Depuis la version 1.15, il propose une exécution déterministe grâce à un mode spécial qui impose un ordre fixe aux atomiques CUDA, au prix d’une légère perte de débit. Le JIT compile chaque kernel en code natif CUDA, applique la fusion de kernels et exploite les CUDA Graphs pour réduire l’overhead de lancement. Warp intègre également un système d’autodifférence : un wp.Tape enregistre les passes avant et génère les gradients lors de l’appel backward(), ce qui rend les kernels différentiables sans recourir à des bibliothèques externes.
Architecture de MJWarp
MJWarp (MuJoCo Warp) repose sur Warp pour porter les modèles MuJoCo compatibles sur le GPU. Le pipeline charge d’abord le fichier MJCF, le compile puis le transmet à Warp qui exécute la physique. Chaque « monde » correspond à une copie indépendante de l’état du robot ; le système a été démontré avec jusqu’à 2 048 environnements parallèles, chaque environnement traitant la même scène (ex. bras SO‑101) mais avec des conditions initiales différentes. Les données restent sur le dispositif grâce aux wp.array explicitement alloués, évitant les copies CPU‑GPU inutiles. Pour les pipelines PyTorch ou JAX, Warp expose des adaptateurs DLPack afin de partager les buffers sans copie supplémentaire.
Exemple de kernel et performances
import numpy as np
import warp as wp
@wp.kernel
def integrate(positions: wp.array[wp.vec3],
velocities: wp.array[wp.vec3],
dt: float):
i = wp.tid()
velocities[i] += wp.vec3(0.0, 0.0, -9.81) * dt
positions[i] += velocities[i] * dt
wp.init()
device = "cuda:0" if wp.is_cuda_available() else "cpu"
start = np.array([[0.0, 0.0, 0.5], [0.2, 0.0, 0.5]], dtype=np.float32)
positions = wp.array(start, dtype=wp.vec3, device=device)
velocities = wp.zeros_like(positions)
wp.launch(integrate, dim=len(start), inputs=[positions, velocities, 0.01], device=device)
wp.synchronize_device(device)
print(positions.numpy())
Le kernel integrate montre trois propriétés exploitées en robotique : chaque fil logique gère un point (wp.tid()), les tableaux résident sur le GPU et la séquence de kernels peut être capturée dans un graph CUDA pour éviter les frais de dispatch répétés. Le même code passe de deux points à des millions sans modification du flux de contrôle, ce qui explique la capacité à remplir les 2 048 environnements simultanément.
Limites et perspectives
Le mode déterministe introduit dans Warp 1.15 impose un ordre fixe aux opérations atomiques, mais il réduit le débit maximal d’environ 5‑10 % selon les benchmarks fournis par NVIDIA. De plus, la conversion d’un tableau Warp vers un numpy déclenche une synchronisation et une copie explicite, ce qui n’est pas optimal pour les boucles d’apprentissage où les données restent sur le GPU. Enfin, MJWarp ne fournit pas encore d’API native pour les capteurs ou les environnements USD, ce qui oblige les développeurs à recourir à des couches supplémentaires comme Isaac Lab pour intégrer la perception. Malgré ces contraintes, la combinaison Warp + MJWarp représente une avancée mesurable pour les charges de travail d’apprentissage par renforcement qui exigent des millions d’évaluations de dynamique robotique en temps réel.