ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

并行编程实战—CUDA Tile编程入门之二创建和应用

并行编程实战—CUDA Tile编程入门之二创建和应用 一、CUDA Tile的创建在知道了基础的Tile的处理机制及相关的核函数的定义声明后就来到了CUDA Tile的真正的目的即处理数据块。也就是说如何设计块的结构、大小以及其形状、元素类型等。在Tile编程中上述的参数表现为一个固定长度的、多维的标题元素数组并且其形状和元素类型必须在编译时确定。这里需要对Shape——形状进行说明一下在CUDA Tile中主要面对的数据结构就是数组多维张量Tile可以理解为这个数组的一个子集想象从一个大的数据块中砍出来一大块。形状其实就是对砍下来的这一块进行维度上的描述。这样说可能不好理解其实就是如何从原始的数组数据块中以何种方式来划分出一个子集子数据块进行加载。并且这里有一个限制Tile的每个维度必须是2幂。举一个简单例子去买豆腐肯定要告诉人家买几块大小是长条形切还是方块形切一个维度是从当中下刀还是从两边又一个维度…而且要求必须是够两顿饭的2的幂。再以Tile为例在ct.load中shape参数是一个由常量整形组成的元组如果shape(tr,tc),则说明加载时会从原数组中切分出一个Tile,其大小为tr行和tc列。其实就是处理数据的粒度。Tile是值语义的很好理解它需要复制过程。不过由于CUDA控制了Tile的硬件内容表示方式所以其复制的开销很低而且这也不需要开发者主动去管理相关的内存。在CUDA的开发中Tile有两种方式来创建。一种是通过数组加载数据来创建另外一种是通过生成填充指定模式的Tile工厂函数来创建。二、创建块的方法CUDA Tile中创建Tile的具体方法有数据加载看下面的定义ct.load(array,index,shape,*,orderC,padding_modePaddingMode.UNDETERMINED,latencyNone,allow_tmaNone)array要从中加载数据的源数组如CuPy或PyTorch张量 index一个元组指定在Tile空间中的起始索引而不是原始元素的绝对索引 shape一个由编译时常量整数组成的元组定义要加载的Tile的尺寸工厂函数看相关的定义填充0或1的 Tile ct.zeros(shape,dtype)和ct.ones(shape,dtype)具有任意常量值的 Tile ct.full(shape,fill_value,dtype)包含[0,1,...,size-1]的1维 Tile ct.arange(size,dtype...)shapeTile 的维度大小必须是2的幂的编译时常量。 fill_value仅ct.full用于填充Tile的标量值。 dtypeTile元素的数据类型。对于Tile来说其工厂需要的shape和dtype参数都是编译期确定的而Python也提供了字面量以及Constant注解的内核参数进行支持。三、初步控制在创建Tile后就可以进行相关的管理和控制了主要包括查询块这个类似于前面的SIMT中通过blockIdx和threadIdx来计算线程索引。不过对Tile来说只需要块索引就可以了块内线程索引由编译器自行处理。在Python中可以使用ct.bid(axis)返回当前块沿指定轴0、1或2的索引作为int32标量或者使用ct.num_blocks(axis)返回沿该轴的总块数其对边界检查和循环计数很有用编译时常量在刚刚的分析中已经说明Tile的形状等必须在编译期确定那么就可以使用字面量或编译期常量来进行处理。如使用字面量来指定Tile 形状和数据类型ct.zeros((64,64),dtypect.float32)#和ct::tilefloat,ct::shape64,64。同样也可以使用Python Constant[T]来指定参数如TILE: ct.Constant[int]3. 控制流Tile是数据块就需要控制访问Tile内核的每个块建立了单一的控制流路径和普通程序一样利用条件和边界限定来进行控制流的处理。内部的Tile操作则由编译自动处理。不过它并不完全等同于其它语言的流控制至少Tile不允许从循环内部返回其它细节参看官方文档。数据访问最重要的就是遍历。所以Tile支持常见的循环主要的方式有range()、for、while 和嵌套循环但在实际的应用中需要注意步长不允许为负四、例程下面看一个相关例程importcuda.tileasctimportcupyascpct.kerneldefcombine_kernel(a:cp.ndarray,b:cp.ndarray,tile_size:ct.Constant[int]):pidct.bid(0)a_tilect.load(a,index(pid,),shape(tile_size,))zero_tilect.zeros((tile_size,),dtypea.dtype)full_tilect.full((tile_size,),5,dtypea.dtype)result_tilea_tilefull_tile-zero_tile# a_tile 5ct.store(b,index(pid,),tileresult_tile)defmain():N1024tile_size256assertN%tile_size0acp.arange(N,dtypecp.float32)bcp.zeros(N,dtypecp.float32)num_blocksN//tile_size# CUDA Tile的启动参数依次为stream、grid、kernel、kernel_args# Tile大小由内核参数和ct.load的shap决定不需要单独传block维度ct.launch(cp.cuda.get_current_stream(),(num_blocks,),combine_kernel,(a,b,tile_size),)expecteda5ifcp.allclose(b,expected):print(验证通过)else:print(验证失败。)print(输入前10:,a[:10])print(输出前10:,b[:10])print(期望前10:,expected[:10])if__name____main__:main()代码来自官方文档修改过来。五、总结利用Python来学习Tile应该是一个很好的入门方式。一来语言本身相对简单二来对Tile的封装比C可能更高级一些。这对于开发是一个好事。入门之后再理解C的开发机制估计会更容易理解。
返回列表