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

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

3天内不再提示

并行计算平台和NVIDIA编程模型CUDA的更简单介绍

星星科技指导员 来源:NVIDIA 作者:Mark Harris 2022-04-11 09:46 次阅读

这篇文章是对 CUDA 的一个超级简单的介绍,这是一个流行的并行计算平台和 NVIDIA 的编程模型。我在 2013 年给 CUDA 写了一篇前一篇 “简单介绍” ,这几年来非常流行。但是 CUDA 编程变得越来越简单, GPUs 也变得更快了,所以是时候更新(甚至更容易)介绍了。

CUDA C ++只是使用 CUDA 创建大规模并行应用程序的一种方式。它让您使用强大的 C ++编程语言来开发由数千个并行线程加速的高性能算法 GPUs 。许多开发人员已经用这种方式加速了他们对计算和带宽需求巨大的应用程序,包括支持人工智能正在进行的革命的库和框架 深度学习

所以,您已经听说了 CUDA ,您有兴趣学习如何在自己的应用程序中使用它。如果你是 C 或 C ++程序员,这个博客应该给你一个好的开始。接下来,您需要一台具有 CUDA – 功能的 GPU 计算机( Windows 、 Mac 或 Linux ,以及任何 NVIDIA GPU 都可以),或者需要一个具有 GPUs 的云实例( AWS 、 Azure 、 IBM 软层和其他云服务提供商都有)。您还需要安装免费的 CUDA 工具箱 。

我们开始吧!

从简单开始

我们将从一个简单的 C ++程序开始,它添加两个数组的元素,每个元素有一百万个元素。

#include 
#include  // function to add the elements of two arrays
void add(int n, float *x, float *y)
{ for (int i = 0; i < n; i++) y[i] = x[i] + y[i];
} int main(void)
{ int N = 1<<20; // 1M elements float *x = new float[N]; float *y = new float[N]; // initialize x and y arrays on the host for (int i = 0; i < N; i++) { x[i] = 1.0f; y[i] = 2.0f; } // Run kernel on 1M elements on the CPU add(N, x, y); // Check for errors (all values should be 3.0f) float maxError = 0.0f; for (int i = 0; i < N; i++) maxError = fmax(maxError, fabs(y[i]-3.0f)); std::cout << "Max error: " << maxError << std::endl; // Free memory delete [] x; delete [] y; return 0;
}

首先,编译并运行这个 C ++程序。将代码放在一个文件中,并将其保存为add.cpp,然后用 C ++编译器编译它。我在 Mac 电脑上,所以我用的是clang++,但你可以在 Linux 上使用g++,或者在 Windows 上使用 MSVC 。

> clang++ add.cpp -o add

然后运行它:

> ./add Max error: 0.000000

(在 Windows 上,您可能需要命名可执行文件添加. exe 并使用.dd运行它。)

正如预期的那样,它打印出求和中没有错误,然后退出。现在我想让这个计算在 GPU 的多个核心上运行(并行)。其实迈出第一步很容易。

首先,我只需要将我们的add函数转换成 GPU 可以运行的函数,在 CUDA 中称为内核。要做到这一点,我所要做的就是把说明符__global__添加到函数中,它告诉 CUDA C ++编译器,这是一个在 GPU 上运行的函数,可以从 CPU 代码调用。

// CUDA Kernel function to add the elements of two arrays on the GPU
__global__
void add(int n, float *x, float *y)
{ for (int i = 0; i < n; i++) y[i] = x[i] + y[i];
}

这些__global__函数被称为果仁,在 GPU 上运行的代码通常称为设备代码,而在 CPU 上运行的代码是主机代码

CUDA 中的内存分配

为了在 GPU 上计算,我需要分配 GPU 可访问的内存, CUDA 中的统一存储器通过提供一个系统中所有 GPUs 和 CPU 都可以访问的内存空间,这使得这一点变得简单。要在统一内存中分配数据,请调用cudaMallocManaged(),它返回一个指针,您可以从主机( CPU )代码或设备( GPU )代码访问该指针。要释放数据,只需将指针传递到cudaFree()

我只需要将上面代码中对new的调用替换为对cudaMallocManaged()的调用,并将对delete []的调用替换为对cudaFree.的调用

 // Allocate Unified Memory -- accessible from CPU or GPU float *x, *y; cudaMallocManaged(&x, N*sizeof(float)); cudaMallocManaged(&y, N*sizeof(float)); ... // Free memory cudaFree(x); cudaFree(y);

最后,我需要发射内核,它在add()上调用它。 CUDA 内核启动是使用三角括号语法指定的。我只需要在参数列表之前将它添加到对 CUDA 的调用中。

add<<<1, 1>>>(N, x, y);

容易的!我很快将详细介绍尖括号内的内容;现在您只需要知道这行代码启动了一个 GPU 线程来运行add()

还有一件事:我需要 CPU 等到内核完成后再访问结果(因为 CUDA 内核启动不会阻塞调用的 CPU 线程)。为此,我只需在对 CPU 进行最后的错误检查之前调用cudaDeviceSynchronize()

以下是完整的代码:

#include 
#include 
// Kernel function to add the elements of two arrays
__global__
void add(int n, float *x, float *y)
{ for (int i = 0; i < n; i++) y[i] = x[i] + y[i];
} int main(void)
{ int N = 1<<20; float *x, *y; // Allocate Unified Memory – accessible from CPU or GPU cudaMallocManaged(&x, N*sizeof(float)); cudaMallocManaged(&y, N*sizeof(float)); // initialize x and y arrays on the host for (int i = 0; i < N; i++) { x[i] = 1.0f; y[i] = 2.0f; } // Run kernel on 1M elements on the GPU add<<<1, 1>>>(N, x, y); // Wait for GPU to finish before accessing on host cudaDeviceSynchronize(); // Check for errors (all values should be 3.0f) float maxError = 0.0f; for (int i = 0; i < N; i++) maxError = fmax(maxError, fabs(y[i]-3.0f)); std::cout << "Max error: " << maxError << std::endl; // Free memory cudaFree(x); cudaFree(y); return 0;
}

CUDA 文件具有文件扩展名;.cu。所以把代码保存在一个名为

> nvcc add.cu -o add_cuda
> ./add_cuda
Max error: 0.000000

这只是第一步,因为正如所写的,这个内核只适用于一个线程,因为运行它的每个线程都将在整个数组上执行 add 。此外,还有一个竞争条件,因为多个并行线程读写相同的位置。

注意:在 Windows 上,您需要确保在 Microsoft Visual Studio 中项目的配置属性中将“平台”设置为 x64 。

介绍一下!

我认为找出运行内核需要多长时间的最简单的方法是用nvprof运行它,这是一个带有 CUDA 工具箱的命令行 GPU 分析器。只需在命令行中键入nvprof ./add_cuda

$ nvprof ./add_cuda
==3355== NVPROF is profiling process 3355, command: ./add_cuda
Max error: 0
==3355== Profiling application: ./add_cuda
==3355== Profiling result:
Time(%) Time Calls Avg Min Max Name
100.00% 463.25ms 1 463.25ms 463.25ms 463.25ms add(int, float*, float*)
...

上面是来自nvprof的截断输出,显示了对add的单个调用。在 NVIDIA Tesla K80 加速器上需要大约半秒钟的时间,而在我 3 岁的 Macbook Pro 上使用 NVIDIA GeForce GT 740M 大约需要半秒钟的时间。

让我们用并行来加快速度。

把线捡起来

既然你已经用一个线程运行了一个内核,那么如何使它并行?键是在 CUDA 的<<<1, 1>>>语法中。这称为执行配置,它告诉 CUDA 运行时要使用多少并行线程来启动 GPU 。这里有两个参数,但是让我们从更改第二个参数开始:线程块中的线程数。 CUDA GPUs 运行内核时使用的线程块大小是 32 的倍数,因此 256 个线程是一个合理的选择。

add<<<1, 256>>>(N, x, y);

如果我只在这个修改下运行代码,它将为每个线程执行一次计算,而不是将计算分散到并行线程上。为了正确地执行它,我需要修改内核。 CUDA C ++提供了关键字,这些内核可以让内核获得运行线程的索引。具体来说,threadIdx.x包含其块中当前线程的索引,blockDim.x包含块中的线程数。我只需修改循环以使用并行线程跨过数组。

__global__
void add(int n, float *x, float *y)
{ int index = threadIdx.x; int stride = blockDim.x; for (int i = index; i < n; i += stride) y[i] = x[i] + y[i];
}

add函数没有太大变化。事实上,将index设置为 0 ,stride设置为 1 会使其在语义上与第一个版本相同。

将文件另存为add_block.cu,然后再次在nvprof中编译并运行。在后面的文章中,我将只显示输出中的相关行。

Time(%) Time Calls Avg Min Max Name
100.00% 2.7107ms 1 2.7107ms 2.7107ms 2.7107ms add(int, float*, float*)

这是一个很大的加速( 463 毫秒下降到 2 . 7 毫秒),但并不奇怪,因为我从 1 线程到 256 线程。 K80 比我的小 MacBookProGPU 快( 3 . 2 毫秒)。让我们继续取得更高的表现。

走出街区

CUDA GPUs 有许多并行处理器组合成流式多处理器或 SMs 。每个 SM 可以运行多个并发线程块。例如,基于 Tesla 的 Tesla P100帕斯卡 GPU 体系结构有 56 个短消息,每个短消息能够支持多达 2048 个活动线程。为了充分利用所有这些线程,我应该用多个线程块启动内核。

现在您可能已经猜到执行配置的第一个参数指定了线程块的数量。这些平行线程块一起构成了所谓的网格。因为我有N元素要处理,每个块有 256 个线程,所以我只需要计算块的数量就可以得到至少 N 个线程。我只需将N除以块大小(注意在N不是blockSize的倍数的情况下向上取整)。

int blockSize = 256;
int numBlocks = (N + blockSize - 1) / blockSize;
add<<<numBlocks, blockSize>>>(N, x, y);

我还需要更新内核代码来考虑线程块的整个网格。threadIdx.x提供了包含网格中块数的gridDim.x和包含网格中当前线程块索引的blockIdx.x。图 1 说明了使用 CUDA 、gridDim.xthreadIdx.x在 CUDA 中索引数组(一维)的方法。其思想是,每个线程通过计算到其块开头的偏移量(块索引乘以块大小:blockIdx.x * blockDim.x),并将线程的索引添加到块内(threadIdx.x)。代码blockIdx.x * blockDim.x + threadIdx.x是惯用的 CUDA 。

__global__
void add(int n, float *x, float *y)
{ int index = blockIdx.x * blockDim.x + threadIdx.x; int stride = blockDim.x * gridDim.x; for (int i = index; i < n; i += stride) y[i] = x[i] + y[i];
}

更新的内核还将stride设置为网格中的线程总数(blockDim.x * gridDim.x)。 CUDA 内核中的这种类型的循环通常称为栅格步幅循环

将文件另存为&[EZX63 ;&[编译并在&[EZX37 ;&]中运行它]

Time(%) Time Calls Avg Min Max Name
100.00% 94.015us 1 94.015us 94.015us 94.015us add(int, float*, float*)

这是另一个 28 倍的加速,从运行多个街区的所有短信 K80 !我们在 K80 上只使用了 2 个 GPUs 中的一个,但是每个 GPU 都有 13 条短信。注意,我笔记本电脑中的 GeForce 有 2 条(较弱的)短信,运行内核需要 680us 。

总结

下面是三个版本的add()内核在 Tesla K80 和 GeForce GT 750M 上的性能分析。

如您所见,我们可以在 GPUs 上实现非常高的带宽。这篇文章中的计算是非常有带宽限制的,但是 GPUs 也擅长于密集矩阵线性代数深度学习、图像和信号处理、物理模拟等大量计算限制的计算。

关于作者

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

审核编辑:郭婷

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

    关注

    14

    文章

    5075

    浏览量

    103670
  • gpu
    gpu
    +关注

    关注

    28

    文章

    4768

    浏览量

    129327
  • 计算机
    +关注

    关注

    19

    文章

    7534

    浏览量

    88604
收藏 人收藏

    评论

    相关推荐

    xgboost的并行计算原理

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

    NVIDIA Cosmos世界基础模型平台发布

    NVIDIA 宣布推出NVIDIA Cosmos,该平台由先进的生成式世界基础模型、高级 tokenizer、护栏和加速视频处理管线组成,将推动自动驾驶汽车(AV)和机器人等物理 AI
    的头像 发表于 01-08 10:39 183次阅读

    《CST Studio Suite 2024 GPU加速计算指南》

    的各个方面,包括硬件支持、操作系统支持、许可证、GPU计算的启用、NVIDIA和AMD GPU的详细信息以及相关的使用指南和故障排除等内容。 1. 硬件支持 - NVIDIA GPU:详细列出了支持
    发表于 12-16 14:25

    NVIDIA与谷歌量子AI部门达成合作

    NVIDIA CUDA-Q 平台使谷歌量子 AI 研究人员能够为其量子计算机创建大规模的数字模型,以解决设计中面临的各种挑战
    的头像 发表于 11-20 09:39 307次阅读

    NVIDIA向开放计算项目捐赠Blackwell平台设计

    近日,在美国加利福尼亚州举行的 OCP 全球峰会上,NVIDIA 宣布已把 NVIDIA Blackwell 加速计算平台的一些基础元素捐赠给开放
    的头像 发表于 11-19 15:30 259次阅读

    【「算力芯片 | 高性能 CPU/GPU/NPU 微架构分析」阅读体验】--了解算力芯片GPU

    方式可以提高处理器的吞吐量。并行计算模式(而非图形模式下)GPGPU的流水线是针对线程束进行管理的,也就是NVIDIA所说的 CUDA环境下的 warp 或者AMD 所说的 OpenCL 环境下
    发表于 11-03 12:55

    GPU加速计算平台是什么

    GPU加速计算平台,简而言之,是利用图形处理器(GPU)的强大并行计算能力来加速科学计算、数据分析、机器学习等复杂计算任务的软硬件结合系统。
    的头像 发表于 10-25 09:23 289次阅读

    【「大模型时代的基础架构」阅读体验】+ 第一、二章学习感受

    每个核心在某一时刻只能执行一个线程。CPU的设计注重的是低延迟,即快速响应和处理单个任务。而GPU则不同,它拥有成百上千个更小、专一的处理单元,这些单元可以同时处理大量的简单任务。GPU的这种并行计算
    发表于 10-10 10:36

    简单认识NVIDIA网络平台

    NVIDIA Spectrum-X800 平台是业界第一代 800Gb/s 的以太网网络平台,包括了 NVIDIA Spectrum SN5600 800Gb/s 以太网交换机和
    的头像 发表于 09-09 09:22 495次阅读

    NVIDIA提供一套服务、模型以及计算平台 加速人形机器人发展

    的发展,NVIDIA 于今日宣布,为全球领先的机器人制造商、AI 模型开发者和软件制造商提供一套服务、模型以及计算平台,以开发、训练和构建下
    的头像 发表于 07-31 10:41 767次阅读

    毕昇大模型应用开发平台+浪潮信息AIStation,让大模型定制简单

    与技能模板,使用简单直观的表单填写方式,即可高效利用企业自有数据,快速开发贴合真实业务场景的大模型应用,加速大模型在智能问答、报告生成、自动化、数据分析等典型行业场景的落地。   毕昇大模型
    的头像 发表于 06-05 11:58 540次阅读
    毕昇大<b class='flag-5'>模型</b>应用开发<b class='flag-5'>平台</b>+浪潮信息AIStation,让大<b class='flag-5'>模型</b>定制<b class='flag-5'>更</b><b class='flag-5'>简单</b>

    英伟达CUDA-Q平台推动全球量子计算研究

    英伟达今日公布了其重要战略决策,即采用开源的CUDA-Q平台,旨在推动德国、日本和波兰等国家超运中心在量子计算领域的创新研究。CUDA-Q作为英伟达推出的一款开源
    的头像 发表于 05-14 11:45 707次阅读

    NVIDIA 通过 CUDA-Q 平台为全球各地的量子计算中心提供加速

    —— NVIDIA 于今日宣布将通过开源的 NVIDIA CUDA-Q™ 量子计算平台,助力全球各地的国家级超算中心加快量子
    发表于 05-13 15:21 221次阅读
    <b class='flag-5'>NVIDIA</b> 通过 <b class='flag-5'>CUDA</b>-Q <b class='flag-5'>平台</b>为全球各地的量子<b class='flag-5'>计算</b>中心提供加速

    基于NVIDIA开源CUDA-Q量子计算平台发布

    NVIDIA 于太平洋时间 3 月 18 日推出一项云服务,旨在帮助研究人员和开发人员在化学、生物学、材料科学等关键科学领域的量子计算研究中取得突破。
    的头像 发表于 03-21 09:54 488次阅读

    NVIDIA 推出云量子计算机模拟微服务

    量子云基于 NVIDIA 开源 CUDA-Q 量子计算平台 —— 部署量子处理器(QPU)的公司有四分之三都在
    发表于 03-19 11:27 493次阅读
    <b class='flag-5'>NVIDIA</b> 推出云量子<b class='flag-5'>计算</b>机模拟微服务