6

Paralelismo de operadores y optimización de compilación

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

En EvoX y su moderna base de cómputo, la unidad mínima de ejecución de los operadores evolutivos ya no es el «individuo», sino todo eltensor de población: operadores como la mutación, el cruce y la selección cubren todos los individuos con una sola llamada; sobre esta base, lacompilación de grafos de cómputoorganiza la secuencia de operadores en un plan de ejecución optimizable, mejorando la eficiencia de ejecución sin alterar la semántica del algoritmo.

Paralelismo de operadores y optimización de compilaciónEvolución por lotes basada en tensores · Compilación de grafos de cómputo para optimizar el plan de ejecucióntensor de población[N, D] · Todos los individuosLa dimensión de individuo actúa como dimensión de loteUna llamada cubre todos los individuosUna llamadaCubre toda la poblaciónGrafo de cómputo de operadores evolutivosMutaciónCruceSelecciónDependencias entre operadores → Grafo de cómputoOptimización global sobre todo el grafoEn lugar de invocar implementaciones predefinidas operador por operadorCompilarAntes de compilarBucle individuo por individuo · Muchos lanzamientos de kernelMuchos lanzamientos de kernel, dimensión de lote de solo 1Fusión de operadores · Generación de kernels · Reutilización de memoria · Optimización de la planificaciónDespués de compilarGrafo de cómputo fusionado · Pocos kernels eficientesUna sola pasada por lotes completa toda la evolución generacionalCómo se calcula · Forma de ejecuciónNúmero de kernelsTensores intermediosEstrategias de memoria y planificaciónLa optimización incide solo en la capa de ejecución, sin cambiar las reglas de actualización de los operadoresQué se calcula · Semántica del algoritmoMutaciónCruceSelecciónCon la misma semilla aleatoria, los resultados evolutivos se mantienen consistentes≠

Paralelismo de operadores: evolución por lotes a nivel de tensor

  • Operadores a nivel de tensor: la mutación, el cruce y la selección pueden aplicarse directamente sobre el tensor de población de forma [N, D] (N es el tamaño de la población y D la dimensión de las variables de decisión); una sola llamada al operador cubre los N individuos.
  • Eliminación del bucle individuo por individuo: ya no es necesario escribir for ind in population: ind = mutate(ind) bucles secuenciales de Python/CPU como este, reduciendo así el coste de las frecuentes llamadas al planificador y al framework.
  • Cambiar dimensión de lote por rendimiento: al convertir la dimensión de individuo en dimensión de lote, los operadores se ejecutan de forma masivamente paralela en el dispositivo, y el rendimiento de la evolución por lotes se amplifica con el tamaño de la población.

Compilación de grafos de cómputo: de la secuencia de operadores al plan de ejecución

Al organizar las dependencias de los operadores «mutación → cruce → selección» en ungrafo de cómputo, el compilador puede realizar optimizaciones globales sobre todo el grafo, en lugar de limitarse a invocar implementaciones predefinidas operador por operador:

  • Fusión de operadores: combina varias operaciones elemento a elemento (element-wise) en un solo kernel, reduciendo la creación y la lectura/escritura repetida de tensores intermedios.
  • Generación y planificación de kernels: genera kernels eficientes para el subgrafo fusionado, completando toda la evolución generacional con menos lanzamientos de kernel.
  • Reutilización de memoria: planifica previamente búferes para los resultados intermedios, reduciendo el coste adicional de la asignación de memoria de la GPU y del trasiego de datos.

La eficiencia cambia, la semántica no

Es necesario distinguir entre«cómo se calcula»y«qué se calcula». El paralelismo de operadores y la optimización de compilación solo cambian la forma de ejecución —el número de kernels, el tamaño de los tensores intermedios, las estrategias de memoria y planificación— sin alterar el significado lógico de la mutación, el cruce y la selección. La capa algorítmica sigue componiendo los operadores según la semántica evolutiva; por ello, con la misma semilla aleatoria, la trayectoria evolutiva y los resultados antes y después de la optimización deben coincidir. En resumen: la tensorización resuelve «con qué rapidez se calculan los lotes» y la compilación de grafos de cómputo resuelve «con qué eficacia se planifican los kernels», mientras que «qué se calcula» permanece inalterado.

Errores frecuentes
Confundir la «tensorización» con «haber cambiado de algoritmo evolutivo». La tensorización solo aplica el mismo operador a todo el tensor de población; las reglas de actualización entre individuos no cambian, y la optimización de compilación tampoco hace que el algoritmo «sea más inteligente». Lo que realmente hay que vigilar es no realizar una validación de regresión tras la optimización y confundir diferencias de implementación con mejoras algorítmicas.