Tutoriel pour calculer, pas à pas

Pour calculer sur le cluster, il est nécessaire de soumettre son travail (job) au gestionnaire de tâches (SLURM) qui va planifier la réservation des ressources demandées (sauf erreur de la part de l’utilisateur). L’exemple ci-dessous vous donne un premier aperçu des étapes à réaliser et quelques exemples d’utilisation des commandes SLURM. Pour plus de détail, nous vous invitons à consulter la rubrique consacrée à Débuter avec SLURM.

Exemple de code

Pour guider vos premiers pas sur la machine, vous pouvez suivre cet exemple, dans lequel il s’agit de calculer \pi par une méthode intégrale. Le code de l’exemple, nommé pi.c est écrit en C et nécessite, à l’exécution, de spécifier en variable d’entrée, un entier qui contient le nombre d’intervalles d’intégration. Le code est le suivant :

/**
 * @brief Calculate pi by numerical integration 
 *
 * \pi = 4 \int_0^1 \sqrt{1-x^2} dx
 * 
 */
#include <stdio.h>
// for atoi, to read from arguments
#include <stdlib.h>
// for M_PI fabs ; sqrt (with option -lm)
#include <math.h>

int main(int argc, char *argv[])
{
  int n;
  double pi, dx, sum, x, xa, xb;

  // read n from arguments
  if(argc != 2) {
    printf("Usage: %s number-of-intervals\n", argv[0]);
    return 1;
  }

  n = atoi(argv[1]);
  printf("Number of intervals = %d\n", n);

  // discretize the integral by the trapeze method
  dx  = 1.0 / (double)n;
  sum = 0.0;
  for (int i = 0; i < n; i++) 
  {
    xa = (double)i * dx;
    xb = xa + dx;
    sum += 0.5 * ( sqrt(1.0-xa*xa) + sqrt(1.0-xb*xb) ) * dx;
  }
  pi = 4.0 * sum;

  // print result
  printf("Estimation of Pi %24.16E\nExact value      %24.16E Error : %24.16E\n",
          pi, M_PI, fabs(pi - M_PI));

  return 0;
}

Vous pouvez éditer le fichier ci-dessus sur votre machine locale (nommez le code pi.c pour la suite) ou vous pouvez le récupérer en clonant le projet git correspondant :

git clone git@gitlab.pmcs2i.ec-lyon.fr:PMCS2I/hpc-quick-start.git

Cela vous dépose le projet hpc-quick-start sur votre machine locale. Pour accéder au code, allez dans le répertoire correspondant

cd hpc-quick-start/pi_integral/src

La commande bash

ls -1

vous renvoie l’ensemble des fichiers présents ce répertoire, soient

Makefile
pi.c

Vous y trouverez un Makefile et également, dans différents réportoires, les scripts utilisés dans cet exemple pour lancer, exécuter et analyser le code. Dans toute la suite, on supposera que les fichiers sont récupérés uniquement en copiant à la souris les codes exposés sur cette page web.

Transfert sur le cluster

Transférer le code pi.c et le fichier d’entrée input.dat sur le cluster

rsync -avu pi.c LOGIN@login.pmcs2i.ec-lyon.fr:pi_integral/

LOGIN est votre identifiant PMCS2I. Cette commande crée le répertoire pi_integral à la racine de votre compte et copie dedans le code pi.c que vous avez créé sur votre machine locale.

Compilation

La compilation d’effectue sur une des machines prepost-*. Allez sur prepost-haswell via SSH :

ssh LOGIN@prepost-haswell.pmcs2i.ec-lyon.fr

et dans le répertoire où se trouve le code :

cd pi_integral

Chargez le compilateur C d’intel dans votre environnement, avec la commande module

module load intel

Lorsque vous demandez au système quel compilateur icc vous utilisez,

which icc

cela doit vous retourner quelque chose comme

/softs/eb/common/software/icc/2018.1.163-GCC-6.4.0-2.28/compilers_and_libraries_2018.1.163/linux/bin/intel64/icc

qui montre qu’il s’agit bien du module que vous venez de charger. La compilation du code s’effectue par

icc pi.c

ce qui vous génère l’exécutable a.out.

Script SLURM

Le script de soumission à l’ordonnanceur de tâches est le suivant :

#!/bin/bash
#SBATCH --job-name=pi-serial # nom du job en queue
#SBATCH --output=job.%j.out # fichier de sortie du job de soumission
#SBATCH --error=job.%j.err # fichier de sortie d'erreur du job de soumission
#SBATCH --nodes=1 # nombre de noeuds
#SBATCH --ntasks=1 # nombre total de tâches sur tous les noeuds
#SBATCH --partition=haswell # nom de la partition
#SBATCH --mem-per-cpu=1G # mémoire par coeur
#SBATCH --time=00:10:00 # limite de temps d'exécution total (HH:MM:SS)

echo $SLURM_NODELIST

module purge
module load intel


./a.out 1000000000

Il s’agit d’un script bash (ligne 1) pour une tâche séquentielle, demandant 1 coeur sur 1 noeud, avec 1G de mémoire par processus et pour un temps de 10 minutes maximum. Les lignes 2 à 9 continnent les instructions passées au SLURM pour effectuer la demande d’allocation de ressources. La ligne 11 affiche à l’exécution la liste des noeuds qui aura été réservée. Les lignes suivantes contiennent les mêmes commandes que celles que vous auriez utilisé si vous aviez lancé votre calcul directement sur un noeud du calculateur : la ligne 14 charge l’environnement intel avec lequel vous avez compilé le code (il est important que les deux environnements (de compilation et d’exécution) soient identiques. La ligne 17 lance l’exécutable avec en donnée d’entrée un milliard d’intervalles pour calculer l’intégrale.

Enregistrez ce fichier dans newton.slurm si vous n’avez pas cloné le dépôt des exemples. La dernière ligne lance

Soumission du job

Soumettez la tâche en exécutant la commande suivante :

sbatch job.slurm

Cela vous retourne quelque chose comme

Submitted batch job JOBID

où JOBID est le numéro du job dans la file d’attente, haswell ici. Vous pouvez surveiller l’état de votre travail avec

squeue -u LOGIN

où LOGIN est votre identifiant Newton (LOGIN=$USER). Si le champ ST est PD (en attente), votre travail attend que d’autres travaux se terminent. S’il s’exécute, il devient en R. Si vous ne le voyez pas dans la liste, c’est qu’il est terminé. Exemple :

JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON)
628598   haswell pi-seria  acadiou  R       0:02      1 haswell-t16-41

Sorties du job

Le code n’effectue aucune écriture de fichiers. Il renvoie sur la sortie standard des lignes ASCII, qui sont écrites dans la sortie du job. Une fois la tâche exécutée, deux fichiers auront été écrits par le gestionnaire de tâches :

job.JOBID.out
job.JOBID.err

où JOBID serait 628598 dans l’exemple ci-dessus. Vous pouvez afficher le contenu de la sortie en faisant

cat job.*.out

ce qui doit vous afficher quelque chose comme

haswell-t16-41
Number of intervals = 1000000000
Estimation of Pi   3.1415926535896967E+00
Exact value        3.1415926535897931E+00 Error :   9.6367358537463588E-14

Vous pouvez avoir une analyse de l’usage des ressouces avec la commande

seff JOBID

qui vous indique quelque chose comme

Job ID: 628598
Cluster: newton
User/Group: acadiou/lmfa
State: COMPLETED (exit code 0)
Cores: 1
CPU Utilized: 00:00:04
CPU Efficiency: 66.67% of 00:00:06 core-walltime
Job Wall-clock time: 00:00:06
Memory Utilized: 1.07 MB
Memory Efficiency: 0.10% of 1.00 GB

Cela vous permet d’ajuster éventuellement les ressources demandées (i.e. la taille mémoire pour ce travail séquentiel).

Réalisation d’une exploration paramétrique

Dans cet exemple, le nombre d’intervalles d’intégration peut être varié afin d’explorer son influence sur la précision du calcul numérique de $pi$. On peut imaginer varier $n$ entre $10^1$ et $10^9$. Cela peut être réalisé en soumettant autant de jobs que de cas à traiter, via le script bash ci-dessous, que vous pouvez copier dans un fichier nommé run-jobs.sh :

 1#!/bin/bash
 2
 3slurm_script=newton.slurm
 4
 5for p in $(seq 1 9); do
 6  let "n = 10 ** $p"
 7  rundir="case-n-$n"
 8  mkdir -p $rundir
 9  cp $slurm_script $rundir
10  cd $rundir
11    sed -i "s/N-INTERVALS/$n/g" $slurm_script
12    sbatch $slurm_script
13  cd ..
14done

où la ligne 14 du script de soumission SLURM, newton.slurm a été modifiée sous forme de modèle, comme suit

../a.out N-INTERVALS

En effet, chaque cas à traiter sera rangé dans un répertoire case-n-* et contiendra le script de soumission correspondant au cas, généré à partir du modèle en remplaçant N-INTERVALS par la valeur effective du nombre d’intervalles choisis. Les résultats seront écrits pour chaque cas dans le répertoire correspondant.

Lancer le script run-jobs.sh par la commande

. ./run-jobs.sh

Cela affiche une sortie du type

Submitted batch job 629982
Submitted batch job 629983
Submitted batch job 629984
Submitted batch job 629985
Submitted batch job 629986
Submitted batch job 629987
Submitted batch job 629988
Submitted batch job 629989
Submitted batch job 629990

Les résultats sont dans les différents répertoires :

case-n-10
case-n-100
case-n-1000
case-n-10000
case-n-100000
case-n-1000000
case-n-10000000
case-n-100000000
case-n-1000000000

Vous pouvez voir qu’ils contiennent, après exécution des tâches, les scripts de soumission ainsi que les fichiers de résultats :

ls -1 case-n-10
job.629982.err
job.629982.out
newton.slurm

avec

cat  case-n-10/newton.slurm
#!/bin/bash
#SBATCH --job-name=pi-serial # nom du job en queue
#SBATCH --output=job.%j.out # fichier de sortie du job de soumission
#SBATCH --error=job.%j.err # fichier de sortie d'erreur du job de soumission
#SBATCH --nodes=1 # nombre de noeuds
#SBATCH --ntasks=1 # nombre total de tâches sur tous les noeuds
#SBATCH --partition=haswell # nom de la partition
#SBATCH --mem-per-cpu=1200K # mémoire par coeur
#SBATCH --time=00:10:00 # limite de temps d'exécution total (HH:MM:SS)

echo $SLURM_NODELIST

module purge
module load intel

../a.out 10

Chacune de ces expériences peut être rejouée si nécessaire.

Analyse des résultats

Supposons que l’on veuille tracer l’évolution de l’erreur du calcul de $pi$, estimée par le code comme la valeur absolue de l’écart à la valeur de $pi$ fournie par la bibliothèque de math du C et enregistrée à chaque calcul dans le fichier de sortie, qui ressemble à

Number of intervals = 10
Estimation of Pi   3.1045183262483187E+00
Exact value        3.1415926535897931E+00 Error :   3.7074327341474422E-02

Pour faire ce tracé, il suffit de récupérer la valeur du nombre d’intervalles et la valeur de l’erreur. Pour cela, copiez le script awk suivant dans un fichier, que vous nommerez accuracy.awk :

BEGIN {
  }
  {
    if ($1 == "Number") n=$5;
    if ($1 == "Exact") err=$6;
  }
END {
  print n, err
}

et utiliser ce script pour écrire un fichier contenant les deux variables extraites de chaque cas, à l’aide de ce script bash (que vous nommerez mk-error.sh)

#!/bin/bash

resfile=error.txt

rm -f $resfile
touch $resfile
for p in $(seq 1 9); do
  let "n = 10 ** $p"
  rundir="case-n-$n"
  awk -f accuracy.awk < $rundir/job*out >> $resfile
done
`

Exécutez-le via la commande

. ./mk-error.sh

Cela génère le fichier error.txt, qui contient quelque chose comme :

10 3.7074327341474422E-02
100 1.1756218107472627E-03
1000 3.7186678765621650E-05
10000 1.1759784688258890E-06
100000 3.7187836809948749E-08
1000000 1.1759393458987688E-09
10000000 3.7435388122730728E-11
100000000 7.0077277314339881E-13
1000000000 9.6367358537463588E-14

qui peut être visualisé avec gnuplot. Pour ce faire, vous pouvez utiliser le script suivant (que vous nommerez show-error.gnu)

# script pour gnuplot version > 5.0
reset 

set terminal pop
set output

set logscale xy 
set format x '10^{%T}'
set format y '10^{%T}'
set xlabel 'n'
set ylabel '| pi - M\_PI |'

s(x)=-1.00*x-2

#M_PI = 3.14159265358979323846
set title 'Erreur'

plot 'error.txt' u 1:2 w l lt 1 lc -1 dt 1 lw 2 title 'Newton'
set label 6 '-1' at 20000,2e-5 center tc ls -1
replot [x=6000:60000] exp(s(log(x))) w l lt 1 lc -1 dt 2 lw 2 notitle


set terminal push
set terminal pdf color
set output 'error.pdf'
replot
set terminal pop 

Pour visualiser les fenêtres éditées par cette application, il faut que vous soyez connecté en mode graphique que les machines de prepost-, sinon vous aurez un message d’erreur du type :

gnuplot_qt: cannot connect to X server

vous rappelant l’absence de mode graphique dans votre connection. Pour travailler en mode graphique de façon efficace, connectez-vous via X2Go sur la machine prepost-haswell :

sessiob X2Go

Dans un terminal, chargez gnuplot par les modules :

module load gnuplot/5.0.3-foss-2016a

puis dans le répertoire où vous avez vos fichiers error.txt et votre script show-error.gnu, lancez

gnuplot < show-error.gnu

ou bien

gnuplot

	G N U P L O T
	Version 5.2 patchlevel 8    last modified 2019-12-01 

	Copyright (C) 1986-1993, 1998, 2004, 2007-2019
	Thomas Williams, Colin Kelley and many others

	gnuplot home:     http://www.gnuplot.info
	faq, bugs, etc:   type "help FAQ"
	immediate help:   type "help"  (plot window: hit 'h')

Terminal type is now 'qt'
gnuplot> load 'show-error.gnu' 

Vous aurez alors une fenêtre X11 avec laquelle vous pouvez interagir. Nota bene : le script écrit aussi un fichier pdf, que vous pouvez rapatrier sur votre machine locale.

Visualisation interactive