Thread如何划分为Warp?

2024-09-09 07:04
文章标签 划分 thread warp

本文主要是介绍Thread如何划分为Warp?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1 .Thread如何划分为Warp? https://jielahou.com/code/cuda/thread-to-warp.html

 Thread Index和Thread ID之间有什么关系呢?(线程架构参考这里:CUDA C++ Programming Guide (nvidia.com)open in new window)

  • 1维的Thread Index,其Thread ID就是Thread Index

  • 2维的Thread Index,其Thread ID为tx + ty * DX

  • 3维的Thread Index,其Thread ID为tx + ty * DX + tz * DX * DY

由此再回到本文的问题:Thread如何划分为Warp?

  • 对于1维的Thread Index,直接32个为一组划分(e.g. 0~3132~6364~95...)
  • 对于2维的Thread Index,先按照x分,然后再按照y分(e.g. 假设Thread Block大小为[dx]16*[dy]32,那么(0,0),(1,0)...(14,0),(15,0),(0,1),(1,1)...(14,1),(15,1)是一个warp内的)
  • 对于3维的Thread Index,先按照x分,然后再按照y分,最后按照z分(例子略)

2. CUDA ---- 线程配置 

前言

线程的组织形式对程序的性能影响是至关重要的,本篇博文主要以下面一种情况来介绍线程组织形式:

  • 2D grid 2D block

线程索引

矩阵在memory中是row-major线性存储的:

 

在kernel里,线程的唯一索引非常有用,为了确定一个线程的索引,我们以2D为例:

  • 线程和block索引
  • 矩阵中元素坐标
  • 线性global memory 的偏移

首先可以将thread和block索引映射到矩阵坐标:

ix = threadIdx.x + blockIdx.x * blockDim.x

iy = threadIdx.y + blockIdx.y * blockDim.y

之后可以利用上述变量计算线性地址:

idx = iy * nx + ix

 

上图展示了block和thread索引,矩阵坐标以及线性地址之间的关系,谨记,相邻的thread拥有连续的threadIdx.x,也就是索引为(0,0)(1,0)(2,0)(3,0)...的thread连续,而不是(0,0)(0,1)(0,2)(0,3)...连续,跟我们线代里玩矩阵的时候不一样。

现在可以验证出下面的关系:

thread_id(2,1)block_id(1,0) coordinate(6,1) global index 14 ival 14

下图显示了三者之间的关系:

 

 

 

3、CUDA编程:深入理解GPU中的并行机制(八) - 知乎 (zhihu.com)

4、CUDA ---- Warp解析 - 苹果妖 - 博客园 (cnblogs.com)

Warp Divergence

控制流语句普遍存在于各种编程语言中,GPU支持传统的,C-style,显式控制流结构,例如if…else,for,while等等。

CPU有复杂的硬件设计可以很好的做分支预测,即预测应用程序会走哪个path。如果预测正确,那么CPU只会有很小的消耗。和CPU对比来说,GPU就没那么复杂的分支预测了(CPU和GPU这方面的差异的原因不是我们关心的,了解就好,我们关心的是由这差异引起的问题)。

这样我们的问题就来了,因为所有同一个warp中的thread必须执行相同的指令,那么如果这些线程在遇到控制流语句时,如果进入不同的分支,那么同一时刻除了正在执行的分之外,其余分支都被阻塞了,十分影响性能。这类问题就是warp divergence。

请注意,warp divergence问题只会发生在同一个warp中。

下图展示了warp divergence问题:

Latency Hiding

指令从开始到结束消耗的clock cycle称为指令的latency。当每个cycle都有eligible warp被调度时,计算资源就会得到充分利用,基于此,我们就可以将每个指令的latency隐藏于issue其它warp的指令的过程中。

和CPU编程相比,latency hiding对GPU非常重要。CPU cores被设计成可以最小化一到两个thread的latency,但是GPU的thread数目可不是一个两个那么简单。

当涉及到指令latency时,指令可以被区分为下面两种:

  1. Arithmetic instruction
  2. Memory instruction

顾名思义,Arithmetic  instruction latency是一个算数操作的始末间隔。另一个则是指load或store的始末间隔。二者的latency大约为:

  1. 10-20 cycle for arithmetic operations
  2. 400-800 cycles for global memory accesses

下图是一个简单的执行流程,当warp0阻塞时,执行其他的warp,当warp变为eligible时从新执行。

你可能想要知道怎样评估active warps 的数量来hide latency。Little’s Law可以提供一个合理的估计:

 

对于Arithmetic operations来说,并行性可以表达为用来hide  Arithmetic latency的操作的数目。下表显示了Fermi和Kepler相关数据,这里是以(a + b * c)作为操作的例子。不同的算数指令,throughput(吞吐)也是不同的。

这里的throughput定义为每个SM每个cycle的操作数目。由于每个warp执行同一种指令,因此每个warp对应32个操作。所以,对于Fermi来说,每个SM需要640/32=20个warp来保持计算资源的充分利用。这也就意味着,arithmetic operations的并行性可以表达为操作的数目或者warp的数目。二者的关系也对应了两种方式来增加并行性:

  1. Instruction-level Parallelism(ILP):同一个thread中更多的独立指令
  2. Thread-level Parallelism (TLP):更多并发的eligible threads

对于Memory operations,并行性可以表达为每个cycle的byte数目。

因为memory throughput总是以GB/Sec为单位,我们需要先作相应的转化。可以通过下面的指令来查看device的memory frequency:

$ nvidia-smi -a -q -d CLOCK | fgrep -A 3 "Max Clocks" | fgrep "Memory"

以Fermi为例,其memory frequency可能是1.566GHz,Kepler的是1.6GHz。那么转化过程为:

 

乘上这个92可以得到上图中的74,这里的数字是针对整个device的,而不是每个SM。

有了这些数据,我们可以做一些计算了,以Fermi为例,假设每个thread的任务是将一个float(4 bytes)类型的数据从global memory移至SM用来计算,你应该需要大约18500个thread,也就是579个warp来隐藏所有的memory latency。

 

Fermi有16个SM,所以每个SM需要579/16=36个warp来隐藏memory latency。

Occupancy

当一个warp阻塞了,SM会执行另一个eligible warp。理想情况是,每时每刻到保证cores被占用。Occupancy就是每个SM的active warp占最大warp数目的比例:

 

我们可以使用的device篇提到的方法来获取warp最大数目:

cudaError_t cudaGetDeviceProperties(struct cudaDeviceProp *prop, int device);

然后用maxThreadsPerMultiProcessor来获取具体数值。

grid和block的配置准则:

  • 保证block中thrad数目是32的倍数。
  • 避免block太小:每个blcok最少128或256个thread。
  • 根据kernel需要的资源调整block。
  • 保证block的数目远大于SM的数目。
  • 多做实验来挖掘出最好的配置。

Occupancy专注于每个SM中可以并行的thread或者warp的数目。不管怎样,Occupancy不是唯一的性能指标,Occupancy达到当某个值是,再做优化就可能不在有效果了,还有许多其它的指标需要调节,我们会在之后的博文继续探讨。

Synchronize

同步是并行编程的一个普遍的问题。在CUDA的世界里,有两种方式实现同步:

  1. System-level:等待所有host和device的工作完成
  2. Block-level:等待device中block的所有thread执行到某个点

因为CUDA API和host代码是异步的,cudaDeviceSynchronize可以用来停住CUP等待CUDA中的操作完成:

cudaError_t cudaDeviceSynchronize(void);

因为block中的thread执行顺序不定,CUDA提供了一个function来同步block中的thread。

__device__ void __syncthreads(void);

当该函数被调用,block中的每个thread都会等待所有其他thread执行到某个点来实现同步。

这篇关于Thread如何划分为Warp?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1150479

相关文章

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

RT-Thread(Nano版本)的快速移植(基于NUCLEO-F446RE)

目录 概述 1 RT-Thread 1.1 RT-Thread的版本  1.2 认识Nano版本 2 STM32F446U上移植RT-Thread  2.1 STM32Cube创建工程 2.2 移植RT-Thread 2.2.1 安装RT-Thread Packet  2.2.2 加载RT-Thread 2.2.3 匹配相关接口 2.2.3.1 初次编译代码  2.2.3.

【电子通识】洁净度等级划分及等级标准

洁净度常用于评估半导体、生物制药、医疗、实验室及科研院所、新能源等领域的洁净室、无尘室或者无菌室等环境。         一般来说,晶圆光刻、制造、测试等级为100级或1000级的洁净间,百级洁净间要求空气中0.5微米的尘埃粒子数不得超过每立方米3520个;等级为1000级的洁净间要求0.5微米的尘埃粒子数不得超过每立方米35200个。         晶圆切割或封装工序一

洛谷 凸多边形划分

T282062 凸多边形的划分 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 先整一个半成品,高精度过两天复习一下补上 #include <iostream>#include <algorithm>#include <set>#include <cstring>#include <string>#include <vector>#include <map>

n条直线最多能划分出多少个平面?

N条直线,两两相交,其交点各不不同,则产生的交点数目为N个数中取2个数的组合; 同时,也只有这种情况下(两两相交,也交点不同),分割的平面数最多, 数目为: 2 + (N-1)(N+2)/2.  这里求最少平面数没有意义,因为最少平面数就是N+1, 即N条直线两两平行的时候,分割的平面最少。 举例: 1条直线分割平面数最多为2; a1 = 2 2条直线分割平面数最多为4;

GTK中创建线程函数g_thread_new和g_thread_create的区别

使用GThread函数,需要引用glib.h头文件。 这两个接口的核心区别就是  g_thread_create 是旧的接口,现在已经不使用了,而g_thread_new是新的接口,建议使用。 g_thread_create: g_thread_create has been deprecated since version 2.32 and should not be used in n

在不损坏数据的情况下给WIN7重新划分分区

小易接到个求助电话:我的机器上已经装好了系统,但是只有一个分区。我不想重装系统重新分区,能不能再分出一个分区?   这个故障可能是困惑很多网友的一个故障。一般,有一些第三方的软件可以实现这些功能。但是,现在在 Windows Vista/Windows 7 里允许你对现有分区大小进行一定范围的调整。   来看一下操作办法:   准备工作   这个操作必须要求你的文件系统是 N

基于 rt-thread的I2C操作EEPROM(AT24C02)

一、AT24C02 The AT24C01A/02/04/08A/16A provides 1024/2048/4096/8192/16384 bits of serial electrically erasable and programmable read-only memory (EEPROM) organized as 128/256/512/1024/2048 words of 8 b

[项目][CMP][Thread Cache]详细讲解

目录 1.设计&结构2.申请内存3.释放内存4.框架 1.设计&结构 Thread Cache是哈希桶结构,每个桶是一个按桶位置映射大小的内存块对象的自由链表 每个线程都会有一个Thread Cache对象,这样每个线程在这里获取对象和释放对象时是无锁的 TLS – Thread Local Strorage Linux gcc下TLSWindows vs下TLS

线程池工具类——Thread学习笔记

记录一下线程池工具类: /*** 线程池工具类* @author lixiang* @date 2018年10月10日 - 11:10* @history 2018年10月10日 - 11:10 lixiang create.*/public class ThreadPoolHelper {private static final Logger logger = LoggerFactory.g