如何快速将CPU上运行的代码扩展到GPU?

前不久看到太极图形的加速python的代码示例。它可以在开头指定用什么计算,后面代码就不用管了。

import taichi as ti

ti.init(arch=ti.gpu)

下面这些都是能换着填的:

# Choose any of the following backend when initializing Taichi
# - ti.cpu
# - ti.gpu
# - ti.cuda
# - ti.vulkan
# - ti.metal
# - ti.opengl
# - ti.cpu

我目前还没怎么用过显卡计算。因为以前拿1060跟着CUDA.jl官方教程一点点复现,结果一样的代码,简单的矢量相加,我的GPU还不如CPU快。后面就不用了。但是当时感觉,“难道不是VectorCuArray的事儿吗?怎么搞这么复杂?”反正感觉是个麻烦事。

现在我自己写一堆粒子碰撞的小程序玩,Julia CPU版本的已经写好了。但是粒子一多,速度就下来了。我现在想让它更快。

有经验的可以分享下CPU上运行的代码改GPU版本的心得:怎么才能更快?如果GPU版本的代码全部要从0开始写,感觉有点麻烦。

Quickstart · KernelAbstractions.jl (juliagpu.github.io)

lucifer1004/Taichi.jl: Using Taichi in Julia! (github.com)

2 个赞

这是因为taichi 内部做了关于backend 的抽象。如果是自行开发包,这部分抽象需要自己写。CUDA.jl 在自己包的简单使用可以参考例子: OMEinsum.jl CUDAExt。这部分代码还用到了Julia 1.9 的extensions 机制,只有用户真的用到了GPU计算才会安装CUDA.jl。

benchmark的结果跟多种因素有关。GPU不如CPU快可能是GPU的算力本来不如CPU或者例子的计算规模太小。另外,就是要注意所有的GPU操作都是异步的,需要使用正确的profile 函数,详见 CUDA.jl profiling


一些个人觉得比较有用的经验:

  1. GPU只适合大批量地做重复计算,复杂逻辑运算应放在CPU上做;
  2. 减少数据在CPU和GPU之间的来回拷贝;
  3. 多用CUDA生态里地预定义函数,因为这些函数是高度优化的。
3 个赞

CPU和GPU的计算过程差异很大,你无法简单的把CPU上的算法逻辑迁移到GPU计算中。
GPU的计算是并发的,计算的执行单元单位一般叫一批或者一波,每一波都是多线程并发执行,例如32x32线程。GPU的线程比CPU要简单的多,很多CPU中的复杂逻辑不能用在GPU中,例如大量的if-else判断分支。
某一波处理中如果出现了一次分支就需要重新执行这一波所有的线程,只不过之前没有出现分支的不会再执行,如果某一波有着大量的分支(if,for都算是分支),那么这一波会重复执行很多次。虽然有技巧可以消除if等分支逻辑,但这些技巧都与具体算法有关没有”统一完美“的解决方案,而且这些技巧会让计算代码变得非常难以理解。
因此,面向GPU的算法需要进行”并行性分析”——把那些可以并行的、必须串行的部分识别拆分出来,分别用GPU和CPU的方式去实现。
此外,CPU到GPU、GPU到CPU的数据传输走的PCIE,传输的时间开销是必须考虑的因素。如果传输开销大于计算加速节省的时间,那还不如用CPU。

1 个赞