HyCuda,用于 CUDA 的混合框架代码生成器






4.89/5 (4投票s)
用于 CUDA 的混合框架代码生成器
引言
本文将介绍我最新的与 CUDA 相关的项目 HyCuda。这个名字是 PyCUDA 的一个文字游戏,但与 Python 无关。相反,它生成 C++ 代码,让您可以轻松构建混合算法。
在我进行 CUDA 实现时,我意识到我不是唯一一个想要尝试不同设备设置的人。我想看看当我在不同设备上运行一个或多个子程序时,我的程序的性能会受到怎样的影响。这当然意味着需要实现两次相同的算法(用于 CPU 和 GPU),但代码需要进行一些修改,以确保所有数据都在正确的位置。这时我开始开发一个模板框架来处理这些任务。然而,该框架特别适合我的需求以及我正在使用的特定算法。然后我开始思考如何更通用地解决这个问题,并提出了 HyCuda。
HyCuda 的工作方式是读取程序员提供的描述算法的规范文件。它包含有关子程序、它们使用的数据以及数据类型的信息。然后,它生成一个 C++ 模板框架,您可以在其中实例化一个类模板,其模板参数包含您想使用的设备信息。这允许您实例化多个执行相同算法但位于不同机器上的对象。模板(元编程)机制在编译时(显然)会处理好哪些数据需要何时移动。
本文不会深入介绍细节,因为我已经在 SourceForge 上提供了一份手动(PDF)供您下载,网址是 sourceforge。相同的说明也可以在 http://hycuda.sourceforge.net 上以 HTML 格式提供,但格式不是很好。我只是使用了 latex2html 生成它,没有费力去使其看起来美观。
背景
如果您不熟悉 C++ 和/或 CUDA,本文可能不适合您。如果您想了解 GPU 编程(特别是使用 CUDA),我建议您先阅读相关资料。
使用代码
规范文件
如引言中所述,HyCuda 基于描述算法的规范文件生成 C++ 代码。该文件包含 4 个部分,由两个连续的百分号(%%
)分隔。
- 指令(类名、命名空间等)
- 数据(类型、大小、输入/输出到算法)
- 例程(它们的名称是什么,它们依赖于哪些数据以及如何依赖?)
- 顺序(例程应该按什么顺序执行?)
/* filename: spec.hycuda */
// Directives-Section
%namespace: Example
%class-name: ExampleAlgorithm
%parameters: Params {
float m1;
float m2;
}
%% // Memory-Section
vec1 (i) : float, vectorSize
vec2 (i) : float, vectorSize
vec3 : float, vectorSize
sum (o) : float, 1
%% // Routine-Section
multiplyM1 : vec1 (rw), vec2 (rw)
multiplyM2 : vec3 (rw)
addVec1Vec2 : vec1 (r), vec2 (r), vec3 (w)
sumVec3 : vec3 (r), sum (w)
%% // Routine-order
%order : $1, $3, $2, $4
这个相当愚蠢的例子描述了一个执行一些向量运算的算法。它将生成一个名为 ExampleAlgorithm
的类,声明在 Example
命名空间中,其行为由两个名为 m1
和 m2
的 float 参数定义。沿途将使用 3 个不同的向量,分别为 vec1
、vec2
和 vec3
,它们都包含总共 vectorSize
个 float。前两个向量将用作输入,由 (i)
指定,而第三个向量将是中间产品,因此没有输入或输出说明符与之关联。输出将称为 sum
,并且只包含一个元素(再次是一个 float
)。
将有 4 个子程序构成整个算法。(当然,这是执行这些运算的一种非常愚蠢的方式。算法被分解成这样纯粹是为了教学目的。)
- 将两个输入向量乘以参数
m1
。 - 将得到的向量相加,并将结果称为
vec3
。 - 将
vec3
乘以另一个参数m2
。 - 将
vec3
中的每个元素相加,并将结果存储在sum
中。
例程部分列出了每个子程序的名称,并告诉 HyCuda 它将使用哪些数据。它还指定数据的使用方式:读取 (r)
、写入 (w)
或两者兼有 (rw)
/(wr)
。此信息用于确定是否需要将数据移动到当前设备。
设备策略
我将跳过实现函数/内核的部分。这在手册中有详细描述。相反,我将转移到您已经设置好所有内容并希望运行算法的部分。为此,您需要指定哪个子程序将在哪个设备上运行。这是通过将模板参数传递给生成的类来实现的,该参数称为 DevicePolicies
。为了方便起见,HyCuda 生成了一个已设置默认策略的头文件。该文件看起来像下面的代码片段:
/* filename: examplealgorithm.h */
#include "examplealgorithm_algorithm.h"
#include "examplealgorithm_hybrid.h"
#include "examplealgorithm_devicepolicies.h"
namespace Example {
// Specify which device to use for each of the routines (CPU/GPU)
typedef DevicePolicies <
MultiplyM1Device < CPU >,
MultiplyM2Device < CPU >,
AddVec1Vec2Device < CPU >,
SumVec3Device < CPU >
> CustomPolicy;
typedef Hybrid_< CustomPolicy > Hybrid;
typedef ExampleAlgorithm_< Hybrid > ExampleAlgorithm;
} //Example
这个小片段实际上有很多内容,但主要的是 DevicePolicies
类模板的 typedef。它接受一些模板参数,这些参数本身也是类模板(例如 MultiplyM1Device
)。这些参数都以 spec 文件中的例程命名,并且它们自己的参数告诉算法使用哪个设备来执行每个特定的例程。正确实现后,策略列表中 CPU/GPU 参数的每种排列都会导致设备以不同的方式使用,但产生相同的结果。
以下是使用该算法处理一些输入的 main 函数:
/* examplealgorithm_main.cc */
#include "examplealgorithm.h"
using namespace Example
using namespace std;
size_t readVectorsFromFile(char const *filename,
float **v1, float **v2);
int main(int argc, char **argv)
{
// Initialize parameters
Params params;
params.m1 = 2;
params.m2 = 3;
// Initialize vectors
float *v1, *v2;
size_t vectorSize = readVectorsFromFile(argv[1], &v1, &v2);
// Initialize input
Input in;
in.vec1 = {v1, vectorSize};
in.vec2 = {v2, vectorSize};
// Initialize output
float sum;
Output out;
out.sum = {&sum, 1};
// Process
ExampleAlgorithm alg(params);
alg.process(in, out);
// Output
cout << "Sum: " << sum << '\n';
}
输入向量不归用户所有,用户应确保它们已正确分配和初始化。输出数据(在此例中为 sum
)也是如此。然后使用参数初始化算法,并调用其成员 process
,将输入和输出作为其参数。当它返回时,sum
将包含适当的值。
最后的 remarks
我意识到本文中的信息对于您实际使用该程序来说是绝对不够的。但是,我希望它能让您对 HyCuda 是什么以及它是如何工作的有一个初步的了解。有关更多信息,我再次推荐您访问项目页面:http://www.sourceforge.net/projects/hycuda。
此外,我非常希望得到一些反馈。到目前为止,我还没有编写一个合适的构建脚本来让您轻松使用。我想只有当确实有人打算使用它时,我才需要这样做。因此,如果您认为这个程序有任何潜力(或者完全没有),请告诉我,我会付出额外的努力。
构建 HyCuda
目前,我将发布一些关于如何开始使用 HyCuda 的说明。它附带一个 makefile,您可以在任何类 Unix 环境中使用它。我诚挚地为 Windows 用户带来的不便表示歉意。但是,生成器需要骨架文件,这些文件的路径包含在 parser/skeletons.h
头文件中。因此,在调用 make 之前,您应该找到一个合适的(绝对)目录来包含这些文件(例如 /etc/skel/hycuda
)。编辑 skeletons.h
指向此目录,然后使用 make 构建程序。现在,只需将 skeletons
目录的内容复制到您创建的目录中,并在您的路径中添加一个指向 hycuda
的符号链接,您就可以开始使用了!
历史
- 2013 年 12 月 18 日:初稿。