当前位置: 首页 > news >正文

高性能计算-CUDA性能优化-transpose

1.介绍

  • 对 2048 * 512 矩阵转置,使用NCU进行性能分析,并进行性能优化

2. Native: 二维 Block

  • 二维block,一个线程处理一个元素
点击查看代码
//native:二维block,一个线程处理一个元素
//矩阵 M * N
template<uint32_t M,uint32_t N>
__global__ void kernel_transpose_native(float *arr,float *out)
{uint32_t gidx = blockIdx.x * blockDim.x + threadIdx.x;uint32_t gidy = blockIdx.y * blockDim.y + threadIdx.y;if(gidx<N && gidy<M)out[gidx*M+gidy] = arr[gidy*N+gidx];
}
  • blockSize: 64 * 8
    image
  • 计算吞吐量 12.8% 和内存吞吐量 49.5%;

image

  • global 从 L1 数据读入 4sector/request,一个warp 请求 128B,即 4sector,正常;
  • 问题:global 写入数据 32sector/request,一个warp 有 32个 sector,逻辑写入数据量/物理写入数据量=13107232B/104857632B,写入效率只有 12.5%。
  • 优化方案:考虑增加写回数据的访存合并度,减少 sector 数量。

image

  • 目前未能解答:L2 与 DRAM 写回数据为什么没有数据流动??

3. 二维 Block + 共享内存

  • 介绍:考虑到一次内存事务请求 128B,使用二维block(32,32),正好一个blcok读取的数据可以增加缓存命中,数据放在共享内存
点击查看代码
//blocksize: BM * BN
template<uint32_t M,uint32_t N,uint32_t BM,uint32_t BN>
__global__ void kernel_transpose_SM(float *arr,float *out)
{uint32_t tidx = threadIdx.x;uint32_t tidy = threadIdx.y;uint32_t gidx = blockIdx.x * blockDim.x + threadIdx.x;uint32_t gidy = blockIdx.y * blockDim.y + threadIdx.y;__shared__ float blockShared[BM][BN];if(gidy<M && gidx<N){blockShared[tidy][tidx] = arr[gidy*N+gidx];__syncthreads();out[gidx*M+gidy] = blockShared[tidy][tidx];}
}
  • 备注: 设置 native kernel 为baseline

image

  • 计算单元吞吐量 14.3%,提升 11.62%;存储吞吐量%30.2,下降39%;SM active Cycle(SM至少有一个 active warp 的时钟周期数量)提升 60.7%,其余指标都大幅降低。
  • 分析:native blockSize 为 64 * 8=512,tile blockSize 为 32 * 32=1024 过大会导致 SM active Cycle 指标提升。
  • 问题1:存储吞吐指标下降。
  • 问题2:耗时增加 63%。
  • 共享内存数据读进来只使用一次,没有使用共享内存的必要;并且会带来 bank conflict 问题,设置 block 8 * 8的 bank conflict情况严重于32 * 32的 baseline, 如下图:
    image

  • blockSize: (8, 8),使用共享内存作为 baseLine与不使用共享内存比较:
    image

  • 不使用共享内存耗时减少24%,计算单元吞吐量 24.4%,下降30%;存储吞吐量 32.9%,提升 26%;
  • 共享内存的 bank conflict 负面影响消除;

4. 二维 Block 不使用共享内存的对比测试

  • blockSize: (8, 8)对比(32, 32)的计算单元吞吐量 35.4%,提升148.8%;存储吞吐量 34.58%,提升14.47%;性能大幅增加,应使用较小的 blocksize,增加并行度;如下图:
    image

  • blockSize: (8, 8)的数据写回合并度大大增加,sectors 减少了75%,如下图:
    image

  • 不同 blockSize的耗时对比:
    image

  • (8, 8) 效率最高

5. 二维Block + 线程Tile

  • 实现了三个版本:

tile + float4, 源数据直接写入目标地址: kernel_transpose_tile_FL4;
tile + float4, 使用寄存器中转,每个线程获取部分数据就写入: kernel_transpose_tile_FL4_2;
tile + float4,, 使用寄存器中转,每个线程获取全部数据后再写入,kernel_transpose_tile_FL4_3:

  • 详见代码
点击查看代码
//强转优先级大于 []
#define CAST_FLOAT4(pointer) reinterpret_cast<float4*>(pointer)//2维block + tile(float4 单个线程处理 4*4 的数据)
//源数据直接写入目标地址
//blocksize: BM * BN
template<uint32_t M,uint32_t N,uint32_t BM,uint32_t BN>
__global__ void kernel_transpose_tile_FL4(float *arr,float *out)
{uint32_t gidx = blockIdx.x * blockDim.x + threadIdx.x;uint32_t gidy = blockIdx.y * blockDim.y + threadIdx.y;if(gidy<<2 <M && gidx<<2 <N){//取源数据地址float4 *srcTemp[4];//展开,四条独立指令,可指令级并行#pragma unrollfor(int i=0;i<4;i++)srcTemp[i] = CAST_FLOAT4(arr+((gidy<<2) + i)*N + (gidx<<2));//重组数据CAST_FLOAT4(out+((gidx<<2))*M + (gidy<<2))[0]  = make_float4(srcTemp[0]->x,srcTemp[1]->x,srcTemp[2]->x,srcTemp[3]->x);CAST_FLOAT4(out+((gidx<<2)+1)*M + (gidy<<2))[0]  = make_float4(srcTemp[0]->y,srcTemp[1]->y,srcTemp[2]->y,srcTemp[3]->y);CAST_FLOAT4(out+((gidx<<2)+2)*M + (gidy<<2))[0]  = make_float4(srcTemp[0]->z,srcTemp[1]->z,srcTemp[2]->z,srcTemp[3]->z);CAST_FLOAT4(out+((gidx<<2)+3)*M + (gidy<<2))[0]  = make_float4(srcTemp[0]->w,srcTemp[1]->w,srcTemp[2]->w,srcTemp[3]->w);}
}//二维block + tile(float4 单个线程处理 4*4 的数据)
//使用寄存器中转数据的写法
template<uint32_t M,uint32_t N,uint32_t BM,uint32_t BN>
__global__ void kernel_transpose_tile_FL4_2(float *arr,float *out)
{uint32_t gidx = blockIdx.x * blockDim.x + threadIdx.x;uint32_t gidy = blockIdx.y * blockDim.y + threadIdx.y;if(gidy<<2 <M && gidx<<2 <N){//取源数据float srcTemp[4][4];//展开,四条独立指令,可指令级并行#pragma unrollfor(int i=0;i<4;i++)CAST_FLOAT4(&srcTemp[i])[0] = CAST_FLOAT4(arr+((gidy<<2) + i)*N + (gidx<<2))[0];//重组数据float4 resultTemp[4];#pragma unrollfor(int i=0;i<4;i++){resultTemp[i] = make_float4(srcTemp[0][i],srcTemp[1][i],srcTemp[2][i],srcTemp[3][i]);CAST_FLOAT4(out+((gidx<<2)+i)*M + (gidy<<2))[0]  = resultTemp[i];}}
}//二维block + tile(float4 单个线程处理 4*4 的数据)
//使用寄存器中转数据的写法,拆分循环
template<uint32_t M,uint32_t N,uint32_t BM,uint32_t BN>
__global__ void kernel_transpose_tile_FL4_3(float *arr,float *out)
{uint32_t gidx = blockIdx.x * blockDim.x + threadIdx.x;uint32_t gidy = blockIdx.y * blockDim.y + threadIdx.y;if(gidy<<2 <M && gidx<<2 <N){//取源数据float srcTemp[4][4];//展开,四条独立指令,可指令级并行#pragma unrollfor(int i=0;i<4;i++)CAST_FLOAT4(&srcTemp[i])[0] = CAST_FLOAT4(arr+((gidy<<2) + i)*N + (gidx<<2))[0];//重组数据float4 resultTemp[4];#pragma unrollfor(int i=0;i<4;i++)resultTemp[i] = make_float4(srcTemp[0][i],srcTemp[1][i],srcTemp[2][i],srcTemp[3][i]);#pragma unrollfor(int i=0;i<4;i++)CAST_FLOAT4(out+((gidx<<2)+i)*M + (gidy<<2))[0]  = resultTemp[i];}
}
  • 每个kernel 执行4轮,blockSize(8,8),NC 总体数据如下:

image

  • 第二种实现,每个线程获取部分数据就写入的计算单元和内存吞吐量较高。
  • block 处理总数据量不变: 32 * 32,kernel_transpose_tile_FL4_2 计算单元吞吐量为25.5%,提升183%;内存吞吐量为52.7,提升39%。
  • 每个kernel 执行4轮,blockSize(32,32),NC 总体数据如下:

image

  • 仍是第二种实现的计算单元和内存吞吐量稍高。

6. 总结

  • 使用较小的 blockSize 可以增加并行度;
  • 使用不同的 blockSize 对读写访存的 sector 利用率不一样,应该做计算取合适的大小。
http://www.sczhlp.com/news/15428/

相关文章:

  • 提交网站入口百度竞价排名医院事件
  • 福田公司全称太原网站制作优化seo
  • 江苏广泽建设有限公司网站网站竞价推广怎么做
  • 哪些网站做电商比较好seo软件哪个好
  • 网站域名注册哪个好门户网站怎么做
  • 江阴哪里有做网站的友情链接英文
  • 做视频网站 许可证网站设计制作哪家好
  • 如何提升网站访问速度seo薪酬
  • 深圳网站建设公司哪家好高质量网站外链平台
  • 钟表 东莞网站建设外贸营销网站建站
  • SpringAI踩坑记录
  • 一个Vue3 PDF阅读器组件及用法(功能可定制)
  • 【可线上线下参会、往届会后4个月EI检索】第二届机器人与先进制造技术国际学术会议(RAMT 2025)
  • MCP协议演进:从SSE到Streamable HTTP的技术革命
  • 外贸网站建设经验电商网站平台有哪些
  • 做网站论坛赚钱58和百度哪个推广效果好
  • 武汉手机网站设计如何广州网站建设方案维护
  • 建网站程序工具辽源seo
  • 方块世界:分块解决区间动态排名问题
  • 数字孪生技术如何驱动工厂的数字化转型?
  • 【稳定EI检索】2025年智能决策与机器学习国际学术会议 (ICIDML 2025)
  • 动态网站订单怎么做2345网址导航设为主页
  • 开发动态网站有哪些技术在线crm网站
  • 昆明做网站建设价位网站的推广方法有哪些
  • 广州购物网站开发无人区在线观看高清1080
  • 百度网站建设怎么联系网页游戏推广平台
  • 企业网站建设联系方式网络营销专业怎么样
  • 茂名网站建设托管信息流优化师面试常见问题
  • 网站设计怎么做超链接个人自己免费建网站
  • 网站开发图标seo顾问培训