VASP avec accélération GPU

Exploitez le plein potentiel de VASP avec notre guide de démarrage pour application GPU.

VASP

VASP (Vienna Ab Initio Simulation Package) est une application dédiée aux calculs de premiers principes pour des structures électroniques simulées à l’échelle atomique avec l’équation de Schrödinger. VASP - qui s’appuie sur la théorie standard de la fonctionnelle de la densité - inclut des fonctionnalités de pointe comme des méthodes basées sur la fonction de Green (GW et ACFDT-RPA), des options fonctionnelles hybrides et des méthodes MP2 basées sur la théorie de la perturbation de Møller-Plesset.

VASP offre des performances jusqu’à 10 fois plus rapides avec les GPU NVIDIA Tesla P100 par rapport aux configurations uniquement basées sur le traitement CPU, ce qui permet de recourir à des méthodes de calcul plus précises et plus intensives.

VASP s’exécute jusqu’à 10 fois plus vite grâce à l’accélération GPU

Installation

Configuration système

VASP est distribué sous forme de code source et propose deux dépendances de compilation et d’exécution. Dans le cadre de ce guide, nous considérons que les modules logiciels suivants ont déjà été installés sur votre système Linux et que leurs variables d’environnement respectives ont préalablement été définies :

  1. Suite de compilateurs Intel (plus spécifiquement Fortran, C/C++ et MKL)
  2. Intel MPI
  3. NVIDIA CUDA 8.0

Dans la plupart des cas, ces modules logiciels ont été installés sur votre système par votre administrateur et ils peuvent être chargés via un module système. L’installation de ces modules logiciels dépasse la portée du présent guide. Veuillez contacter votre équipe de support technique en cas de besoin.

Vous pouvez compiler la dernière révision du portage GPU de l’application VASP avec la suite de compilateurs PGI. L’édition communautaire est fournie gratuitement. Malgré tout, comme la plupart des utilisateurs de VASP ont traditionnellement recours au compilateur Intel, c’est ce dernier que nous utiliserons dans le cadre de ce guide.

Téléchargement et compilation

TÉLÉCHARGEMENT DES SOURCES REQUISES

VASP est un logiciel commercial. Les détenteurs d’une licence VASP standard peuvent télécharger la version la plus récente de son portage GPU. Pour acquérir une licence, rendez-vous sur cette page. Saisissez vos identifiants de connexion sur la droite, sous "Community Portal", puis cliquez sur l’option de connexion pour accéder à la zone de téléchargement. Cliquez sur "VASP5" et sélectionnez le dossier "src". Au moment où nous publions ce guide, il convient de télécharger les fichier(s) suivant(s) :

vasp.5.4.4.tar.gz

Assurez-vous de visiter aussi fréquemment que possible le site Internet de VASP pour vous procurer les dernières versions et mises à jour.

DÉCOMPRESSION ET PATCHING

Commencez par décompresser le code source de VASP que vous venez de télécharger :

tar xfz vasp.5.4.4.tar.gz

Accédez au répertoire qui vient d’être créé et qui contient les sources requises. Le cas échéant, appliquez les patchs.

cd vasp.5.4.4

Le fichier Makefile de VASP nécessite plusieurs modifications afin de s’adapter à votre environnement logiciel local. VASP est livré avec une sélection de modèles Makefile pour différentes configurations. Vous trouverez ces modèles dans le sous-répertoire "arch/". Veuillez copier un fichier approprié "makefile.include" à partir du sous-répertoire "arch/" (dans ce guide, nous utilisons le compilateur Fortran d’Intel et le langage NVIDIA CUDA sous Linux).

cp arch/makefile.include.linux_intel makefile.include

Si vous devez procéder à des adaptations de "makefile.include", veuillez vous référer à la section Dépannage ci-dessous.

La plupart des options "makefile.include" doivent fonctionner de manière immédiate en détectant les valeurs nécessaires à partir de vos variables d’environnement, mais il est fortement recommandé de bien penser à définir la variable GENCODE_ARCH en fonction de votre configuration. Veuillez par ailleurs vérifier les capacités de calcul (CC) de vos cartes graphiques avant d’éditer le fichier "makefile.include" avec votre éditeur favori (nano, vim ou emacs sont accessibles par défaut sur de nombreux systèmes) :

nano makefile.include

Dans ce guide, nous utilisons des cartes graphiques NVIDIA P100 et, par conséquent, nous avons procédé à une compilation pour les capacités de calcul 6.0 afin d’obtenir des performances optimales. Par conséquent, la ligne de commande GENCODE_ARCH appliquée par défaut convient à notre configuration :

GENCODE_ARCH  := -gencode=arch=compute_30,code=\"sm_30,compute_30\" \
-gencode=arch=compute_35,code=\"sm_35,compute_35\" \
-gencode=arch=compute_60,code=\"sm_60,compute_60\"

Spécifier des drapeaux CC non requis par votre configuration (exemple : 3.5) peut sembler superflu, mais cela permettra néanmoins d’exécuter le code binaire sur des architectures GPU tierces et potentiellement moins récentes. Si votre propre configuration GPU est basée sur des capacités de calcul différentes, n’oubliez pas d’adapter votre ligne de code en conséquence. Par exemple, si vous utilisez un GPU Tesla V100, n’oubliez pas de le mentionner. (Et veillez à utilisez CUDA version 9).

GENCODE_ARCH  := -gencode=arch=compute_30,code=\"sm_30,compute_30\" \
-gencode=arch=compute_35,code=\"sm_35,compute_35\" \
-gencode=arch=compute_60,code=\"sm_60,compute_60\" \
-gencode=arch=compute_70,code=\"sm_70,compute_70\"

Assemblez ensuite le portage GPU de VASP en exécutant la commande suivante (assurez-vous de ne pas ajouter la variable -j car VASP ne prend pas en charge l’assemblage parallélisé) :

make gpu

Si la compilation a réussi, vous devriez voir apparaître la version accélérée par GPU de VAST dans le répertoire "bin/vasp-gpu". Vérifiez la présence des fichiers binaires avec la commande :

ls -l bin/vasp-gpu

En cas d’échec de l’assemblage, veuillez vous référer aux explications sur l’adaptation des variables de version dans la section Dépannage.

Après l’assemblage, il convient d’adapter le portage GPU de VAST pour permettre des calculs non-colinéaires (si LNONCOLLINEAR=.TRUE. ou LSORBIT=.TRUE. dans INCAR) avec la variable-cible gpu_ncl. Veuillez noter que la version Γ de VASP n’est pas encore compatible avec l’accélération GPU.

 

Vous pouvez, au besoin, déployer des versions CPU de VASP (std, ncl, gam) :

make gpu_ncl std ncl gam

Vous obtiendrez la liste suivante de fichiers binaires mais, pour ce guide, nous utiliserons seulement vasp_gpu et (en option) vasp_std :

Tableau 1 - Aperçu des différents fichiers exécutables disponibles pour VASP.

vasp_std Default version of VASP
vasp_ncl Special version required to run calculations with LNONCOLLINEAR=.TRUE. or LSORBIT=.TRUE. in the INCAR
vasp_gam Special version that saves memory and computations for calculations at Γ only.
vasp_gpu Same as vasp_std, but with GPU acceleration
vasp_gpu_ncl Same as vasp_ncl, but with GPU acceleration

Nous vous recommandons d’installer les fichiers binaires de VASP dans un dossier externe à votre répertoire d’assemblage (exemple : ~/bin) afin d’éviter de les écraser avec des versions ultérieures :

mkdir -p ~/bin
cp bin/vasp* ~/bin

INSTALLATION DE LA BASE DE DONNÉES POTCAR

VASP s’appuie sur des données tabulées afin de fluidifier le traitement des fonctions d’ondes AE (ou pseudopotentiels). Vous pouvez télécharger ces pseudopotentiels.

Saisissez vos identifiants de connexion sur la droite, sous "Community Portal", puis cliquez sur l’option de connexion pour accéder à la zone de téléchargement. Cliquez ensuite sur "Potentials" et commencez avec "LDA". Téléchargez tous les fichiers disponibles puis procédez de manière similaire avec les dossiers "PBE" et "PW91".

Vous devriez obtenir les jeux de fichiers suivants :

  1. potpaw_PBE.tgz
  2. potpaw_PBE.54.tar.gz
  3. potpaw_PBE.52.tar.gz
  4. potpaw_LDA.tgz
  5. potpaw_LDA.54.tar.gz
  6. potpaw_LDA.52.tar.gz
  7. potpaw_GGA.tar.gz
  8.  potUSPP_LDA.tar.gz
  9. potUSPP_GGA.tar.gz

Décompressez-les avec le script suivant : extractPOTCARs.sh.

Tous les scripts présentés dans ce guide sont disponibles au téléchargement. Vous pouvez également choisir de cloner le dépôt correspondant pour votre système de fichiers :

Exécution

Première exécution avec la version de VASP accélérée par GPU

Le fichier de contrôle INCAR principal comporte plusieurs options qui nécessitent votre considération vis-à-vis du portage GPU de VASP. La version accélérée de VASP affichera des messages d’erreur et d’avertissement si jamais des paramètres du fichier INCAR ne sont pas pris en charge ou ne sont pas recommandés.

N’ignorez pas les messages relatifs au portage GPU et agissez en conséquence ! Cette section détaille les paramètres INCAR relatifs au calcul sur GPU.

Limitez-vous aux options suivantes pour le drapeau ALGO :

  1. Normal
  2. Fast
  3. Veryfast

D’autres algorithmes sont disponibles avec VAPS, mais ils n’ont pas été testés de manière approfondie et nous ne pouvons pas garantir qu’ils fourniront des performances optimales ou des résultats fiables. Vous devez par ailleurs utiliser les paramètres suivants dans votre fichier INCAR :

  1. LREAL = .TRUE. or LREAL = A
  2. NCORE = 1

Pour commencer, nous vous fournissons ci-après quelques exemples - que nous utiliserons aussi ultérieurement pour obtenir des performances optimales avec des configurations avancées. Vous pouvez trouver ces exemples de fichiers d’entrée dans le dépôt git. Accédez au répertoire de destination et examinez rapidement votre fichier INCAR. Vous vous apercevrez qu’il correspond aux options mentionnées ci-dessus :

cd gpu-vasp-files/benchmarks

Pour des raisons de copyright, vous devez générer vous-même les fichiers POTCAR requis. Dans le présent guide, nous considérons que vous avez téléchargé et décompressé la base de données de pseudopotentiels comme expliqué, puis utilisé ~/vasp/potcars/ comme répertoire de stockage. Les fichiers d’entrée fournis à titre d’exemple sont accompagnés d’un script de génération automatique, mais le chemin d’accès de votre base de données POTCAR doit néanmoins être renseigné :

cd siHugeShort
bash generatePOTCAR.sh ~/vasp/potcars

Vous pouvez désormais exécuter vos premiers calculs accélérés sur GPU avec VASP :

~/bin/vasp_gpu

Cette commande lancera un processus utilisant uniquement un cœur GPU et un cœur CPU, peu importe le nombre de cœurs dont votre système dispose. Ce processus initial, qui peut s’avérer chronophage, permet de confirmer que tout fonctionne correctement. Pour vérifier que votre GPU est utilisé activement, veuillez saisir

nvidia-smi -l

dans un terminal connecté au même nœud que celui qui exécute le processus. Votre processus VASP devrait figurer dans la liste, tout comme la charge GPU correspondante. Appuyez sur CTRL+c pour fermer la liste.

Utilisation d’un nœud de calcul unique

Tout comme la version standard de VASP, le portage GPU exploite la parallélisation avec MPI et peut distribuer la charge de calcul sur des CPU, des GPU et des nœuds multiples. Dans ce guide, nous utiliserons Intel MPI, mais toutes les techniques décrites ci-après fonctionnent également avec d’autres implémentations MPI. Veuillez vous référer à la documentation de votre implémentation MPI concrète pour trouver les options équivalentes en matière de ligne de commande.

VASP prend en charge toute une variété de fonctionnalités et d’algorithmes, c’est pourquoi son profil de calcul dédié est d’une grande polyvalence. Selon les calculs que vous mettrez en place, des paramètres spécifiques peuvent s’avérer requis pour obtenir les meilleurs délais d’exécution. Ces aspects s’appliquent également au portage GPU.

Dans ce guide, nous vous fournissons des techniques variées qui pourront vous aider à accélérer vos instances GPU. Cependant, comme il n’existe pas de profil optimal unanimement reconnu pour les raisons énoncées ci-dessus, vous devrez mettre en place vos propres benchmarks afin de trouver le meilleur rapport paramètres/performances.

Tout d’abord, commençons par définir combien de GPU et quels modèles possède votre serveur :

nvidia-smi –L

Les résultats de sortie indiquent que le système comporte 4 GPU Tesla P100. Leurs identifiants uniques (UUID) y sont par ailleurs spécifiés.

GPU 0 : Tesla P100-PCIE-16Go (UUID : GPU-74dff14b-a797-85d9-b64a-5293c94557a6)
GPU 1 : Tesla P100-PCIE-16Go (UUID : GPU-576df4e5-8f0c-c773-23d2-7560fd29542e)
GPU 2 : Tesla P100-PCIE-16Go (UUID : GPU-cff44500-e07e-afef-8231-0bdd32dde61f)
GPU 3 : Tesla P100-PCIE-16Go (UUID : GPU-54c0a6db-b406-3e24-c28b-0b517549e824)

Dans la plupart des cas d’utilisation, les GPU doivent transférer des données de leur mémoire dédiée à la mémoire du système. Sur les systèmes multi-sockets, les performances de transfert dépendent du chemin d’accès que les données doivent suivre. Dans le meilleur des cas, il existe un bus interne permettant d'assurer un transfert direct entre les deux zones de mémoire. Dans le scénario le plus défavorable, le processus CPU doit accéder indirectement à la mémoire qui est physiquement située dans un module de RAM associé à un autre socket CPU, puis il doit copier les données dans la mémoire GPU exclusivement accessible via un module PCIe contrôlé par un autre socket CPU. La commande suivante vous permet d’afficher les informations relatives à la topologie du bus :

nvidia-smi topo -m

La version de VASP accélérée par GPU ne supportant pas (encore) les communications directes de GPU à GPU, nous pouvons ignorer la plupart des valeurs de sortie relatives à la topologie du bus (plus spécifiquement la vitesse de communication).

  GPU0 GPU1 GPU2 GPU3 mlx5_0 CPU Affinity
GPU0 X SOC SOC SOC SOC 0-15
GPU1 SOC X PHB PHB PHB 16-31
GPU2 SOC PHB X PIX PHB 16-31
GPU3 SOC PHB PIX X PHB 16-31
mlx5_0 SOC PHB PHB PHB X  

Cela étant dit, veuillez noter que la dernière colonne ("CPU Affinity") est particulièrement importante car elle nous indique sur quels cœurs CPU les rangs MPI devraient être idéalement exécutés et, surtout, s’ils communiquent avec un GPU spécifique. Nous voyons ici que tous les cœurs CPU du premier socket (0-15) peuvent communiquer directement avec le GPU 0, tandis que les CPU du deuxième socket (16-31) fournissent des performances optimales dès lors qu’ils sont combinés aux GPU 1, GPU 2 et GPU 3.

Benchmarks

Résultats attendus

Dès lors que vous souhaitez comparer les délais d’exécution de vos versions avec différentes configurations, il est essentiel d’éviter les écarts de performance inattendus. Les GPU NVIDIA incluent des fonctionnalités permettant d’augmenter ou d’abaisser temporairement les fréquences du processeur selon les spécifications thermiques et la charge de calcul. Même si ces modifications de fréquence peuvent permettre d’économiser de l’énergie, elles sont susceptibles de provoquer des résultats de benchmark trompeurs en raison de la variation des délais d’exécution entre chaque version. Pour réaliser des benchmarks comparatifs, il convient donc d’utiliser tous les processeurs graphiques du système à la même fréquence.

Quand vos benchmarks sont terminés, vous pouvez réinitialiser les fréquences de vos cartes et, au besoin, réactiver d’éventuelles valeurs d’overclocking.

AVERTISSEMENT SUR LES RAPPORTS DE PERFORMANCE

Même si les données de nos rapports de performance ont été générées sur des systèmes de production, elles sont uniquement fournies à titre indicatif pour illustrer les méthodes décrites dans ce guide. Les performances de votre système peuvent différer en raison des nombreux paramètres relatifs à votre configuration CPU et GPU.

Méthode la plus simple : un processus par GPU

La méthode la plus simple pour utiliser les quatre GPU de notre système consiste à lancer quatre processus MPI avec VAST, puis procéder à l’allocation des cœurs CPU de manière automatique :

mpirun -n 4 ~/bin/vasp_gpu

L’environnement Intel MPI associe automatiquement les processus à certains cœurs CPU pour que le système d’exploitation ne puisse pas les déplacer vers d’autres cœurs pendant l’exécution des tâches, ce qui permet de prévenir les erreurs relatives au transfert des données. Cela peut néanmoins engendrer une solution sous-optimale, car l’implémentation de MPI n’a pas connaissance de la topologie GPU. Nous pouvons examiner l’association des processus en augmentant la verbosité :

mpirun -n 4 -genv I_MPI_DEBUG=4 ~/bin/vasp_gpu

L’examen des données de sortie et leur comparaison à la topologie d’interconnexion permet de constater que ces résultats ne sont pas idéaux :

...

[0] MPI startup():
Rank
Pid
Node name
Pin cpu

 
[0] MPI startup():
0
41587
hsw227
{16,17,18,19,20,21,22,23}

 
[0] MPI startup():
1
41588
hsw227
{24,25,26,27,28,29,30,31}

 
[0] MPI startup():
2
41589
hsw227
{0,1,2,3,4,5,6,7}

 
[0] MPI startup():
3
41590
hsw227
{8,9,10,11,12,13,14,15}


Using device 0 (rank 0, local rank 0, local size 4) : Tesla P100-PCIE-16GB

Using device 1 (rank 1, local rank 1, local size 4) : Tesla P100-PCIE-16GB

Using device 2 (rank 2, local rank 2, local size 4) : Tesla P100-PCIE-16GB

Using device 3 (rank 3, local rank 3, local size 4) : Tesla P100-PCIE-16GB

...

Le rang 0 utilise le GPU 0, mais il est lié aux cœurs CPU 16 à 23, pourtant bien trop distants. Le même problème s’applique aux rangs 2 et 3. Seul le rang 1 utilise le GPU 1 et est associé aux cœurs 24 à 31, en fournissant ainsi des performances de transfert optimales.

Examinons maintenant des résultats de performance concrets. En utilisant l’ensemble des 32 cœurs des deux CPU Intel® Xeon® E5-2698 v3 de notre système sans accélération GPU, nous constatons que l’exécution complète du benchmark siHugeShort aura nécessité 607.142s.(1) En utilisant 4 GPU avec le mode par défaut de manière sous-optimale, l’exécution complète du benchmark ne nécessite plus que 273.320s, ce qui représente un gain de vitesse de 2,22 fois. Vous pouvez utiliser les métriques suivantes, disponibles avec VASP, pour déterminer le délai d’exécution de vos calculs.

1 Si vous avez déjà précédemment assemblé la version CPU de VAST, vous pouvez utiliser la commande suivante pour déterminer le délai d’exécution sur votre système : mpirun -n 32 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ~/bin/vasp_std

grep Elapsed\ time OUTCAR

VASP associe les cœurs GPU aux rangs MPI de manière consécutive, en éludant les GPU présentant des capacités de calcul insuffisantes (le cas échéant). En adoptant cette technique et la syntaxe suivante, nous pouvons contrôler manuellement le placement des processus sur le CPU et distribuer les rangs de manière à ce que chaque processus utilise un GPU avec le plus court chemin d’accès possible pour les transferts de mémoire :

mpirun -n 4 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=0,16,21,26 ~/bin/vasp_gpu

Cette méthode n’apporte pas des gains de performance significatifs sur notre système (délai d’exécution : 273,370s), et s’explique probablement par une utilisation déséquilibrée des ressources CPU communes comme la bande passante mémoire et les caches mémoire (3 processus partageant le même CPU). En solution de compromis, il est possible de redistribuer les rangs pour équilibrer leur allocation sur les sockets de CPU, mais seul un rang peut utiliser le chemin d’accès le moins rapide vers les ressources GPU :

mpirun -n 4 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=0,8,16,24 ~/bin/vasp_gpu

Avec un de délai d’exécution 268.939s, nous constations une légère amélioration d’environ 3% pour ce benchmark. Si votre charge de travail pèse plus lourdement sur les transferts de mémoire, ce gain pourrait sensiblement meilleur.

Avec les configurations incluant un nombre de rangs plus élevé, la sélection manuelle de la distribution peut s’avérer fastidieuse, ou vous pouvez décider que le partage équitable des ressources CPU est plus important que la vitesse des transferts de mémoire sur votre système. La commande suivante permet de cartographier les rangs de manière consécutive et elle évite du mieux possible de partager des ressources communes.

mpirun -n 4 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ~/bin/vasp_gpu

Nous obtenons ainsi un délai d’exécution de l’ordre de 276.299s, ce qui peut s’avérer très utile si des cœurs CPU restent inactifs. Vous pouvez même le faire délibérément si jamais un processus unique par GPU entraîne une saturation des ressources avec une réduction des performances. Toute surcharge additionnelle du GPU peut en effet provoquer une limitation indésirable de la performance. C’est particulièrement visible dans le benchmark siHugeShort fourni à titre d’exemple (n’hésitez pas à tester différentes options !). De manière générale, il n’est pas recommandé de recourir à des cœurs CPU tant que des ressources GPU sont encore disponibles, de manière à profiter pleinement des avantages du calcul sur GPU.

Seconde méthode la plus simple : plusieurs processus par GPU

Pour illustrer comment utiliser un plus grand nombre de cœurs CPU que de ressources GPU, nous allons recourir au benchmark silicaIFPEN. Une exécution exclusive avec 32 cœurs CPU nécessite 710.156s. En utilisant quatre GPU Tesla P100 avec un rang MPI par GPU et en faisant quelques compromis sur le placement du processus, le délai d’exécution passe à 241.674s (soit une accélération de l’ordre de 2,9 fois). Les GPU NVIDIA offrent l’avantage de pouvoir être partagés entre des processus multiples. Pour utiliser cette fonctionnalité, nous devons nous assurer que tous les GPU exploitent le mode de calcul par défaut :

Nous pouvons ensuite procéder au calcul :

mpirun -n 8 -env I_MPI_PIN_PROCESSOR_LIST=0,8,16,24,4,12,20,28 ~/bin/vasp_gpu

Avec le benchmark silicaIFPEN, le délai d’exécution est passé sur notre système à 211.576s (pour 12 processus se partageant 4 GPU Tesla P100, ce qui représente une accélération de l’ordre de 3,36 fois). Recourir à 4 processus ou plus par GPU peut cependant avoir un impact sur le délai d’exécution. La comparaison du tableau ci-après démontre que les processus attribués manuellement n’offrent pas d’avantage significatif.

Tableau 2 - Comparaison du temps écoulé avec le benchmark silicaIFPEN variant suivant le nombre de processus MPI par GPU.

MPI ranks per GPU Total MPI ranks Elapsed time (map:scatter) Elapsed time (map:ideal)
0 32 (CPU only) 710.156 s  
1 4 242.247 s 241.674 s
2 8 214.519 s 212.389 s
3 12 212.623 s 211.576 s2
4 16 220.611 s 224.013 s3
5 20 234.540 s  
6 24 243.665 s  
7 28 259.757 s  
8 32 274.798 s  

2 mpirun -n 12 -env I_MPI_PIN_PROCESSOR_LIST=0,8,16,24,3,11,19,27,6,14,22,30 ~/bin/vasp_gpu
3 mpirun -n 16 -env I_MPI_PIN_PROCESSOR_LIST=0,8,16,24,2,10,18,26,4,12,20,28,6,14,22,30 ~/bin/vasp_gpu

Au-delà d’un niveau idéal, l’ajout de nouveaux processus par GPU impacte davantage les performances. Mais pourquoi ? Tout simplement parce que quand un GPU change de contexte (exemple : quand il permet l’irruption d’un nouveau processus), il introduit un point de synchronisation fixe. Par conséquent, il n’est pas possible de superposer des processus sur les ressources GPU, et abuser de cette fonctionnalité peut en réalité allonger le délai d’exécution. Veuillez vous référer au schéma ci-dessous.

En conclusion, nous vous conseillons de tester à quel point la sursouscription peut s’avérer bénéfique à vos calculs. Des calculs complexes pourront évidemment avoir tendance à saturer un GPU qui utilise un processus unique au lieu de plusieurs processus, mais nous vous encoyurageons à mener vos propres tests !

VASP s’exécute jusqu’à 10 fois plus vite grâce à l’accélération GPU

NVIDIA MPS : overlapping avec partage des ressources GPU

Cette méthode ressemble à la précédente, mais permet de remédier à l’absence d’overlapping des instructions relatives aux processus multiples (comme illustré dans la seconde ligne du schéma). Il est recommandé d’activer les GPU en mode exclusif lors de l'utilisation de MPS:

Pour exploiter cette fonctionnalité, il convient ensuite d’initialiser le serveur MPS avant d’exécuter les tâches MPI :

nvidia-cuda-mps-control -d
mpirun -n 8 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ~/bin/vasp_gpu
echo quit | nvidia-cuda-mps-control

La première commande permet d’initialiser le serveur MPS à l’arrière-plan (en mode Daemon) : Une fois activé, le serveur interceptera les instructions générées par les processus se partageant des ressources GPU, puis les placera dans le même contexte avant de les renvoyer vers leur GPU-cible. La différence avec la section précédente réside dans la perspective GPU : les instructions répondent désormais au même processus et au même contexte, c’est pourquoi il devient possible de les superposer comme si vous utilisiez les flux d’une application CUDA. Vous pouvez utiliser la commande nvidia-smi -l pour constater qu’un seul processus accède aux ressources GPU.

Cette méthode d’exécution du portage GPU de VASP permet d'optimiser l’utilisation GPU afin de ne pas saturer les ressources disponibles. Pour le démontrer, nous allons recourir à notre troisième exemple (B.hR105), comportant un calcul basé sur un échange exact avec la fonctionnelle HSE06. Nous l’avons exécuté avec différentes valeurs de rangs MPI par GPU, avec et sans MPS.

Tableau 3 – Comparaison des délais d’exécution du benchmark B.hR105 selon différents rangs MPI par GPU, avec ou sans MPS pour NSIM=4

MPI ranks per GPU Total MPI ranks Elapsed time without MPS Elapsed time with MPS
0 32 (CPU only) 1027.525 s
1 4 213.903 s 327.835 s
2 8 260.170 s 248.563 s
4 16 221.159 s 158.465 s
7 28 241.594 s 169.441 s
8 32 246.893 s 168.592 s

Le recours à MPS permet de réduire le délai d’exécution de 55.4s (soit un gain de 26%) afin d’obtenir une valeur finale de 158.465s. Sans MPS, le meilleur résultat s’élève à 213.903s. Même si l’utilisation de 4 rangs par GPU sans MPS offre des résultats satisfaisants, exécuter autant de processus qu’il y a de cœurs CPU disponibles délivre les meilleures performances avec MPS.

Nous avons volontairement choisi de ne pas effectuer de calculs avec 3, 5 et 6 rangs par GPU car le nombre de bandes (224) utilisé dans cet exemple n’est pas divisible par le nombre de rangs qui en résulte. VASP aurait alors incrémenté automatiquement cette valeur, ce qui aurait pesé de manière significative sur la charge de travail. Si seul le rapport temps-solution vous intéresse, nous vous suggérons d’effectuer des tests avec le paramètre NSIM. En l’activant sur 32 et utilisant exclusivement un processus par GPU (sans MPS), nous avons pu réduire le délai d’exécution à 108.193s, ce qui correspond à une accélération de 10 fois.

Méthode avancée : une instance MPS par GPU

Avec certaines configurations, plus spécifiquement les anciennes versions de CUDA, il peut s’avérer bénéfique de lancer plusieurs instances de MPS Daemon (exemple : un serveur MPS par GPU). Cette méthode peut malgré tout être plus intensive car il faut attribuer à chaque processus MPI le serveur MPS qui lui correspond, tandis que chaque instance MPS doit être liée à un GPU. Nous ne recommandons pas cette méthode pour les configurations P100 avec CUDA 8.0, mais cela ne vous empêche pas de la tester au besoin. Le script suivant peut être utilisé pour initialiser les instances de MPS.

Pour que cette méthode soit probante, le portage GPU de VASP doit être lancé via un détour basé sur un script de wrapping. Cela déterminera les variables d’environnement pour que chaque processus corresponde à la bonne instance des serveurs MPS que vous avez initialisés : runWithMultipleMPSservers-RR.sh.

Ce script génère une liste des chemins d’accès pour les variables d’environnement qui permettront de définir vos instances MPS. La quatrième et dernière ligne (myMpsInstance=...) permet de sélectionner chaque instance selon l’identifiant local du processus MPI. Nous avons décidé de recourir à une méthode Round-robin en distribuant les processus 1 à 4 du GPU 0 au GPU 3. Le processus 5 utilise le GPU 0, tout comme le processus 9 (et ainsi de suite). Si vous avez utilisé un autre chemin d’accès pour installer vos fichiers binaires de GPU VASP, assurez-vous d’adapter votre code en conséquence avec la commande runCommand. Commençons ensuite les calculs.

mpirun -n 16 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ./runWithMultipleMPSservers-RR.sh

Lors de l’utilisation de MPS, rappelez-vous que le(s) serveur(s) MPS utilise(nt) des ressources CPU. Par conséquent, si vous utilisez autant de processus MPS que votre système compte de cœurs CPU, vous risquez d’alourdir la charge CPU. Cela peut être s’avérer bénéfique de conserver un cœur ou deux pour les opérations MPS.

Une fois les calculs terminés, le script suivant permet de nettoyer les instances MPS.

Utilisation de nœuds de calcul multiples

UN PROCESSUS PAR GPU

Tout ce que nous avons expliqué précédemment pour un seul nœud s’applique également aux configurations basées sur des nœuds multiples. La cartographie des processus que vous avez retenue précédemment s’appliquera donc également pour des nœuds multiples. Ci-après, nous estimons que possédez une configuration hostfile incluant la liste des nœuds associés à vos tâches de calcul. Dans le cas présent, nous avons utilisé deux nœuds. Nos valeurs hostfile ressemblent donc à ceci :

hsw224
hsw225

Si vous procédez manuellement à la cartographie de vos processus, il existe une légère différence par rapport à la commande décrite au chapitre précédent.

mpirun -f hostfile -n 8 -ppn 4 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=0,8,16,24 ~/bin/vasp_gpu

Le débogage de l’implémentation MPI spécifie que les processus sont désormais distribués sur deux nœuds et résident sur les cœurs CPU :

[0] MPI startup(): Rank Pid Node name Pin cpu
[0] MPI startup(): 0 38806 hsw224 0
[0] MPI startup(): 1 38807 hsw224 8
[0] MPI startup(): 2 38808 hsw224 16
[0] MPI startup(): 3 38809 hsw224 24
[0] MPI startup(): 4 49492 hsw225 0
[0] MPI startup(): 5 49493 hsw225 8
[0] MPI startup(): 6 49494 hsw225 16
[0] MPI startup(): 7 49495 hsw225 24

Les données de sortie de GPU VASP nous confirment également que les GPU sont correctement liés aux rangs MPI :

Using device 0 (rank 0, local rank 0, local size 4) : Tesla P100-PCIE-16GB
Using device 1 (rank 1, local rank 1, local size 4) : Tesla P100-PCIE-16GB
Using device 2 (rank 2, local rank 2, local size 4) : Tesla P100-PCIE-16GB
Using device 3 (rank 3, local rank 3, local size 4) : Tesla P100-PCIE-16GB
Using device 0 (rank 4, local rank 0, local size 4) : Tesla P100-PCIE-16GB
Using device 1 (rank 5, local rank 1, local size 4) : Tesla P100-PCIE-16GB
Using device 2 (rank 6, local rank 2, local size 4) : Tesla P100-PCIE-16GB
Using device 3 (rank 7, local rank 3, local size 4) : Tesla P100-PCIE-16GB

Les performances enregistrées avec le benchmark siHugeShort sont sensiblement supérieures à 258.917s. Par rapport au délai d’exécution de  268.939s enregistré sur un seul nœud, rien n’en justifie l’usage. Le benchmark silicaIFPEN démontre quant à lui des gains de performance significatifs (de 241.674s à 153.401s) lors du passage de 4 à 8 GPU P100 avec un processus MPI par GPU.

PROCESSUS MULTIPLES PAR GPU

Par rapport aux sections précédentes, le recours à des nœuds multiples avec plusieurs processus par GPU est relativement simple :

mpirun -f hostfile -n 16 -ppn 8 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=0,8,16,24,4,12,20,28 ~/bin/vasp_gpu

Si jamais vous souhaitez utiliser tous les cœurs CPU, utilisez la commande suivante :

mpirun -f hostfile -n 64 -ppn 32 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ~/bin/vasp_gpu

Lors de l’augmentation du nombre de rangs par GPU, le benchmark silicaIFPEN présente un comportement légèrement différent par rapport au cas d’utilisation à un seul nœud (la configuration la plus rapide ayant alors requis 211.576s). L’utilisation de deux processus par GPU améliore à peine le délai d’exécution (149.818s par rapport à 153.401s avec le cas d’utilisation à un seul nœud). Charger davantage les ressources GPU entraîne un effet indésirable car utiliser 3 processus par GPU allonge déjà le délai d’exécution à 153.516s (avec un total de 64 rangs, le délai passe même à 231.015s). Le recours à 1 ou 2 processus par GPU sur chaque nœud est donc amplement suffisant.

NVIDIA MPS SUR DES NŒUDS MULTIPLES

L’utilisation d’une instance MPS unique par nœud est triviale par rapport aux exécutions multi-instances. Certains planificateurs de tâches comportent des options de soumission automatique ( SLURM peut par exemple proposer -cuda-mps). Si de telles options venaient à être disponibles sur votre cluster, nous vous recommandons fortement de les utiliser et de procéder comme expliqué dans la section précédente.

Mais que faire si votre planificateur de tâches ne propose pas une telle option ? Vous devez vous assurer que, sur chaque nœud, une instance MPS unique est lancée avant l’initialisation de VASP. Nous fournissons un script à cet effet : runWithOneMPSPerNode.sh.

Si vous avez installé les fichiers binaires du portage GPU de VASP dans un emplacement personnalisé, veuillez adapter la variable runCommand au début de votre script. Les variables qui suivent permettent de calculer le rang local de chaque nœud car l’implémentation Intel MPI ne fournit pas ces informations de manière automatique. Le script initialise un serveur MPS sur le premier rang de chaque nœud afin de vérifier que le processus MPS n’est pas lié au même nœud qu’un processus VASP qui pourrait être utilisé ultérieurement. Cette étape est cruciale car, dans le cas contraire, MPS serait limité à l’utilisation d’un seul cœur alors qu’il peut en exploiter davantage ou, encore pire, MPS pourrait entrer en conflit avec VASP au regard des cycles CPU. Le script continue d’exécuter VASP puis stoppe le processus MPS.

Ce script doit être appelé avec la commande mpirun (comme vous avez pu le voir dans la section avancée). La commande mpirun fonctionne de la même manière qu’une exécution sans MPS, mais veuillez noter que nous appelons le script et non la commande binaire VASP.

mpirun -f hostfile -n 24 -ppn 12 -genv I_MPI_DEBUG=4 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ./runWithOneMPSPerNode.sh

En ce qui concerne le benchmark B.hR105, le recours à MPS a permis d’améliorer le délai d’exécution sur un seul nœud et sur deux nœuds. L’activation de MPS accélère les temps de calcul, et l’utilisation de rangs multiples par GPU est largement bénéfique (tant que les ressources ne sont pas saturées). Sur notre système, le juste équilibre a été atteint en utilisant 4 rangs par GPU (délai d’exécution : 104.052s). Par rapport à la configuration de base (Haswell sur un seul nœud), cela représente une accélération de 9,05 fois. Par rapport à l’utilisation sur 64 cœurs CPU, cela correspond à une accélération de l’ordre de 6,61 fois.

Si nous utilisons NSIM=32 avec 4 rangs par GPU sur chacun des 2 nœuds et que nous n’utilisons pas MPS, les calculs requièrent 71,222s.

Tableau 4 – Comparaison des délais d’exécution du benchmark B.hR105 selon différents rangs MPI par GPU, avec ou sans MPS pour NSIM=4 sur 2 nœuds

MPI ranks per GPU Total MPI ranks Elapsed time without MPS Elapsed time with MPS
0 32 (CPU only – 1 node) 1027.525 s
0 64 (CPU only) 763.939 s4
1 8 127.945 s 186.853 s
2 16 117.471 s 110.158 s
4 32 130.454 s 104.052 s
7 56 191.211 s 148.662 s
8 64 234.307 s5 182.260 s

4, 5 Here 256 bands were used, which increases the workload.

VASP s’exécute jusqu’à 10 fois plus vite grâce à l’accélération GPU

Configuration système recommandée

Configuration matérielle

Workstation

Parameter
Specs

CPU Architecture

x86_64

System Memory

32-64 GB

CPUs

8 Cores, 3+ GHz
10 cores, 2.2+ GHz
16 Cores, 2+ GHz

GPU Model

NVIDIA Quadro GP100

GPUs

2-4

Servers

Parameter
Specs

CPU Architecture

x86_64

System Memory

64-128 GB

CPUs

16+ Cores, 2.7+ GHz

GPU Model

NVIDIA Tesla P100, V100

GPUs per Node

2-4

Configuration logicielle

Software stack

Parameter
Version

OS

Linux 64

GPU Driver

352.79 or newer

CUDA Toolkit

8.0 or newer

Compiler

PGI Compiler 16.10
Intel Compiler Suite 16

MPI

OpenMPI
Intel MPI

Dépannage

ADAPTATION DES VARIABLES DE VERSION (en option)

Votre environnement logiciel local peut s’avérer incompatible avec les capacités de gestion automatique du système VASP. Dans ce cas, la version ne sera pas générée automatiquement et vous devrez procéder manuellement à des ajustements mineurs dans le fichier "makefile.include". Pour ce faire, vous devez ouvrir le fichier "makefile.include" avec votre éditeur favori (nano, vim ou emacs sont accessibles par défaut sur de nombreux systèmes) afin d’apporter vous-même les changements requis (voir ci-dessous).

nano makefile.include

Dès que vous apportez des modifications à n’importe quel fichier, assurez-vous d’exécuter la commande suivante pour le régénérer :

make veryclean

Vous trouverez ci-après les messages d’erreur rencontrés le plus fréquemment ainsi que la procédure de dépannage associée.

mpiifort: Command not found

Ce message d’erreur vous explique simplement que, sur votre système, le compilateur MPI Intel Fortran possède un nom qui diffère de la valeur par défaut. Ouvrez le fichier "makefile.include" et changez toutes les occurrences de mpiifort par l’occurrence utilisée sur votre système (exemple : mpif90).

# error "This Intel is for use with only the Intel compilers!"

Il existe deux méthodes pour résoudre cette erreur. Ouvrez le fichier "makefile.include" et ajoutez -ccbin=icc à la variable NVCC, de manière à obtenir la ligne suivante :

NVCC := $(CUDA_ROOT)/bin/nvcc -ccbin=icc

Vous devez ensuite éditer un second fichier :

nano src/CUDA/common.mk

Vous y trouverez une section de ce type :

# Compilers

NVCC ?= $(CUDA_INSTALL_PATH)/bin/nvcc

CXX := g++ -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

CC := gcc -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

#CXX := icc -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

#CC := icc -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

LINK := g++ -fPIC

Veuillez apporter la modification suivante :

# Compilers

NVCC ?= $(CUDA_INSTALL_PATH)/bin/nvcc

#CXX := g++ -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

#CC := gcc -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

CXX := icc -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

CC := icc -fPIC -DADD_ -Wall -openmp -DMAGMA_WITH_MKL -DMAGMA_SETAFFINITY -DGPUSHMEM=300 -DHAVE_CUBLAS

LINK := g++ -fPIC

/usr/local/cuda//bin/nvcc: Command not found

Ce message vous indique que le compilateur NVIDIA CUDA nvcc n’a pas pu être trouvé. Vous pouvez soit rétablir le chemin d’accès approprié en modifiant la ligne

CUDA_ROOT := /usr/local/cuda/

soit enregistrer un commentaire (en utilisant # comme premier symbole de la ligne) si la commande CUDA_ROOT a été définie en tant que variable d’environnement.

No rule to make target `/cm/shared/apps/intel/composer_xe/2015.5.223/mkl/interfaces/fftw3xf/libfftw3xf_intel.a', needed by `vasp'. Stop.

Votre module MKL local a probablement été installé sans support de l’interface FFTW3 en tant que bibliothèque statique. Si vous choisissez de commenter cette ligne afin de référencer cette bibliothèque statique (en utilisant # comme premier symbole de la ligne), une liaison analogue dynamique pourra être appliquée. Assurez-vous de commenter également la ligne associée à OBJECTS_GPU (et les suivantes, le cas échéant), et pas uniquement la ligne liée à OBJECTS.

Mon erreur n’est pas couverte dans ce guide

Si votre système répond à la configuration requise mentionnée dans ce guide, sachez que les erreurs les plus courantes relèvent le plus généralement d’un chemin d’accès erroné vers une bibliothèque spécifique. Il convient alors de changer ce chemin d’accès dans le fichier "makefile.include". Vous trouverez de plus amples informations sur les variables à l’adresse http://cms.mpi.univie.ac.at/wiki/index.php/Installing_VASP.

Déployez votre solution GPU personnalisée.