首页 > 其他分享 >CUDA教学(1):前向转播

CUDA教学(1):前向转播

时间:2024-05-28 20:11:20浏览次数:26  
标签:__ torch const 转播 points threads CUDA 前向 feats

一个简单的CUDA实践。用于实现前向传播。

一、算法设计

(一)问题背景描述和算法设计?

问题描述:计算某个点 f 的特征值的“插值”结果。

以二维为例,为了“插值”得到 f 的特征值,需要用到:各顶点的特征值 \(f_i\) 和 f 距离该顶点对面的两条边的距离的乘积之和。

image

如果扩展到三维,那么需要求出 \(f\) 和 \(XYZ\) 三个面的距离(记为 uvw),然后再用一样的思路即可写出上面的式子。

(二)输入输出、以及它们的维度关系

输入:

  • feats 特征向量:(N,8,F) 分别代表:N 个立方体、每个立方体有 8 个顶点、每个顶点的特征向量的长度为 F;
  • points 待求解的点坐标:(N,3) 分别代表:N 个待求解的 f 点(和 N 个立方体对应),每个点的坐标是三维的。

image

输出:feats_interp N 个点插值后的特征向量结果。(N,F) 代表:N 个长度为 F 的特征向量,对应 points 的每个点。

(三)如何并行运算呢?

我们这个问题一共有 N 个立方体,利用每个立方体八个格点的特征值“插值”出最后的特征值;并且每个特征向量的长度是 F,而这 F 个计算也是相互独立的。因此可以并行计算两个量。

其实还有个小技巧:看最后的输出的维度,能并行的数量不大于输出的维度。比如这里输出是 (N,F) 是二维,所以最大能并行两个量。

二、CUDA 编程

(一)GPU的基本结构

GPU 分为:Kernel - Grid - Block - Thread 四级架构。

image

(二)Block 层的作用是什么?

为什么使用 Block+thread 的二层结构,而不是让 grid 直接管理 thread 的一层结构?

image

因为受限于 GPU 的硬件结构,Threads 数量是存在上限的。通过引入 Block 这个结构可以扩展线程数量。

(三)如何合理设置Block的大小?

假设 N=20,F=10,那么最后的输出维度是 20x10;我们一个Block的维度是 16*16,所以需要两个Block才能覆盖整个输出

image

具体到代码上就是下面两行(也比较好理解):

const dim3 threads(16, 16);
const dim3 blocks((N+threads.x-1)/threads.x, (F+threads.y-1)/threads.y);

上面这也提示我们一件事情:有的 thread 是不参与运算的,也就是说 thread 的行、列的 index 值不能超过 N 和 F 的范围,否则会出错。

(三)编写核函数的注意事项?

以下面的核函数为例:

template <typename scalar_t>
__global__ void trilinear_fw_kernel(
    const torch::PackedTensorAccessor<scalar_t, 3, torch::RestrictPtrTraits, size_t> feats,
    const torch::PackedTensorAccessor<scalar_t, 2, torch::RestrictPtrTraits, size_t> points,
    torch::PackedTensorAccessor<scalar_t, 2, torch::RestrictPtrTraits, size_t> feat_interp
){
    const int n = blockIdx.x * blockDim.x + threadIdx.x;
    const int f = blockIdx.y * blockDim.y + threadIdx.y;

    // 如果超过输出范围,则跳过不能算
    if (n>=feats.size(0) || f>=feats.size(2)) return;

    // point -1~1. 需要正规化到 [0,1] 之间
    const scalar_t u = (points[n][0]+1)/2;
    const scalar_t v = (points[n][1]+1)/2;
    const scalar_t w = (points[n][2]+1)/2;
  
    const scalar_t a = (1-v)*(1-w);
    const scalar_t b = (1-v)*w;
    const scalar_t c = v*(1-w);
    const scalar_t d = 1-a-b-c;
    // 保存计算结果
    feat_interp[n][f] = (1-u)*(a*feats[n][0][f] +
                               b*feats[n][1][f] +
                               c*feats[n][2][f] +
                               d*feats[n][3][f]) + 
                            u*(a*feats[n][4][f] +
                               b*feats[n][5][f] +
                               c*feats[n][6][f] +
                               d*feats[n][7][f]);
}

核函数没有任何返回值,因此需要把输入、输出通过参数传递到函数内部。并且这里是个泛型函数(scalar_t 代表所有可能的数据类型)。__global__ 是 CUDA 特有的关键字,代表该函数是被CPU调用、GPU执行;类似的还有 __host__ 代表 cpu 调用 + 执行、__device__ 代表 GPU 调用 + 执行。

torch::PackedTensorAccessor 的作用是把 Tensor 的类型具体化,才能给C++使用。也就是说:torch::Tensor 这种类型在 C++ 中指代不明确,需要进行具体化。我们以 feats 为例讲一下它的几个参数:

  • 第二个参数 3 代表 [N,8,F] 共三维结构,类比 points[N,F] 是二维结构,所以它的参数写的是 2 ;
  • size_t 是位置索引的类型,默认都是 size_t 类型;

最后还有个细节,如何确定每个 thread 的 index 呢?也就是代码的这两行:

const int n = blockIdx.x * blockDim.x + threadIdx.x;
const int f = blockIdx.y * blockDim.y + threadIdx.y;

blockDim.x 代表 x 方向上(竖直向下)每个 block 由多少行 thread 构成,也就是 threads[0] 的值;blockIdx.x 则就是第几个 block 了;threadIdx.x 代表当前 block 内的第 x 行。

(四)如何调用核函数?

调用核函数主要有两个工作:输入输出、Block&Thread 的大小。

torch::Tensor trilinear_fw_cu(
    const torch::Tensor feats,
    const torch::Tensor points
){
    // 1. 输入输出
    const int N = feats.size(0), F = feats.size(2);
    torch::Tensor feat_interp = torch::empty({N, F}, feats.options());

    // 2. thread & block 大小
    const dim3 threads(16, 16);// 这里N和F是可并行的,共两个次元(256不会出错)
    const dim3 blocks((N+threads.x-1)/threads.x, (F+threads.y-1)/threads.y);

    // 建议调用该Kernel的第二个参数为当前函数名,方便报错debug
    AT_DISPATCH_FLOATING_TYPES(feats.type(), "trilinear_fw_cu", 
    ([&] {
        trilinear_fw_kernel<scalar_t><<<blocks, threads>>>(
            feats.packed_accessor<scalar_t, 3, torch::RestrictPtrTraits, size_t>(),
            points.packed_accessor<scalar_t, 2, torch::RestrictPtrTraits, size_t>(),
            feat_interp.packed_accessor<scalar_t, 2, torch::RestrictPtrTraits, size_t>()
        );
    }));

    return feat_interp;
}

我们以上面这个例子为例。feat_interp 是最后的输出,它的维度是 (N,F) ,这里直接通过 feats.options() 设置参数属性;如果需要自定义的话,比如 torch::empty({N,F}, torch::dtype(torch::kInt32).device(feats.device)); 来自定义类型和位置。

threads 的总数一般是 256 或 512,最大不超过 1024。上面我们分析过,可并行量共有两个,所以我们设置 256=16*16 作为 threads 的大小,blocks 的数量就按照上面的公式计算即可。

我们使用了 AT_DISPATCH_FLOATING_TYPES 这个函数来调用核函数,注意调用时通过 <<<, >>> 包裹 blocks 和 threads 的大小,这是 cuda 的特殊写法。

标签:__,torch,const,转播,points,threads,CUDA,前向,feats
From: https://www.cnblogs.com/7ytr5/p/18218750

相关文章

  • CUDA教学(2):反向传播
    cuda没有提供自动求导机制,因此我们需要完成以下两步,实现反向传播。一、计算所有trainable参数的偏微分判断哪些参数是trainable的?本例中,输入f的坐标是固定的,所以uvw的值也是固定的,因此只需要求feats_interp对各个顶点的特征量\(f_i\)的偏微分即可。‍如何进行反......
  • 使用 Unity Barracuda 和 Compute Shader,Yolov2 进行高效物体识别
    前言通过整合UnityBarracuda和TinyYOLOv2模型,开发者可以在Unity中实现高效的实时物体识别功能。这种技术不仅可以增强游戏和应用的交互性,还可以应用于虚拟现实(VR)和增强现实(AR)等创新项目中,为用户创造更加沉浸和动态的体验。TinyYOLOv2模型概述TinyYOLOv2是YOLO(You......
  • ubuntu24.04安装cuda12.5版本
    概述最近新学习的JAX在使用时,提示:2024-05-2619:46:32.016388:Wexternal/xla/xla/service/gpu/nvptx_compiler.cc:760]TheNVIDIAdriver'sCUDAversionis12.2whichisolderthantheptxasCUDAversion(12.5.40).Becausethedriverisolderthantheptxasvers......
  • Ubuntu上使用QT creator运行cuda程序 转载的文章
    突发奇想想尝试一下QT界面中使用CUDA加速过的程序,然后查了一下资料,总结一下有以下几点吧1、CUDA配置全部放在.pro文件中2、main.cpp为主函数使用g++编译3、kernel.cu为核函数使用nvcc编译不多说上代码以下为main.cpp代码   #include<QtCore/QCoreApplication>       ......
  • Visual Studio 2015 编写 CUDA 关键字高亮并自动补全_cuda vs 波浪线
    CSDN搬家失败,手动导出markdown后再导入博客园第一步,是在vs2015里面设置vc++文件支持.cu;cuh;文件。方法:工具->选项->文本编辑器->文件扩展名。得到如图所示的界面:注意,在右侧可以添加vc++类型的文件扩展名,这是我的设置效果,操作就不用细说了。![[output/attachme......
  • 编译mmdetection3d时,无root权限下为虚拟环境单独创建CUDA版本
    在跑一些深度学习代码的时候,如果需要使用mmdetection3d框架,下载的pytorch的cudatoolkit最好需要和本机的cuda版本是一样的,即输入nvcc-V命令后显示的版本一样。但是如果是在学校里,一般是服务器管理员装的cuda驱动是啥版本,cudatoolkit就是啥版本,且非root用户改变不了。但是有非ro......
  • 配置所需的各种信息(Nvidia对应Cuda,显卡等兼容信息)
    nvidia各型号显卡算力、CUDA、cuDNN、驱动对应版本表1:显卡型号信息表2:CUDA工具包和CUDA最小版本兼容性所需的最低驱动程序版本GeForceRTX3090显卡仅支持CUDA11以上的版本。且Pytorch1.7.0开始支持CUDA11......
  • 链式前向星+dijkstra
    #include<iostream>#include<vector>usingnamespacestd;constintN=1000;struct{intto;intw;intnext;}edge[N];inthead[N];voidadd_edge(intu,intv,intw){staticintcnt=0;edge[cnt].to=v;edge[cnt].w=......
  • pytorch调试时CUDA报错,如何精确定位
    由于pytorch的python语句执行和CUDA后端实际上是异步执行的,所以当CUDAruntime报错时,并不一定能正确地反映在python抛出的错误语句中,因此,可以添加一个环境变量来实现:CUDA_LAUNCH_BLOCKING=1这里再补充一些关于cuda和pytorch异步执行的知识,当你写了一句torch.mm(X,Y)时,实际上......
  • linux安装cuda和cudnn
    //安装cudawgethttps://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pinsudomvcuda-wsl-ubuntu.pin/etc/apt/preferences.d/cuda-repository-pin-600wgethttps://developer.download.nvidia.com/compute/cuda/12.4.1/local_ins......