一、CUDA异构计算基础

1.CUDA简介

CUDA(Compute Unified Device Architecture),是一种基于C/C++的编程方法,支持异构编程的扩展方法,提供了简单明了的APIs,能够轻松的管理存储系统。目前CUDA支持的编程语言包括C/C++/Python/Java/Fortran等。

2.CUDA生态


上图从上到下依次为:基于GPU计算的应用程序,一些GPU计算的库与中间件,编程语言,不同架构的GPU设备。

3.CUDA并行计算模式

并行计算即是同时应用多个计算资源解决一个可以并行处理计算问题。
关键点有两个:一是拥有多个计算资源或处理器,二是一个大问题可以拆分为多个离散的部分同时进行

4.术语:

当今的显卡拥有强大的计算能力,它有自己的计算核心和内存(在显卡中的内存称为现存),为了对CPU计算资源和GPU计算资源进行区分,异构计算中给出以下术语。
host:指的是CPU和主机内存(host memory)
device:指的是GPU和显存(device memory)
kernels:核函数,是一个由CPU发起的在GPU上执行的函数。
device function:只能由GPU调用的GPU上执行的函数

5.CPU结构


上图是一个8核心CPU内部结构图,可以看到核心(Core)所占的芯片的面积大概只有三分之一,更多的空间被用于作为存储(高速缓存Cache和内存控制器等)。

6、GPU结构

GPU结构则与CPU的设计理念有所不同,下图为GPU结构示意图,可以看到GPU中的计算核(图中深绿色部分)明显增多且数量巨大(实际的核心比示意图更多,多达上千个)。

可以看到计算单元又被划分到不同的SM(stream multi-processor,流多处理器) 中,其中又包括了处理不同数据的核,有用于双精度浮点型计算的FP64,整数型计算的INT,单精度计算的FP32以及用于深度学习的TENSOR CORE以及计算之外寄存器和缓存等。

7.计算流程

①首先需要将数据从主机内存复制到GPU显存,因为CPU只能访问主机内存,GPU只能访问显存,因此需要并行计算的任务数据需要从host 拷贝到device。

②然后,CPU会下发指令给GPU,使其加载GPU程序并进行执行

③GPU程序执行完毕后,将显存中的结果从device拷贝回主机内存中,再进行其他的操作(保存,打印,展示等)。

上面三步便是GPU编程的基本步骤,可以总结出一个简单的异构计算流程如下:先顺序执行CPU代码,遇到耗时的可以并行执行的程序时,采用GPU并行编程,然后回到CPU顺序执行处理逻辑,如此反复进行。

8.CUDA的线程层次

之前都是根据硬件的物理结构进行讨论的,接下来再对编程模型上CUDA的线程层次进行描述。CUDA将线程进行了二级分组,从大到小依次为Grid,Block,Thread。一个Grid包含多个Block,一个Block包含多个Thread。Grid与Block均可以是二维的或者三维的,一个二维的Grid和Block如图所示:

在默认情况下,执行一个GPU并行计算任务时,一个Thread对应于一个Thread Processor,即硬件里面的一个处理器核心。一个Block会对应一个SM单元。一个Grid则对应了一个GPU设备,如下所示:

9.CUDA存储结构

下图为一个Grid(对应一个物理GPU设备)的存储结构示意图。图中的Global Memory(全局内存),Constant Memory(常量内存)与Texture Memory(纹理内存)都属于显存的一部分,与Host主机内存相对,在每一个Block之中又有Shared Memory(共享内存)提供给该Block所有线程的共享内存。每一个Thread(线程)又拥有自己的寄存器组(Registers)和本地内存(Local Memory)。

二、CUDA图像处理简单例子

1.环境需求

  • python3.6+
  • numba
  • py-opencv

2.示例代码

import cv2
from numba import cuda
import time
import math# GPU function
@cuda.jit()
def process_gpu(img):tx = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.xty = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.yfor channel in range(3):color = img[tx, ty][channel] * 2.0 + 30if color > 255:img[tx, ty][channel] = 255elif color < 0:img[tx, ty][channel] = 0else:img[tx, ty][channel] = color# CPU function
def process_cpu(img, dst):height, width, channels = img.shapefor h in range(height):for w in range(width):for c in range(channels):color = img[h, w][c] * 2.0 + 30if color > 255:dst[h, w][c] = 255elif color < 0:dst[h, w][c] = 0else:dst[h, w][c] = colorif __name__ == '__main__':img = cv2.imread("../image.jpg")height, width, channels = img.shapedst_cpu = img.copy()start_cpu = time.time()process_cpu(img, dst_cpu)end_cpu = time.time()time_cpu = (end_cpu - start_cpu)print("CPU process time: " + str(time_cpu))##GPU functiondImg = cuda.to_device(img)threadsperblock = (32, 32)blockspergrid_x = int(math.ceil(height / threadsperblock[0]))blockspergrid_y = int(math.ceil(width / threadsperblock[1]))blockspergrid = (blockspergrid_x, blockspergrid_y)cuda.synchronize()start_gpu = time.time()process_gpu[blockspergrid, threadsperblock](dImg)end_gpu = time.time()cuda.synchronize()time_gpu = (end_gpu - start_gpu)print("GPU process time: " + str(time_gpu))dst_gpu = dImg.copy_to_host()# savecv2.imwrite("result_cpu.jpg", dst_cpu)cv2.imwrite("result_gpu.jpg", dst_gpu)print("Done.")

3.代码解读

为了方便查看,再将线程层次图放在这里:
示例程序主要读入图片并进行亮度变换,算法为针对图片的每一个像素点的每一个通道的值都乘以2加上30。因为每一个像素都是独立的,所以该任务可以拆分为并行执行的任务。GPU部分,需要根据线程模型给每一个GPU处理核心分配任务。首先使用cuda.to_device(img) 将图片从主机内存拷贝到显存。然后,分配一个Block有(32,32)即1024个线程。然后根据图像的宽高和刚刚定义的Block大小,来确定Grid中分为多少个Block。

#GPU function
dImg = cuda.to_device(img)
threadsperblock = (32, 32)
blockspergrid_x = int(math.ceil(height / threadsperblock[0]))
blockspergrid_y = int(math.ceil(width / threadsperblock[1]))
blockspergrid = (blockspergrid_x, blockspergrid_y)

准备好以后,便通过调用核函数(Kernel function)来执行GPU程序,需要指定上方代码计算出的threadsperblock和blockspergrid :

    process_gpu[blockspergrid, threadsperblock](dImg)

关于核函数(即GPU执行的函数)的定义,需要在函数上方写上 @cuda.jit() 注解,用于标识其为核函数。核函数内部可以根据定义的block大小和grid大小将计算任务分配到不同的计算单元上。根据cuda.blockIdx.x获取块ID的横坐标,乘以块的横坐标维度(本例为32)再加上线程ID的横坐标,即可唯一确定图像上的一个横坐标点。纵坐标的表示类似(这一块的实现类似于如何将二维数组转化为一维数组进行表示,可以参考相关内容进行理解)。

def process_gpu(img):tx = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.xty = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.yfor channel in range(3):color = img[tx, ty][channel] * 2.0 + 30if color > 255:img[tx, ty][channel] = 255elif color < 0:img[tx, ty][channel] = 0else:img[tx, ty][channel] = color

4.结果展示

运行本程序,可以看到GPU处理速度比CPU快很多,对比结果如下:

PS:上述实验结果为R7 1700X与RTX 2070对比。
原图为988 x 988如下:

CPU与GPU处理获得相同的结果如下:

参考

https://images.nvidia.cn/cn/webinars/2020/jun09/download-358293.pdf
https://info.nvidia.com/358293-ondemand.html

CUDA PYTHON 并行计算基础相关推荐

  1. Python机器学习基础篇二《监督学习》

    前言 前期回顾: Python机器学习基础篇一<为什么用Python进行机器学习> 前面说过,监督学习是最常用也是最成功的机器学习类型之一.本章将会详细介绍监督学 习,并解释几种常用的监督 ...

  2. 【Python并行计算】- Dask 让你的python更快更强

    [Python并行计算]- Dask 让你的python更快更强 Dask(https://docs.dask.org/en/latest/) 是一个灵活的python并行/分布式计算的框架. 类似的 ...

  3. python并行计算|pycuda测试、对比及分析

    python并行计算|pycuda测试.对比及分析 增量式学习算法能够同时学习网络的节点与参数,但是随着模型结构的增长,计算成本也越来越高,有两个途径可以减少计算所需的时间成本:(1)研究模型划分方法 ...

  4. 【python语言基础】疑难点整理2

    [python语言基础]疑难点整理1 第五章 在python语法中,循环体中的语句没有做限制,因此,可以是任何合法语句,当然也可以是循环语句.这样就形成了循环语句的嵌套. while循环语句和for循 ...

  5. Python入门基础教程 Working with Python – Introductory Level

    学会像计算机科学家一样用世界上最流行的编程语言之一思考 你会学到: 学习Python的基础知识,Python是当今最流行的编程语言之一 通过编写一个基于文本的冒险游戏来学习Python语言的语法 了解 ...

  6. python数据分析基础 余本国_Python数据分析基础

    本书根据作者多年教学经验编写, 条理清楚, 内容深浅适中, 尽量让读者从实例出发, 结合课后练习, 少走弯路.本书涉及的内容主要包括Python数据类型与运算.流程控制及函数与类.Pandas库的数据 ...

  7. Python培训基础教程都教哪些

    根据相关数据统计,目前学习Python技术的同学大多数是零基础,都是从其他行业转型来学习的,那么Python培训基础教程都教哪些呢?好不好学呢?来看看下面的详细介绍. Python培训基础教程都教哪些 ...

  8. Python零基础自学会有哪些弊端

    Python在人工智能领域的发展前景非常好,很多人都想要学习Python技术,有一些小伙伴会选择通过自学来学习,但是如果是零基础,自学的话一定要注意这些弊端,下面就为大家详细的介绍一下Python零基 ...

  9. 【python教程入门学习】Python零基础入门爬虫项目

    Python入门爬虫项目 这是我的第一个python项目,分享给大家. 需求 我们目前正在开发一款产品其功能大致是:用户收到短信如:购买了电影票或者火车票机票之类的事件.然后app读取短信,解析短信, ...

最新文章

  1. 【牛腩新闻发布系统】开发前的准备01
  2. 华为机考HJ7取近似值
  3. 初创企业购买企业邮箱_支持#NetNeutrality =支持设计师及其创建的初创企业
  4. php7 v8js,Centos 7PHP7.0 安装V8JS扩展几乎都能安装成功
  5. java stringbuffer长度_java – 具有极大变化长度的输入的最佳StringBuffer初始容量是多少?...
  6. CentOS 6.5 使用docker 容器
  7. 无招胜有招之Java进阶JVM(三)内存模型
  8. Opportunity creation case in Firebug
  9. python re 简单实例_python的re模块应用实例
  10. 全新 HTML 5.1 工作草案发布
  11. 使用工具连接linux中的mysql8.0
  12. 小米手机与win10连接
  13. django官方文档3.0学习笔记 02
  14. Android自己动手打造XML解析框架
  15. 外卖行业现状分析_2020年中国外卖行业市场现状和发展趋势分析 外卖下沉趋势明显「组图」...
  16. nividia-smi命令不显示占用显卡的进程
  17. SAP 登录的一些参数
  18. 【刷题】BZOJ 2069 [POI2004]ZAW
  19. 最接地气的Android面试总结心得
  20. [NOIP2017模拟]豆豆游戏

热门文章

  1. 2022-2028全球与中国聚对苯二甲酸丁二酯(PBT)市场现状及未来发展趋势
  2. 经典养生:百会穴实乃“百岁穴”
  3. 前端常见面试基础问题
  4. win10电脑任务栏底部设置成正方形的小图标
  5. 雷霆战舰 游戏简化版
  6. 问题一:使用foreach遍历字符串
  7. C++中嵌入ie浏览器
  8. gma3600显卡linux,Intel GMA 3600 (简体中文)
  9. 富人收割穷人的100种方法
  10. 医学科研课题设计的分类