Introduction à l’optimisation des performances CPU et GPU
Dans le calcul haute performance et le traitement de tâches parallèles à grande échelle, les GPU sont devenus des accélérateurs indispensables. Pour exploiter pleinement les capacités de calcul des GPU, il est crucial d’optimiser les relations entre CPU et GPU en allouant et en liant raisonnablement les cœurs CPU aux GPU. Cet article examinera en profondeur les concepts de socket et de NUMA (accès mémoire non uniforme) et discutera de la manière de mettre en œuvre la liaison des cœurs CPU et GPU en fonction de ces architectures matérielles afin de garantir des performances système optimales.
Concept de socket
Qu’est-ce qu’un socket ?
Un socket désigne généralement l’emplacement d’installation physique du CPU sur la carte mère. Chaque socket correspond à un CPU physique, contenant généralement plusieurs cœurs et un ou plusieurs niveaux de cache (par exemple, cache L1, L2, L3). Dans les systèmes multi-sockets (par exemple, les serveurs double ou quadruple), chaque socket possède un CPU physique connecté via une interconnexion haute vitesse (par exemple, QPI d’Intel ou Infinity Fabric d’AMD).
Caractéristiques des systèmes multi-sockets
Dans les systèmes multi-sockets, le CPU de chaque socket peut accéder à sa mémoire locale ainsi qu’à la mémoire d’autres sockets. Ce modèle d’accès mémoire introduit le concept de NUMA, visant à optimiser l’efficacité de l’accès mémoire.
Architecture NUMA (accès mémoire non uniforme)
Qu’est-ce que le NUMA ?
NUMA signifie Non-Uniform Memory Access, soit accès mémoire non uniforme. Contrairement à l’accès mémoire uniforme (UMA) traditionnel, dans l’architecture NUMA, la mémoire système est divisée en plusieurs régions, chacune associée à un CPU (socket) spécifique. L’accès à la mémoire locale (dans le même socket) est plus rapide que l’accès à la mémoire distante (d’un autre socket), ce qui entraîne une latence plus élevée.
Nœuds NUMA et latence d’accès mémoire
Dans les systèmes NUMA, chaque socket et sa mémoire directement connectée forment un nœud NUMA. L’accès mémoire au sein du même nœud NUMA est plus rapide, tandis que l’accès mémoire entre nœuds entraîne une latence plus élevée en raison du transport supplémentaire sur le bus. Optimiser l’affinité mémoire et CPU, en s’assurant que les tâches s’exécutent dans le même nœud NUMA, est une étape cruciale d’optimisation des performances.
Relation physique entre CPU et GPU
Architecture matérielle des GPU
Les GPU communiquent généralement avec les CPU via des bus PCIe (Peripheral Component Interconnect Express). Dans les systèmes multi-sockets, les GPU sont généralement connectés à un seul socket (et à son nœud NUMA correspondant) et non entre les sockets. Cela signifie qu’en fonctionnement réel, le GPU a une bande passante plus élevée et une latence plus faible avec les cœurs CPU et la mémoire du socket auquel il est connecté.
Affinité CPU et GPU
La communication entre CPU et GPU repose principalement sur le transfert de données. Les données sont transférées du CPU vers le GPU et inversement, impliquant des opérations d’accès mémoire qui impactent significativement les performances. Si le cœur CPU lié au GPU se trouve dans le même nœud NUMA que le GPU, la latence de transfert des données est considérablement réduite. Par conséquent, lier les cœurs CPU aux GPU est une étape clé d’optimisation des performances.
Stratégie de liaison des cœurs CPU et GPU basée sur l’affinité
Dans le déploiement conteneurisé Docker, la mise en œuvre de la liaison d’affinité entre GPU et cœurs CPU est cruciale pour améliorer les performances des tâches conteneurisées. Les fonctionnalités de contrôle des ressources CPU et GPU de Docker permettent un contrôle précis des cœurs CPU et des GPU utilisés par les conteneurs.
Allocation des ressources CPU et GPU dans les conteneurs
Les conteneurs Docker permettent un contrôle précis des ressources CPU et GPU allouées. En spécifiant les cœurs CPU et les périphériques GPU utilisés par un conteneur, la liaison d’affinité peut être mise en œuvre pour optimiser les performances.
Paramètres CPU dans Docker
Dans Docker, l’allocation des ressources CPU peut être contrôlée à l’aide des paramètres suivants :
--cpuset-cpus: Spécifie les cœurs CPU physiques que le conteneur peut utiliser. Par exemple,--cpuset-cpus="0-3"limite le conteneur aux cœurs CPU 0 à 3.--cpu-shares: Contrôle le poids d’utilisation CPU du conteneur mais ne restreint pas l’utilisation de cœurs spécifiques.--cpus: Limite le nombre total de cœurs CPU (en cœurs virtuels) que le conteneur peut utiliser.
Paramètres GPU dans Docker
La liaison des périphériques GPU peut être réalisée à l’aide des paramètres Docker suivants :
--gpus: Spécifie les périphériques GPU accessibles par le conteneur. Par exemple,--gpus '"device=0"'attribue le GPU 0 au conteneur.
Implémentation de la liaison cœurs CPU et GPU dans Docker
Pour implémenter la liaison des cœurs CPU et GPU dans les conteneurs Docker, combinez les paramètres CPU et GPU. Voici un exemple de liaison de cœurs CPU spécifiques et d’un GPU lors du démarrage d’un conteneur Docker :
docker run --cpuset-cpus="0-3" --gpus '"device=0"' --memory="8g" my_gpu_container
Dans cette commande :
--cpuset-cpus="0-3"lie le conteneur aux cœurs CPU 0 à 3, qui doivent être dans le même nœud NUMA que le GPU 0.--gpus '"device=0"'attribue le GPU 0 au conteneur.--memory="8g"limite l’utilisation mémoire du conteneur à 8 Go, garantissant que l’allocation mémoire est également alignée sur l’affinité CPU/GPU.
Obtention de la liaison optimale
Pour garantir une liaison optimale dans les conteneurs, déterminez d’abord la topologie physique de la machine. Utilisez nvidia-smi topo -m pour afficher la topologie de la machine :
nvidia-smi topo -m
À partir de la sortie, vous pouvez déterminer la configuration NUMA de la machine et les attributions GPU. Par exemple, si la machine dispose de deux nœuds NUMA, NUMA 0 avec 4 GPU et NUMA 1 avec 4 GPU, vous pouvez identifier l’affinité GPU et CPU pour chaque nœud NUMA.
Pseudo-code pour le calcul de l’affinité
Voici un pseudo-code simplifié pour calculer l’affinité et déterminer les ID GPU et CPU :
type Affinity struct {
}
// Calcule si l'affinité est satisfaite et retourne les ID GPU et CPU correspondants
func calAffinity(affinity *Affinity, cpuUse []int, gpuUse []int, gpuReq int, cpuReq int) (bool, []int, []int) {
return true, []int{}, []int{}
}
Cette méthode peut être utilisée pour déterminer si un nœud satisfait aux exigences de liaison optimale. Lorsque plusieurs nœuds existent, cette méthode peut être utilisée pour évaluer et sélectionner le nœud le plus approprié.Novita AI a développé un moteur de conteneur adapté au calcul IA de nouvelle génération, ajustant dynamiquement les algorithmes pour surveiller en temps réel l’utilisation du matériel et effectuer une optimisation de bout en bout. Les utilisateurs peuvent bénéficier des performances de calcul les plus puissantes sans se soucier des détails techniques NUMA.
Les utilisateurs peuvent également effectuer des réglages NUMA plus avancés dans le panneau de configuration. Si vous avez d’autres besoins connexes, n’hésitez pas à nous contacter sur Discord !
Visitez Novita AI pour plus de détails !

