title: 【CUDA 基础】2.3 组织并行线程

categories:

  • CUDA
  • Freshman

    tags:
  • Thread
  • Block
  • Grid

    toc: true

    date: 2018-03-09 21:00:38



Abstract: 本文介绍CUDA模型中的线程组织模式

Keywords: Thread,Block,Grid

开篇废话

一天写两段废话也是有点累了,天天写废话,后面可以开个系列叫做废话。写一句吧,做研究别有民科精神就好,用自己的理论A证明自己的理论B,在用理论B证明理论A的这种循环证明,还坚持不懈的那种不可取。

2.0 CUDA编程模型中我们大概的介绍了CUDA编程的几个关键点,包括内存,kernel,以及今天我们要讲的线程组织形式,2.0中还介绍了每个线程的编号是依靠,块的坐标(blockIdx.x等),网格的大小(gridDim.x 等),线程编号(threadIdx.x等),线程的大小(tblockDim.x等)

这一篇我们就详细介绍每一个线程是怎么确定唯一的索引,然后建立并行计算,并且不同的线程组织形式是怎样影响性能的:

  • 二维网格二维线程块
  • 一维网格一维线程块
  • 二维网格一维线程块

使用块和线程建立矩阵索引

多线程的优点就是每个线程处理不同的数据计算,那么怎么分配好每个线程处理不同的数据,而不至于多个不同的线程处理同一个数据,或者避免不同的线程没有组织的乱访问内存。如果多线程不能按照组织合理的干活,那么就相当于一群没训练过的哈士奇拉雪橇,往不同的方向跑,那么是没办法前进的,必须有组织,有规则的计算才有意义。

我们的线程模型前面2.0中已经有个大概的介绍,但是下图可以非常形象的反应线程模型,不过注意硬件实际的执行和存储不是按照图中的模型来的,大家注意区分:



这里(ix,iy)就是整个线程模型中任意一个线程的索引,或者叫做全局地址,局部地址当然就是(threadIdx.x,threadIdx.y)了,当然这个局部地址目前还没有什么用处,他只能索引线程块内的线程,不同线程块中有相同的局部索引值,比如同一个小区,A栋有16楼,B栋也有16楼,A栋和B栋就是blockIdx,而16就是threadIdx啦

图中的横坐标就是:

ix=threadIdx.x+blockIdx.x×blockDim.x
ix=threadIdx.x+blockIdx.x \times blockDim.x
ix=threadIdx.x+blockIdx.x×blockDim.x

纵坐标是:

iy=threadIdx.y+blockIdx.y×blockDim.y
iy=threadIdx.y+blockIdx.y \times blockDim.y
iy=threadIdx.y+blockIdx.y×blockDim.y

这样我们就得到了每个线程的唯一标号,并且在运行时kernel是可以访问这个标号的。前面讲过CUDA每一个线程执行相同的代码,也就是异构计算中说的多线程单指令,如果每个不同的线程执行同样的代码,又处理同一组数据,将会得到多个相同的结果,显然这是没意义的,为了让不同线程处理不同的数据,CUDA常用的做法是让不同的线程对应不同的数据,也就是用线程的全局标号对应不同组的数据。

设备内存或者主机内存都是线性存在的,比如一个二维矩阵 (8×6)(8\times 6)(8×6),存储在内存中是这样的:

我们要做管理的就是:

  • 线程和块索引(来计算线程的全局索引)
  • 矩阵中给定点的坐标(ix,iy)
  • (ix,iy)对应的线性内存的位置

线性位置的计算方法是:

idx=ix+iy∗nx
idx=ix+iy*nx
idx=ix+iy∗nx

我们上面已经计算出了线程的全局坐标,用线程的全局坐标对应矩阵的坐标,也就是说,线程的坐标(ix,iy)对应矩阵中(ix,iy)的元素,这样就形成了一一对应,不同的线程处理矩阵中不同的数据,举个具体的例子,ix=10,iy=10的线程去处理矩阵中(10,10)的数据,当然你也可以设计别的对应模式,但是这种方法是最简单出错可能最低的。

我们接下来的代码来输出每个线程的标号信息:

#include <cuda_runtime.h>
#include <stdio.h>
#include "freshman.h" __global__ void printThreadIndex(float *A,const int nx,const int ny)
{
int ix=threadIdx.x+blockIdx.x*blockDim.x;
int iy=threadIdx.y+blockIdx.y*blockDim.y;
unsigned int idx=iy*nx+ix;
printf("thread_id(%d,%d) block_id(%d,%d) coordinate(%d,%d)"
"global index %2d ival %2d\n",threadIdx.x,threadIdx.y,
blockIdx.x,blockIdx.y,ix,iy,idx,A[idx]);
}
int main(int argc,char** argv)
{
initDevice(0);
int nx=8,ny=6;
int nxy=nx*ny;
int nBytes=nxy*sizeof(float); //Malloc
float* A_host=(float*)malloc(nBytes);
initialData(A_host,nxy);
printMatrix(A_host,nx,ny); //cudaMalloc
float *A_dev=NULL;
CHECK(cudaMalloc((void**)&A_dev,nBytes)); cudaMemcpy(A_dev,A_host,nBytes,cudaMemcpyHostToDevice); dim3 block(4,2);
dim3 grid((nx-1)/block.x+1,(ny-1)/block.y+1); printThreadIndex<<<grid,block>>>(A_dev,nx,ny); CHECK(cudaDeviceSynchronize());
cudaFree(A_dev);
free(A_host); cudaDeviceReset();
return 0;
}

这段代码输出了一组我们随机生成的矩阵,并且核函数打印自己的线程标号,注意,核函数能调用printf这个特性是CUDA后来加的,最早的版本里面不能printf,输出结果:



由于截图不完全,上面有一段打印信息没贴全,但是我们可以知道每一个线程已经对应到了不同的数据,接着我们就要用这个方法来进行计算了,最简单的当然就是二维矩阵加法啦。

二维矩阵加法

完整内容参考https://face2ai.com/CUDA-F-2-3-组织并行线程/

【CUDA 基础】2.3 组织并行线程的更多相关文章

  1. CUDA编程模型——组织并行线程2 (1D grid 1D block)

    在”组织并行编程1“中,通过组织并行线程为”2D grid 2D block“对矩阵求和,在本文中通过组织为 1D grid 1D block进行矩阵求和.一维网格和一维线程块的结构如下图: 其中,n ...

  2. 【CUDA 基础】3.2 理解线程束执行的本质(Part I)

    title: [CUDA 基础]3.2 理解线程束执行的本质(Part I) categories: CUDA Freshman tags: 线程束分化 CUDA分支 toc: true date: ...

  3. CUDA编程模型——组织并行线程3 (2D grid 1D block)

    当使用一个包含一维块的二维网格时,每个线程都只关注一个数据元素并且网格的第二个维数等于ny,如下图所示: 这可以看作是含有二维块的二维网格的特殊情况,其中块儿的第二个维数是1.因此,从块儿和线程索引到 ...

  4. 【CUDA 基础】3.6 动态并行

    title: [CUDA 基础]3.6 动态并行 categories: - CUDA - Freshman tags: - 动态并行 - 嵌套执行 - 隐式同步 toc: true date: 20 ...

  5. 《GPU高性能编程CUDA实战》第五章 线程并行

    ▶ 本章介绍了线程并行,并给出四个例子.长向量加法.波纹效果.点积和显示位图. ● 长向量加法(线程块并行 + 线程并行) #include <stdio.h> #include &quo ...

  6. 【CUDA 基础】5.6 线程束洗牌指令

    title: [CUDA 基础]5.6 线程束洗牌指令 categories: - CUDA - Freshman tags: - 线程束洗牌指令 toc: true date: 2018-06-06 ...

  7. CUDA基础介绍

    一.GPU简介 1985年8月20日ATi公司成立,同年10月ATi使用ASIC技术开发出了第一款图形芯片和图形卡,1992年4月ATi发布了Mach32图形卡集成了图形加速功能,1998年4月ATi ...

  8. 【CUDA 基础】5.3 减少全局内存访问

    title: [CUDA 基础]5.3 减少全局内存访问 categories: - CUDA - Freshman tags: - 共享内存 - 归约 toc: true date: 2018-06 ...

  9. 【CUDA 基础】5.2 共享内存的数据布局

    title: [CUDA 基础]5.2 共享内存的数据布局 categories: - CUDA - Freshman tags: - 行主序 - 列主序 toc: true date: 2018-0 ...

随机推荐

  1. 【sublime Text】关闭sublime的更新提醒和激活提醒

    下载了原版的sublime Text,未激活的,每次启动都会提醒要去更新么?需要激活吧 ? 超级烦.[谁让没有激活呢?] 那没办法 ,激活吧! Help ---- Enter License--> ...

  2. 4-Perl 数据类型

    1.Perl 数据类型Perl 是一种弱类型语言,所以变量不需要指定类型,Perl 解释器会根据上下文自动选择匹配类型.Perl 有三个基本的数据类型:标量.数组.哈希.以下是这三种数据类型的说明:1 ...

  3. 你不知道的css各类布局(二)之流体布局、液体布局、栅格布局

    流体布局 什么是流 在谈论流体布局之前我们需要知道一件事情就是何为“流”,所谓“流”就是“文档流”,是css中的一种基本定位和布局 概念 流体布局(Liquid/Fluid Layout)指的是利用元 ...

  4. [转载]python with语句的用法

    https://www.cnblogs.com/DswCnblog/p/6126588.html 看这篇文章的时候看到了python的类名()用法,很好奇,上网查了下,原来这就相当于对类进行实例化了. ...

  5. centos7配置rsync+inotify数据实时共享

    关于centos7版本上面搭建rsync服务并且实现实时同步之前一直是在6版本上面搭建rsync服务,在7版本上面折腾了半天.此处总结下inotify下载地址:http://github.com/do ...

  6. Flutter-动画-原理篇

    一.动画关键类的源码分析 1.Animation Animation没有做什么与动画有关的事情,它只是记录了动画的“状态”.当前的“值”和一些注册回调接口的方法. abstract class Ani ...

  7. 第十五章、线程之queue模块的各种队列

    目录 第十五章.线程之queue模块的各种队列 一.Queue 二.LifoQueue堆栈 三.PriorityQueue优先级队列 第十五章.线程之queue模块的各种队列 一.Queue impo ...

  8. 提升Scrapy框架爬取数据效率的五种方式

    1.增加并发线程开启数量 settings配置文件中,修改CONCURRENT_REQUESTS = 100,默认为32,可适当增加: 2.降低日志级别 运行scrapy时会产生大量日志占用CPU,为 ...

  9. 解决办法:Message: 对实体 "useUnicode" 的引用必须以 ';' 分隔符结尾

    Hibernate 5.3.1 INFO: HHH000206: hibernate.properties not foundException in thread "main" ...

  10. linux命令详解——xargs

    1. 简介 之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了xargs命令,例如: find /sbin -perm +700 |ls -l     ...