PyCUDA_multiplicar_sumar_elementos#
Instalación de la librería#
!pip install pycuda
Requirement already satisfied: pycuda in /usr/lib/python3/dist-packages (2018.1.1)
Importando las librerías#
# Para inicializar automáticamente PyCUDA y la GPU
import pycuda.autoinit
# Para interactuar directamente con el controlador de la GPU
import pycuda.driver as drv
# Para manipular arrays y operaciones en la CPU
import numpy as np
# Para compilar y cargar módulos CUDA en la GPU
from pycuda.compiler import SourceModule
# Para medir los tiempos
import time
# Para generar gráficos
import matplotlib.pyplot as plt
Verificación de los recursos de GPU#
drv.init()
print("%d dispositivo(s) encontrado." % drv.Device.count())
for i in range(drv.Device.count()):
dev = drv.Device(i)
print("\n Dispositivo #%d: %s" % (i, dev.name()))
print(" Memoria Total: %s GB" % (dev.total_memory() // (1024 * 1024 * 1024)))
print(" Capacidad de Computación: %d.%d" % dev.compute_capability())
1 dispositivo(s) encontrado.
Dispositivo #0: NVIDIA GeForce GTX 1070 with Max-Q Design
Memoria Total: 7 GB
Capacidad de Computación: 6.1
Ejemplo del uso de PyCUDA, multiplicación por número#
# Establece la semilla para reproducibilidad
np.random.seed(42)
# Crea una matriz con números aleatorios enteros entre 0 y 10
a = np.random.randint(0, 11, size=(8,8))
# Convierte la matriz a formato de punto flotante de 32 bits
a = a.astype(np.float32)
a
array([[ 6., 3., 10., 7., 4., 6., 9., 2.],
[ 6., 10., 10., 7., 4., 3., 7., 7.],
[ 2., 5., 4., 1., 7., 5., 1., 4.],
[ 0., 9., 5., 8., 0., 10., 10., 9.],
[ 2., 6., 3., 8., 2., 4., 2., 6.],
[ 4., 8., 6., 1., 3., 8., 1., 9.],
[ 8., 9., 4., 1., 3., 6., 7., 2.],
[ 0., 3., 1., 7., 3., 1., 5., 5.]], dtype=float32)
GPU#
# Asigna memoria en la GPU para la matriz
a_gpu = drv.mem_alloc(a.nbytes)
# Copia los datos de la matriz de la memoria del host a la memoria de la GPU
drv.memcpy_htod(a_gpu, a)
# Define un módulo de código CUDA
mod = SourceModule("""
__global__ void doublify(float *a)
{
//int blockId = (gridDim.x * blockIdx.y) + blockIdx.x;
//int idx = (blockId * (blockDim.x * blockDim.y)) + (threadIdx.y * blockDim.x) + threadIdx.x;
int idx= (blockIdx.x * blockDim.x + threadIdx.x) + (blockIdx.y * blockDim.y + threadIdx.y) * 8;
//int idx = threadIdx.x + threadIdx.y * blockDim.x;
a[idx] *= 2;
printf("threadIdx.x: %d, threadIdx.y:%d, blockDim.x:%d, blockDim.y :%d idx: %d\\n",threadIdx.x, threadIdx.y, blockDim.x,blockDim.y, idx);
}
""")
# Obtiene la función 'doublify' del módulo CUDA
func = mod.get_function("doublify")
# Inicia el tiempo de medición para la operación CUDA
start_time_cuda = time.time()
# Ejecuta la función 'doublify' en un bloque de 4x4 hilos
func(a_gpu, block=(4,4,1), grid=(2,2))
#func(a_gpu, block=(8,8,1))
# Sincroniza el contexto de CUDA para asegurarse de que todas las operaciones en la GPU se hayan completado
drv.Context.synchronize()
# Crea una matriz vacía con la misma forma y tipo que la matriz 'a'
a_doubled = np.empty_like(a)
# Copia los datos de la matriz procesada desde la memoria de la GPU al host
drv.memcpy_dtoh(a_doubled, a_gpu)
# Detiene el tiempo de medición para la operación CUDA
end_time_cuda = time.time()
# Calcula el tiempo total de la operación CUDA
time_cuda = end_time_cuda - start_time_cuda
print("Tiempo de ejecución CUDA: ", time_cuda)
# Imprime la matriz procesada
print("Matriz procesada:\n")
print(a_doubled)
# Imprime la matriz original
print("Matriz original:\n")
print(a)
threadIdx.x: 0, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 36
threadIdx.x: 1, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 37
threadIdx.x: 2, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 38
threadIdx.x: 3, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 39
threadIdx.x: 0, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 44
threadIdx.x: 1, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 45
threadIdx.x: 2, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 46
threadIdx.x: 3, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 47
threadIdx.x: 0, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 52
threadIdx.x: 1, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 53
threadIdx.x: 2, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 54
threadIdx.x: 3, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 55
threadIdx.x: 0, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 60
threadIdx.x: 1, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 61
threadIdx.x: 2, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 62
threadIdx.x: 3, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 63
threadIdx.x: 0, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 4
threadIdx.x: 1, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 5
threadIdx.x: 2, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 6
threadIdx.x: 3, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 7
threadIdx.x: 0, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 12
threadIdx.x: 1, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 13
threadIdx.x: 2, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 14
threadIdx.x: 3, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 15
threadIdx.x: 0, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 20
threadIdx.x: 1, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 21
threadIdx.x: 2, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 22
threadIdx.x: 3, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 23
threadIdx.x: 0, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 28
threadIdx.x: 1, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 29
threadIdx.x: 2, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 30
threadIdx.x: 3, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 31
threadIdx.x: 0, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 0
threadIdx.x: 1, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 1
threadIdx.x: 2, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 2
threadIdx.x: 3, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 3
threadIdx.x: 0, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 8
threadIdx.x: 1, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 9
threadIdx.x: 2, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 10
threadIdx.x: 3, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 11
threadIdx.x: 0, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 16
threadIdx.x: 1, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 17
threadIdx.x: 2, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 18
threadIdx.x: 3, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 19
threadIdx.x: 0, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 24
threadIdx.x: 1, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 25
threadIdx.x: 2, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 26
threadIdx.x: 3, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 27
threadIdx.x: 0, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 32
threadIdx.x: 1, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 33
threadIdx.x: 2, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 34
threadIdx.x: 3, threadIdx.y:0, blockDim.x:4, blockDim.y :4 idx: 35
threadIdx.x: 0, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 40
threadIdx.x: 1, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 41
threadIdx.x: 2, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 42
threadIdx.x: 3, threadIdx.y:1, blockDim.x:4, blockDim.y :4 idx: 43
threadIdx.x: 0, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 48
threadIdx.x: 1, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 49
threadIdx.x: 2, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 50
threadIdx.x: 3, threadIdx.y:2, blockDim.x:4, blockDim.y :4 idx: 51
threadIdx.x: 0, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 56
threadIdx.x: 1, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 57
threadIdx.x: 2, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 58
threadIdx.x: 3, threadIdx.y:3, blockDim.x:4, blockDim.y :4 idx: 59
Tiempo de ejecución CUDA: 0.0026025772094726562
Matriz procesada:
[[12. 6. 20. 14. 8. 12. 18. 4.]
[12. 20. 20. 14. 8. 6. 14. 14.]
[ 4. 10. 8. 2. 14. 10. 2. 8.]
[ 0. 18. 10. 16. 0. 20. 20. 18.]
[ 4. 12. 6. 16. 4. 8. 4. 12.]
[ 8. 16. 12. 2. 6. 16. 2. 18.]
[16. 18. 8. 2. 6. 12. 14. 4.]
[ 0. 6. 2. 14. 6. 2. 10. 10.]]
Matriz original:
[[ 6. 3. 10. 7. 4. 6. 9. 2.]
[ 6. 10. 10. 7. 4. 3. 7. 7.]
[ 2. 5. 4. 1. 7. 5. 1. 4.]
[ 0. 9. 5. 8. 0. 10. 10. 9.]
[ 2. 6. 3. 8. 2. 4. 2. 6.]
[ 4. 8. 6. 1. 3. 8. 1. 9.]
[ 8. 9. 4. 1. 3. 6. 7. 2.]
[ 0. 3. 1. 7. 3. 1. 5. 5.]]
CPU#
# Inicia el tiempo de medición para la operación con bucle en cpu
start_time_cpu = time.time()
# Duplica cada elemento de la matriz utilizando un bucle for
a_doubled_python = np.empty_like(a)
for i in range(a.shape[0]):
for j in range(a.shape[1]):
a_doubled_python[i, j] = a[i, j] * 2
# Detiene el tiempo de medición para la operación con bucle en Python
end_time_cpu = time.time()
# Calcula el tiempo total de la operación con bucle en Python
time_cpu= end_time_cpu - start_time_cpu
print("Tiempo de ejecución cpu: ", time_cpu)
Tiempo de ejecución cpu: 0.0015554428100585938
np.array_equal(a_doubled_python,a_doubled)
True
Comparación GPU vs CPU#
# Nombres de las operaciones
labels = ['GPU', 'CPU']
# Valores de tiempo
values = [time_cuda, time_cpu]
# Graficar en barras con escala logarítmica en el eje y
plt.bar(labels, values, color=['green', 'orange'])
plt.title('Comparación de tiempos de multiplicación por número')
plt.show()
![../../_images/97f1272a26271aebde5746261456449019eda02bc64c795b64db4fd88ab04897.png](../../_images/97f1272a26271aebde5746261456449019eda02bc64c795b64db4fd88ab04897.png)
Suma de matrices#
# Establece la semilla para reproducibilidad
np.random.seed(42)
# Crear dos matrices aleatorias
b = np.random.randint(0, 11, size=(128, 128)).astype(np.float32)
c = np.random.randint(0, 11, size=(128, 128)).astype(np.float32)
# Imprime las matrices originales
print("Matriz 'b':")
print(b)
print("\nMatriz 'c':")
print(c)
Matriz 'b':
[[ 6. 3. 10. ... 6. 6. 10.]
[ 8. 9. 9. ... 0. 1. 0.]
[ 4. 4. 10. ... 2. 8. 9.]
...
[ 4. 4. 7. ... 9. 5. 6.]
[ 8. 0. 3. ... 3. 9. 8.]
[ 3. 3. 6. ... 0. 1. 5.]]
Matriz 'c':
[[ 8. 4. 3. ... 1. 7. 5.]
[ 6. 0. 5. ... 9. 0. 7.]
[ 0. 8. 5. ... 5. 0. 1.]
...
[ 0. 1. 3. ... 6. 1. 9.]
[ 1. 5. 8. ... 10. 1. 10.]
[ 6. 0. 3. ... 8. 9. 4.]]
GPU#
# Definición del módulo de CUDA que contiene la función del kernel
mod2 = SourceModule("""
__global__ void add2(float *a, float *b)
{
int idx = (blockIdx.x * blockDim.x + threadIdx.x) + (blockIdx.y * blockDim.y + threadIdx.y) * 128;
a[idx] += b[idx];
}
""")
# Asignar memoria en la GPU para las matrices
b_gpu = drv.mem_alloc(b.nbytes)
c_gpu = drv.mem_alloc(c.nbytes)
# Copiar los datos de las matrices al dispositivo (GPU)
drv.memcpy_htod(b_gpu, b)
drv.memcpy_htod(c_gpu, c)
# Obtener la función del kernel compilado
func = mod2.get_function("add2")
# Iniciar el cronómetro
start_time = time.time()
# Llamar a la función del kernel con las matrices como argumentos
func(b_gpu, c_gpu, block=(32,32,1), grid=(4,4))
# Crear un array vacío del mismo tamaño que b para almacenar el resultado
added = np.empty_like(b)
# Copiar los resultados desde la memoria de la GPU al array creado
drv.memcpy_dtoh(added, b_gpu)
# Parar el cronómetro
end_time = time.time()
# Calcular la duración
duration1 = end_time - start_time
# Imprimir los resultados
print("Matriz B:\n")
print(b)
print("Matriz C:\n")
print(c)
print("Resultado suma:\n")
print(added)
# Imprimir el tiempo de ejecución
print("Tiempo de ejecución: {:.6f} segundos".format(duration1))
Matriz B:
[[ 6. 3. 10. ... 6. 6. 10.]
[ 8. 9. 9. ... 0. 1. 0.]
[ 4. 4. 10. ... 2. 8. 9.]
...
[ 4. 4. 7. ... 9. 5. 6.]
[ 8. 0. 3. ... 3. 9. 8.]
[ 3. 3. 6. ... 0. 1. 5.]]
Matriz C:
[[ 8. 4. 3. ... 1. 7. 5.]
[ 6. 0. 5. ... 9. 0. 7.]
[ 0. 8. 5. ... 5. 0. 1.]
...
[ 0. 1. 3. ... 6. 1. 9.]
[ 1. 5. 8. ... 10. 1. 10.]
[ 6. 0. 3. ... 8. 9. 4.]]
Resultado suma:
[[14. 7. 13. ... 7. 13. 15.]
[14. 9. 14. ... 9. 1. 7.]
[ 4. 12. 15. ... 7. 8. 10.]
...
[ 4. 5. 10. ... 15. 6. 15.]
[ 9. 5. 11. ... 13. 10. 18.]
[ 9. 3. 9. ... 8. 10. 9.]]
Tiempo de ejecución: 0.000581 segundos
CPU#
# Iniciar el cronómetro
start_time = time.time()
# Crear un array vacío del mismo tamaño que b para almacenar el resultado
addedCPU = np.empty_like(b)
# Realizar la suma elemento a elemento
for i in range(b.shape[0]):
for j in range(b.shape[1]):
addedCPU[i, j] = b[i, j] + c[i, j]
# Parar el cronómetro
end_time = time.time()
# Calcular la duración
duration2 = end_time - start_time
# Imprimir los resultados
print("Matriz B:\n", b)
print("Matriz C:\n", c)
print("Resultado suma:\n", addedCPU)
# Imprimir el tiempo de ejecución
print("Tiempo de ejecución en CPU: {:.6f} segundos".format(duration2))
Matriz B:
[[ 6. 3. 10. ... 6. 6. 10.]
[ 8. 9. 9. ... 0. 1. 0.]
[ 4. 4. 10. ... 2. 8. 9.]
...
[ 4. 4. 7. ... 9. 5. 6.]
[ 8. 0. 3. ... 3. 9. 8.]
[ 3. 3. 6. ... 0. 1. 5.]]
Matriz C:
[[ 8. 4. 3. ... 1. 7. 5.]
[ 6. 0. 5. ... 9. 0. 7.]
[ 0. 8. 5. ... 5. 0. 1.]
...
[ 0. 1. 3. ... 6. 1. 9.]
[ 1. 5. 8. ... 10. 1. 10.]
[ 6. 0. 3. ... 8. 9. 4.]]
Resultado suma:
[[14. 7. 13. ... 7. 13. 15.]
[14. 9. 14. ... 9. 1. 7.]
[ 4. 12. 15. ... 7. 8. 10.]
...
[ 4. 5. 10. ... 15. 6. 15.]
[ 9. 5. 11. ... 13. 10. 18.]
[ 9. 3. 9. ... 8. 10. 9.]]
Tiempo de ejecución en CPU: 0.013484 segundos
np.array_equal(added,addedCPU)
True
# Nombres de las operaciones
labels = ['GPU', 'CPU']
# Valores de tiempo
values = [duration1, duration2]
# Graficar en barras con escala logarítmica en el eje y
plt.bar(labels, values, color=['green', 'orange'])
plt.title('Comparación de tiempos de multiplicación por número')
plt.show()
![../../_images/bebe1f373217665e64237b03b18638e455a837db82b6551b10734814a5d952d8.png](../../_images/bebe1f373217665e64237b03b18638e455a837db82b6551b10734814a5d952d8.png)
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
# Define el código del kernel de CUDA
kernel_code = """
__global__ void print_kernel()
{
printf("Hello from block %d, thread %d\\n", blockIdx.x, threadIdx.x);
}
"""
# Compila el kernel
mod = SourceModule(kernel_code)
# Obtiene la función del kernel
print_kernel = mod.get_function("print_kernel")
# Lanza el kernel
print_kernel(block=(1, 1, 1), grid=(1, 1))
# Sincroniza para asegurar que se completa la ejecución del kernel
cuda.Context.synchronize()
Hello from block 0, thread 0