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

此刻我正在喝茶!

 

Cm compiler的repo如下:

https://github.com/intel/cm-compiler.git

 

这一节写一写CM中client端如何编程。client是直接与应用程序交互,同时连接GPU的kernel。如下内容按照调用的先后顺序来写。话不多罗嗦,上干货。

 

前言

cm_result_check()这个函数主要用来检查client函数的返回值。

 

第一步:

创建CM的device

 

CmDevice *device = nullptr;

unsigned int version = 0;

cm_result_check(::CreateCmDevice(device, version));

 

这一步中,CreateCmDevice()会创建一个GPU的device,后续所有的操作都是在这个device上操作。实际上是物理GPU的一个抽象,也可以理解为物理上的GPUdevice,。

 

第二步:

1,加载kernel部分的加速文件,这个文件是由kernel的执行函数由编译器编译而来。以.isa后缀结尾。例如,linear_walker_genx.isa这个文件是由linear_walker_genx.cpp文件经过编译而来。(这一部分在后面介绍)

 

std::string isa_code = cm::util::isa::loadFile("linear_walker_genx.isa");

 

2,加载program。

 

CmProgram *program = nullptr;

cm_result_check(device->LoadProgram(const_cast<char*>(isa_code.data()),isa_code.size(),program));

 

这一步是创建包含上述isa中内核的program.

 

第三步:

上面有了device,有了可以放kernel的program,那么就可以创建kernel了,比如说我们创建了一个“linear”的kernel。

 

CmKernel *kernel = nullptr;

cm_result_check(device->CreateKernel(program, "linear", kernel));

 

第四步:

有了kernel,那么函数执行时,输入输出,就需要有保存和接收的地方。这就是surface。

我们先创建一个输入的surface:(相当于分配内存空间了)

 

CmSurface2D *input_surface = nullptr;

cm_result_check(device->CreateSurface2D(width*3/4,height,CM_SURFACE_FORMAT_A8R8G8B8,input_surface));

 

注意,这个地方为什么有个*3/4呢,因为,对于CM_SURFACE_FORMAT_A8R8G8B8格式,每个像素占用32位,而对于RGB图像而言,占用24位,所以创建的surface,应该是format的宽度乘以3除以4,例如,32*3/4=24。

将图像写到分配的空间中:

 

cm_result_check(input_surface->WriteSurface(input_image.getData(), nullptr));

 

我们再创建一个输出的surface:

 

cm_result_check(device->CreateSurface2D(width*3/4,height,CM_SURFACE_FORMAT_A8R8G8B8,output_surface));

 

第五步:

这一步我们就是想有多少个相同的kernel来执行kernel函数。怎么做呢?

假设,我们每次处理的块大小为:6x8,那么,

 

int thread_width = width/8;

int thread_height = height/6;

 

Thread_width 和thread_height实际上是一个线程池,这个线程池的宽度和高度跟处理的块大小是有关系的,width和height是图像的高度和宽度。线程池里面有很多线程,系统会把这些线程分配给每个GPU的执行单元去并行执行。好了,让我们创建线程池:

 

CmThreadSpace *thread_space = nullptr;

cm_result_check(device->CreateThreadSpace(thread_width,thread_height,thread_space));

 

第六步:

这一步,我们需要把输入和输出的surface关联起来,什么意思呢?Kernel有了,线程池有了,输入输出有了,那么,把这些都整合起来才能去执行,不是吗?

1,拿到输入的一个引用,用surfaceindex来表示。

 

SurfaceIndex *input_surface_idx = nullptr;

cm_result_check(input_surface->GetIndex(input_surface_idx));

 

2,把输入关联到kernel里面

 

cm_result_check(kernel->SetKernelArg(0,sizeof(SurfaceIndex),input_surface_idx));

 

3,拿到输出的引用:

 

SurfaceIndex *output_surface_idx = nullptr;

cm_result_check(output_surface->GetIndex(output_surface_idx));

 

4,把输出关联到kernel里面

 

cm_result_check(kernel->SetKernelArg(1,sizeof(SurfaceIndex),output_surface_idx));

 

第七步:

准备,让GPU跑起来。

1,创建一个队列,这个队列里面放的都是task.

 

CmQueue *cmd_queue = nullptr;

cm_result_check(device->CreateQueue(cmd_queue));

 

2,创建一个task,这个task里面放的是要执行的kernel。

 

CmTask *task = nullptr;

cm_result_check(device->CreateTask(task));

 

3,把kernel放到task里面。

 

cm_result_check(task->AddKernel(kernel));

 

4,把task放到queue里面,一个task 执行完了,就执行下一个task。

 

CmEvent *sync_event = nullptr;

cm_result_check(cmd_queue->Enqueue(task,sync_event,thread_space));

 

第八步:

拿结果,卸载CM的device。

1,我们把task终结了。

 

cm_result_check(device->DestroyTask(task));

 

2,把线程池也搞没

 

cm_result_check(device->DestroyThreadSpace(thread_space));

 

3,把surface变成图像。

 

cm_result_check(output_surface->ReadSurface(output_image.getData(),sync_event));

 

4,还想了解下GPU跑了多长时间。

 

UINT64 execution_time = 0;

cm_result_check(sync_event->GetExecutionTime(execution_time));

 

5,把event也摧毁了。(还记得第七步的最后一步吗,event是用来监控task执行状态的。)

 

cm_result_check(cmd_queue->DestroyEvent(sync_event));

 

6,摧毁GPU虚假的device。

 

cm_result_check(::DestroyCmDevice(device));

 

7,保存处理完的图像。

 

output_image.save("linear_out.bmp");

 

好了,跟app打交道的client端设计就介绍到这里了,相信细心的同学肯定会发现问题,比如说,第二步和第三步中的ISA又是个什么东西,怎么来的。这一部分,且听下回分解。

 

本文已同步至公众号,欢迎关注!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值