持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第13天,点击查看活动详情
首先,先了解一下pycuda是干什么的,既然带有cuda,那一定与GPU有关,确实是的,除了pytorch,tensorflow等这些框架之外,使用GPU加速时pycuda也是可以的,它与numpy的使用方法类似,主要实现的就是一些矩阵的运算。
如果想要使用pycuda,首先需要安装CUDA和Cudnn,确保电脑中配置了NVIDIA的框架。然后就是确保python已经安装好,既然考虑使用pycuda进行加速,想必上述两个环节都已经准备妥当,第三步就是安装pycuda的库函数,命令如下。
pip install pycuda
接下来就可以通过python的代码进行测试了,查看当前电脑的环境是否准备妥当,以及pycuda是否可以使用。
import pycuda.driver as drv
import pycuda as cuda
import numpy as np
from pycuda import gpuarray
import time
drv.init()
print("Detected {} CUDA Capble device(s)".format(drv.Device.count()))
for i in range(drv.Device.count()):
gpu_device = drv.Device(i)
print("Device {} : {}".format(i, gpu_device.name()))
compute_capability = float("%d.%d"%gpu_device.compute_capability())
print("Compute Capability:{}".format(compute_capability))
print("Total Memry:{}M".format(gpu_device.total_memory()//(1024**2)))
我电脑使用的是RTX2070的显卡,算力是7.5,显存大小为8G,执行上述代码时输出如下
表明,目前我的各项环境正常,库函数的调用也正常,具备了使用pycuda的条件,下面就是通过numpy与pycuda讲解其中的一些相互转换关系。
在使用numpy时,其中numpy.array()是我们用的最多的一个工具,由于pycuda和numpy及其相似,可以说非常容易相互转换,因此也有一个同样的工具,就是gpuarray。如果没有这个函数,我们需要频繁的将数据在CPU和GPU通过cudaMemcpyHostToDevice和cudaMemcpyDeviceToHost之间来回转换。
import numpy as np
import pycuda.autoinit
from pycuda import gpuarray
cpu_data = np.array(np.random.randn(3, 3), dtype=np.float32)
print(cpu_data)
gpu_data = gpuarray.to_gpu(cpu_data)
gpu_data = gpu_data**2 * gpu_data
cpu_data_get = gpu_data.get()
print(gpu_data)
print(cpu_data_get)
上面的代码中主要使用了pycuda的gpuarray.to_gpu()和get()函数,为什么数据还要通过numpy进行转换,这不是麻烦嘛,直接建立在gpu上多好,但是目前还没有更好的办法直接绕过cpu而直接将数据传递给gpu,如果有人知道可以评论区告诉我,感激不尽。
使用gpuayyay,我们必须首先将数据以numpy的形式存储在主机内,然后才能使用gpuarray.to_gpu()将数据传递给gpu,这同时,gpu会在其内部进行内存的分配和数据的拷贝,简化了自己申请内存的步骤。
当数据在gpu中计算完成之后,我们可以使用get(),将数据从gpu转移到主机上。 下面就是上面程序的计算结果。
对于pycuda为什么计算要快一些呢?有没有想过这个问题,假设我们将矩阵转移到gpu上之后对其进行乘法运算,那么gpu的操作是将每一个乘法运算转移到单独的线程上执行,而并非等待串行执行,五个人干活互不干扰,总比一个人干活快得多,下面我们通过一组测试,对其速度进行评估一下。
import numpy as np
import pycuda.autoinit
from pycuda import gpuarray
import time
# 大约22M的数据量
cpu_data = np.array(np.random.randn(12000, 19200), dtype=np.float32)
t1 = time.time()
cpu_data_add = cpu_data + cpu_data
t2 = time.time()
print("CPU Add time: {} seconds".format(t2-t1))
t3 = time.time()
cpu_data_mul = cpu_data * cpu_data
t4 = time.time()
print("CPU multiplication time: {} seconds".format(t4-t3))
gpu_data = gpuarray.to_gpu(cpu_data)
t5 = time.time()
gpu_data_add = gpu_data + gpu_data
t6 = time.time()
print("GPU Add time: {} seconds".format(t6-t5))
t7 = time.time()
gpu_data_mul = gpu_data * gpu_data
t8 = time.time()
print("GPU multiplication time: {} seconds".format(t8-t7))
第一次运行时的结果如下,看了一下,GPU居然比CPU慢那么多
第二次运行的结果,突然比第一次笑了很多,我们继续把数据扩大10倍看一下
换了数据继续,第一次的结果
第二次的结果
可以看到第一二次的运行结果相差很大,说明GPU的调用需要时间,但是一旦首次完成之后产生缓存,下次调用就会快很多。