0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CUDA并行计算平台的C/C++接口的简单介绍

星星科技指导员 来源:NVIDIA 作者:Mark Harris 2022-04-11 10:13 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本文是 CUDA C 和 C ++的一个系列,它是 CUDA 并行计算平台的 C / C ++接口。本系列文章假定您熟悉 C 语言编程。我们将针对 Fortran 程序员运行一系列关于 CUDA Fortran 的文章。这两个系列将介绍 CUDA 平台上并行计算的基本概念。从这里起,除非我另有说明,我将用“ CUDA C ”作为“ CUDA C 和 C ++”的速记。 CUDA C 本质上是 C / C ++,具有几个扩展,允许使用并行的多个线程在 GPU 上执行函数。

CUDA 编程模型基础

在我们跳转到 CUDA C 代码之前, CUDA 新手将从 CUDA 编程模型的基本描述和使用的一些术语中受益。

CUDA 编程模型是一个异构模型,其中使用了 CPU 和 GPU 。在 CUDA 中, host 指的是 CPU 及其存储器, device 是指 GPU 及其存储器。在主机上运行的代码可以管理主机和设备上的内存,还可以启动在设备上执行的函数 kernels 。这些内核由许多 GPU 线程并行执行。

鉴于 CUDA 编程模型的异构性, CUDA C 程序的典型操作序列是:

声明并分配主机和设备内存。

初始化主机数据。

将数据从主机传输到设备。

执行一个或多个内核。

将结果从设备传输到主机。

记住这个操作序列,让我们看一个 CUDA C 示例。

第一个 CUDA C 程序

在最近的一篇文章中,我演示了 萨克斯比的六种方法 ,其中包括一个 CUDA C 版本。 SAXPY 代表“单精度 A * X + Y ”,是并行计算的一个很好的“ hello world ”示例。在这篇文章中,我将剖析 CUDA C SAXPY 的一个更完整的版本,详细解释它的作用和原因。完整的 SAXPY 代码是:

#include 

__global__
void saxpy(int n, float a, float *x, float *y)
{
 int i = blockIdx.x*blockDim.x + threadIdx.x;
 if (i < n) y[i] = a*x[i] + y[i];
}

int main(void)
{
  int N = 1<<20;
  float *x, *y, *d_x, *d_y;
  x = (float*)malloc(N*sizeof(float));
  y = (float*)malloc(N*sizeof(float));

  cudaMalloc(&d_x, N*sizeof(float)); 
  cudaMalloc(&d_y, N*sizeof(float));

  for (int i = 0; i < N; i++) {
    x[i] = 1.0f;
    y[i] = 2.0f;
  }

  cudaMemcpy(d_x, x, N*sizeof(float), cudaMemcpyHostToDevice);
  cudaMemcpy(d_y, y, N*sizeof(float), cudaMemcpyHostToDevice);

  // Perform SAXPY on 1M elements
  saxpy<<<(N+255)/256, 256>>>(N, 2.0f, d_x, d_y);

  cudaMemcpy(y, d_y, N*sizeof(float), cudaMemcpyDeviceToHost);

  float maxError = 0.0f;
  for (int i = 0; i < N; i++)
    maxError = max(maxError, abs(y[i]-4.0f));
  printf("Max error: %f
", maxError);

  cudaFree(d_x);
  cudaFree(d_y);
  free(x);
  free(y);
}

函数saxpy是在 GPU 上并行运行的内核,main函数是宿主代码。让我们从宿主代码开始讨论这个程序。

主机代码

main 函数声明两对数组。

  float *x, *y, *d_x, *d_y;
  x = (float*)malloc(N*sizeof(float));
  y = (float*)malloc(N*sizeof(float));

  cudaMalloc(&d_x, N*sizeof(float));
  cudaMalloc(&d_y, N*sizeof(float));

指针x和y指向以典型方式使用malloc分配的主机阵列,d_x和d_y数组指向从CUDA运行时API使用cudaMalloc函数分配的设备数组。CUDA中的主机和设备有独立的内存空间,这两个空间都可以从主机代码进行管理(CUDAC内核也可以在支持它的设备上分配设备内存)。

然后,主机代码初始化主机数组。在这里,我们设置了一个 1 数组,以及一个 2 数组。

  for (int i = 0; i < N; i++) {
    x[i] = 1.0f;
    y[i] = 2.0f;
  }

为了初始化设备数组,我们只需使用cudaMemcpy将数据从xy复制到相应的设备数组d_xd_y,它的工作方式与标准的 Cmemcpy函数一样,只是它采用了第四个参数,指定了复制的方向。在本例中,我们使用cudaMemcpyHostToDevice指定第一个(目标)参数是设备指针,第二个(源)参数是主机指针。

  cudaMemcpy(d_x, x, N*sizeof(float), cudaMemcpyHostToDevice);
  cudaMemcpy(d_y, y, N*sizeof(float), cudaMemcpyHostToDevice);

在运行内核之后,为了将结果返回到主机,我们使用cudaMemcpycudaMemcpyDeviceToHost,从d_y指向的设备数组复制到y指向的主机数组。

cudaMemcpy(y, d_y, N*sizeof(float), cudaMemcpyDeviceToHost);

启动内核

cord [EZX13 内核由以下语句启动:

saxpy<<<(N+255)/256, 256>>>(N, 2.0, d_x, d_y);

三个 V 形符号之间的信息是 执行配置 ,它指示有多少设备线程并行执行内核。在 CUDA 中,软件中有一个线程层次结构,它模仿线程处理器在 GPU 上的分组方式。在 CUDA 编程模型中,我们谈到启动一个 grid 为 螺纹块 的内核。执行配置中的第一个参数指定网格中线程块的数量,第二个参数指定线程块中的线程数。

线程块和网格可以通过为这些参数传递 dim3 (一个由 CUDA 用 x 、 y 和 z 成员定义的简单结构)值来生成一维、二维或三维的线程块和网格,但是对于这个简单的示例,我们只需要一维,所以我们只传递整数。在本例中,我们使用包含 256 个线程的线程块启动内核,并使用整数算术来确定处理数组( (N+255)/256 )的所有 N 元素所需的线程块数。

对于数组中的元素数不能被线程块大小平均整除的情况,内核代码必须检查内存访问是否越界。

清理

完成后,我们应该释放所有分配的内存。对于使用 cudaMalloc() 分配的设备内存,只需调用 cudaFree() 。对于主机内存,请像往常一样使用 free() 。

cudaFree(d_x);
  cudaFree(d_y);
  free(x);
  free(y);

设备代码

现在我们继续讨论内核代码。

__global__
void saxpy(int n, float a, float *x, float *y)
{
 int i = blockIdx.x*blockDim.x + threadIdx.x;
 if (i < n) y[i] = a*x[i] + y[i];
}

在 CUDA 中,我们使用 __global__ de __global__ 说明符定义诸如 Clara 这样的内核。设备代码中定义的变量不需要指定为设备变量,因为假定它们驻留在设备上。在这种情况下, n 、 a 和 i 变量将由每个线程存储在寄存器中,指针 x 和 y 必须是指向设备内存地址空间的指针。这确实是真的,因为当我们从宿主代码启动内核时,我们将 d_x 和 d_y 传递给了内核。但是,前两个参数 n 和 a 没有在主机代码中显式传输到设备。因为函数参数在 C / C ++中是默认通过值传递的,所以 CUDA 运行时可以自动处理这些值到设备的传输。 CUDA 运行时 API 的这一特性使得在 GPU 上启动内核变得非常自然和简单——这几乎与调用 C 函数一样。

在我们的 saxpy 内核中只有两行。如前所述,内核由多个线程并行执行。如果我们希望每个线程处理结果数组的一个元素,那么我们需要一种区分和标识每个线程的方法。 CUDA 定义变量 blockDim 、 blockIdx 和 threadIdx 。这些预定义变量的类型为 dim3 ,类似于主机代码中的执行配置参数。预定义变量 blockDim 包含在内核启动的第二个执行配置参数中指定的每个线程块的维度。预定义变量 threadIdx 和 blockIdx 分别包含线程块中线程的索引和网格中的线程块的索引。表达式:

    int i = blockDim.x * blockIdx.x + threadIdx.x

生成用于访问数组元素的全局索引。我们在这个例子中没有使用它,但是还有一个 gridDim ,它包含在启动的第一个执行配置参数中指定的网格维度。

在使用该索引访问数组元素之前,将根据元素的数量 n 检查其值,以确保没有越界内存访问。如果一个数组中的元素数不能被线程块大小平均整除,并且结果内核启动的线程数大于数组大小,则需要进行此检查。内核的第二行执行 SAXPY 的元素级工作,除了边界检查之外,它与 SAXPY 主机实现的内部循环相同。

if (i < n) y[i] = a*x[i] + y[i];

编译和运行代码

CUDA C 编译器 nvcc 是 NVIDIA CUDA 工具箱 的一部分。为了编译我们的 SAXPY 示例,我们将代码保存在一个扩展名为。 cu 的文件中,比如说 saxpy.cu 。然后我们可以用 nvcc 编译它。

nvcc -o saxpy saxpy.cu

然后我们可以运行代码:

% ./saxpy
Max error: 0.000000

总结与结论

通过对 SAXPY 的一个简单的 CUDA C 实现的演练,您现在了解了编程 CUDA C 的基本知识。将 C 代码“移植”到 CUDA C 只需要几个 C 扩展:设备内核函数的 __global__ de Clara 说明符;启动内核时使用的执行配置;内置的设备变量 blockDim 、 blockIdx 和 threadIdx 用来识别和区分并行执行内核的 GPU 线程。

异类 CUDA 编程模型的一个优点是,将现有代码从 C 移植到 CUDA C 可以逐步完成,一次只能移植一个内核。

在本系列的下一篇文章中,我们将研究一些性能度量和度量。

关于作者

Mark Harris 是 NVIDIA 杰出的工程师,致力于 RAPIDS 。 Mark 拥有超过 20 年的 GPUs 软件开发经验,从图形和游戏到基于物理的模拟,到并行算法和高性能计算。当他还是北卡罗来纳大学的博士生时,他意识到了一种新生的趋势,并为此创造了一个名字: GPGPU (图形处理单元上的通用计算)。

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    39

    文章

    7714

    浏览量

    170830
  • cpu
    cpu
    +关注

    关注

    68

    文章

    11216

    浏览量

    222904
  • gpu
    gpu
    +关注

    关注

    28

    文章

    5099

    浏览量

    134440
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    为啥 AI 计算速度这么惊人?—— 聊聊 GPU、内存与并行计算

    提到AI,大家常说它“算得快”,其实是指AI能在眨眼间处理海量数据。可它为啥有这本事?答案就藏在“GPU+高速内存+并行计算”这trio(组合)里。咱们可以把AI要处理的数据,想象成一大堆“小任务
    的头像 发表于 12-05 14:35 196次阅读
    为啥 AI <b class='flag-5'>计算</b>速度这么惊人?—— 聊聊 GPU、内存与<b class='flag-5'>并行计算</b>

    神经网络的并行计算与加速技术

    问题。因此,并行计算与加速技术在神经网络研究和应用中变得至关重要,它们能够显著提升神经网络的性能和效率,满足实际应用中对快速响应和大规模数据处理的需求。神经网络并行
    的头像 发表于 09-17 13:31 882次阅读
    神经网络的<b class='flag-5'>并行计算</b>与加速技术

    从自然仿真到智能调度——GPU并行计算的多场景突破

    的体系结构,成为科学仿真与智能调度的核心计算平台。在自然现象模拟中,风沙流、流体力学等问题往往涉及海量粒子间的相互作用,计算负担极为沉重,而GPU的并行邻居搜索与空间
    的头像 发表于 09-03 10:32 577次阅读
    从自然仿真到智能调度——GPU<b class='flag-5'>并行计算</b>的多场景突破

    Kintex UltraScale 纯 FPGA 开发平台,释放高速并行计算潜能,高性价比的 FPGA 解决方案

    璞致电子PZ-KU060-KFB开发板采用Xilinx Kintex UltraScale KU060芯片,提供高密度并行计算能力,配备4GB DDR4内存、20对GTH高速收发器和多种扩展接口
    的头像 发表于 08-18 13:28 503次阅读
    Kintex UltraScale 纯 FPGA 开发<b class='flag-5'>平台</b>,释放高速<b class='flag-5'>并行计算</b>潜能,高性价比的 FPGA 解决方案

    请问是否可以在通用Windows平台中构建OpenVINO™ GenAI C++ 应用程序?

    无法在通用 Windows 平台中构建OpenVINO™ GenAI C++ 应用程序
    发表于 06-24 07:35

    边缘AI广泛应用推动并行计算崛起及创新GPU渗透率快速提升

    是时候重新教育整个生态了。边缘AI的未来不属于那些高度优化但功能狭窄的芯片,而是属于可编程的、可适配的并行计算平台,它们能与智能软件共同成长并扩展。
    的头像 发表于 06-11 14:57 475次阅读

    主流的 MCU 开发语言为什么是 C 而不是 C++

    在单片机的地界儿里,C语言稳坐中军帐,C++想分杯羹?难喽。咱电子工程师天天跟那针尖大的内存空间较劲,C++那些花里胡哨的玩意儿,在这儿真玩不转。先说内存这道坎儿。您当stm32f4的256kRAM
    的头像 发表于 05-21 10:33 777次阅读
    主流的 MCU 开发语言为什么是 <b class='flag-5'>C</b> 而不是 <b class='flag-5'>C++</b>?

    读懂极易并行计算:定义、挑战与解决方案

    GPU经常与人工智能同时提及,其中一个重要原因在于AI与3D图形处理本质上属于同一类问题——它们都适用极易并行计算。什么是极易并行计算?极易并行计算指的是符合以下特征的计算任务:任务独
    的头像 发表于 04-17 09:11 647次阅读
    读懂极易<b class='flag-5'>并行计算</b>:定义、挑战与解决方案

    GPU加速计算平台的优势

    传统的CPU虽然在日常计算任务中表现出色,但在面对大规模并行计算需求时,其性能往往捉襟见肘。而GPU加速计算平台凭借其独特的优势,吸引了行业内人士的广泛关注和应用。下面,AI部落小编为
    的头像 发表于 02-23 16:16 755次阅读

    Type-C连接器接口的功能介绍

    Type-C连接器是近年来电子设备连接领域中的一项重要革新,Type-C连接器以卓越的功能和广泛的应用范围迅速成为了连接器市场的主流。本文由连欣科技为大家深入介绍Type-C
    的头像 发表于 02-20 11:32 2339次阅读
    Type-<b class='flag-5'>C</b>连接器<b class='flag-5'>接口</b>的功能<b class='flag-5'>介绍</b>

    基于OpenHarmony标准系统的C++公共基础类库案例:ThreadPoll

    。每个线程每秒打印1段字符串,10秒后停止。2、基础知识C++公共基础类库为标准系统提供了一些常用的C++开发工具类,包括:文件、路径、字符串相关操作的能力增强接口
    的头像 发表于 02-10 18:09 595次阅读
    基于OpenHarmony标准系统的<b class='flag-5'>C++</b>公共基础类库案例:ThreadPoll

    xgboost的并行计算原理

    在大数据时代,机器学习算法需要处理的数据量日益增长。为了提高数据处理的效率,许多算法都开始支持并行计算。XGBoost作为一种高效的梯度提升树算法,其并行计算能力是其受欢迎的原因
    的头像 发表于 01-19 11:17 1569次阅读

    Spire.XLS for C++组件说明

    开发人员可以快速地在 C++ 平台上完成对 Excel 的各种编程操作,如根据模板创建新的 Excel 文档,编辑现有 Excel 文档,以及对 Excel 文档进行转换。 Spire.XLS
    的头像 发表于 01-14 09:40 1294次阅读
    Spire.XLS for <b class='flag-5'>C++</b>组件说明

    EE-112:模拟C++中的类实现

    电子发烧友网站提供《EE-112:模拟C++中的类实现.pdf》资料免费下载
    发表于 01-03 15:15 0次下载
    EE-112:模拟<b class='flag-5'>C++</b>中的类实现

    AKI跨语言调用库神助攻C/C++代码迁移至HarmonyOS NEXT

    量;某知名社交电商平台使用后减少了50%以上跨语言调用接口代码量;某图像处理软件所有C++代码复用通过AKI来实现。使用AKI后这些项目不仅减少了项目代码量,还显著优化了代码复用与迁移流程。 目前
    发表于 01-02 17:08