Python

L’informations ci-dessous est complémentaire à la documentation technique de l’Alliance sur Python.

Modules

Pour charger une version de Python compatible avec l’environnement logiciel par défaut :

[alice@ip09 ~]$ module avail python
------------------------------------- Core Modules --------------------------------------
   ipython-kernel/3.10              python-build-bundle/2024a (D)
   ipython-kernel/3.11       (D)    python/3.10.13            (t,3.10)
   ipython-kernel/3.12              python/3.11.5             (t,D:3.11)
   python-build-bundle/2023b        python/3.12.4             (t)

...

[alice@ip09 ~]$ module load python/3.11.5

La version recommandée (module par défaut) est indiquée par (D). Si vous avez besoin d’une version qui n’est pas disponible dans l’environnement logiciel par défaut, utilisez module spider python pour afficher toutes les versions disponibles.

En plus de modules pour Python lui-même, les logiciels de l’Alliance contiennent scipy-stack, des modules pour Scientific Python fournissant scipy mais aussi numpy, pandas, matplotlib, etc. Une fois votre module pour Python lui-même chargé :

[alice@ip09 ~]$ module avail scipy-stack

------------------------------------- Core Modules --------------------------------------
   scipy-stack/2023b (math)    scipy-stack/2024a (math)    scipy-stack/2024b (math,D)

...

[alice@ip09 ~]$ module load scipy-stack/2024b

Environnements virtuels

Tel qu’expliqué dans la section suivante, les paquets Python (autres que Scientific Python) peuvent être installés avec la commande pip, en les téléchargeant de PyPI ou en les choisissant parmis les paquets précompilés par l’équipe des logiciels de l’Alliance. Toutefois, nous recommandons de ne jamais installer de paquets Python sans d’abord activer un environnement virtuel.

Les environnements virtuels Python sont des répertoires contenant un ensemble de paquets installés pour une tâche donnée. Par exemple, vous pourriez avoir un environnement virtuel pour simuler des systèmes quantiques avec QuTiP et un autre pour l’apprentissage machine avec TensorFlow. Vous pourriez même avoir plusieurs environnements virtuels pour différentes versions de QuTiP ou TensorFlow au gré de vos projets.

En utilisant systématiquement un environnement virtuel pour vos tâches Python, vous faciliterez l’installation de vos logiciels, le déboguage de vos calculs et la reproducibilité de votre recherche. À l’inverse, si vous installez tous vos paquets Python dans l’emplacement par défaut sans discernement, vous ferez face à plus de problèmes de compatibilité et l’installation ou la mise à jour d’un paquet pourrait boguer une tâche qui fonctionnait précédemment. De plus, vous ne pourrez pas alterner entre différentes versions d’un paquet. Finalement, il sera plus difficile de ré-exécuter une tâche sur un autre ordinateur ou de fournir des instructions pour reproduire votre environnement logiciel et, donc, votre recherche.

Pour suivre cette longue explication, voici une courte démonstration. D’abord, chargeons les modules pour Python et Scientific Python :

[alice@ip09 ~]$ module load python/3.11.5
[alice@ip09 ~]$ module load scipy-stack/2024b

Ensuite, créons un environnement virtuel :

[alice@ip09 ~]$ virtualenv --no-download $HOME/venv/qutip

Activons l’environnement :

[alice@ip09 ~]$ source $HOME/venv/qutip/bin/activate

Vous remarquerez que l’invite de commande change pour indiquer l’environnement virtuel actif. Toutes les actions de la commande pip (installer, désinstaller, mettre à jour des paquets) cibleront désormais le répertoire $HOME/venv/qutip.

La première chose à faire est de mettre à jour pip :

(qutip) [alice@ip09 ~]$ pip install --no-index --upgrade pip

Ensuite, nous pouvons installer des paquets, par exemple QuTiP :

(qutip) [alice@ip09 ~]$ pip install --no-index qutip==5.0.1

Finalement, l’environnement peut être désactivé :

(qutip) [alice@ip09 ~]$ deactivate

Une fois l’environnement créé, il peut être réutilisé simplement en l’activant à nouveau ; nul besoin de réinstaller les paquets. Par exemple, l’environnement construit ci-dessus peut être utilisé dans un script de tâche avec :

module purge
module load python/3.11.5
module load scipy-stack/2024b
source $HOME/venv/qutip/bin/activate

Installer hors d’un environnement virtuel

Si vous essayez d’installer des paquets Python sans d’abord activer un environnement virtuel, vous obtiendrez l’erreur suivante :

[alice@ip09 ~]$ pip install --no-index numpy
ERROR: Could not find an activated virtualenv (required).

Si vous souhaitez néanmoins installer un paquet à l’extérieur d’un environnement virtuel, vous pouvez le faire avec :

[alice@ip09 ~]$ PIP_REQUIRE_VIRTUALENV=false pip install --no-index numpy

Note

Ce comportement est différent de celui des grappes de l’Alliance, où il est possible par défaut d’installer des paquets Python à l’extérieur d’un environnement virtuel.

Paquets Python précompilés

La commande avail_wheels liste les paquets logiciels Python précompilés par l’équipe des logiciels de l’Alliance. Ces paquets sont optimisés pour le CHP. Par exemple, pour chercher Qiskit:

[alice@ip09 ~]$ avail_wheels qiskit
name    version    python    arch
------  ---------  --------  -------
qiskit  1.2.4      cp38      generic

Pour installer cette version pré-compilée dans un environnement virtuel actif :

(qiskit) [alice@ip09 ~]$ pip install --no-index qiskit==1.2.4

Parallélisation avec Python

Le code Python n’est pas typiquement parallélisé. Par conséquent, demander plusieurs cœurs CPU n’accélérera pas vos tâches automatiquement ! Vous devez d’abord paralléliser votre code, soit explicitement, soit en utilisant des fonctions parallélisées d’une bibliothèque, comme certaines fonctions de NumPy ou SciPy.

À cause d’une limitation intrinsèque, le « global interpreter lock », le code Python ne peut être parallélisé avec le modèle de mémoire partagée. Il existe toutefois des alternatives. L’une d’elles est de coder une extension Python en C/C++ en utilisant une bibliothèque de programmation parallèle telle qu’OpenMP. Une autre est d’utiliser le modèle de mémoire distribué avec de multiple processus Python. Pour ce faire, vous pouvez utiliser le module multiprocessing, ou encore une bibliothèque telle que mpi4py (passage de messages) ou Dask (calcul distribué).

Sur-souscription de fils

Un problème commun avec le parallélisme dans Python est la sur-souscription de fils d’éxécution (« thread oversubscription »), c’est-à-dire que le nombre de fils d’exécution lancés dans une tâche est supérieur au nombre de cœurs CPU alloués à la tâche. Le module multiprocessing, en particulier, lance par défaut autant de fils d’exécution qu’il y a de cœurs CPU, sans considérer si les cœurs sont tous accessibles. Par exemple, multiprocessing lancera par défaut 64 fils d’exécution si vous l’utilisez dans une tâche sur un nœud CPU de la plateforme CHP-IQ, même si n’avez demandé que 2, 4 ou 8 cœurs.

Ce problème est aggravé si l’on utilise aussi une fonction parallélisée qui lance par défaut fils qu’il y a de cœurs (telle que scipy.sparse.linalg.eigsh). Si l’on poursuit l’exemple ci-haut, dans une tâche qui utilise à la fois multiprocessing et eigsh, 4096 fils d’exécution (64 × 64) seront lancés par défaut, même si la tâche n’a accès qu’à 2, 4 ou 8 cœurs. La performance sera drastiquement réduite.

Pour palier ce problème, vous devez spécifier à SciPy, multiprocessing, Dask, etc. le nombre de fils d’exécution à utiliser. En ajoutant les instructions suivantes à votre script de tâche (avant votre calcul), vous désactiverez la parallélisation implicite de la plupart des fonctions, incluant celles de SciPy, qui utilise OpenMP ou Intel MKL en arrière-plan :

export OMP_NUM_THREADS=1
export MKL_NUM_THREADS=1

Pour contrôler le nombre de processus à lancer avec multiprocessing :

from multiprocessing import Pool
from os import environ

nprocesses = int(environ.get('SLURM_CPUS_PER_TASK', default=1))

pool = Pool(nprocesses)

Avec Dask :

from os import environ
from dask.distributed import LocalCluster

nprocesses = int(environ.get('SLURM_CPUS_PER_TASK', default=1))

cluster = LocalCluster(n_workers=nprocesses)

Si, au contraire, vous n’utilisez pas multiprocessing, Dask, etc. mais que vous souhaitez plutôt prendre avantage des fonctions parallèles de SciPy, contrôlez le nombre de fils d’exécution avec :

export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
export MKL_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}

Voir aussi

  • Cette entrée de notre FAQ discute les problèmes de performance et de fils d’exécution de manière plus générale.