我习惯将较大的工作分解为较小的功能,只要该功能执行或多或少的独特操作即可。因此,我通常将代码结构如下:
def func1(data):
do something
def func2(data):
do something else
def func3(data):
do something else again
# main section of code
data = importData()
data = func1(data)
data = func2(data)
data = func3(data)
但是,如果我将这些函数转换为通过numba在cuda上运行(使用@njit装饰器),则结果会更快...但是可能会更快(即,当每个函数在gpu上传输数据或从gpu传输数据时,我会承受巨大的开销叫做)。但是,如果我将所有函数转换为可以在gpu上运行,并创建一个也可以在gpu上运行的主函数,这会将我的数据保留在gpu内存中,还是仍然必须通过cpu传递它?换句话说,一旦将数据发送到master函数,数据是否会保留在gpu内存上,直到master函数返回数据为止?这就是我的想法。
@njit
def func1(data):
do something
@njit
def func2(data):
do something else
@njit
def func3(data):
do something else again
@njit
def masterFunc():
data = func1(data)
data = func2(data)
data = func3(data)
# main section of code
data = importData()
data = masterFunc(data)
一般而言,如果您可以避免将数据复制到GPU或从GPU复制数据到最小化,则代码将更快。复制数据需要CPU和PCI总线以及主机内存,以及上面的所有协调物流。您的代码执行得越少,速度就会越快。
对于您的特定示例,如果您将masterfunc()作为GPU内核启动,并在masterfunc()中循环访问数据,同时在各个元素上调用三个函数,则速度会更快。这将允许numba在最大数量的块/线程之间并行化。您可以根据数据大小计算最佳的块和线程数...确保在三个子函数中包括数组边界检查。
func2()是否取决于func1()中完成的数据操作?在func2()上使用func3()吗?如果没有,则可以通过允许cuda异步调度所有三个代码来进一步并行化代码。这里没有足够的信息要说,但是似乎三个功能需要在给定的数据元素上顺序执行?
编辑(在下面的评论之后):如何将数据复制到GPU或从GPU复制数据取决于CUDA库版本以及编写masterfunc()的精确程度。
如果masterfunc()是CUDA内核,则所有内容都留给了numba,并且您使用的是10.3之前版本的cuda ...那么,当调用masterfunc()时,数据将被复制到CPU中,并在完成时被复制回去。这是默认设置,但是numba经常会假设最坏的情况,并且它可能会复制不必要的更多内存(上面的masterfunc示例可能会行得通,但取决于细节)。
您(程序员)可以通过python方法编写masterfunc()来承担对何时以及如何复制数据的更多控制。您可以在GPU上创建数据(例如使用cuPy或cuDF)。您还可以使用numba.cuda函数(例如cuda.to_device(数据进入GPU)和cuda.to_host(将数据复制回CPU))承担更多控制权。假设更多的控制权还可能需要CPU代码等待在任何GPU流上同步(这样做是一个好主意)。
我还没有找到一个提供与编写本地cuda C一样多的控制权的Python库,我想这并不奇怪。例如,我使用cuPy在随机数的GPU上创建了一个大型ndarray(这将调用cuRAND生成随机数)。但是,如果您多次调用新数组,它总是会分配一个新数组,目前尚无办法告诉它在现有数组中生成新一批随机数。这意味着很多malloc()和free()在后台进行,更实际的是从python编码器的角度来看,这意味着无法创建一个大于可用GPU内存一半的数组(旧数组不会被垃圾回收)并在新数组获得malloc之前释放)。根据我的经验,显式删除变量(手动触发GPU上的免费和垃圾回收)非常缓慢。
最后警告:cuda版本11引入了共享内存(仅对已更改的数据进行异步复制),并直接访问某些存储介质的GPU。那是在2020年5月左右发布的,它的前沿是……我在GTC上看到了nvidia的发布,但是我没有任何使用它的经验。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句