美文网首页
cuda的ldmatrix指令的详细解释

cuda的ldmatrix指令的详细解释

作者: 我是周健康 | 来源:发表于2024-05-10 22:49 被阅读0次

<font color=red size=6>如果对本篇文章有疑问,欢迎直接评论,我必然秒回!!</font>

  • ldmatrix指令是 PTX 级别的指令,它是个warp级别的数据加载指令,当然数据是从shared memory中加载到32个cuda thread中的寄存器中。

1. ldmatrix指令的使用格式例子: ldmatrix.sync.aligned.m8n8.x1.shared.b16 { %0 }, [ %1 ];

  • 直接看例子吧,例如这个指令ldmatrix.sync.aligned.m8n8.x1.shared.b16 { %0 }, [ %1 ];
  • 这个PTX的指令呢?
    • 他的意思就是让一个warp中的32个线程,从shared memory中加载1(x1)个8*8(m8n8)的矩阵,这个矩阵有8行!
    • 每行的8个元素必须连续存放,不同行之间可以不连续
    • 这个矩阵的元素粒度是b16,也就是一个元素占两个字节。
    • 那也就是说这个warp读取了8*8=64个元素,warp中的每个cuda thread占据了两个元素,因此正好是一个32位的寄存器!
  • 由于每行的位置可以不连续,因此用户需要指定8行的开始地址,这个需要用户确保thread0-thread7的%1寄存器填充的是这8个地址即可
    • 至于thread 8-thread 31 的 %1 寄存器数据,那就可以随便设置了!
  • 返回的32位数据,也就是在0%寄存器里了。
  • 我们好奇的第一个问题就是,这64个元素是如何分布在32个cuda thread之间的呢?
    • 答案就是看下面这个图即可,红色的表示线程id,每个线程被占据了两个数据,也就是正好是1个32位寄存器!
    • 图上只给出了thread0-thread7的情况,其他线程的占据的数据以此类推即可,我这里省略了。
    • 图中一共是8行,注意每行的首地址必须由thread0-thread7的%1寄存器指定!
image.png

2. ldmatrix指令的使用格式例子:ldmatrix.sync.aligned.m8n8.x4.shared.b16 { %0, %1, %2, %3 }, [ %4 ]

  • 这个例子使用更广泛,他的意思就是让一个warp中的32个线程,从shared memory中加载4个8*8的矩阵,矩阵的元素仍然是16位!
    • 因此这个时候需要指定32个地址了,也就是thread0-thread31的%4寄存器需要指定这32个地址!
    • 同时每个cuda thread应该瓜分到4*8*8/32=8个bf16元素,也就是4个32位寄存器啦!
    • 所以他的返回值有四个寄存器了!

ldmatrix.sync.aligned指令的例子

  • 下面的代码非常简单,值得一看。编译命令是 nvcc A.cu -arch sm_80
  • 下面的代码就是让一个warp中的32个线程,从shared memory中加载48*8的矩阵,但是我这个里面写的是uint32_t元素类型,因此其实是48*4的矩阵!
  • 下面代码打印出线程1占据的四个32位寄存器的值!
#include <stdio.h>
#include <iostream>

__global__ void helloFromGPU (void)
{
  __shared__ uint32_t aTile[4*8*4];

  int tidx = threadIdx.x + blockDim.x * threadIdx.y;
  // 下面的代码是把smem中的4*8*4的矩阵,初始化数值!
  if (tidx == 0) {
    for (int i = 0; i < 4*8*4; ++i) {
        aTile[i] = I;
    }
  }
  __syncthreads();

  int aTile_index = tidx % 16 * 8 + tidx / 16 * 4;
  uint32_t a[4];
  uint32_t smem = __cvta_generic_to_shared(aTile+aTile_index);
  asm("ldmatrix.sync.aligned.m8n8.x4.shared.b16 { %0, %1, %2, %3 }, [ %4 ];\n"
  : "=r"(a[0]), "=r"(a[1]), "=r"(a[2]), "=r"(a[3]) 
  : "r"(smem)
  );

  if (tidx == 1) {
    printf("%d \n", (a[0])); printf("%d \n", (a[1]));
    printf("%d \n", (a[2])); printf("%d \n", (a[3]));
  }
}

int main(void) {
uint3 block = {32,1,1};
uint3 grid = {1,1,1};
helloFromGPU <<<grid, block>>>();

cudaDeviceReset();
return 0;
}
  • 上面代码中int aTile_index = tidx % 16 * 8 + tidx / 16 * 4;这个代码是让
  • 每个线程分别指向每行的首地址!
  • 其中tidx % 16 表示的是行id,
  • tidx / 16 * 4 表示的是列id,
    • 从按照b16的角度来看,其实列id应该是tidx / 16 * 8
    • 但是我代码里面写的是uint32类型,所以就是tidx / 16 * 4了!
  • 最后打印出来的四个数字如下图所示哦!
image.png

为什么需要这样的指令呢?

  • 为啥需要ldmatrix指令呢?是因为这个指令主要和mma指令搭配使用的!
  • 也就是先用ldmatrix指令将数据从shared memory中加载到寄存器,然后调用 mma 指令计算!
  • 请我们来看一下这个链接 9.7.13.4.8. Matrix Fragments for mma.m16n8k16 with floating point type,这个链接上展示了mma.m16n8k16指令。
  • 这个指令的功能是计算A矩阵16*16和B矩阵16*8,然后得到一个16*8的矩阵C。
  • 其中A矩阵16*16的元素分布在32线程的寄存器中,每个线程占据着8个元素,那么这么多元素是如何分布在32个线程中的呢?
  • 就是下面这个图片所示哦
image.png
  • 从上图可以看出,mma指令对于输入在32个cuda thread之间的分布恰好就是 ldmatrix指令那样!
  • B矩阵16*8的元素分布在32个寄存器中,那么这么多元素是如何分布在32个寄存器中的呢?
  • 就是下面这个图片所示哦
image.png
  • 从上图可以看出,mma指令对于输入在32个cuda thread之间的分布恰好就是 ldmatrix指令那样!
    • 当然啦,这个要求smem中B矩阵必须是col major的哦!,否则就不能调用ldmatrix指令哦!

相关文章

  • rewrite知多少

    首先mark两个博客: 1,nginx rewrite 指令last break区别最详细的解释 2,Nginx中...

  • cuda9.0安装

    ## 卸载指令: ### 1.全部卸载 sudo apt-get purge nvidia-cuda* sudo ...

  • 宏定义指令

    常用的宏定义指令 详细应用

  • directive 指令

    详细参考 directive指令大全

  • 控制器、微程序控制

    1.控制器的功能 (1)取指令(指令地址,控制信号) (2)分析指令(解释指令,指令译码) (3)执行指令 (4)...

  • Ubuntu 14.04安装CUDA-8.0

    about 本blog参考了masa_fish 的 Ubuntu 14.04 上安装 CUDA 7.5 超详细教...

  • 机器学习资源

    Ubuntu16.04 安装 NVIDIAUbuntu16.04 安装 CUDA 9.0 详细教程Ubuntu下C...

  • 卸载cuda10.1并重装cuda10.0

    基本可以参考以下指令 其中install from network deb中,将cuda10.1的下载地址换成10...

  • 04-Vue指令

    什么是指令 官方解释: 指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 att...

  • RemoteViews详细解释

    RemoteViews详细解释 原载于:RemoteViews详细解释 说明 想要完全的理解RetmoteView...

网友评论

      本文标题:cuda的ldmatrix指令的详细解释

      本文链接:https://www.haomeiwen.com/subject/edwefjtx.html