MX底层im2col代码

基本的文件配置参考官方教程
自己写的一个卷积,大家可以看看


关于GPU的kernel函数的撰写是gpu运算的核心,其中涉及到一个宏CUDA_KERNEL_LOOP,它定义在src/operator/mxnet_op.h:L57,具体定义如下:

#define CUDA_KERNEL_LOOP(i, n) \
  for (int i = blockIdx.x * blockDim.x + threadIdx.x; \ // blockIdx.x: 一个线程格grid在x维度线程块block的索引,
                                                        // blockDim.x: 一个线程块block在x维度上的线程thread数量, 
                                                                      //在mx中往往等于kBaseThreadNum
                                                        // threadIdx.x: 一个线程块block在x维度上线程thread的索引
      i < (n); \                                        // n代表核函数所要处理的元素总个数
      i += blockDim.x * gridDim.x)                      //gridDim.x: 一个线程格grid在x维度上的block数量, 
                                                                     //由核函数<<<>>>中的第一个参数决定
                                                                     //往往等于cuda_get_num_blocks(N)

说一下,根据这个指出,blockDim.x* gridDim.x表示的是该线程格所有线程的数量,n表示核函数总共要处理的元素个数有时候,n会大于blockDim.x* gridDim.x,因此并不能一个线程处理一个元素。由此通过上面的方法,让一个线程串行(for循环)处理几个元素。这其实是常用的伎俩,得借鉴学习一下。在incubator-mxnet/3rdparty/mshadow/mshadow/cuda/tensor_gpu_inl.cuh:L25-31中可以看到,每个block的线程数最多可以达到1024,在L41中可以看到grid的最大个数为65535。

这里涉及到cuda中的线程概念,可以参考这篇博文,还有这篇,或者谷歌搜索线程格,也有很多解释。另外发现一个写的很棒的博主,对cuda编程有较深的理解和通俗的解释。


下面看im2col_gpu_kernel这个函数:

template <typename DType>
__global__ void im2col_gpu_kernel(const int n, const DType* data_im, // __global__ 表示共享内存,并行调用的关键字,gpu核函数必须以此声明
                                                                     // n 代表conv kernel的个数,n=Cin*H*W
                                                                     // DType*  data_im 代表一个张量,为输入图像(Cin,H,W),以_im为后缀的都与输入图像相关
    const int height, const int width, const int kernel_h, const int kernel_w, //feature map的高宽和kernel的高宽
    const int pad_h, const int pad_w,
    const int stride_h, const int stride_w,
    const int dilation_h, const int dilation_w,
    const int height_col, const int width_col, // 输出column的高宽
    DType* data_col) { // 输出column张量,(Kh*Kw*Cin,H,W)
  CUDA_KERNEL_LOOP(index, n) { // 在0-gridDim.x*blockDim.x之间并行,到n结束,index代表第i个conv kernel的索引
    // index index of output matrix
    // 说明一下,若%某个维度的大小,表示要索引这个维度上的位置,而/这个维度的大小则要索引下个维度的位置
    // 这里说的下个维度,是从右向左。如这里的data_im,shape为(Cin,H,W),那么顺序为W,H,Cin
    // / 是总的索引,%是在某个维度上某个范围内的索引
    const int h_index = index / width_col;
    const int h_col = h_index % height_col; // // 在某个c_in的维度下高的索引
    const int w_col = index % width_col;    // 在某个高的维度下宽的索引
    const int c_im = h_index / height_col;  // 输入通道索引
    const int c_col = c_im * kernel_h * kernel_w; // 输出通道索引
    const int h_offset = h_col * stride_h - pad_h; // 输出h的偏移
    const int w_offset = w_col * stride_w - pad_w; // 输出w的偏移
    DType* data_col_ptr = data_col;  //获得输出张量的指针拷贝
    // 指针向前移动,由于index是0-Cin*H*W,c_col,h_col和w_col有Cin、H和W种取值,正好对应index
    data_col_ptr += (c_col * height_col + h_col) * width_col + w_col; 
    const DType* data_im_ptr = data_im; //获取输入张量的指针拷贝
    data_im_ptr += (c_im * height + h_offset) * width + w_offset; //指针向前移动
    for (int i = 0; i < kernel_h; ++i) {
      for (int j = 0; j < kernel_w; ++j) { // 对单个kernel进行循环
        int h_im = h_offset + i * dilation_h;
        int w_im = w_offset + j * dilation_w;
        *data_col_ptr = // *+指针是只取指针所指位置的数值,这里赋值给对应位置
            (h_im >= 0 && w_im >= 0 && h_im < height && w_im < width) ? // 若索引不越界
            data_im_ptr[i * dilation_h * width + j * dilation_w] : static_cast<DType>(0);
        data_col_ptr += height_col * width_col;
      }
    }
  }
}

然后是col2im

template <typename DType>
__global__ void col2im_gpu_kernel(const int n, const DType* data_col,
    const int channels, const int height, const int width,
    const int kernel_h, const int kernel_w,
    const int pad_h, const int pad_w,
    const int stride_h, const int stride_w,
    const int dilation_h, const int dilation_w,
    const int height_col, const int width_col,
    DType* data_im, OpReqType req) {
  CUDA_KERNEL_LOOP(index, n) {
    DType val = 0;
    const int w_im = index % width + pad_w;
    const int h_im = (index / width) % height + pad_h;
    const int c_im = index / (width * height);
    int kernel_extent_w = (kernel_w - 1) * dilation_w + 1;
    int kernel_extent_h = (kernel_h - 1) * dilation_h + 1;
    // compute the start and end of the output
    const int w_col_start =
        (w_im < kernel_extent_w) ? 0 : (w_im - kernel_extent_w) / stride_w + 1;
    const int w_col_end = min(w_im / stride_w + 1, width_col);
    const int h_col_start =
        (h_im < kernel_extent_h) ? 0 : (h_im - kernel_extent_h) / stride_h + 1;
    const int h_col_end = min(h_im / stride_h + 1, height_col);
    // TODO(caffe): use LCM of stride and dilation to avoid unnecessary loops
    for (int h_col = h_col_start; h_col < h_col_end; h_col += 1) {
      for (int w_col = w_col_start; w_col < w_col_end; w_col += 1) {
        int h_k = (h_im - h_col * stride_h);
        int w_k = (w_im - w_col * stride_w);
        if (h_k % dilation_h == 0 && w_k % dilation_w == 0) {
          h_k /= dilation_h;
          w_k /= dilation_w;
          int data_col_index = (((c_im * kernel_h + h_k) * kernel_w + w_k) *
                                height_col + h_col) * width_col + w_col;
          val += data_col[data_col_index];
        }
      }
    }
    KERNEL_ASSIGN(data_im[index], req, val);
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容