Rebajar cinco precios a la vez
Un kernel es la función pequeña que ejecuta cada hilo de una GPU, cada uno sobre su propio elemento. Escribir un kernel es escribir el trabajo de un solo hilo y dejar que el lanzamiento lo repita miles de veces.
Una tienda quiere rebajar un 10 % todos sus precios. El kernel rebaja un precio, y la rejilla se encarga del resto. Aquí no hay GPU: el lanzamiento se simula en Python.
Al final de esta lección
Nota
Puedo escribir un kernel para un trabajo sencillo y simular su lanzamiento en Python.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Kernel | la función que ejecuta cada hilo sobre su elemento |
| Lanzamiento | la orden que crea la rejilla y ejecuta el kernel en cada hilo |
| Guarda | la comprobación que deja sin trabajar a los hilos ociosos |
| Entrada | el array del que el kernel lee |
| Salida | el array en el que el kernel escribe |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un kernel trabaja para un solo hilo
El kernel no tiene un bucle sobre todos los datos. Cuenta lo que hace un hilo: calcula su índice global, lee su elemento y escribe su resultado.
Si hay 5 elementos, habrá 5 hilos útiles ejecutando ese mismo kernel. El bucle lo pone la rejilla, no el programador.
Un kernel sigue siempre tres pasos
Primero, calcular el índice global con bloque, tamaño y hilo. Segundo, comprobar que el índice cae dentro de los datos: es la guarda, y protege a los hilos ociosos. Tercero, leer de la entrada y escribir en la salida, solo en la posición propia.
Si un hilo escribe en la posición de otro, los resultados dependen del orden y dejan de ser fiables.
Un kernel de descuento y su lanzamiento
# Precios en céntimos. Un kernel que aplica un 10 % de descuento a UN elemento.
precios = [1200, 3000, 800, 2500, 990]
rebajados = [0] * len(precios)
tam_bloque = 2
def kernel_descuento(bloque, hilo):
i = bloque * tam_bloque + hilo
if i < len(precios):
rebajados[i] = precios[i] * 90 // 100
bloques = (len(precios) + tam_bloque - 1) // tam_bloque
for bloque in range(bloques):
for hilo in range(tam_bloque):
kernel_descuento(bloque, hilo)
print("Rebajados:", rebajados)
# Cambia el 90 por 80 y vuelve a ejecutar.
Ejemplo: cinco precios con bloques de 2 hilos
| Bloque | Hilo | Índice | Precio | Resultado |
|---|---|---|---|---|
| 0 | 0 | 0 | 1200 | 1080 |
| 0 | 1 | 1 | 3000 | 2700 |
| 1 | 0 | 2 | 800 | 720 |
| 1 | 1 | 3 | 2500 | 2250 |
| 2 | 0 | 4 | 990 | 891 |
| 2 | 1 | 5 | no hay | ocioso, no escribe |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
El orden de los hilos no importa
En una GPU real, los hilos corren a la vez y ninguno espera a otro. No se sabe qué hilo acaba primero.
Por eso un buen kernel no necesita lo que escribió otro hilo. Si el resultado cambia al ejecutar los hilos en otro orden, el kernel está mal escrito. La simulación permite comprobarlo.
Lanzar los bloques en orden inverso
# El mismo lanzamiento, pero con los bloques en orden inverso.
precios = [1200, 3000, 800, 2500, 990]
rebajados = [0] * len(precios)
tam_bloque = 2
def kernel_descuento(bloque, hilo):
i = bloque * tam_bloque + hilo
if i < len(precios):
rebajados[i] = precios[i] * 90 // 100
bloques = (len(precios) + tam_bloque - 1) // tam_bloque
for bloque in range(bloques - 1, -1, -1):
for hilo in range(tam_bloque):
kernel_descuento(bloque, hilo)
print("Rebajados:", rebajados)
# Sale lo mismo: ningún hilo necesita lo que hizo otro.
Qué enseña el script del laboratorio
El laboratorio trae steps/kernel.py, una simulación de un lanzamiento. Con el primer comando verás su texto: el kernel, la guarda y los dos bucles que simulan la rejilla.
Es un script en inglés, con los nombres en inglés. Solo se lee: lo que hace con sus datos lo viste en la lección anterior.
Leer el kernel del laboratorio
docker exec jupyter cat steps/kernel.pySe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Contar los bloques al revés
docker exec jupyter python3 -c "print(list(range(2, -1, -1)))"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error muy común
Advertencia
Mucha gente escribe un bucle dentro del kernel para recorrer todos los datos. Entonces cada hilo repite el trabajo de todos. El kernel debe tratar un solo elemento, el que dice su índice global, y la rejilla pone tantos hilos como elementos.