因为学习cuda的原因,所以最近一段时间对GPU编程比较感兴趣。大家都知道,cuda是属于nvidia公司的产品,那么我就在想,对于其他公司开发的GPU产品,他们是怎么做的?结果就是opencl编程。
1、opencl编程
opencl支持nvidia、ati、arm等多个gpu,也可以在嵌入式设备上完成。
2、opencl的编译环境
大部分安装了gpu的编程环境,比如说cuda sdk,就可以开发opencl编程了。
3、最简单的opencl代码
https://github.com/giobauermeister/OpenCL-test-apps/blob/master/cl_sample_timer/cl_sample_timer.c
4、编译opencl代码的时候需要注意什么
在链接的时候添加OpenCL.lib即可
5、查询opencl设备信息
用到的api主要就是两个,clGetPlatformIDs & clGetPlatformInfo
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <CL/cl.h>
int main()
{
//cl_platform 表示一个OpenCL的执行平台,关联到GPU硬件,如N卡,AMD卡
cl_platform_id *platforms;
//OpenCL中定义的跨平台的usigned int和int类型
cl_uint num_platforms;
cl_int i, err, platform_index = -1;
char* ext_data;
size_t ext_size;
const char icd_ext[] = "cl_khr_icd";
//要使platform工作,需要两个步骤。1 需要为cl_platform_id结构分配内存空间。2 需要调用clGetPlatformIDs初始化这些数据结构。一般还需要步骤0:询问主机上有多少platforms
//查询计算机上有多少个支持OpenCL的设备
err = clGetPlatformIDs(5, NULL, &num_platforms);
if (err < 0)
{
perror("Couldn't find any platforms.");
exit(1);
}
printf("本机上支持OpenCL的环境数量: %d\n", num_platforms);
//为platforms分配空间
platforms = (cl_platform_id*)
malloc(sizeof(cl_platform_id) * num_platforms);
clGetPlatformIDs(num_platforms, platforms, NULL);
//获取GPU平台的详细信息
for (i = 0; i < num_platforms; i++)
{
//获取缓存大小
err = clGetPlatformInfo(platforms[i],
CL_PLATFORM_EXTENSIONS, 0, NULL, &ext_size);
if (err < 0)
{
perror("Couldn't read extension data.");
exit(1);
}
printf("缓存大小: %d\n", ext_size);
ext_data = (char*)malloc(ext_size);
//获取支持的扩展功能
clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS,
ext_size, ext_data, NULL);
printf("平台 %d 支持的扩展功能: %s\n", i, ext_data);
//获取显卡的名称
char *name = (char*)malloc(ext_size);
clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME,
ext_size, name, NULL);
printf("平台 %d 是: %s\n", i, name);
//获取显卡的生产商名称
char *vendor = (char*)malloc(ext_size);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VENDOR,
ext_size, vendor, NULL);
printf("平台 %d 的生产商是: %s\n", i, vendor);
//获取平台版本
char *version = (char*)malloc(ext_size);
clGetPlatformInfo(platforms[i], CL_PLATFORM_VERSION,
ext_size, version, NULL);
printf("平台 %d 的版本信息: %s\n", i, version);
//查询显卡是独立的还是嵌入的
char *profile = (char*)malloc(ext_size);
clGetPlatformInfo(platforms[i], CL_PLATFORM_PROFILE,
ext_size, profile, NULL);
printf("平台 %d 是独立的(full profile)还是嵌入式的(embeded profile)?: %s\n", i, profile);
//查询是否支持ICD扩展
if (strstr(ext_data, icd_ext) != NULL)
platform_index = i;
std::cout << "平台ID = " << platform_index << std::endl;
/* Display whether ICD extension is supported */
if (platform_index > -1)
printf("平台 %d 支持ICD扩展: %s\n",
platform_index, icd_ext);
std::cout << std::endl;
//释放空间
free(ext_data);
free(name);
free(vendor);
free(version);
free(profile);
}
if (platform_index <= -1)
printf("No platforms support the %s extension.\n", icd_ext);
getchar();
//释放资源
free(platforms);
return 0;
}
6、opencl和cuda的相同之处
opencl和cuda结构上差不多,一部分api代码都是在cpu运行的,只有kernel部分的代码才是在gpu上运行的。当然除了c语言之外,opencl和cuda都是支持python的,将kernel代码传给python就可以了,使用起来不复杂。
7、opencl和cuda的关系
opencl&cuda有点类似于opengl&direct x的关系。对于跨平台来说,opencl更好,但是cuda的生态更棒。本身cuda除了cudnn之外,还提供了很多的第三方库,这些对于需要数学库移植的同学来说太方便了。
8、个人怎么学习使用
如果是在nvidia的环境开发,那么多多使用cuda,总归没有错的。但是如果arm系列的gpu,opencl基本上是你唯一的选择。不然,你只能使用neon指令去加速一些特殊的算法了。
9、opencl & opencv
现在很多朋友使用opencl加速opencv的效果不明显,建议可以自己编写opencv算法、测试高分辨率图片、用很多图片来测试算法,看看原因出在哪里。另外测试的时候多让图像在gpu保存,不要在cpu和gpu之间搬来搬去,这样很浪费时间。
其他:
当然,现在除了opencl,也有一种并发框架,类似于openmp,它就是openacc,https://www.openacc.org/get-started
参考代码链接在这https://yq.aliyun.com/articles/71262,内容如下
#include<stdio.h>
#define N 256
int main()
{
int i,a[N],b[N],c[N];
for(i=0;i<N;i++)
{
a[i]=0;
b[i]=c[i]=i;
}
#pragma acc kernels
for(i=0;i<N;i++)
{
a[i]=b[i]+c[i];
}
printf("a[N-1]=%d \n",a[N-1]);
return 0;
}
ps:
目前soc上面的dsp、fpga、gpu、nn其实都是可以用来数据计算的。只不过因为功耗、习惯、技术积累的原因,通常我们只使用其中的一种技术。这个时候,就要适当多了解一下其他硬件编程技术。不管从哪方面讲,opencl&cuda都是非常不错的,既满足了并发的要求,而且学习难度上也小很多。dsp&fpga太小众了,nn也是每一家都有自己的方案,自己能控制的可能只剩下GPU编程这一种了。
安装了cuda之后,就可以opencl编程