C语言程序自动化转CUDA的方法研究

C2CUDA

目录


目标分析

首先,目标是将C语言转化为CUDA加速程序的运行,从这里我们引出两点需要探究的内容是:

  • CUDA加速的原理是什么?
  • C语言中的什么部分可以被CUDA有效的加速?

CUDA加速的原理

2006年,NVIDIA推出了统一计算设备架构(CUDA),使任何计算工作负载都能不受图形API的限制,利用GPU的吞吐量能力。

------CUDA编程指南

从上面的引用可以看出,CUDA设计的目的是利用GPU的吞吐量能力(单位时间内系统能处理的任务总量(或数据量))

而CUDA 的核心逻辑是将大规模并行的软件逻辑(Thread)映射到大规模并行的硬件资源(Core)上

因此最核心的原理就是并行,因此C语言中的什么部分可以被CUDA有效的加速?


C语言中可被有效加速的部分

最核心的就是可以并行的部分,那么在一个串行的C语言代码中有什么可以并行呢?

  • 数据并行:在C 语言 中,循环(Loop)是数据并行最主要、甚至几乎是唯一的显式表现形式。
  • 任务并行:代码中互不相关的函数调用代码块也可以并行, 任务并行的规模通常很小(比如同时跑 2-4 个不同的任务),相比于循环动辄上百万次的迭代,它带来的加速效果远不如循环并行明显
  • 指令级并行: 这种并行通常由硬件和后端编译器(如 NVCC)自动优化,通常不需要关注它。

因此,核心目标就是将C语言中的循环利用CUDA并行化运行在不同硬件上。

!IMPORTANT

将循环并行化意味着不同循环循环迭代之间的运行顺序是不保证的!

CUDA 执行线程是并发的,硬件调度是不确定的。迭代 \(100\) 可能比迭代 \(1\) 先执行,也可能同时执行。这本质上就是一种极端的"重排序"

因此要确保并行化有效(不能改变程序的最终结果),就必须识别什么循环可以用并行。这里从一个定理引入

定理 迭代重排序 一个变换重排\(k\)层循环迭代的顺序,此外不做任何其他的改变,如果该循环不携带依赖,那么它是有效的。

**------现代体系结构的优化编译器 ([美] Randy Allen) **

这个定理可以说是本项目研究的基石,项目的入口,因为只有确定什么样的循环迭代能够进行迭代重排序我们才能使用CUDA对其进行并行化。

由此定理引出的概念:循环携带依赖循环携带依赖的层

到此为止, 我们已经知道项目要如何进行了, 使用定义循环携带依赖判断循环嵌套是否有依赖以及依赖所在的层(对于某些存在循环携带的层, 利用循环变换能够转化为不携带依赖的层), 根据定理迭代重排序对不存在携带依赖的层利用CUDA并行化


定义定理

!NOTE

定理皆来自于现代体系结构的优化编译器 ([美] Randy Allen)
定义 循环携带依赖 语句\(S_2\)对语句\(S_1\)有一个循环携带依赖,当且仅当\(S_1\)在迭代i中引用单元\(M\),而\(S_2\)在迭代j中引用\(M\),且\(d(i,j)>0\)(即\(D(i,j)\)包含一个"<"作为它的最左非"="分量)

此定义引出的概念:迭代向量,依赖距离向量,依赖方向向量

定义 循环携带依赖的层 此依赖的\(D(i,j)\)的最左非"="索引
定义 迭代向量 给定\(n\)个循环的嵌套,最内层循环的一个特定的迭代 的迭代向量\(i\)是一个整形向量,它包含按嵌套层顺序的每层循环的迭代号.

\[i=\{i_1,i_2,\dots,i_n\} \]

其中\(i_k,(1\le k \le n)\)表示在嵌套层k上的循环迭代号.

此定义引出的概念:循环迭代号

定义 依赖距离向量 假设从循环嵌套迭代 \(i\) 中语句 \(S_1\) 到迭代 j 中语句 \(S_2\) 有一依赖,则依赖距离向量 \\mathbf{d}(i, j) 定义为长度为 \(n\) 的向量,使得

\[\mathbf{d}(i, j)_k = j_k - i_k \]

定义 依赖方向向量 假设从 \(n\) 层循环嵌套的迭代 \(i\) 中语句 \(S_1\) 到迭代 j 中语句 \(S_2\) 有一依赖;那么依赖方向向量 \\mathbf{D}(i, j) 定义为长度为 \(n\) 的向量,使得

\[\mathbf{D}(i, j)_k = \begin{cases} < , & \text{如果 } \mathbf{d}(i, j)_k > 0 \\ = , & \text{如果 } \mathbf{d}(i, j)_k = 0 \\ > , & \text{如果 } \mathbf{d}(i, j)_k < 0 \end{cases} \]

定义 循环迭代号 对任意一个循环,其中循环索引 I 以步长 S L 步进到 U ,一个特定迭代的(正规化)迭代号 \(i\) 等于值 (I - L + S) / S ,其中 I 是该迭代中索引变量的值。

相关推荐
deephub9 个月前
计算加速技术比较分析:GPU、FPGA、ASIC、TPU与NPU的技术特性、应用场景及产业生态
人工智能·深度学习·gpu·计算加速