热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Cuda中Globalmemory中coalescing例程解释

Globalmemory是cuda中最常见的存储类型,又叫做Devicememory,位于Host主机区域上,它的生命周期是在整个Grid
Global memory是cuda中最常见的存储类型,又叫做Device memory,位于Host主机区域上,它的生命周期是在整个Grid里面,大约具有500个cycle latency。在cuda并行程序中,尽量用Coalesing accessing的策略来最大化带宽bandwidth。什么是Coalesing accessing呢?如图所示:







当半个Warp的16个threads在一次memory transaction中coalesced时,Global memory中的带宽得到了最大的利用。其中,需要注意的是,Device在一次transaction中,从global memory中可以一次读取32-bit,64-bit,128-bit,例如

64 bytes - each thread reads a word: int, float, …

128 bytes - each thread reads a double-word: int2, float2, …

32 bytes (compute capability 1.2+) - each thread reads a short  int.

下面有两个实例来说明Global memory中的coalescing问题:

1)float3型Uncoalesced

__global__ void accessFloat3(float3 *d_in,
float3* d_out)
{
int index = blockIdx.x * blockDim.x + threadIdx.x;
float3 a = d_in[index];
a.x += 2;
a.y += 2;
a.z += 2;
d_out[index] = a;
}







在这段代码中,float3有12个bytes,不等于要求的4,8,16 bytes,半个warp读取3个64bytes中非连续区域,如图:







有三种方法可以解决这个问题

1:使用shared memory,也叫做3-step approach

假如每个block中使用256个threads,这样一个thread block需要 sizeof(float3)*256 bytes的share memory空间,每个thread读取3个单独的float型,这实质上是指讲输入定义为float型,在核函数里面讲读取在share memory中的float变量转换为float3型并进行操作,最后再转换成float型输出,如图;






代码如下:



如果不好理解的话,假设我们的blockDim=4,取4个float3型变量,我们会发现,每一个thread中输入操作(输出操作一样)为:

Thread 0:

S_data[0]=g_in[0]; S_data[4]=g_in[4]; S_data[8]=g_in[8];

Thread 1:
S_data[1]=g_in[1]; S_data[5]=g_in[5]; S_data[9]=g_in[9];
Thread 2:
S_data[2]=g_in[2]; S_data[6]=g_in[6]; S_data[10]=g_in[10];
Thread 3:
S_data[3]=g_in[3]; S_data[7]=g_in[7]; S_data[11]=g_in[11];
可以看出,对于每个thread同一时刻(similar step)的数据读入,地址均是连续,这样就达到了coalescing。
2)使用数组的结构体(SOA)来取代结构体的数组(AOS)




3)使用alignment specifiers

__align__(X), where X = 4, 8, or 16

struct __align__(16) {float x; float y;  float z; };

尽管这损失了比较多的空间:



2)第二个实例:矩阵转置 Matrix Transpose.

一般做法:Uncoalesced Transpose,GMEM为Global memory



我们发现一般的做法,在写output时,地址是不连续的,即uncoalesced,因此我们利用shared memory存储输入数据,根据转置的关系,来实现coalescing,SMEM为shared memory,如下图:



代码如下:

__global__ void transpose(float *odata, float *idata, int width, int height)

{

__shared__ float block[BLOCK_DIM*BLOCK_DIM];

unsigned int xBlock = blockDim.x * blockIdx.x;

unsigned int yBlock = blockDim.y * blockIdx.y;

unsigned int xIndex = xBlock + threadIdx.x;

unsigned int yIndex = yBlock + threadIdx.y;

unsigned int index_out, index_transpose;

if (xIndex

{

unsigned int index_in = width * yIndex + xIndex;

unsigned int index_block = threadIdx.y * BLOCK_DIM + threadIdx.x;

block[index_block] = idata[index_in];

index_transpose = threadIdx.x * BLOCK_DIM + threadIdx.y;

index_out = height * (xBlock + threadIdx.y) + yBlock + threadIdx.x;

}

__syncthreads();

if (xIndex

odata[index_out] = block[index_transpose];

程序的逻辑关系有时还挺绕的,我们以一个4*4矩阵为例,将逻辑关系展示如下:



设dim3 gridDim(4,1), dim3 blockDim(1,4),以橙色block为例,如输入数据时,将其放入到sharememory中,代码体现在:

unsigned int index_in = width * yIndex + xIndex;
unsigned int index_block = threadIdx.y * BLOCK_DIM + threadIdx.x;
block[index_block] = idata[index_in];
接下来的代码实际上是将block的区域给换了,如左下图所示,block换成了一列四种不同颜色的,最终转置的矩阵如右下图所示,从图示可以看出,最终结果的坐标系Height、Width、blockIdx.x、blockIdx.y均对位变换了,这时我们只需要找threadIdx.x'、threadIdx.y'与threadIdx.x、threadIdx.y之间的关系,其实可以看出,一个block里面的坐标系没有发生变换,则threadIdx.x'=threadIdx.x,threadIdx.y'=threadIdx.y,所以代码如下:
index_transpose = threadIdx.x * BLOCK_DIM + threadIdx.y;
index_out = height * (xBlock + threadIdx.y) + yBlock + threadIdx.x;
odata[index_out] = block[index_transpose];

总体来说,Global memory中coalescing就是保证其在数据读取或者写入时,使用连续的地址,且地址所存储的变量尺寸为32、64、128 bit,我们常常使用share memory来解决coalescing问题。





推荐阅读
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 本文基于刘洪波老师的《英文词根词缀精讲》,深入探讨了多个重要词根词缀的起源及其相关词汇,帮助读者更好地理解和记忆英语单词。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • MySQL DateTime 类型数据处理及.0 尾数去除方法
    本文介绍如何在 MySQL 中处理 DateTime 类型的数据,并解决获取数据时出现的.0尾数问题。同时,探讨了不同场景下的解决方案,确保数据格式的一致性和准确性。 ... [详细]
  • 利用决策树预测NBA比赛胜负的Python数据挖掘实践
    本文通过使用2013-14赛季NBA赛程与结果数据集以及2013年NBA排名数据,结合《Python数据挖掘入门与实践》一书中的方法,展示如何应用决策树算法进行比赛胜负预测。我们将详细讲解数据预处理、特征工程及模型评估等关键步骤。 ... [详细]
  • Docker的安全基准
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 数据管理权威指南:《DAMA-DMBOK2 数据管理知识体系》
    本书提供了全面的数据管理职能、术语和最佳实践方法的标准行业解释,构建了数据管理的总体框架,为数据管理的发展奠定了坚实的理论基础。适合各类数据管理专业人士和相关领域的从业人员。 ... [详细]
  • 本教程涵盖OpenGL基础操作及直线光栅化技术,包括点的绘制、简单图形绘制、直线绘制以及DDA和中点画线算法。通过逐步实践,帮助读者掌握OpenGL的基本使用方法。 ... [详细]
  • 基因组浏览器中的Wig格式解析
    本文详细介绍了Wiggle(Wig)格式及其在基因组浏览器中的应用,涵盖variableStep和fixedStep两种主要格式的特点、适用场景及具体使用方法。同时,还提供了关于数据值和自定义参数的补充信息。 ... [详细]
  • 深入解析TCP/IP五层协议
    本文详细介绍了TCP/IP五层协议模型,包括物理层、数据链路层、网络层、传输层和应用层。每层的功能及其相互关系将被逐一解释,帮助读者理解互联网通信的原理。此外,还特别讨论了UDP和TCP协议的特点以及三次握手、四次挥手的过程。 ... [详细]
  • 本文介绍如何在C#中将GridView控件的内容保存为图片文件。通过代码示例,详细说明了创建位图、绘制图形并保存图像的步骤。 ... [详细]
author-avatar
Rain雨露Dew
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有