Pytorch中cuda kernel进行离散访存的几个基本函数

基本概念

Pytorch原生CUDA C的大部分kernel都支持离散数据访存,其主要的逻辑都是通过thread id去匹配到输入输出Tensor的内存索引。

熟悉Pytorch的同学都知道,Tensor的内存索引在pytorch中是通过size,stride和offset来表示的。

基本的规则便是:

index = \sum size[i] * stride[i]

假设一个Tensor a 的size为(2, 4, 4),stride为(16, 4, 1)对其进行slice操作后,Tensor b的size为(2, 4, 2),stride为(16, 4, 2)

那么对应点b[1][3][1]的位置便是原生内存上起始点向后的:1\ast 16+3\ast 4+1\ast 2=30偏移。

python代码:

>>> a = torch.randn((2,4,4))
>>> b = a[:,:,0:3:2]
>>> b.size()
torch.Size([2, 4, 2])
>>> b.stride()
(16, 4, 2)
>>> b[1][3][1]
tensor(0.4909)
>>> a.flatten()[30]
tensor(0.4909)

C++代码实现

对应C++层面的实现中,cuda c代码进行数据读取时就是通过size和stride类似的逻辑关系,和thread idx进行映射。其主要函数分别为:

1 TensorInfo类

代码位置: aten/src/ATen/cuda/detail/TensorInfo.cuh

其是对Tensor信息的抽象,主要抽象Tensor的dim,size和stride信息。存在两个模板参数:分别表示dptr的数据类型和size,stride的数据类型。

1.1)最大支持维度为25;

1.2)支持维度折叠的逻辑,代码路径: aten/src/ATen/CollapseDims.h

1.2.1)只支持正向的维度折叠功能,核心的处理是 size[i] = size[i] * stride[i];

1.2.2)支持中间插值的维度折叠功能,即excludeDim;

注释:其维度折叠的核心逻辑就是借助于公式 size[i] = size[i+1] * stride[i+1]来判断维度间是否连续。(维度值为1可以天然不考虑)

    for (; oldIndex < stopDim; ++oldIndex) {
      if (sizes[oldIndex] == 1) {
        continue;
      }

      if (strides[newIndex] == sizes[oldIndex] * strides[oldIndex]) {
        sizes[newIndex] *= sizes[oldIndex];
        strides[newIndex] = strides[oldIndex];
      } else {
        ++newIndex;
        sizes[newIndex] = sizes[oldIndex];
        strides[newIndex] = strides[oldIndex];
      }
    }

2 IndexToOffset类

代码位置: aten/src/ATen/cuda/detail/TensorInfo.cuh

其核心逻辑就是通过LinearId(通常就是GPU的thread_id)去映射返回Tensor的真实内存位置。

2.1)通过特化支持动态Dim的方式,也就是info.dim。 但看起来这种方式不利于nvcc编译,导致编译时间长。

其核心逻辑get()函数就是通过size和stride取余进行获取内存偏移位置。

template <typename T, typename IndexType>
struct IndexToOffset<T, IndexType, -1> {
  static inline __host__ __device__ IndexType get(
    IndexType linearId,
    const TensorInfo<T, IndexType>& info) {

      IndexType offset = 0;

      for (int i = info.dims - 1; i > 0; --i) {
        IndexType curDimIndex = linearId % info.sizes[i];
        IndexType curDimOffset = curDimIndex * info.strides[i];
        offset += curDimOffset;
        linearId /= info.sizes[i];
      }

      return offset + linearId * info.strides[0];
  }
};

3 getTensorInfo函数实现

代码位置:aten/src/ATen/cuda/detail/IndexUtils.cuh

其核心逻辑就是通过构造TensorInfo对象并返回。 其中scalarType是通过data_ptr<scalarType>来保证一致。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值