当前位置:   article > 正文

深度学习完全攻略!(连载四:GPU加速技术指南)

深度学习完全攻略!(连载四:GPU加速技术指南)

本文已同步至公众号,方便交流:

 

第三章 库函数

这一章,我们就写一写CM中提供了哪些库函数,来帮助我们实现内核的编程。

第一节 属性限定符

Cm设置有函数的限定符,便于这些函数能够被编译器编译成GPU可以运行的文件。在前面的博客中,《深度学习完全攻略。(连载二:GPU加速技术指南)》主要阐述了client端的编程,但是client端的编程是不适用这些限定符的。那么cm中都有哪些限定符呢。

(1)_GENX_MAIN_

此限定符主要用于限定一个GenX的核函数,而且这个核函数不能被其他函数调用

(2)_GENX_

此限定符用于限定一个用户定义的核函数,这个核函数可以被其他核函数调用。

 

第二节 用户定义核函数

用户定义的核函数的参数,可以是基本类型,也可以是vector/matrix,或者 SurfaceIndex/SamplerIndex/VmeIndex,或者vector_ref 和matrix_ref。但是输出类型不能是vector_ref/matrix_ref。

举个例子:

#include <cm/cm.h>

_GENX_ matrix<float, 2, 2> M;    // global data declaration

_GENX_ matrix<float, 4, 4> plusone(matrix<float, 4, 4> m)

{

  return m+1.0f;

}

_GENX_ float foo(matrix<float, 4, 4> m)              // user defined GenX function

{

  matrix<float, 4, 4> newm = plusone(m);

  return cm_sum<float>(newm);

}

_GENX_ void bar_value(vector<float, 16> v, float f)  // user defined GenX function

                                                     // using pass-by-value for "v"

{

  matrix<float, 4, 4> m1;

  m1 = v + f;

  float s = foo(m1);

  M = m1.select<2, 2, 2, 2>(0, 0) * s;

}

_GENX_ void bar_ref(vector<float, 16> v, float f,    // user defined GenX function

                    matrix_ref<float, 2, 2> m)       // using pass-by-reference for "m"

{

  matrix<float, 4, 4> m1;

  m1 = v + f;

  float s = foo(m1);

  m = m1.select<2, 2, 2, 2>(0, 0) * s;

}

_GENX_MAIN_ void kernel(SurfaceIndex inbuf, SurfaceIndex outbuf, // GenX kernel function

                        int x_pos, int y_pos)

{

  matrix<float, 4, 4> m;

  vector<float, 16>   v;

  read(inbuf, x_pos, y_pos, m);

  v = m;

  bar_value(v, 0.5f);

  write(outbuf, x_pos, y_pos, M);

  bar_ref(v, 1.0f, M);

  write(outbuf, x_pos, y_pos + 2, M);

}

 

第三节  内置函数

Cm提供了一些内置的数学运算函数,便于直接调用。就像c语言中<math.h>函数库。这里大致列举一些。如果使用的话,还请查询手册。这些函数主要是以模板的形式存在。

(1)cm_abs<T>:求绝对值

参数1: matrix(_ref), vector(_ref) or scalar type

参数2: flags (default is 0; use SAT for saturation)

返回值: vector or scalar

(2)cm_add<T>:求和

参数1: matrix(_ref), vector(_ref) or scalar

参数2: matrix(_ref), vector(_ref) or scalar

参数3: flags (default is 0; use SAT for saturation)

返回值: vector or scalar

(3)cm_mul<T>:乘法

参数1: matrix(_ref), vector(_ref) or scalar

参数2: matrix(_ref), vector(_ref) or scalar

参数3: flags (default is 0; use SAT for saturation)

返回值: vector or scalar

限制:如果输入是int类型,输出不能位float类型,如果其中一个输入时int,则另外一个不能时float,如果其中一个输入时int或者uint类型,则SAT不能时1.

其他就不多说了。

 

第四节  数据接口

这一节主要介绍两个很重要的函数,read和write。Read和write的作用就是从内存中读数据和写数据。

(1)Media Block Read/Write

主要有两个函数,主要用于读和写整个surface:

void read (SurfaceIndex IND, int X, int Y, matrix_ref<TYPE, M, N> m);

IND:surface index(请参考我的另外一个系列的博客,连载二)

X:左上角x的坐标,以字节为单位,

Y:左上角y的坐标,实际上就是rows

M:是目标矩阵

void write(SurfaceIndex IND, int X, int Y, const matrix m);

IND:surface index(请参考我的另外一个系列的博客,连载二)

X:左上角x的坐标,以字节为单位,

Y:左上角y的坐标,实际上就是rows

M:是目标矩阵

(2)Media Block Read/Write for Planar Surface

这个函数主要用于读或写单个通道的数据

void read_plane(SurfaceIndex IND, CmSurfacePlaneIndex plane_index, int X, int Y, matrix_ref m);

IND:surface index

plane_index:the index to the plane.

X:左上角坐标

Y:左上角坐标就是rows

M:目标数据

void write_plane(SurfaceIndex IND, int plane_index, int X, int Y, const matrix m);

IND:surface index

plane_index:the index to the plane.

X:左上角坐标

Y:左上角坐标就是rows

M:目标数据

 

(3)OWord Block Read/Write

这两个函数主要是针对vector来定义的:

void read(SurfaceIndex IND, int offset, vector_ref v);

INDsurface index,

Offset:开始的坐标

V:目标数据

void write(SurfaceIndex IND, int offset, const vector v);

INDsurface index,

Offset:开始的坐标

V:目标数据

 

第五节  Sampler Interface

这里只介绍一个函数,是对surface进行采样。如下:

void sample16(matrix_ref<T, N, 16> m, ChannelMaskType channelMask,  SurfaceIndex surfIndex, SamplerIndex sampIndex, vector<float, 16> u,  vector<float, 16> v = 0, vector<float, 16> r = 0);

M:保存返回的结果。 N表示最小的返回的channel的个数。

channelMask:表是对哪些channel做采样。

surfIndexsurface index.

sampIndex:采样状态表的index

U:归一化的x坐标 the normalized x coordinates of the texels to be sampled.

V (optional, default = 0) :归一化的y坐标

R (optional, default = 0):归一化的z坐标,对于3D而言。

 

第六节  Media Walker Interface

这个是非常关键的一个参数。当输入的图像很大时,我们当然希望让GPU中的每个执行单元去分别计算每个块,那么怎么设置呢?这就是这一节的主要内容。

unsigned short get_thread_origin_x();

unsigned short get_thread_origin_y();

这两个函数就是返回所有需要GPU运行内核的初始坐标。比如说:一个图像按照16*16这样划分块。在不考虑块重叠的情况下,那么确定每个内核操作图像的起始坐标就是:

get_thread_origin_x()*16, get_thread_origin_y()*16)

系统会自动帮你返回当前这个内核在图像某个块的起始位置,然后乘以块的长度即可,实际上就是stride。

 

这一章主要是作为一个引子,cm内核里面还有很多东西,本文就不一一列举了。

 

我相信很多同学已经发现了,既然client端和server端的代码都有了,那么这两者是怎么联系起来的呢,又是如何运行起来的呢?这些将在下一篇博客中介绍。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/677387
推荐阅读
相关标签
  

闽ICP备14008679号