opencl:改造C++接口增加对内存编译(compile)的支持

原创 2016年03月03日 16:38:39

OpenCL 1.2以前的标准(1.0,1.1),只支持单个源文件编译成可执行程序(Executable Program),所以只提供了clBuildProgram函数。
OpenCL 1.2以后,可以将complie/link两个动作分开,增加了clCompileProgram, clLinkProgram函数,允许将多个源码编译成一个可执行程序。
clCompileProgram将一段内核代码编译成非可执行的cl::Progam对象(类似于obj文件)。
clLinkProgram则可以将多个obj对象连接生成新的可执行的cl::Program对象(Executable Program)。
下面是clCompileProgram 的函数定义:

cl_int clCompileProgram (   cl_program program,
    cl_uint num_devices,
    const cl_device_id *device_list,
    const char *options,// 编译选项字符串
    cl_uint num_input_headers, // 代码#include的文件数目
    const cl_program *input_headers,// 每个#include的文件对应的cl_program对象(由clCreateProgramWithSource生成)
    const char **header_include_names,//与input_headers对应的每个cl_program对象在代码中的#include<文件名>
    void (CL_CALLBACK *pfn_notify)( cl_program program, void *user_data),
    void *user_data)

假设一段内核源码中有#include语句,导入了一个头文件定义,那么OpenCL编译器该从哪里找这些头文件呢?
有两种方法:

  1. 在options指定的编译选项中加入-I path 选项,告诉编译器在path指定的路径下寻找#include文件
  2. 将内核源码中所有#include文件内容转成cl_program,以数组形式提供作为input_headers参数,同时将每个#include的文件名作为名字表以数组形式提供作为header_include_names(与input_headers一一对应),这样编译就会从这张表中找到代码中每个#include文件的内容了。

如果在编译代码时以上两个方法都使用了,编译器优先使用方法2提供的头文件

第一种方法很常用也很容易理解,就跳过不说了,这里要着重说明的是第二种编译方法的意义:
clCompileProgram在编译一段OpenCL内核源码(字符串)时,源码中所#include的文件内容可以像源码本身一样不必存在于本地文件系统(硬盘/存储卡),也就是不依赖文件系统只依赖内存的编译,所以在嵌入式系统或网络应用中这种方式适应性更好。

原本我的项目中是打算使用第二种方式来编译源码的。但打开OpenCL 1.2的C++接口代码(cl.hpp)找到clCompileProgram对应的cl::Program::compile成员函数一看,傻了:

#if defined(CL_VERSION_1_2)
    cl_int compile(
        const char* options = NULL,
        void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
        void* data = NULL) const
    {
        return detail::errHandler(
            ::clCompileProgram(
                object_,
                0,
                NULL,
                options,
                0,
                NULL,
                NULL,
                notifyFptr,
                data),
                __COMPILE_PROGRAM_ERR);
    }
#endif

上面的代码可以看出cl::Program::compile在调用clCompileProgram的时候,直接将num_input_headers置为0,将 input_headers,header_include_names置为NULL了。也就是说Open CL C++接口没有提供第二种引入#include的编译方式,尼玛,你故意的吧?!

所以基于OpenCL C++接口开发,且需要进行内核源码的内存编译的情况下,需要自己写compile函数,实现这部分功能,我的办法是继承cl::Program写个新的类ProgramExt,增加一个支持内存编译compile函数:

下面是complie函数源码:

#define _DEF_STRING(x) #x
// 内核源码描述类 pair.first为源码名字,pair.second为源码对象(cl::Progam)
using program_info_type =std::pair<std::string,cl::Program>;
class ProgramExt:public cl::Program{
    using cl::Program::Program; // 继承cl::Program所有的构造函数
#if defined(CL_VERSION_1_2)
    cl_int compile(
        const std::vector<cl::Device> &devices,
        const std::vector<program_info_type> &input_headers,
        const char* options = NULL,
        void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
        void* data = NULL) const
{
    // 生成cl_device_id数组
    auto deviceIDs=cl::cl_c_vector(devices);//cl_c_vector函数实现参见下面的代码
    // 生成cl_program数组
    auto headers=cl::cl_c_vector2(input_headers);//cl_c_vector2函数实现参见下面的代码
    // 生成头文件名字数组
    auto include_names=cl::cl_c_vector1(input_headers);//cl_c_vector1函数实现参见下面的代码
    return cl::detail::errHandler(
        ::clCompileProgram(
            object_,
            (cl_uint)deviceIDs.size(),
            deviceIDs.size()?deviceIDs.data():nullptr,
            options,
            (cl_uint)headers.size(),
            // headers.size()为0时input_headers设置为nullptr
            headers.size()?headers.data():nullptr,
            // headers.size()为0时header_include_names设置为nullptr
            (const char**)(headers.size()?include_names.data():nullptr),
            notifyFptr,
            data),
            _DEF_STRING(clCompileProgram));
}        
#endif
};
// 上面代码中用到的支持函数cl_c_vector,cl_c_vector1,cl_c_vector2模板函数的实现代码
namespace cl{
/* 将OpenCL C++对象数组转为对应的C对象数组 */
template<typename F,typename T=typename F::cl_type>
std::vector<T> cl_c_vector(const std::vector<F> &from){
    std::vector<T> v(from.size());
    for( auto i = from.size(); i >0; --i ) {
        v[i-1] = from[i-1]();
    }
    return std::move(v);
}
/* 拆分std::pair,返回pair::first数组 */
template<typename F1,typename F2 >
std::vector<F1> cl_c_vector1(const std::vector<std::pair<F1,F2>> &from){
    auto v=std::vector<F1>(from.size());
    for(auto i=from.size();i>0;--i){
        v[i-1]=from[i-1].first;
    }
    return std::move(v);
}
/* 拆分std::pair,返回pair::first数组(char*) */
template<typename F2 >
std::vector<char*> cl_c_vector1(const std::vector<std::pair<std::string,F2>> &from){
    auto v=std::vector<char*>(from.size());
    for(auto i=from.size();i>0;--i){
        v[i-1]=(char*)from[i-1].first.data();
    }
    return std::move(v);
}
/* 拆分std::pair,返回pair::second(OpenCL C++对象)数组并转为C对象数组 */
template<typename F1,typename F2 ,typename T=typename F2::cl_type>
std::vector<T> cl_c_vector2(const std::vector<std::pair<F1,F2>> &from){
    auto v=std::vector<T>(from.size());
    for(auto i=from.size();i>0;--i){
        v[i-1]=from[i-1].second();
    }
    return std::move(v);
}
} /* namespace cl */

关于如何调用OpenCL C++接口编译内核代码的更详细内容,参见我的上一篇博客《C++代码设计:向Java借鉴Builder模式塈OpenCL内核代码编译》

(以上所有代码都为C++11撰写)

版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/50789158

error MSB4018 “CL”任务意外失败

vs2010:error MSB4018 “CL”任务意外失败 vs2010:error MSB4018 “CL”任务意外失败 今天VS2010编译一个工程突然报“error MSB4018 “CL...
  • kbkiss2010
  • kbkiss2010
  • 2015-11-24 21:28:46
  • 2659

一步步通过命令行cl.exe编译Windows程序

Windows系统下有强大的IDE工具VS,VC6.0等,隐藏了很多编译和链接的细节。   事实上,VS是通过调用cl.exe进行源代码编译。接着调用link.exe进行目标文件的链接等操作。    ...
  • faithzzf
  • faithzzf
  • 2016-08-26 14:51:31
  • 5078

在cmd下用cl命令编译运行C/C++源文件

一直用java来写程序,java配置好jre路径之后,在cmd下编译运行,很方便。 刚好要给一个舍友改下C程序,想到可不可以像java一样在环境变量里配置好C的编译路径呢? 于是上网搜了一下,得到...
  • canlets
  • canlets
  • 2014-06-07 18:53:46
  • 9296

cl命令编译

CL.exe 的命令行编译断断续续接触了几次,每次都忘,每次都重新查。每次都搞不清楚语法,尤其是传给linker的参数...
  • u012787710
  • u012787710
  • 2015-09-18 22:22:11
  • 662

DOS 使用CL命令 编译程序

微软官方msdn上介绍cl的使用方法是”开始“-”所有程序“-"Visual Studio 2013"-"Visual Studio Tools"再以管理员身份运行”VS2013开发人员命令提示”,之...
  • guchaono1
  • guchaono1
  • 2017-05-09 09:25:25
  • 646

VC++中cl.exe编译器的使用

转自百度博客:http://hi.baidu.com/lingyin55/blog/item/1545ddd08e421895a0ec9cdb.html   和在IDE中编译相比,命令行模式编译速...
  • zssureqh
  • zssureqh
  • 2012-05-29 19:03:09
  • 9886

opencv+opencl c++程序移植心酸路

因为想要再其他两个节点运行该程序,所以踏上不归路。方法一 在每个节点配置相同环境在其他节点配置的环境必须要与原机的环境完全相同。这里包括: 1. 编译环境相同 2. ffmpeg版本相同 (在...
  • hallao0
  • hallao0
  • 2017-05-21 13:41:29
  • 288

Opencl 在线和离线编译

OpenCL 支持在线编译和离线编译两种编译方式,主要区别是内核程序提供给主机调用方式。 在线编译:在host程序中引入的是Kernel的源代码 离线编译:Host程序中引入的是在目标器件上运行的二进...
  • u013625961
  • u013625961
  • 2016-11-24 16:09:48
  • 967

【VS2013】OpenCl环境搭建&helloworld

前面几步都比较简单,最后一步需要如下3小步: 1.项目属性->VC++目录->包含目录(include),C:\Program Files (x86)\AMD APP SDK\2.9\include。...
  • hermittt
  • hermittt
  • 2016-02-15 15:56:42
  • 5321

OpenCL学习笔记(三):OpenCL安装,编程简介与helloworld

从图中可以看出(参考《OpenCL 编程入门》): 1. 异构计算设备,可以是CPU或GPU。现在也有支持OpenCL的FPGA设备和至强融核协处理设备(MIC)。 2. OpenCL的API通过Co...
  • xbinworld
  • xbinworld
  • 2015-06-14 23:37:27
  • 8249
收藏助手
不良信息举报
您举报文章:opencl:改造C++接口增加对内存编译(compile)的支持
举报原因:
原因补充:

(最多只允许输入30个字)