CUDA 编程基础

news2025/1/11 12:46:31

    • 1. GPU与CPU并行计算框架
    • 2. CUDA编程模型
    • 3 CUDA程序

1. GPU与CPU并行计算框架

GPU并不是一个独立运行的计算平台,而需要与CPU协同工作,可以看成是CPU的协处理器,因此当我们在说GPU并行计算时,其实是指的基于CPU+GPU的异构计算架构。在异构计算架构中,GPU与CPU通过PCIe总线连接在一起来协同工作,CPU所在位置称为为主机端(host),而GPU所在位置称为设备端(device),如下图所示。
在这里插入图片描述GPU包括更多的运算核心,其特别适合数据并行的计算密集型任务,如大型矩阵运算,而CPU的运算核心较少,但是其可以实现复杂的逻辑运算,因此其适合控制密集型任务。另外,CPU上的线程是重量级的,上下文切换开销大,但是GPU由于存在很多核心,其线程是轻量级的。因此,基于CPU+GPU的异构计算平台可以优势互补,CPU负责处理逻辑复杂的串行程序,而GPU重点处理数据密集型的并行计算程序,从而发挥最大功效。

CUDA是NVIDIA公司所开发的GPU编程模型,它提供了GPU编程的简易接口,基于CUDA编程可以构建基于GPU计算的应用程序。CUDA提供了对其它编程语言的支持,如C/C++,Python,Fortran等语言。
CUDA编译器:nvcc
CUDA调试器:nvcc-gdb
CUDA性能分析:nsight
在这里插入图片描述

2. CUDA编程模型

host指代CPU及其内存,包含host程序。
device指代GPU及其内存,包含device程序。
host与device之间可以进行通信,这样它们之间可以进行数据拷贝。

经典CUDA程序的执行流程如下:

  1. 分配host内存,并进行数据初始化;
  2. 分配device内存,并从host将数据拷贝到device上;
  3. 调用CUDA的核函数在device上完成指定的运算;
  4. 将device上的运算结果拷贝到host上;
  5. 释放device和host上分配的内存。

·kernel在device上执行时实际上是启动很多线程,一个kernel所启动的所有线程称为一个网格grid,同一个网格上的线程共享相同的全局内存空间,grid是线程结构的第一层次,而网格又可以分为很多线程块block,一个线程块里面包含很多线程,这是第二个层次。
在这里插入图片描述核函数声明:
·核函数用__global__符号声明

__global__ 返回值类型 核函数名(形参列表){
	...
}

核函数调用:

·在调用时需要用<<<grid, block>>>来指定kernel要执行的线程数量,grid是网格块数,block是每块的线程数。grid和block都是定义为dim3类型的变量,dim3可以看成是包含三个无符号整数(x,y,z)成员的结构体变量,在定义时,xyz的缺省值初始化为1。 因此grid和block可以灵活地定义为1-dim,2-dim以及3-dim结构。

dim3 grid(3, 2);//一个grid包含6个block
dim3 block(5, 3);//每个block包含15个线程
核函数名<<<grid,block>>>(实参列表)

线程ID:
·在CUDA中,每一个线程都要执行核函数,并且每个线程会分配一个唯一的线程号thread ID,这个ID值可以通过核函数的内置变量threadIdx来获得。
因此一个线程需要两个内置的坐标变量(blockIdx,threadIdx)来唯一标识,它们都是dim3类型变量,其中blockIdx指明线程所在grid中的位置,而threaIdx指明线程所在block中的位置。
如上图中Thread(1,1)标识如下:

threadIdx.x = 1
threadIdx.y = 1
blockIdx.x = 1
blockIdx.y = 1

一个线程块上的线程是放在同一个流式多处理器(SM)上的,现代GPUs的线程块可支持的线程数可达1024个。
要知道一个线程在blcok中的全局ID,此时就必须还要知道block的组织结构,这是通过线程的内置变量blockDim来获得,它获取线程块各个维度的大小。另外线程还有内置变量gridDim,用于获得网格块各个维度的大小
·grid的内置变量:x,y,z,gridDim
·block的内置变量:x,y,z,blockDim

host和device函数区分:
由于GPU实际上是异构模型,区别host和device上的函数,主要的三个函数类型限定词如下:

__global__:在device上执行,从host中调用(一些特定的GPU也可以从device上调用),返回类型必须是void,不支持可变参数参数,不能成为类成员函数。注意用__global__定义的kernel是异步的,这意味着host不会等待kernel执行完就执行下一步。
__device__:在device上执行,单仅可以从device中调用,不可以和__global__同时用。
__host__:在host上执行,仅可以从host上调用,一般省略不写,不可以和__global__同时用,但可和__device__,此时函数会在device和host都编译。

内存模型
CUDA的内存模型:每个线程有自己的私有本地内存(Local Memory),而每个线程块有包含共享内存(Shared Memory),可以被线程块中所有线程共享,其生命周期与线程块一致。此外,所有的线程都可以访问全局内存(Global Memory)。还可以访问一些只读内存块:常量内存(Constant Memory)和纹理内存(Texture Memory)。内存结构涉及到程序优化,这里不深入探讨。在这里插入图片描述硬件基础

GPU硬件的一个核心组件是SM,前面已经说过,SM是英文名是 Streaming Multiprocessor,翻译过来就是流式多处理器。SM的核心组件包括CUDA核心,共享内存,寄存器等,SM可以并发地执行数百个线程,并发能力就取决于SM所拥有的资源数。当一个kernel被执行时,它的gird中的线程块被分配到SM上,一个线程块只能在一个SM上被调度。SM一般可以调度多个线程块,这要看SM本身的能力。那么有可能一个kernel的各个线程块被分配多个SM,所以grid只是逻辑层,而SM才是执行的物理层。SM采用的是SIMT (Single-Instruction, Multiple-Thread,单指令多线程)架构,基本的执行单元是线程束(warps),线程束包含32个线程,这些线程同时执行相同的指令,但是每个线程都包含自己的指令地址计数器和寄存器状态,也有自己独立的执行路径。所以尽管线程束中的线程同时从同一程序地址执行,但是可能具有不同的行为,比如遇到了分支结构,一些线程可能进入这个分支,但是另外一些有可能不执行,它们只能死等,因为GPU规定线程束中所有线程在同一周期执行相同的指令,线程束分化会导致性能下降。当线程块被划分到某个SM上时,它将进一步划分为多个线程束,因为这才是SM的基本执行单元,但是一个SM同时并发的线程束数是有限的。这是因为资源限制,SM要为每个线程块分配共享内存,而也要为每个线程束中的线程分配独立的寄存器。所以SM的配置会影响其所支持的线程块和线程束并发数量。总之,就是网格和线程块只是逻辑划分,一个kernel的所有线程其实在物理层是不一定同时并发的。所以kernel的grid和block的配置不同,性能会出现差异,这点是要特别注意的。还有,由于SM的基本执行单元是包含32个线程的线程束,所以block大小一般要设置为32的倍数。

在进行CUDA编程前,可以先检查一下自己的GPU的硬件配置,这样才可以有的放矢,可以通过下面的程序获得GPU的配置属性:

#include <iostream>
using namespace std;
 
__global__ void helloFromGPU ()
{
        printf("Hello, world! from GPU!\n");
}
 
int main()
{
        cudaDeviceReset();
        int dev = 0;
        cudaDeviceProp devProp;
        cudaGetDeviceProperties(&devProp, dev);
        std::cout << "使用GPU device " << dev << ": " << devProp.name << std::endl;
        std::cout << "SM的数量:" << devProp.multiProcessorCount << std::endl;
        std::cout << "每个线程块的共享内存大小:" << devProp.sharedMemPerBlock / 1024.0 << " KB" << std::endl;
        std::cout << "每个线程块的最大线程数:" << devProp.maxThreadsPerBlock << std::endl;
        std::cout << "每个EM的最大线程数:" << devProp.maxThreadsPerMultiProcessor << std::endl;
        std::cout << "每个SM的最大线程束数:" << devProp.maxThreadsPerMultiProcessor / 32 << std::endl;
        return 0;
}

    	// 输出如下
    	使用GPU device 0: NVIDIA GeForce GTX 1650
		SM的数量:16
		每个线程块的共享内存大小:48 KB
		每个线程块的最大线程数:1024
		每个EM的最大线程数:1024
		每个SM的最大线程束数:32

3 CUDA程序

kernel的这种线程组织结构天然适合vector,matrix等运算

1-dim结构实现两个向量的加法,每个线程负责处理每个位置的两个元素相加,代码如下所示。线程块大小为(256,1,1),然后将长度n的向量均分为不同的线程块来执行加法运算。

CUDA实现向量并行加法


#include <stdio.h>
#include <cuda.h>

#include "aux.h"

typedef float FLOAT;

/* host, add */
void vec_add_host(FLOAT *x, FLOAT *y, FLOAT *z, int N);

/* device function */
__global__ void vec_add(FLOAT *x, FLOAT *y, FLOAT *z, int N)
{
    /* 1D block */
    int idx = get_tid();

    if (idx < N) z[idx] = z[idx] + y[idx] + x[idx];
}

void vec_add_host(FLOAT *x, FLOAT *y, FLOAT *z, int N)
{
    int i;

    for (i = 0; i < N; i++) z[i] = z[i] + y[i] + x[i];
}

int main()
{
    int N = 20000000;
    int nbytes = N * sizeof(FLOAT);

    /* 1D block */
    int bs = 256;

    /* 2D grid */
    int s = ceil(sqrt((N + bs - 1.) / bs));
    dim3 grid = dim3(s, s);

    FLOAT *dx = NULL, *hx = NULL;
    FLOAT *dy = NULL, *hy = NULL;
    FLOAT *dz = NULL, *hz = NULL;

    int itr = 30;
    int i;
    double th, td;

    /* allocate GPU mem */
    cudaMalloc((void **)&dx, nbytes);
    cudaMalloc((void **)&dy, nbytes);
    cudaMalloc((void **)&dz, nbytes);

    if (dx == NULL || dy == NULL || dz == NULL) {
        printf("couldn't allocate GPU memory\n");
        return -1;
    }

    printf("allocated %.2f MB on GPU\n", nbytes / (1024.f * 1024.f));

    /* alllocate CPU mem */
    hx = (FLOAT *) malloc(nbytes);
    hy = (FLOAT *) malloc(nbytes);
    hz = (FLOAT *) malloc(nbytes);

    if (hx == NULL || hy == NULL || hz == NULL) {
        printf("couldn't allocate CPU memory\n");
        return -2;
    }
    printf("allocated %.2f MB on CPU\n", nbytes / (1024.f * 1024.f));

    /* init */
    for (i = 0; i < N; i++) {
        hx[i] = 1;
        hy[i] = 1;
        hz[i] = 1;
    }

    /* copy data to GPU */
    cudaMemcpy(dx, hx, nbytes, cudaMemcpyHostToDevice);
    cudaMemcpy(dy, hy, nbytes, cudaMemcpyHostToDevice);
    cudaMemcpy(dz, hz, nbytes, cudaMemcpyHostToDevice);

    /* call GPU */
    cudaDeviceSynchronize();
    td = get_time();
    
    for (i = 0; i < itr; i++) vec_add<<<grid, bs>>>(dx, dy, dz, N);

    cudaDeviceSynchronize();
    td = get_time() - td;

    /* CPU */
    th = get_time();
    for (i = 0; i < itr; i++) vec_add_host(hx, hy, hz, N);
    th = get_time() - th;

    printf("GPU time: %e, CPU time: %e, speedup: %g\n", td, th, th / td);

    cudaFree(dx);
    cudaFree(dy);
    cudaFree(dz);

    free(hx);
    free(hy);
    free(hz);

    return 0;
}

参考视频:https://www.bilibili.com/video/BV1vJ411D73S/?spm_id_from=333.999.0.0&vd_source=b2549fdee562c700f2b1f3f49065201b

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学习日记(XML 文件解析、检索、工厂设计模式、装饰设计模式)

文章目录学习日记&#xff08;XML 文件解析、检索、工厂设计模式、装饰设计模式&#xff09;一、XML 概述1. 概念2. XML 的创建及语法规则3. XML 文档约束方式二、XML 文件的解析技术1. 使用 Dom4j 解析 XML 文件2. XML 解析案例三、XML 文件的数据检索技术&#xff1a;XPath四…

Nacos 注册中心、配置文件中心的常用配置(springcloud)

Nacos Discovery Starter 更多的配置项 1.服务端地址 spring.cloud.nacos.discovery.server-addr 无 Nacos Server 启动监听的 ip 地址和端口2.服务名 spring.cloud.nacos.discovery.s ervice ${spring.application.name} 给当前的服务命名3.服务分组spring.cloud.nacos.disc…

Nuttx系统学习笔记(三)——使用Nuttx操作STM32F429外设

在上一篇&#xff0c;我们已经学会了如何将Nuttx进行烧录&#xff0c;以及学会了如何部署这个操作系统&#xff0c;接下来我们就要使用这个操作系统来实现我们对嵌入式设备的控制&#xff0c;当然也是从点灯开始的。这个基于Posix架构的操作系统使用起来是跟FreeRTOS那些操作系…

【微信开发第五章】SpringBoot实现微信分享

前言 在进行微信公众号业务开发的时候&#xff0c;有些时候需要用到微信分享&#xff0c;以卡片的形式分享出去&#xff0c;例如订单信息&#xff0c;用户信息。该篇文章每一步都有记录&#xff0c;力争理解的同时各位小伙伴也能够实现功能 效果图如下&#xff1a; 文章目录前…

centos 7.9 部署 harbor 镜像仓库实践

centos 7.9 harbor 部署镜像仓库 文章目录centos 7.9 harbor 部署镜像仓库1. 安装 docker1.1 配置 docker2. 安装 docker-compose3. 下载 harbor4. 定制配置文件 harbor.yml5. 配置证书5.1 生成证书颁发机构证书5.2 生成服务器证书5.3 向 Harbor 和 Docker 提供证书6. 部署 har…

【软件测试】一线大厂的测试开发基本技能,我不再想庸庸碌碌......

目录&#xff1a;导读一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结能在一线大厂工作是大多数人的目标&#xff0c;不仅薪酬高&#xf…

[Git] 系列四Push Pull —— Git 远程仓库和高级操作

[Git] 系列四Push & Pull —— Git 远程仓库和高级操作 Author: Xin Pan Date: 2022.11.06 git fetch git fetch 完成了仅有的但是很重要的两步: 从远程仓库下载本地仓库中缺失的提交记录更新远程分支指针(如 o/main) git fetch 并不会改变你本地仓库的状态。它不会更…

Github 注册

Github 注册 https://github.com/ 1. 简介 初始 Github 注册 填写注册信息 一开始注册github发现了这个问题&#xff0c;多试几次。或者换个浏览器&#xff0c;或者对octocaptcha做一个域名映射。 140.82.112.4 http://octocaptcha.com填写验证码 当注册成功后&#xff0…

【小专题】正交试验法设计测试用例

正交试验法设计测试用例 正交试验法设计工具&#xff1a; 1、正交设计助手 2、AllPairs 3、python&#xff08;本质是使用AllPairs&#xff09; 先看一个案例&#xff1a; 假设一个web系统&#xff0c;需要做兼容性测试&#xff0c;该系统兼容不同操作系统、数据库和web服…

【ESP8266与ESP12E 电机拓展板握手】

【ESP8266与ESP12E 电机拓展板握手】 1. 前言2. 材料3. 工作原理3.1 板载功能3.2 引脚接口定义图3.3 产品技术规格4. 方案4.1 普通马达接线图4.2 42步进电机接线图5. 相关代码5.1 直流电机的管理代码5.2 步进电机管理代码6. 应用7. 来源1. 前言 ESP12E Motor Shield 是一块扩展…

代码改成多线程,竟有 这些问题

文章目录CPU 使用率飙高事务问题导致服务挂掉CPU 使用率飙高 每条数据都有些业务逻辑&#xff0c;如果单线程导入所有的数据&#xff0c;导入效率会非常低。于是改成了多线程导入。 如果 excel 中有大量的数据&#xff0c;很可能会出现 CPU 使用率飙高的问题。 我们都知道&am…

公众号免费查题系统

公众号免费查题系统 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击…

8.for循环

循环控制语句 1.for循环 for (表达式1&#xff1b;表达式2&#xff1b;表达式3) { //复合语句&#xff0c;循环体 } 第一次进入循环的时候执行表达式1&#xff0c;表达式1只执行一次 表达式2是循环的条件&#xff0c;只有表达式2为真了&#xff0c;才执行循环体&#xff0c;也…

Android 应用模块的构建

Google文档说明&#xff1a;https://developer.android.com/studio/build?hlzh-cn Android 构建系统会编译应用资源和源代码&#xff0c;然后将它们打包成 APK 或 Android App Bundle 文件&#xff0c;供您测试、部署、签名和分发。Android Studio 使用高级构建工具包 Gradle …

Python学习----面向对象

面向对象思想就不说了&#xff0c;和java一样。这是为了自己能够一直看教程不走神做得笔记。 类的定义和赋初值 成员变量和成员方法&#xff0c;含义和java一样。 成员变量的申明没啥好记得 需要关注得是成员方法&#xff0c;在定义成员方法得时候&#xff0c;需要传入self关…

设计模式学习笔记 - 组合模式

设计模式学习笔记 - 组合模式一、学校院系展示问题二、传统方案解决学校院系展示问题三、组合模式介绍1、基本介绍2、组合模式原理四、组合模式解决学校院系展示问题五、组合模式在JDK集合的源码分析六、组合模式的注意事项和细节一、学校院系展示问题 展示一个学校院系结构&am…

机器学习可视化技术(Towards Data Science)

介绍 作为任何数据科学项目的一部分&#xff0c;数据可视化在了解更多可用数据和识别任何主要模式方面发挥着重要作用。 如果能够使分析中的机器学习部分尽可能直观&#xff0c;那不是很好吗&#xff1f; 在本文中&#xff0c;我们将探讨一些可以帮助我们应对这一挑战的技术&am…

HCIA 链路聚合与LACP

一、前言 虽然很多文章在介绍链路聚合时会从链路备份的角度来介绍链路聚合的作用&#xff0c;然后再说其有提升链路带宽的作用&#xff0c;但我感觉链路聚合主要还是提升链路带宽的作用&#xff0c;链路备份只是顺带的不是主要目标。 二、链路聚合 考虑下面的网络&#xff0…

Windows 环境下的 Socket 编程 3 - 基于 TCP 的服务器/客户端

基于 TCP 的服务器端/客户端 绝大多数 TCP 服务器端都按照如下顺序调用&#xff1a; 在 Windows 环境下&#xff0c;代码表示为&#xff1a; WSADATA wsaData;SOCKET hServSock, hClntSock;SOCKADDR_IN servAddr, clntAddr;int szClntAddr;/*Windows 环境 Socket 编程必须*/i…

leetcode:6243. 到达首都的最少油耗【变种子树大小统计 + 从边的角度出发 + 思维转换】

目录题目截图题目分析ac code总结题目截图 题目分析 0作为root从边的角度出发每个点都向0的方向聚集比如大家已经由四面八方聚集到了x&#xff0c;x的fa固定的&#xff0c;假设为y那么x到y要多少辆车&#xff1f;设size&#xff08;x&#xff09;为x子树大小x到y需要ceil&…