当前位置:   article > 正文

CUDA入门

CUDA入门

CUDA编程入门

CUDA C 编程权威指南 电子书链接: CUDA C
提取码:n58h
CUDA编程入门

1.什么是GPU

1.1 GPU架构

image-20220919161456048

1.2 GPU架构

image-20220919161749583

image-20220919161900226

1.3 什么是GPU计算

GPU一般作为一个运算的平台,一般时候需要与CPU进行合作,CPU在控制整个的架构和逻辑,GPU作为一个协同计算的模块,可以加速运行。

image-20220919162438670

1.4 为什么使用GPU计算

image-20220919162612279

1.5 CPU和GPU的分工协作

image-20220919162904790

1.6 GPU 计算架构

image-20220919163011495

1.7 程序架构

image-20220919163210098

一个CUDA程序的运行,其实是CPU和GPU协作的结果:

CPU做一些初始化/逻辑控制的工作=>某个模块的计算两特别大适合并行计算时,将计算任务放在GPU上运行=>CPU对于GPU运算的结果会进行重新的加工=>处理完成之后又接着使用GPU做运算。

1.8 语言选择和编译器

image-20220919163655516

image-20220919163734866

image-20220919163805249

2. GPU硬件架构

2.1 GT200 例子

image-20220919164307944

专业术语:SPA(streaming Processor Array) 所有的流处理器成为一个流处理器阵列

TPC/GPC(Texture(Graphics) Processor Cluster):将多个流多处理器分成一个小组

SM(Streaming Mutiprocessir 32SP):设计程序的块,有自己的内存单元,作业调度单元

SP(CUDA Core):流多处理器含有32个或者更多的SP流处理器

image-20220919165455549

每个块中含有的线程数不能太多,1-512,现在最多能做到2048,并且每32个线程共享一个Wrap Scheduler

2.2 硬件架构

image-20220919165806029

首先将所有的进程组成称为一个网格,网格之内分为不同的块,每个块中含有的线程数不能太少也不能太多,如果太大如2048个线程,那么每个块需要给线程提供的内存空间和资源就太多,块的开销就比较大。但是如果太小的话,不足以隐藏其调度的开销。

因为每32个线程组成一个warp,所以块内的线程数最好是32的倍数

每个块都独立的在SM中运行,不能够跨SM运行

image-20220919170503172

每32个线程可以组成一个warps, Warp Scheduler可以根据优先级,安排某个Warp内的线程执行

image-20220919170634474

warp的切换是没有开销的,它依赖于硬件的调度器以及算法的判断,那个warp可以执行了就将其放入可执行的队列中去,按照优先级对其进行执行。

image-20220919171154813

GPU的速度非常快,很适合做计算加速处理

image-20220919171126327

在GPU中,每个线程有自己的局部内存,而同一个块中,线程可以通过共享内存进行消息传递。

全局内存:被所有的线程共享。

因为共享内存针对于块,使用共享内存一般能比使用全局内存快两个数量级。但是共享内存是比较小的,

其和L1 cache加起来才64字节,但是全局内存可以达到8G,16G,64G甚至更多

image-20220919171423406

在块内的线程可以共享数据或者结果,所以块内的线程有一些共享合作的机制

image-20220919171832357

在设计程序时,一个块可能需要2k或者3k的寄存器。如果一个blocks需要4k寄存器,而一共有64k的寄存器,那么最多可以有16个block在同时运行

image-20220919172038936

共享内存的访问:共享内存分多个banks。如果半个warp中的每个线程只访问一个bank,并且没有多个线程访问一个bank的话,一次内存访问就可以将所有的数据拿到。如果有多个线程访问同一个bank的话,就会产生冲突,那么这个访问会变成顺序执行的,这样使得本来一个时钟周期能够完成的事情需要多个时钟周期来完成

image-20220919172338695

  • 举例:线程之间访问没有冲突

image-20220919172759234

  • 举例:线程之间访问有冲突

    image-20220919172825718

    在使用GPU的共享内存时,要尽量保证线程之间没有bank冲突

image-20220919172908698

  • 举例:求序列和

    这时候,两个线程之间通过共享内存通信可能会存在冲突

    image-20220919173200568

  • 因为每半个warp访问一个banks共享内存组,这样可以保证没有冲突

    image-20220919173341129

3. GPU编程模型

3.1 GPU计算基础知识

CPU的核心较少,一般用于做逻辑控制,GPU的核心数多,用于做计算加速器。

image-20220919173555632

3.2 CUDA程序执行流程

image-20220919173820669

3.3 CUDA程序

image-20220919173926944

3.4 CUDA代码实例

image-20220919174652128

3.5 GPU程序层次结构

image-20220919174740335

image-20220919174952266

3.6 CUDA程序调用

image-20220919175123401

3.7 区分host的device代码

image-20220919175449245

image-20220919182057316

image-20220919182444674

4. 详细介绍grip warp,block,thread

image-20220920120529752

image-20220920120836742

grip/block都可以是一维、二维、或者三维的

image-20220920121533343

image-20220920121935234

5.GPU内存

image-20220920122155249

  • GPU和CPU速度对比

    image-20220920122229396

  • 可编程内存

image-20220920122334995

  • 内存作用域和生命周期

image-20220920122426815

  • 寄存器

    尽量少使用寄存器可以使得处于活动状态的块比较多

    image-20220920122602635

  • 本地内存

    image-20220920122704669

  • 共享内存

    image-20220920122735102

image-20220920122852606

  • 共享内存的访问冲突

image-20220920122959616

image-20220920123101811

image-20220920123124793

  • 常量内存

    image-20220920123225818

  • 纹理内存

    image-20220920123309447

  • 全局内存

    image-20220920123331128

  • 全局内存的访问

    image-20220920123433624

  • GPU缓存

    image-20220920123535582

6.GPU内存管理

  • 内存使用

image-20220920142134240

  • cpu内存

    image-20220920142238213

  • gpu内存

    image-20220920142300648

  • GPU全局内存分配释放

image-20220920142338907

  • Host内存分配和释放

    image-20220920142504988

  • 统一内存的分配与释放 一般是比较新的GPU才能申请统一内存

    image-20220920142641488

  • 同步拷贝 只有当拷贝这个操作完成之时,才能够继续进行

image-20220920142815683

  • 异步拷贝,拷贝命令下达之后不等待,有一个问题就是不知道拷贝是否完成,如果数据从A->B,此时若读取B中的数据,有可能拷贝没有完成得到的是之前的数据。需要做一些检测来获知拷贝是否完成

    image-20220920143058167

  • 共享内存

    image-20220920143409212

image-20220920143601373

7.GPU内存使用代码解析

image-20220920143652032

8.CPU程序架构以及硬件映射

image-20220920144508406

  • 流式多处理器

    image-20220920144606981

  • CUDA内置变量

    image-20220920145207683

  • WARP技术细节

image-20220920145356974

  • 性能优化

    image-20220920145652304

9.规约算法

image-20220920150137513

  • 对于这种累加求和的方式,如果平常使用for循环的算法计算,时间复杂度为O(n)。

    image-20220920150302018

  • 如果采用并行的计算方法,利用二叉树的方法进行并行。时间复杂度自然为log2N

    image-20220920150352942

  • 如果是4个一组进行并行,时间复杂度可以答案log4N

image-20220920150553705

  • 在GPU中如果只使用一个块,可以进行并行操作,但是会有很多SM块空闲。所以使用多个块,每个块负责一小部分,然后对块的结果再进行加和。

image-20220920150758176

  • 需要一个同步的操作,各个块得到自己的一个局部的规约结果,经过全局的同步,将其规约结果相加或者相乘。

    image-20220920150933028

image-20220920150959145

  • CUDA是不支持全局同步的,因为全局同步块处于空闲状态,造成资源浪费

image-20220920151334782

image-20220920151923765

image-20220920152054000

  • CUDA的解决方案 将其分成多个Kernal来进行计算

    image-20220920152304758

image-20220920152331092

9.1 并行规约算法 二叉树

image-20220920152556680

image-20220920152628140

  • 实现向量相加的伪代码

    image-20220920152936029

  • 从算法的带宽来看,他的带块是2.083G每秒,说明其效果不是很理想

image-20220920153039374

  • 由于tid%2==0时候才进行相邻元素相加的操作,会导致同一个warp里的线程运行不同的命令,因此提出以下改进方法。

image-20220920153801398

image-20220920154041757

image-20220920154107182

9.2 并行规约算法 改进共享内存访问

  • 不将相邻的元素访问,跨一个bank的距离进行访问

image-20220920154505700

image-20220920154610839

  • 运行效率对比

image-20220920154705123

9.3改进全局内存访问

  • 第一次将两个数据相加到共享内存

image-20220920155132080

  • 优化后运行性能对比

image-20220920155159678

9.4 warp内循环展开

一些内存读取,存储的操作,循环变量的循环,同步,寻址操作,使得算法没有达到理论最优

image-20220921143936397

image-20220921144337698

  • 算法改进

image-20220921144602288

image-20220921144627312

9.5 完全循环展开

image-20220921145139576

image-20220921145224336

image-20220921145235917

image-20220921145354189

image-20220921145458114

注意 编程是共享内存需要用volatile修饰,以免编译器的自动优化使得最后得出的结果是错误的

volatile是一个特征修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

image-20220921150330186

10.CUDA:高性能计算

image-20220921150844852

GPU的线程是轻量级的,一般只需要完成一些非常简单的操作加减或者乘除,同时GPU的线程数量是非常庞大的

image-20220921151002051

image-20220921151205135

image-20220921151248349

image-20220921151420627

image-20220921151520319

image-20220921151634974

11.并行规约算法 内积

image-20220921152309418

image-20220921152359307

12. CUDA程序优化

  • 最大化并行执行

    image-20220921154246246

image-20220921154524245

image-20220921154703396

  • 最大化 occupancy=正在运行的warps数/最大的warps数

image-20220921155025074

image-20220921155819978

  • 内存优化

  • 在写程序是使用更多的计算,而不是内存访问

    image-20220921162138788

image-20220921171723049

image-20220921171828996

image-20220921172013926

image-20220921172337310

image-20220921173038421

  • t1和t2乱序,无法并行访问/t0对应的不是16的倍数,也无法并行访问

image-20220921173102204

image-20220921173354382

image-20220921173456070

image-20220921173712500

image-20220921173852622

image-20220921173908206

image-20220921173926363

  • 尽量使用一些高通量的指令,而不使用一些低通量的指令

  • 只有在需要的时候才使用一些高精度的函数

  • 尽量不要使warps中的指令不一样

    image-20220921174133492

  • 不同指令的指令周期是不一样的

image-20220921174341339

image-20220921174702381

image-20220921174711178

image-20220921174908873

image-20220921175121079

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/519544
推荐阅读
相关标签
  

闽ICP备14008679号