VASP accelerato da GPU

Inizia subito con questa guida per applicazioni basate su GPU.

VASP

VASP (Vienna Ab Initio Simulation Package) è un'applicazione sviluppata per eseguire calcoli relativi a strutture elettroniche su scala atomica basati su principi primi mediante soluzioni approssimate dell'equazione di Schrödinger. Il software implementa la teoria standard del funzionale densità nonché funzionalità all'avanguardia come funzionali ibridi, metodi basati sulla funzione di Green (GW e ACFDT-RPA) e la teoria della perturbazione del secondo ordine di Møller-Plesset (MP2).

VASP funziona con una velocità quasi decuplicata su NVIDIA Tesla P100 rispetto ai sistemi basati su sola CPU, permettendo l'uso di metodi più esigenti a livello computazionale e più accurati nello stesso tempo.

VASP è fino a 10 volte più veloce su GPU

Installazione

Requisiti di sistema

VASP viene distribuito come codice sorgente ed è soggetto ad alcune dipendenze per la compilazione e il runtime. In questa guida, si presume che i seguenti pacchetti software siano già installati sul sistema Linux e le rispettive variabili di ambiente siano già state configurate:

  1. Intel Compiler Suite (in particolare Fortran, C/C++ e MKL)
  2. Intel MPI
  3. NVIDIA CUDA 8.0

Nella maggior parte dei casi, questi pacchetti sono già stati installati sul supercomputer dagli amministratori e possono essere caricati attraverso un sistema modulare. L'installazione dei suddetti pacchetti non rientra nell'ambito di questa guida, pertanto si consiglia di contattare l'assistenza in caso di necessità.

La versione più recente della porta GPU per il software VASP può essere compilata con la PGI compiler suite, di cui è disponibile una community edition gratuita. Tuttavia, poiché molti utenti di VASP utilizzano il compilatore Intel, sarà quest'ultimo a essere impiegato nel corso di questo tutorial.

Download e compilazione

DOWNLOAD DI TUTTE LE RISORSE NECESSARIE

VASP è un software commerciale ed è possibile scaricare la versione più recente accelerata da GPU per la regolare licenza VASP. Per acquisire una licenza, visitare questa pagina. Inserire le credenziali di accesso a destra in "Community Portal" (Portale community) e fare clic su "Login" (Accedi) per accedere all'area di download. Fare clic su "VASP5" e selezionare la cartella "src". Al momento della redazione di questa guida, i file da scaricare sono i seguenti:

vasp.5.4.4.tar.gz

Assicurarsi di controllare con regolare frequenza il sito VASP per le patch e le versioni più recenti.

ESTRAZIONE E INSTALLAZIONE DELLE PATCH

Prima di tutto, estrarre il codice sorgente di VASP appena scaricato:

tar xfz vasp.5.4.4.tar.gz

Quindi, accedere alla directory appena estratta contenente il codice sorgente e applicare le patch:

cd vasp.5.4.4

Il makefile VASP richiede alcune modifiche per riflettere l'ambiente software locale. VASP viene distribuito con una serie di modelli di makefile per varie configurazioni che si trovano nella sottocartella arch/. Copiare il file makefile.include appropriato dalla cartella arch/ (in questa guida, viene utilizzato il compilatore Intel Fortran e NVIDIA CUDA su Linux):

cp arch/makefile.include.linux_intel makefile.include

Se si necessita di adattare il file makefile.include, consultare la sezione Risoluzione di problemi di seguito.

La maggior parte delle opzioni in makefile.include è preconfigurata in modo da rilevare i valori necessari dalle variabili di ambiente, tuttavia si consiglia vivamente di impostare la variabile GENCODE_ARCH nel file copiato in modo che rifletta le GPU in uso. Verificare le funzionalità di elaborazione (CC) delle schede GPU in uso e modificare il file makefile.include utilizzando un editor (ad es.: nano, vim o emacs sono disponibili su molti sistemi per impostazione predefinita):

nano makefile.include

Ai fini di questa guida, vengono impiegate schede NVIDIA P100, pertanto la compilazione è basata sulla Compute Capability 6.0 in modo da ottenere prestazioni ottimali. Di conseguenza, la riga predefinita GENCODE_ARCH risulta già appropriata e ha il seguente aspetto:

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\"

Lasciare invariati i flag delle capacità di elaborazione non utilizzati (ad es.: 3.5) non crea problemi, ma permette l'esecuzione del file binario risultante anche su altre architetture GPU. Se la GPU di destinazione è dotata di una capacità di elaborazione differente, assicurarsi di adattare la riga di conseguenza. Ad esempio, se si intende utilizzare anche una scheda V100, modificare la riga come segue (e utilizzare CUDA 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\"

A questo punto, compilare la porta GPU di VASP eseguendo (assicurarsi di non aggiungere add -j, poiché VASP non supporta la compilazione in parallelo):

make gpu

Se la compilazione va a buon fine, verrà mostrata la versione di VASP accelerata da GPU in bin/vasp-gpu. Verificare la presenza del file binario con il comando

ls -l bin/vasp-gpu

Se la compilazione non va a buon fine, consultare "Adattamento delle variabili di compilazione" nella sezione Risoluzione dei problemi.

Dopo la corretta creazione della build, è necessario compilare la porta GPU di VASP che consente i calcoli non collineari (condizione: LNONCOLLINEAR=.TRUE. o LSORBIT=.TRUE. in INCAR) utilizzando la destinazione gpu_ncl. Si noti che il punto Γ di VASP non è ancora supportato sulle GPU.

 

Analogamente, è possibile compilare le versioni per sola CPU (std, ncl, gam):

make gpu_ncl std ncl gam

Questa opzione produce il seguente elenco di file binari, ma ai fini di questo tutorial verranno utilizzati solo vasp_gpu e, in alternativa, vasp_std:

Tabella 1. Panoramica di diversi file eseguibili compilati per 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

Si consiglia di installare i file binari di VASP in una posizione esterna alla directory della build, ad esempio in ~/bin, in modo da evitare di sovrascrivere involontariamente i file con versioni future:

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

INSTALLAZIONE DEL DATABASE POTCAR

VASP si basa su dati tabulari utilizzati per agevolare le funzioni d'onda basate su tutti gli elettroni, spesso chiamate pseudopotenziali. Tali pseudopotenziali sono scaricabili.

Inserire le credenziali di accesso a destra in "Community Portal" (Portale community) e fare clic su "Login" (Accedi) per accedere all'area di download. Quindi, fare clic su "Potentials" (Potenziali) e iniziare con "LDA". Scaricare tutti i file disponibili e procedere come indicato per le cartelle "PBE" e "PW91".

Verranno scaricati i seguenti set di pacchetti:

  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

Estrarre i file utilizzando lo script: extractPOTCARs.sh.

Tutti gli script mostrati in questo tutorial sono disponibili per il download. È anche possibile clonare direttamente il repository nel filesystem utilizzando

Esecuzione dei processi

Primo calcolo VASP accelerato da GPU

Il file di controllo principale INCAR offre alcune opzioni che richiedono particolari accorgimenti per la porta GPU di VASP. GPU VASP mostra messaggi di errore e di avviso se le impostazioni nel file INCAR non sono supportate o non sono consigliate.

Non ignorare i messaggi relativi alla GPU e agire di conseguenza. Questa sezione illustra le impostazioni INCAR pertinenti alla GPU.

Limitarsi a utilizzare le seguenti opzioni per il flag ALGO:

  1. Normale
  2. Rapido
  3. Molto rapido

Gli altri algoritmi disponibili in VASP non sono stati ancora sottoposti a collaudo completo, pertanto le prestazioni non sono garantite e i risultati potrebbero non essere corretti. Tuttavia, è necessario utilizzare le seguenti impostazioni nel file INCAR:

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

Per iniziare, vengono qui proposti alcuni esempi di calcoli che verranno utilizzati successivamente per mostrare come raggiungere prestazioni superiori rispetto alle configurazioni di base. Alcuni file di input di esempio sono disponibili in git repository. Accedere alla directory corretta ed esaminare file INCAR: si osserverà che è allineato alle suddette opzioni:

cd gpu-vasp-files/benchmarks

Per motivi di copyright, occorre generare i file POTCAR necessari autonomamente. Si presume che il database degli pseudopotenziali sia già stato scaricato ed estratto come mostrato e che la directory di destinazione utilizzata sia ~/vasp/potcars/. I file di input di esempio includono uno script che consente la generazione automatica, tuttavia occorre specificare il percorso del database POTCAR:

cd siHugeShort
bash generatePOTCAR.sh ~/vasp/potcars

Successivamente, è possibile iniziare il primo calcolo VASP accelerato da GPU:

~/bin/vasp_gpu

Questo comando avvia un solo processo che utilizza una sola GPU e una CPU core, a prescindere dal numero di risorse disponibili sul sistema. Questo tipo di esecuzione potrebbe richiedere un tempo relativamente lungo, ma consente di appurare il corretto funzionamento. Per verificare che la GPU sia attivamente utilizzata, inserire

nvidia-smi -l

su un terminale connesso allo stesso nodo su cui è in esecuzione il processo. Il processo VASP dovrebbe risultare elencato insieme all'uso della GPU. Per interrompere la visualizzazione, premere CTRL+C.

Uso di un singolo nodo di elaborazione

Esattamente come per la versione standard di VASP, la versione per GPU è parallelizzata con MPI ed è in grado di distribuire il carico di lavoro computazionale su più CPU, GPU e nodi di calcolo. In questa guida viene utilizzato Intel MPI, ma tutte le tecniche qui descritte sono compatibili anche con altre implementazioni di MPI. Consultare la documentazione dell'implementazione MPI attualmente in uso per le opzioni equivalenti della riga di comando.

VASP supporta una serie di differenti funzionalità e algoritmi che determinano profili computazionali altrettanto differenti. Pertanto, a seconda dei calcoli specifici, potrebbe essere necessario aggiungere diversi parametri per ottenere tempi di esecuzione più rapidi. Tali aspetti riguardano anche la versione per GPU.

In questo tutorial, vengono offerte varie tecniche che consentono di accelerare le esecuzioni su GPU. Tuttavia, poiché non esiste una sola configurazione ottimale, occorre eseguire un benchmark dei singoli casi in modo da individuare le impostazioni necessarie per ottenere le migliori prestazioni.

In primo luogo, osserviamo quante (e quali) GPU offre il nodo in uso:

nvidia-smi –L

L'output del comando indica che sono presenti 4 GPU Tesla P100 disponibili e restituisce i relativi identificativi univoci (UUID) che verranno utilizzati successivamente:

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

Normalmente le GPU necessitano il trasferimento dei dati da e verso la memoria principale. Su sistemi multi-socket, le prestazioni del trasferimento variano a seconda del percorso che i dati devono percorrere. Nel migliore dei casi, è presente un bus diretto tra due regioni separate della memoria. Nello scenario peggiore, invece, il processo della CPU necessita di accedere alla memoria fisicamente ubicata in un modulo RAM associato all'altro socket CPU e successivamente eseguire la copia nella memoria della GPU ancora una volta accessibile solo tramite PCI-E controllato dall'altro socket CPU. Le informazioni sulla topologia del bus possono essere visualizzate con il comando:

nvidia-smi topo -m

Poiché VASP accelerato da GPU per il momento non supporta la comunicazione diretta GPU-GPU, possiamo ignorare la maggior parte dell'output che indica quali coppie di GPU potrebbero comunicare più rapidamente (PIX o anche NV#) o meno rapidamente (SOC) tra loro:

  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  

L'ultima colonna con intestazione "CPU Affinity" è importante perché indica su quali core CPU eseguire idealmente i rank MPI, quando comunicano con una determinata GPU. Osserviamo che tutti i core CPU del primo socket (0-15) possono comunicare direttamente con la GPU0, mentre le CPU del secondo socket (16-31) dovrebbero raggiungere le prestazioni ottimali se abbinate a GPU1, GPU2 e GPU3.

Benchmark

Prestazioni previste

Per confrontare i tempi di esecuzione di diverse configurazioni, è fondamentale evitare deviazioni non previste. Le GPU NVIDIA offrono funzionalità che consentono di alzare o abbassare il clock-rate temporaneamente sulla base del carico termico e del carico computazionale. Sebbene questa possibilità permetta di risparmiare I consumi elettrici, ai fini del benchmark potrebbe produrre numeri fuorvianti a causa si una varianza leggermente maggiore sui tempi di esecuzione tra più esecuzioni. Pertanto, per un eseguire un benchmark comparativo, tenteremo di disattivare questa funzionalità per tutte le schede presenti nel sistema:

Completato il benchmark, è possibile reimpostare le schede in modo da eseguirle con le massime frequenze supportate:

DECLINAZIONE DI RESPONSABILITÀ IN MERITO ALLE INDICAZIONI DI PRESTAZIONI

Sebbene in numeri che rappresentano le prestazioni siano stati generati su sistemi di produzione, essi vengono forniti esclusivamente a scopo dimostrativo dei modelli presentati nelle sezioni che seguono. Si noti che le prestazioni sui singoli sistemi possono differire a causa dei tanti aspetti che influiscono sulle prestazioni di CPU e GPU.

Metodo più semplice: un processo per GPU

Il metodo più semplice per utilizzare tutte le 4 GPU presenti nel sistema è avviare 4 processi MPI di VASP, mappando i core CPU eseguiti dai processi in modo automatico:

mpirun -n 4 ~/bin/vasp_gpu

L'ambiente Intel MPI associa automaticamente i processi a determinati core CPU, in modo che il sistema operativo non possa spostarli in altri core durante l'esecuzione del processo evitando scenari indesiderati per lo spostamento dei dati. Tuttavia, questa resta comunque una soluzione non totalmente ottimale perché l'implementazione MPI non conosce la topologia della GPU. È possibile indagare sull'associazione dei processi espandendo il comando:

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

Se si osserva l'output e lo si confronta con i risultati relativi alla topologia di interconnessione, si rileverà che le impostazioni non sono ottimali:

...

[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

...

Il rank 0 usa la GPU0, ma è collegato con i core CPU 16-23 più distanti. Lo stesso problema si verifica per i rank 2 e 3. Solo il rank 1 usa la GPU1 ed è associato ai core 24-31, che offrono le migliori prestazioni per il trasferimento.

Osserviamo ora alcuni numeri relativi a prestazioni effettive. Utilizzando tutti i core 32 delle due CPU Intel® Xeon® E5-2698 v3 CPU presenti sul sistema senza alcuna accelerazione con GPU, sono stati necessari 607,142 secondi per completare il benchmark siHugeShort.1 Utilizzando 4 GPU in questa configurazione predefinita, ma non ottimale, si ottengono tempi di esecuzione di 273,320 secondi e un'accelerazione di 2,22 volte. Utilizzare le seguenti metriche incluse in VASP per individuare rapidamente i tempi di esecuzione del calcolo

1 Se è stata compilata la versione di VASP con sola CPU, è possibile utilizzare il seguente comando per verificare i tempi necessari sul proprio sistema: mpirun -n 32 -env I_MPI_PIN_PROCESSOR_LIST=allcores:map=scatter ~/bin/vasp_std

grep Elapsed\ time OUTCAR

VASP mappa le GPU ai rank MPI in modo consecutivo, mentre ignora le GPU con capacità di elaborazione insufficiente (se presenti). Considerato questo dato, e utilizzando la seguente sintassi, è possibile controllare manualmente il posizionamento del processo sulla CPU e distribuire i rank in modo che ogni processo utilizzi una GPU con il percorso più breve alla memoria:

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

Questo metodo non genera un miglioramento sul sistema (il runtime era 273,370 s), il che è causato probabilmente da un uso non bilanciato delle risorse comuni della CPU come la larghezza di banda della memoria e le cache (3 processi che condividono una sola CPU). Per raggiungere un compromesso, è possibile distribuire i rank in modo da ripartirli equamente tra i socket CPU, ma un rank dovrà utilizzare il percorso di memoria più lento sulla GPU:

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

Con un runtime di 268,939 s, si osserva un lieve miglioramento di circa il 3% per questo benchmark, tuttavia se il carico di lavoro è superiore sui trasferimenti di memoria, il guadagno potrebbe risultare anche maggiore.

In particolare, per numeri di rank superiori, la selezione manuale della distribuzione potrebbe risultare un'operazione tediosa e la condivisione equa delle risorse della CPU potrebbe risultare più importante dei trasferimenti di memoria sul sistema. Il seguente comando mappa i rank in modo consecutivo, ma evita il più possibile la condivisione delle risorse comuni:

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

Questa opzione ha restituito un runtime di 276,299 s e può risultare particolarmente utile qualora alcuni core CPU restino inattivi. È possibile adottare questo metodo ove un singolo processo per GPU causi la saturazione di una risorsa GPU limitando le prestazioni. L'ulteriore sovraccarico della GPU, in tal caso, ridurrebbe ulteriormente le prestazioni. È possibile osservarne un esempio nel benchmark siHugeShort, pertanto sul nostro sistema non esistono ulteriori margini di miglioramento (provare anche le successive opzioni). Di norma, non è una buona idea sprecare core CPU disponibili a meno che non vi sia un sovraccarico delle GPU, quindi procedere con dei test.

Secondo metodo più semplice: più processi per GPU

Per dimostrare come utilizzare più core CPU rispetto alle GPU disponibili, verrà impiegato un altro benchmark chiamato silicaIFPEN. Occorrono 710,156 s per l'esecuzione utilizzando solo 32 core CPU. Utilizzando 4 GPU P100 con un rank MPI per GPU e il compromesso relativo al posizionamento del processo, occorrono 241,674 s per il completamento (2,9 volte più veloce). Le GPU NVIDIA possono essere condivise tra più processi. Per utilizzare questa funzionalità, occorre verificare che tutte le GPU siano impostate sulla modalità di elaborazione predefinita:

Eseguire quindi il calcolo effettivo:

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

Per il benchmark silicaIFPEN, sul nostro sistema il runtime è migliorato raggiungendo i 211,576 s per 12 processi che condividono 4 GPU P100 (ovvero 3 processi per GPU), il che ha prodotto una velocità 3,36 volte maggiore. Tuttavia, l'uso di 4 o più processi per GPU produce un effetto negativo sul runtime. Il confronto nella tabella che segue mostra che i processi posizionati manualmente non producono un vantaggio concreto.

Tabella 2. Confronto tra tempi del benchmark silicaIFPEN variando il numero di processi MPI per 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

Una volta raggiunte le prestazioni ottimali, l'aggiunta di più processi per GPU produce effetti negativi. Come mai? Quando una GPU necessita di cambiare contesto, ovvero di consentire a un altro processo di prendere le risorse, si introduce un punto di sincronizzazione forzato. Di conseguenza, non vi è possibilità di sovrapposizione delle istruzioni di diversi processi nella GPU e l'uso eccessivo della funzionalità può in effetti rallentare nuovamente l'esecuzione. Vedere anche l'illustrazione che segue.

In conclusione, è sensato testare fino a che punto sia possibile sovraccaricare le GPU prima che le prestazioni vengano compromesse per il tipo di calcolo da eseguire. Ovviamente, i calcoli molto estesi possono riempire più facilmente una GPU già con un singolo processo rispetto a quelli più piccoli, ma la cosa migliore è sempre quella di testare le configurazioni in base ai calcoli.

VASP è fino a 10 volte più veloce su GPU

NVIDIA MPS: abilitare l'overlapping condividendo le GPU

Questo metodo è strettamente correlato al precedente, ma rimedia al problema della mancata sovrapposizione di più processi sulla GPU come mostrato nella seconda riga dell'illustrazione. Si consiglia di impostare le GPU in modo da funzionare in modalità di elaborazione esclusiva quando si utilizza MPS:

Per utilizzare questa possibilità occorre avviare il server MPS prima di lanciare i processi MPI come di consueto:

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

Il primo comando avvia il server MPS in background (modalità deamon). Durante l'esecuzione, il server intercetta le istruzioni emesse dai processi condividendo una GPU e le posiziona nello stesso contesto prima di inviarle alla GPU. La differenza rispetto al metodo illustrato nella sezione precedente è che dalla prospettiva della GPU le istruzioni appartengono a un singolo processo e contesto e pertanto possono sovrapporsi, proprio come accade con gli stream in un'applicazione CUDA. Verificare con nvidia-smi -l che un solo processo stia accedendo alle GPU.

Questa modalità di esecuzione della versione GPU di VASP aiuta ad aumentare l'utilizzo della GPU, quando un singolo processo non ne satura le risorse. Per dimostrarlo, impieghiamo il terzo esempio B.hR105, un calcolo che utilizza lo scambio esatto all'interno del funzionale HSE06. Il calcolo è stato eseguito con diverse quantità di rank MPI per GPU, ogni volta con e senza MPS abilitato.

Tabella 3. Confronto tra tempi del benchmark B.hR105 variando il numero di processi MPI per GPU ciascuno con e senza MPS per 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

Si noti che MPS migliora i tempi di esecuzione di 55,4 s (il 26%) da 158,465 s se confrontato con il miglior risultato senza MPS (213,903 s). Sebbene si ottengano le prestazioni ottimali con 4 rank per GPU senza MPS, l'avvio di un numero di processi equivalenti al numero di core CPU disponibili produce le migliori prestazioni con MPS.

Abbiamo ignorato i calcoli con 3, 5 e 6 rank per GPU appositamente perché il numero di bande (224) utilizzato in questo esempio non è divisibile per il numero di rank e verrebbe quindi aumentato da VASP automaticamente, aumentando di conseguenza il carico di lavoro. Se si è interessati esclusivamente ai tempi per giungere alla soluzione, si consiglia di sperimentare con il parametro NSIM. Impostandolo su 32 e utilizzando 1 solo processo per GPU (senza MPS) è possibile ridurre i tempi di calcolo fino a 108,193 s, approssimativamente un'accelerazione di 10 volte.

Avanzato: una istanza MPS per GPU

Per alcune configurazioni, soprattutto su versioni meno recenti di CUDA, potrebbe risultare vantaggioso avviare più istanze del deamon MPS, ad esempio un server MPS per GPU. Tuttavia, questo richiede un intervento più massiccio poiché occorre specificare per ogni processo MPI quale server MPS utilizzare e ogni istanza MPS deve essere associata a un'altra GPU. In particolare su P100 con CUDA 8.0, si sconsiglia l'uso di questo metodo, ma questo non significa necessariamente che non possa rivelarsi utile. Per avviare le istanze di MPS, è possibile utilizzare il seguente script:

Perché questo metodo funzioni, la versione GPU di VASP deve essere avviata indirettamente utilizzando uno script wrapper. Questo imposterà le variabili di ambiente in modo che ogni processo usi l'istanza corretta dei server MPS appena avviati: runWithMultipleMPSservers-RR.sh.

Lo script in sostenza genera un elenco di percorsi per impostare le variabili di ambiente che stabiliscono quale istanza MPS viene impiegata. La quartultima riga (myMpsInstance=...) seleziona quindi tale istanza a seconda dell'ID del processo MPI locale. Abbiamo deciso di utilizzare il meccanismo round-robin, distribuendo i processi da 1 a 4 alle da GPU0 a GPU3. Il processo 5 usa nuovamente la GPU 0 così come il processo 9, mentre i processi 6 e 10 sono mappati alla GPU 2 e così via. Se è utilizzato un percorso diverso per l'installazione del file binario VASP GPU, assicurarsi di adattare la riga che inizia con runCommand di conseguenza. Iniziamo il calcolo:

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

Quando si utilizza MPS, tenere presente che i server MPS utilizzano anch'essi la CPU. Pertanto, quando si avviano tanti processi quanti core CPU disponibili la CPU potrebbe comunque risultare sovraccaricata. È quindi una buona idea riservare un core o due per le istanze MPS.

Quando il calcolo è completato, il seguente script pulisce le istanze MPS:

Uso di più nodi di elaborazione

UN PROCESSO PER GPU

In pratica, tutte le informazioni relative all'uso di GPU in un singolo nodo sono valide anche per l'uso di più nodi. Pertanto, la configurazione che è risultata ottimale per i propri sistemi, per quanto riguarda la mappatura dei processi, risulterà valida anche con l'uso di più nodi. Nel seguente esempio si presume sia già stato configurato un file host con tutti i nodi associati al processo. Sono stati utilizzati due nodi e il file host ha il seguente aspetto:

hsw224
hsw225

Se si sceglie la selezione manuale della mappatura dei processi, si noterà solo una lieve differenza nel comando dato nel precedente capitolo:

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

L'output del debug dell'implementazione MPI indica che i processi sono distribuiti su due nodi e risiedono sui core CPU, esattamente come previsto:

[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

L'output di VASP con GPU conferma che anche le GPU sono mappate ai rank MPI, come previsto:

Dispositivo 0 (rank 0, rank locale 0, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 1 (rank 1, rank locale 1, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 2 (rank 2, rank locale 2, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 3 (rank 3, rank locale 3, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 0 (rank 4, rank locale 0, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 1 (rank 5, rank locale 1, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 2 (rank 6, rank locale 2, dimensione locale 4): Tesla P100-PCIE-16GB
Dispositivo 3 (rank 7, rank locale 3, dimensione locale 4): Tesla P100-PCIE-16GB

Le prestazioni del benchmark siHugeShort risultano leggermente più veloci con 258,917 s, ma rispetto al runtime di 268,939 s su un nodo il suo uso non è assolutamente giustificato. Il benchmark silicaIFPEN, d'altro canto, migliora notevolmente da 241,674 s a 153,401 s, passando da 4 a 8 GPU P100 con un processo MPI per GPU.

PIÙ PROCESSI PER GPU

In merito alle precedenti sezioni, l'uso di più nodi con più processi per GPU risulta trasparente:

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

In alternativa, se si desidera utilizzare tutti i core CPU:

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

Per aumentare il numero di rank per GPU, il benchmark silicaIFPEN usa un approccio leggermente diverso rispetto al caso a singolo nodo (la configurazione più rapida ha impiegato 211,576 s): L'uso di due processi per GPU migliora il runtime in modo poco significativi con 149,818 s rispetto ai 153,401 s ottenuti con un processo per GPU. Il sovraccarico ulteriore delle GPU produce ancora una volta un effetto negativo poiché già l'uso di 3 processi per GPU aumenta il runtime a 153,516 s e 64 rank in totale lo portano a 231,015 s. Ne deriva quindi che l'uso di 1 o 2 processi per GPU su ciascun nodo è sufficiente in questo caso.

MPS NVIDIA SU PIÙ NODI

L'uso di una singola istanza di MPS per nodo è irrilevante quando le istanze sono avviate. Alcuni software di schedulazione offrono opzioni di invio che eseguono questa funzionalità automaticamente, ad esempio: SLURM talvolta offre --cuda-mps. Se il cluster in uso offre questa funzionalità, si consiglia vivamente di utilizzarla e di procedere come descritto nella sezione precedente.

Cosa fare se invece il software di schedulazione non offre questa pratica soluzione? È necessario verificare che su ciascun nodo sia avviata una sola istanza MPS prima di lanciare VASP. Ecco un altro script per eseguire tale verifica: runWithOneMPSPerNode.sh.

Ancora una volta, se il file binario VASP accelerato da GPU è stato installato in un'altra posizione, è necessario adattare la variabile runCommand all'inizio dello script. Le variabili che seguono calcolano il rank locale su ciascun nodo, poiché l'implementazione di Intel MPI non fornisce informazioni così facilmente. Lo script avvia un server MPS su ogni primo rank del nodo facendo in modo che il processo MPS non sia legato allo stesso core a cui un processo VASP verrà legato successivamente. Questo passaggio è fondamentale poiché, in caso contrario, l'istanza MPS sarebbe limitata all'uso di un solo core singolo (mentre può utilizzarne di più) e, ancor peggio, potrebbe entrare in conflitto con VASP per i cicli della CPU su tale core. Lo script continua a eseguire VASP quindi successivamente interrompe MPS.

Lo script deve essere richiamato dal comando mpirun, come si evince dalla sezione avanzata. Il comando mpirun funziona esattamente come per l'esecuzione senza MPS, ma viene richiamato lo script invece del file binario VASP:

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

In merito al benchmark B.hR105, MPS migliora il runtime su un singolo nodo e questo vale anche per l'uso di due nodi: l'abilitazione di MPS accelera i tempi di calcolo e l'uso di più rank per GPU risulta vantaggioso fino a un certo punto di (sovra-) saturazione. Le prestazioni ottimali sul nostro sistema sono state raggiunte con 4 rank per GPU, producendo un runtime di 104,052 s. Rispetto a un singolo nodo Haswell si ottiene un'accelerazione di 9,05 volte e rispetto a tutti i 64 core CPU la velocità risulta comunque maggiore di 6,61 volte.

Se si utilizza NSIM=32 con 4 rank per GPU su ciascuno di 2 nodi senza MPS, il calcolo richiede solo 71,222 s.

Tabella 4. Confronto tra tempi del benchmark B.hR105 variando il numero di processi MPI per GPU ciascuno con e senza MPS per NSIM=4 utilizzando 2 nodi

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 è fino a 10 volte più veloce su GPU

Configurazioni del sistema consigliate

Configurazione hardware

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

Configurazione software

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

Risoluzione dei problemi

ADATTAMENTO DELLE VARIABILI DI COMPILAZIONE (facoltativo)

L'ambiente software locale potrebbe differire da quello gestito automaticamente dal sistema di compilazione di VASP. In tal caso, la compilazione non andrà a buon fine ed è necessario apportare alcune modifiche al file makefile.include. Aprire il file makefile.include con un editor (ad es.: nano, vim o emacs sono disponibili su molti sistemi per impostazione predefinita) e apportare le modifiche necessarie (vedere sotto):

nano makefile.include

Ogni volta che si modifica un file, accertarsi di eseguire il seguente comando per iniziare la compilazione da zero:

make veryclean

Di seguito, viene riportato un elenco dei tipici messaggi di errore e come risolverli:

mpiifort: Command not found

Comando non trovato: questo messaggio di errore indica semplicemente che sul sistema in uso il compilatore Intel Fortran compatibile con MPI ha un nome diverso da quello previsto. In makefile.include, modificare tutte le occorrenze di mpiifort con il nome utilizzato sul sistema (ad es.: mpif90).

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

Intel è compatibile solo con compilatori Intel: per risolvere questo errore occorre eseguire due operazioni. Prima di tutto, modificare il file makefile.include aggiungendo -ccbin=icc alla variabile NVCC, in modo che la riga sia:

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

Successivamente, occorre modificare un secondo file:

nano src/CUDA/common.mk

Questo file include la seguente sezione:

# 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

Modificare la sezione come segue:

# 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

Comando non trovato: questo messaggio indica che non è stato possibile trovare il compilatore NVIDIA CUDA nvcc. È possibile correggere il percorso nella riga

CUDA_ROOT := /usr/local/cuda/

oppure inserire un commento (utilizzando il simbolo # all'inizio della riga) se CUDA_ROOT è impostata come variabile di ambiente.

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

Non è stata trovata una regola per impostare la destinazione richiesta da VASP e il comando verrà interrotto: probabilmente l'MKL locale è stato installato senza il supporto per l'interfaccia FFTW3 come libreria statica. Se si inserisce un commento nella riga con un riferimento a tale libreria statica, inserendo il simbolo # all'inizio della riga, il linker estrarrà l'analogo dinamico. Assicurarsi di commentare la riga associata a (e che segue) OBJECTS_GPU e non quella dopo OBJECTS.

L'errore generato non è incluso in questa guida

Se il sistema in uso soddisfa i requisiti definiti all'inizio della guida, molto probabilmente occorre modificare semplicemente il percorso a una libreria nel file makefile.include. Per la spiegazione delle variabili definite consultare http://cms.mpi.univie.ac.at/wiki/index.php/Installing_VASP.

Crea la tua soluzione GPU ideale oggi stesso.