6

Parallélisation des opérateurs et optimisation de compilation

AI999第4课:现代计算底座与张量化并行演化
00:00

Dans EvoX la base de calcul moderne, l'unité d'exécution minimale d'un opérateur évolutionnaire n'est plus « l'individu », mais l'ensemble dutenseur de population: la mutation, le croisement, la sélection et autres opérateurs couvrent tous les individus en un seul appel ; au-dessus de cela, lacompilation de graphe de calculorganise la séquence d'opérateurs en un plan d'exécution optimisable, améliorant l'efficacité d'exécution tout en préservant la sémantique de l'algorithme.

Parallélisation des opérateurs et optimisation de compilationÉvolution par lots à l'échelle du tenseur · Optimisation du plan d'exécution par compilation de graphetenseur de population[N, D] · tous les individusLa dimension individu sert de dimension de lotUn seul appel couvre tous les individusUn seul appelcouvre toute la populationGraphe de calcul des opérateurs évolutionnairesMutationCroisementSélectionDépendances entre opérateurs → graphe de calculOptimisation globale sur tout le graphePlutôt que d'appeler des implémentations toutes faites opérateur par opérateurCompilationAvant compilationBoucle individu par individu · nombreux lancements de noyauxNombreux lancements de noyaux, dimension de lot égale à 1Fusion d'opérateurs · génération de noyaux · réutilisation mémoire · optimisation d'ordonnancementAprès compilationGraphe de calcul fusionné · peu de noyaux efficacesUne seule passe avant par lots accomplit toute une génération d'évolutionComment calculer · mode d'exécutionNombre de noyauxTenseurs intermédiairesStratégies de mémoire et d'ordonnancementL'optimisation n'agit que sur la couche d'exécution, sans modifier les règles de mise à jour des opérateursCe qui est calculé · sémantique algorithmiqueMutationCroisementSélectionAvec la même graine aléatoire, les résultats d'évolution restent identiques≠

Parallélisation des opérateurs : évolution par lots à l'échelle du tenseur

  • Opérateurs au niveau tenseur: la mutation, le croisement et la sélection peuvent s'appliquer directement à un tenseur de population de forme [N, D] (N étant la taille de la population et D la dimension des variables de décision), un seul appel d'opérateur couvrant les N individus.
  • Élimination des boucles individu par individu: plus besoin d'écrire for ind in population: ind = mutate(ind) de telles boucles séquentielles Python/CPU, réduisant ainsi les surcoûts fréquents liés à l'ordonnancement des fonctions et aux appels du framework.
  • Échanger la dimension de lot contre le débit: lorsque la dimension individu devient la dimension de lot, les opérateurs s'exécutent massivement en parallèle sur l'appareil, et le débit de l'évolution par lots augmente avec la taille de la population.

Compilation de graphe de calcul : de la séquence d'opérateurs au plan d'exécution

En organisant les dépendances des opérateurs « mutation → croisement → sélection » en ungraphe de calcul, le compilateur peut effectuer une optimisation globale sur tout le graphe, au lieu d'appeler des implémentations toutes faites opérateur par opérateur :

  • Fusion d'opérateurs: regrouper plusieurs opérations élément par élément (element-wise) en un seul noyau, réduisant la création de tenseurs intermédiaires et les lectures/écritures répétées.
  • Génération et ordonnancement de noyaux: générer des noyaux efficaces pour le sous-graphe fusionné, accomplissant toute une génération d'évolution avec moins de lancements de kernel.
  • Réutilisation de la mémoire: planifier à l'avance les tampons pour les résultats intermédiaires, réduisant les surcoûts liés à l'allocation de mémoire GPU et aux transferts de données.

L'efficacité change, la sémantique reste

Il faut distinguer« comment calculer »et« ce qui est calculé ». La parallélisation des opérateurs et l'optimisation de compilation ne modifient que le mode d'exécution — nombre de noyaux, taille des tenseurs intermédiaires, stratégies de mémoire et d'ordonnancement — sans changer la signification logique de la mutation, du croisement et de la sélection. La couche algorithmique continue de combiner les opérateurs selon la sémantique évolutionnaire ; par conséquent, avec la même graine aléatoire, la trajectoire évolutive et les résultats avant et après optimisation doivent être identiques. En résumé : la tensorisation détermine si le calcul par lots est rapide ou non, la compilation de graphe détermine si l'ordonnancement des noyaux est optimal ou non, et « ce qui est calculé » reste inchangé.

Idées fausses courantes
Confondre la « tensorisation » avec un « changement d'algorithme évolutionnaire ». La tensorisation ne fait qu'appliquer le même opérateur à l'ensemble du tenseur de population, sans modifier les règles de mise à jour entre individus ; l'optimisation de compilation ne rend pas non plus l'algorithme « plus intelligent ». Le vrai danger est de ne pas effectuer de validation de régression après optimisation et de prendre à tort une différence d'implémentation pour une amélioration algorithmique.