第二十九章 使用OpenCL工作
OpenCL 程序用来执行支持OpenCL 1.1或更高版本的视频卡上的计算。 现代视频卡包含数百个小型专用处理器,可以同时与输入数据流进行简单的数学运算。 OpenCL语言组织了并行计算并且为一类特定任务提供了更快的速度。
在一些图形卡中默认禁止处理 双精度(double)类型数字。这可能会导致编译错误5105。若要启用支持 双精度(double)类型数字,请添加以下指令到您的OpenCL程序:
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
更多详情请参考以下链接
https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/cl_khr_fp64.html
但是,如果图形卡不支持 双精度(double)型,启用该指令则不会有帮助。
建议以单独的CL文件编写OpenCL源代码,稍后将其包含进使用资源变量MQL5程序。
运行OpenCL程序中的函数:
函数 | 功能 |
---|---|
CLHandleType | 返回OpenCL句柄的类型,作为ENUM_OPENCL_HANDLE_TYPE枚举值 |
CLGetInfoInteger | 为OpenCL对象 或 设备返回一个整数属性值 |
CLContextCreate | 创建OpenCL上下文(容器) |
CLContextFree | 移除OpenCL上下文(容器) |
CLGetDeviceInfo | 从OpenCL驱动程序接收设备属性 |
CLProgramCreate | 从源代码创建OpenCL程序 |
CLProgramFree | 移除OpenCL程序 |
CLKernelCreate | 创建OpenCL开始函数 |
CLKernelFree | 移除OpenCL开始函数 |
CLSetKernelArg | 为OpenCL函数设置一个参数 |
CLSetKernelArgMem | 设置OpenCL缓冲作为OpenCL函数的参数 |
CLSetKernelArgMemLocal | 设置本地缓冲区作为内核函数参数 |
CLBufferCreate | 创建OpenCL缓冲 |
CLBufferFree | 删除OpenCL缓冲 |
CLBufferWrite | 写入数组到OpenCL缓冲 |
CLBufferRead | 读取OpenCL缓冲到数组 |
CLExecute | 运行OpenCL程序 |
CLExecutionStatus | 返回OpenCL程序执行状态。 |
相关参考
OpenCL, 资源
# 29.1 CLHandleType
返回OpenCL句柄的类型,作为ENUM_OPENCL_HANDLE_TYPE枚举的值。
ENUM_OPENCL_HANDLE_TYPE CLHandleType(
int handle // OpenCL对象处理
);
2
3
参数
handle
[in] OpenCL对象的句柄:上下文,内核或OpenCL程序。
返回值
OpenCL句柄的类型,作为ENUM_OPENCL_HANDLE_TYPE枚举的值。
ENUM_OPENCL_HANDLE_TYPE
标识符 | 描述 |
---|---|
OPENCL_INVALID | 不正确处理 |
OPENCL_CONTEXT | OpenCL上下文的句柄 |
OPENCL_PROGRAM | OpenCL程序的句柄 |
OPENCL_KERNEL | OpenCL内核的句柄 |
OPENCL_BUFFER | OpenCL缓冲的句柄 |
# 29.2 CLGetInfoInteger
返回OpenCL对象 或 设备的整数属性的值。
long CLGetInfoInteger(
int handle, // OpenCL对象处理或OpenCL设备编号
ENUM_OPENCL_PROPERTY_INTEGER prop // 被请求属性
);
2
3
4
参数
handle
[in] OpenCL对象的句柄或OpenCL设备的编号。 OpenCL设备的编号从零开始。
prop
[in] 从ENUM_OPENCL_PROPERTY_INTEGER枚举中请求的属性的类型,您要获取的值。
返回值
如果成功则返回 属性的值,或 一旦错误则返回 -1。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
ENUM_OPENCL_PROPERTY_INTEGER
标识符 | 描述 | 类型 |
---|---|---|
CL_DEVICE_COUNT | OpenCL支持的设备编号. 此属性不要求第一个参数规范,即你可以处理参数通过一个为零的值 | int |
CL_DEVICE_TYPE | 设备类型 | ENUM_CL_DEVICE_TYPE |
CL_DEVICE_VENDOR_ID | 唯一的供应商识别 | uint |
CL_DEVICE_MAX_COMPUTE_UNITS | OpenCL设备中的并行计算任务编号. 一个工作组解决了一个计算任务.最低值为1 | uint |
CL_DEVICE_MAX_CLOCK_FREQUENCY | 设备最高的固定频率兆赫. | uint |
CL_DEVICE_GLOBAL_MEM_SIZE | 设备的全局内存字节大小 | ulong |
CL_DEVICE_LOCAL_MEM_SIZE | 处理数据(事件)本地内存的字节大小 | uint |
CL_BUFFER_SIZE | OpenCL字节缓冲区的实际大小 | ulong |
CL_DEVICE_MAX_WORK_GROUP_SIZE | 可用于OpenCL设备的本地工作组总数。 | ulong |
CL_KERNEL_WORK_GROUP_SIZE | 可用于OpenCL程序的本地工作组总数。 | ulong |
CL_KERNEL_LOCAL_MEM_SIZE | 由OpenCL程序用于解决一组全部并行任务的本地存储器大小(字节)。 使用CL_DEVICE_LOCAL_MEM_SIZE 接收最大可用值 | ulong |
CL_KERNEL_PRIVATE_MEM_SIZE | OpenCL程序内核中,每个任务使用的最小专有存储器大小(字节)。 | ulong |
ENUM_CL_DEVICE_TYPE枚举包含了可能支持OpenCL的设备类型。您可以通过调用CLGetInfoInteger(handle_or_deviceN,CL_DEVICE_TYPE),通过其编号或OpenCL对象的句柄来接收设备类型。
ENUM_CL_DEVICE_TYPE
标识符 | 描述 |
---|---|
CL_DEVICE_ACCELERATOR | OpenCL专用加速器 (比如, IBM CELL Blade). 这些设备使用外设互连如PCIe与主处理器进行通信. |
CL_DEVICE_CPU | 一个OpenCL主机处理器设备. 主机处理器运行OpenCL运转且为一个单核或多核CPU. |
CL_DEVICE_GPU | 一个OpenCL设备是GPU. |
CL_DEVICE_DEFAULT | 系统默认的OpenCL设备. 默认设备不能为 |
CL_DEVICE_CUSTOM | 专用的加速器不支持OpenCL C编写的程序. |
示例:
void OnStart()
{
int cl_ctx;
//--- 初始化 OpenCL 上下文
if((cl_ctx=CLContextCreate(CL_USE_GPU_ONLY))==INVALID_HANDLE)
{
Print("OpenCL not found");
return;
}
//--- 显示有关OpenCL设备的常规信息
Print("OpenCL type: ",EnumToString((ENUM_CL_DEVICE_TYPE)CLGetInfoInteger(cl_ctx,CL_DEVICE_TYPE)));
Print("OpenCL vendor ID: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_VENDOR_ID));
Print("OpenCL units: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_MAX_COMPUTE_UNITS));
Print("OpenCL freq: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_MAX_CLOCK_FREQUENCY)," MHz");
Print("OpenCL global mem: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_GLOBAL_MEM_SIZE)," bytes");
Print("OpenCL local mem: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_LOCAL_MEM_SIZE)," bytes");
//---
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 29.3 CLGetInfoString
返回OpenCL对象 或 设备的属性的字符串值。
bool CLGetInfoString(
int handle, // OpenCL对象处理或者OpenCL设备号码
ENUM_OPENCL_PROPERTY_STRING prop, // 被请求的属性
string& value // 引用字符串
);
2
3
4
5
参数
handle
[in] OpenCL对象句柄或OpenCL设备号。 OpenCL设备的编号从零开始。 prop
[in] 从 ENUM_OPENCL_PROPERTY_STRING 枚举中请求的属性的类型,您要获取的值
value
[out] 接收属性值的字符串.
返回值
成功则返回true, 否则返回 false. 想要获取有关错误的详细信息,请调用 GetLastError() 函数.
ENUM_OPENCL_PROPERTY_STRING
标识符 | 描述 |
---|---|
CL_PLATFORM_PROFILE | OpenCL简介. 简介名称可以是以下值中的某一个: FULL_PROFILE - 实现支持OpenCL(功能定义为核心规范的一部分而无需为OpenCL支持额外的扩展) ; EMBEDDED_PROFILE - 实现支持OpenCL作为增补. 修订简介被定义为每个OpenCL版本的子集. |
CL_PLATFORM_VERSION | OpenCL版本 |
CL_PLATFORM_VENDOR | 平台供应商名称 |
CL_PLATFORM_EXTENSIONS | 由平台支持的扩展名列表。扩展名应该能被所有与这个平台相 |
CL_DEVICE_NAME | 设备名称 |
CL_DEVICE_VENDOR | 供应商名称 |
CL_DRIVER_VERSION | major_number.minor_number格式OpenCL设备版本 |
CL_DEVICE_PROFILE | OpenCL设备简介. 简介名称可以是以下值的某一个: FULL_PROFILE - 实现支持OpenCL(功能定义为核心规范的一部分而无需为OpenCL支持额外的扩展); EMBEDDED_PROFILE -实现支持OpenCL作为增补. 修订简介被定义为每个OpenCL版本的子集. |
CL_DEVICE_VERSION | OpenCL<space><major_version.minor_version><space><vendor-specific information> 格式OpenCL版本 |
CL_DEVICE_EXTENSIONS | 设备所支持的扩展名列表。列表可以包含由供应商支持的扩展,以及一个或多个批准名称: cl_khr_int64_base_atomics cl_khr_int64_extended_atomics cl_khr_fp16 cl_khr_gl_sharing cl_khr_gl_event cl_khr_d3d10_sharing cl_khr_dx9_media_sharing cl_khr_d3d11_sharing |
CL_DEVICE_BUILT_IN_KERNELS | 可支持的内核列表用 ";"分割. |
CL_DEVICE_OPENCL_C_VERSION | 这个设备的编译器所支持的最大版本。版本格式:OpenCL<space>C<space><major_version.minor_version><space><vendor-specific information> |
例如:
void OnStart()
{
int cl_ctx;
string str;
//--- 初始化 OpenCL 上下文
if((cl_ctx=CLContextCreate(CL_USE_GPU_ONLY))==INVALID_HANDLE)
{
Print("OpenCL not found");
return;
}
//--- 显示平台信息
if(CLGetInfoString(cl_ctx,CL_PLATFORM_NAME,str))
Print("OpenCL platform name: ",str);
if(CLGetInfoString(cl_ctx,CL_PLATFORM_VENDOR,str))
Print("OpenCL platform vendor: ",str);
if(CLGetInfoString(cl_ctx,CL_PLATFORM_VERSION,str))
Print("OpenCL platform ver: ",str);
if(CLGetInfoString(cl_ctx,CL_PLATFORM_PROFILE,str))
Print("OpenCL platform profile: ",str);
if(CLGetInfoString(cl_ctx,CL_PLATFORM_EXTENSIONS,str))
Print("OpenCL platform ext: ",str);
//--- 显示设备信息
if(CLGetInfoString(cl_ctx,CL_DEVICE_NAME,str))
Print("OpenCL device name: ",str);
if(CLGetInfoString(cl_ctx,CL_DEVICE_PROFILE,str))
Print("OpenCL device profile: ",str);
if(CLGetInfoString(cl_ctx,CL_DEVICE_BUILT_IN_KERNELS,str))
Print("OpenCL device kernels: ",str);
if(CLGetInfoString(cl_ctx,CL_DEVICE_EXTENSIONS,str))
Print("OpenCL device ext: ",str);
if(CLGetInfoString(cl_ctx,CL_DEVICE_VENDOR,str))
Print("OpenCL device vendor: ",str);
if(CLGetInfoString(cl_ctx,CL_DEVICE_VERSION,str))
Print("OpenCL device ver: ",str);
if(CLGetInfoString(cl_ctx,CL_DEVICE_OPENCL_C_VERSION,str))
Print("OpenCL open c ver: ",str);
//--- 显示OpenCL设备的常规信息
Print("OpenCL type: ",EnumToString((ENUM_CL_DEVICE_TYPE)CLGetInfoInteger(cl_ctx,CL_DEVICE_TYPE)));
Print("OpenCL vendor ID: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_VENDOR_ID));
Print("OpenCL units: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_MAX_COMPUTE_UNITS));
Print("OpenCL freq: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_MAX_CLOCK_FREQUENCY));
Print("OpenCL global mem: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_GLOBAL_MEM_SIZE));
Print("OpenCL local mem: ",CLGetInfoInteger(cl_ctx,CL_DEVICE_LOCAL_MEM_SIZE));
//---
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 29.4 CLContextCreate
创建一个OpenCL上下文并返回其句柄。
int CLContextCreate(
int device // OpenCL设备或宏的序列号
);
2
3
参数
device
[in] 系统中的OpenCL设备序号. 你可以指定下列值之一,而不是一个具体的数字:
• CL_USE_ANY —— 任何支持OpenCL的有效设备均被允许;
• CL_USE_CPU_ONLY —— 仅CPU中的OpenCL仿真被允许;
• CL_USE_GPU_ONLY —— OpenCL仿真被禁止并且仅有OpenCL支持的专业设备 (视频卡) 可以被使用.
返回值
如果成功创建OpenCL上下文对象将返回句柄。一旦出错 则返回 -1 . 想要获取有关错误的详细信息,请调用 GetLastError() 函数.
# 29.5 CLContextFree
删除一个OpenCL上下文并返回其句柄。
void CLContextFree(
int context // 一个OpenCL上下文的句柄
);
2
3
参数
context
[in] OpenCL 上下文的句柄。
返回值
无。 在内部错误情况下 _LastError 的值会改变。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
# 29.6 CLGetDeviceInfo
此函数从OpenCL驱动中接收设备的属性信息。
bool CLGetDeviceInfo(
int handle, // OpenCL设备处理
int property_id, // 被请求的属性 ID
uchar& data[], // 数据接收数组
uint& size // 数组中的元素转换,默认为0
);
2
3
4
5
6
参数
handle
[in] 由CLContextCreate()函数创建的OpenCL设备索引 或 OpenCL句柄。
property_id
[in] 应接收的OpenCL设备属性的ID。这些值可能是下表中列出的枚举值之一。
data[]
[out] 用于接收所请求属性的数据的数组。
size
[out] 接收数据的数组 data[] 的大小.
返回值
如果成功则为true ,否则为 false. 想要获取有关错误的详细信息,请调用 GetLastError() 函数.
注意
对于一维数组,考虑AS_SERIES标帜计算从中读取OpenCL缓冲区的数据的元素的数量。
OpenCL设备属性的可用ID列表
可以在OpenCL官方网站(https://www.khronos.org/opencl/)上找到该属性及其功能的确切描述。
标识符 | 值 |
CL_DEVICE_TYPE | 0x1000 |
CL_DEVICE_VENDOR_ID | 0x1001 |
CL_DEVICE_MAX_COMPUTE_UNITS | 0x1002 |
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS | 0x1003 |
CL_DEVICE_MAX_WORK_GROUP_SIZE | 0x1004 |
CL_DEVICE_MAX_WORK_ITEM_SIZES | 0x1005 |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR | 0x1006 |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT | 0x1007 |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT | 0x1008 |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG | 0x1009 |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT | 0x100A |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE | 0x100B |
CL_DEVICE_MAX_CLOCK_FREQUENCY | 0x100C |
CL_DEVICE_ADDRESS_BITS | 0x100D |
CL_DEVICE_MAX_READ_IMAGE_ARGS | 0x100E |
CL_DEVICE_MAX_WRITE_IMAGE_ARGS | 0x100F |
CL_DEVICE_MAX_MEM_ALLOC_SIZE | 0x1010 |
CL_DEVICE_IMAGE2D_MAX_WIDTH | 0x1011 |
CL_DEVICE_IMAGE2D_MAX_HEIGHT | 0x1012 |
CL_DEVICE_IMAGE3D_MAX_WIDTH | 0x1013 |
CL_DEVICE_IMAGE3D_MAX_HEIGHT | 0x1014 |
CL_DEVICE_IMAGE3D_MAX_DEPTH | 0x1015 |
CL_DEVICE_IMAGE_SUPPORT | 0x1016 |
CL_DEVICE_MAX_PARAMETER_SIZE | 0x1017 |
CL_DEVICE_MAX_SAMPLERS | 0x1018 |
CL_DEVICE_MEM_BASE_ADDR_ALIGN | 0x1019 |
CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE | 0x101A |
CL_DEVICE_SINGLE_FP_CONFIG | 0x101B |
CL_DEVICE_GLOBAL_MEM_CACHE_TYPE | 0x101C |
CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE | 0x101D |
CL_DEVICE_GLOBAL_MEM_CACHE_SIZE | 0x101E |
CL_DEVICE_GLOBAL_MEM_SIZE | 0x101F |
CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE | 0x1020 |
CL_DEVICE_MAX_CONSTANT_ARGS | 0x1021 |
CL_DEVICE_LOCAL_MEM_TYPE | 0x1022 |
CL_DEVICE_LOCAL_MEM_SIZE | 0x1023 |
CL_DEVICE_ERROR_CORRECTION_SUPPORT | 0x1024 |
CL_DEVICE_PROFILING_TIMER_RESOLUTION | 0x1025 |
CL_DEVICE_ENDIAN_LITTLE | 0x1026 |
CL_DEVICE_AVAILABLE | 0x1027 |
CL_DEVICE_COMPILER_AVAILABLE | 0x1028 |
CL_DEVICE_EXECUTION_CAPABILITIES | 0x1029 |
CL_DEVICE_QUEUE_PROPERTIES | 0x102A |
CL_DEVICE_NAME | 0x102B |
CL_DEVICE_VENDOR | 0x102C |
CL_DRIVER_VERSION | 0x102D |
CL_DEVICE_PROFILE | 0x102E |
CL_DEVICE_VERSION | 0x102F |
CL_DEVICE_EXTENSIONS | 0x1030 |
CL_DEVICE_PLATFORM | 0x1031 |
CL_DEVICE_DOUBLE_FP_CONFIG | 0x1032 |
CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF | 0x1034 |
CL_DEVICE_HOST_UNIFIED_MEMORY | 0x1035 |
CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR | 0x1036 |
CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT | 0x1037 |
CL_DEVICE_NATIVE_VECTOR_WIDTH_INT | 0x1038 |
CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG | 0x1039 |
CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT | 0x103A |
CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE | 0x103B |
CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF | 0x103C |
CL_DEVICE_OPENCL_C_VERSION | 0x103D |
CL_DEVICE_LINKER_AVAILABLE | 0x103E |
CL_DEVICE_BUILT_IN_KERNELS | 0x103F |
CL_DEVICE_IMAGE_MAX_BUFFER_SIZE | 0x1040 |
CL_DEVICE_IMAGE_MAX_ARRAY_SIZE | 0x1041 |
CL_DEVICE_PARENT_DEVICE | 0x1042 |
CL_DEVICE_PARTITION_MAX_SUB_DEVICES | 0x1043 |
CL_DEVICE_PARTITION_PROPERTIES | 0x1044 |
CL_DEVICE_PARTITION_AFFINITY_DOMAIN | 0x1045 |
CL_DEVICE_PARTITION_TYPE | 0x1046 |
CL_DEVICE_REFERENCE_COUNT | 0x1047 |
CL_DEVICE_PREFERRED_INTEROP_USER_SYNC | 0x1048 |
CL_DEVICE_PRINTF_BUFFER_SIZE | 0x1049 |
CL_DEVICE_IMAGE_PITCH_ALIGNMENT | 0x104A |
CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT | 0x104B |
void OnStart()
{
//---
int dCount= CLGetInfoInteger(0,CL_DEVICE_COUNT);
for(int i = 0; i<dCount; i++)
{
int clCtx=CLContextCreate(i);
if(clCtx == -1)
Print("ERROR in CLContextCreate");
string device;
CLGetInfoString(clCtx,CL_DEVICE_NAME,device);
Print(i,": ",device);
uchar data[1024];
uint size;
CLGetDeviceInfo(clCtx,CL_DEVICE_VENDOR,data,size);
Print("size = ",size);
string str=CharArrayToString(data);
Print(str);
}
}
//--- example of entries in Experts journal
// 2013.07.24 10:50:48 opencl (EURUSD,H1) 2: Advanced Micro Devices, Inc.
// 2013.07.24 10:50:48 opencl (EURUSD,H1) size = 32
// 2013.07.24 10:50:48 opencl (EURUSD,H1) Tahiti
// 2013.07.24 10:50:48 opencl (EURUSD,H1) Intel(R) Corporation
// 2013.07.24 10:50:48 opencl (EURUSD,H1) size = 21
// 2013.07.24 10:50:48 opencl (EURUSD,H1) 1: Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz
// 2013.07.24 10:50:48 opencl (EURUSD,H1) NVIDIA Corporation
// 2013.07.24 10:50:48 opencl (EURUSD,H1) size = 19
// 2013.07.24 10:50:48 opencl (EURUSD,H1) 0: GeForce GTX 580
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 29.7 CLProgramCreate
从源代码创建一个OpenCL程序.
int CLProgramCreate(
int context, // 一个OpenCL上下文的句柄
const string source // 源代码
);
2
3
4
重载函数版本创建OpenCL程序并将编译器消息写入传递的字符串。
int CLProgramCreate(
int context, // Handle to an OpenCL context
const string source, // Source code
string &build_log // A string for receiving the compilation log
);
2
3
4
5
参数
context
[in] 一个OpenCL上下文的句柄。
source
[in] OpenCL程序源代码字符串。
&build_log
[in] 用于接收OpenCL编译器消息的字符串。
返回值
如果成功,则为OpenCL对象的句柄。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
注意
目前,以下错误代码正在使用:
• ERR_OPENCL_INVALID_HANDLE —— OpenCL上下文的句柄无效。
• ERR_INVALID_PARAMETER —— 无效的字符串参数。
• ERR_NOT_ENOUGH_MEMORY —— 内存不足无法完成操作。
• ERR_OPENCL_PROGRAM_CREATE —— OpenCL 内部错误或编译错误。
在一些图形卡中默认禁止处理 双精度(double)类型数字。这可能会导致编译错误5105。若要启用支持 双精度(double)类型数字,请添加以下指令到您的OpenCL程序:
#pragma OPENCL EXTENSION cl_khr_fp64 : enable
更多详情请参考以下链接 https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/xhtml/cl_khr_fp64.html
但是,如果图形卡不支持 双精度(double)型,启用该指令则不会有帮助。
示例:
//+------------------------------------------------------------------+
//| OpenCL kernel |
//+------------------------------------------------------------------+
const string
cl_src=
//--- 默认一些 GPU 不支持双精度类型
//--- cl_khr_fp64 指令用于启用处理双精度类型
"#pragma OPENCL EXTENSION cl_khr_fp64 : enable \r\n"
//--- OpenCL 核函数
"__kernel void Test_GPU(__global double *data, \r\n"
" const int N, \r\n"
" const int total_arrays) \r\n"
" { \r\n"
" uint kernel_index=get_global_id(0); \r\n"
" if (kernel_index>total_arrays) return; \r\n"
" uint local_start_offset=kernel_index*N; \r\n"
" for(int i=0; i<N; i++) \r\n"
" { \r\n"
" data[i+local_start_offset] *= 2.0; \r\n"
" } \r\n"
" } \r\n";
//+------------------------------------------------------------------+
//| Test_CPU |
//+------------------------------------------------------------------+
bool Test_CPU(double &data[],const int N,const int id,const int total_arrays)
{
//--- 检查数组大小
if(ArraySize(data)==0) return(false);
//--- 检查数组索引
if(id>total_arrays) return(false);
//--- 通过索引id计算数组局部偏移
int local_start_offset=id*N;
//--- 元素乘以2
for(int i=0; i<N; i++)
{
data[i+local_start_offset]*=2.0;
}
return true;
}
//---
#define ARRAY_SIZE 100 // size of the array
#define TOTAL_ARRAYS 5 // total arrays
//--- OpenCL 处理程序
int cl_ctx; // OpenCL 内容处理
int cl_prg; // OpenCL 程序处理
int cl_krn; // OpenCL 核处理
int cl_mem; // OpenCL 缓冲处理
//---
double DataArray1[]; // 用于CPU计算的数据数组
double DataArray2[]; // 用于GPU计算的数据数组
//+------------------------------------------------------------------+
//| 脚本程序起始函数 |
//+------------------------------------------------------------------+
int OnStart()
{
//--- 初始化OpenCL对象
//--- 创建OpenCL 环境
if((cl_ctx=CLContextCreate())==INVALID_HANDLE)
{
Print("OpenCL not found. Error=",GetLastError());
return(1);
}
//--- 创建 OpenCL 程序
if((cl_prg=CLProgramCreate(cl_ctx,cl_src))==INVALID_HANDLE)
{
CLContextFree(cl_ctx);
Print("OpenCL program create failed. Error=",GetLastError());
return(1);
}
//--- 创建OpenCL 内核
if((cl_krn=CLKernelCreate(cl_prg,"Test_GPU"))==INVALID_HANDLE)
{
CLProgramFree(cl_prg);
CLContextFree(cl_ctx);
Print("OpenCL kernel create failed. Error=",GetLastError());
return(1);
}
//--- 创建 OpenCL 缓冲
if((cl_mem=CLBufferCreate(cl_ctx,ARRAY_SIZE*TOTAL_ARRAYS*sizeof(double),CL_MEM_READ_WRITE))==INVALID_HANDLE)
{
CLKernelFree(cl_krn);
CLProgramFree(cl_prg);
CLContextFree(cl_ctx);
Print("OpenCL buffer create failed. Error=",GetLastError());
return(1);
}
//--- 设置OpenCL 内核常量参数
CLSetKernelArgMem(cl_krn,0,cl_mem);
CLSetKernelArg(cl_krn,1,ARRAY_SIZE);
CLSetKernelArg(cl_krn,2,TOTAL_ARRAYS);
//--- 准备数据数组
ArrayResize(DataArray1,ARRAY_SIZE*TOTAL_ARRAYS);
ArrayResize(DataArray2,ARRAY_SIZE*TOTAL_ARRAYS);
//--- 用数据填写数组
for(int j=0; j<TOTAL_ARRAYS; j++)
{
//--- 计算 jth 数组局部开始偏移
uint local_offset=j*ARRAY_SIZE;
//--- 准备 j 索引数组
for(int i=0; i<ARRAY_SIZE; i++)
{
//--- 用MathCos(i+j)函数填写数组;
DataArray1[i+local_offset]=MathCos(i+j);
DataArray2[i+local_offset]=MathCos(i+j);
}
};
//--- 测试CPU 计算
for(int j=0; j<TOTAL_ARRAYS; j++)
{
//--- 计算 j 索引数组
Test_CPU(DataArray1,ARRAY_SIZE,j,TOTAL_ARRAYS);
}
//--- 准备 CLExecute 参数
uint offset[]={0};
//--- 全局工作大小
uint work[]={TOTAL_ARRAYS};
//--- 编写数据到 OpenCL 缓冲
CLBufferWrite(cl_mem,DataArray2);
//--- 执行OpenCL 内核
CLExecute(cl_krn,1,offset,work);
//--- 从OpenCL缓冲阅读数据
CLBufferRead(cl_mem,DataArray2);
//--- 总误差
double total_error=0;
//--- 比较结果和计算误差
for(int j=0; j<TOTAL_ARRAYS; j++)
{
//--- 计算 jth 数组局部偏移
uint local_offset=j*ARRAY_SIZE;
//--- 比较结果
for(int i=0; i<ARRAY_SIZE; i++)
{
double v1=DataArray1[i+local_offset];
double v2=DataArray2[i+local_offset];
double delta=MathAbs(v2-v1);
total_error+=delta;
//--- 显示初始和最后的数组
if((j==0) || (j==TOTAL_ARRAYS-1))
PrintFormat("array %d of %d, element [%d]: %f, %f, [error]=%f",j+1,TOTAL_ARRAYS,i,v1,v2,delta);
}
}
PrintFormat("Total error: %f",total_error);
//--- 删除OpenCL 对象
//--- 释放 OpenCL 缓冲
CLBufferFree(cl_mem);
//--- 释放 OpenCL 内核
CLKernelFree(cl_krn);
//--- 释放 OpenCL 程序
CLProgramFree(cl_prg);
//--- 释放 OpenCL 环境
CLContextFree(cl_ctx);
//---
return(0);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# 29.8 CLProgramFree
删除一个OpenCL程序.
void CLProgramFree(
int program // 一个OpenCL句柄
);
2
3
参数
program
[in] OpenCL句柄。
返回值
无。 在内部错误情况下 _LastError 的值会改变。想要获取有关错误的详细信息,请调用 GetLastError() 函数。
# 29.9 CLKernelCreate
创建OpenCL程序内核并返回其句柄。
int CLKernelCreate(
int program, // 一个OpenCL句柄
const string kernel_name // 内核名称
);
2
3
4
参数
program
[in] 一个OpenCL句柄。
kernel_name
[in] 适当的OpenCL程序中的内核函数的名称,执行开始。
返回值
如果成功将创建一个OpenCL句柄。一旦出错 返回-1。 想要获取有关错误的详细信息,请调用 GetLastError() 函数.
注意
目前,以下错误代码正在使用:
• ERR_OPENCL_INVALID_HANDLE —— 无效的 OpenCL的句柄。
• ERR_INVALID_PARAMETER —— 无效的字符串参数。
• ERR_OPENCL_TOO_LONG_KERNEL_NAME —— 内核名称超过127个字符。
• ERR_OPENCL_KERNEL_CREATE —— 创建OpenCL对象时发生的内部错误。
# 29.10 CLKernelFree
删除一个OpenCL启动函数.
void CLKernelFree(
int kernel // 处理OpenCL程序的内核
);
2
3
参数
kernel_name
[in] 处理OpenCL程序的内核。
返回值
无。 在内部错误情况下 _LastError 的值会改变。想要获取有关错误的详细信息,请调用 GetLastError() 函数。
# 29.11 CLSetKernelArg
为OpenCL函数设置一个参数。
bool CLSetKernelArg(
int kernel, // 处理一个OpenCL程序内核
uint arg_index, // OpenCL函数幅角的数量
void arg_value // 源代码
);
2
3
4
5
参数
kernel
[in] 处理一个OpenCL程序内核.
arg_index
[in] 函数参数的编号,编号从零开始。
arg_value
[in] 函数参数的值.
返回值
如果成功则返回true, 否则返回false。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
注意
目前,以下错误代码正在使用:
• ERR_INVALID_PARAMETER —— 无效的参数
• ERR_OPENCL_INVALID_HANDLE —— OpenCL内核的句柄无效
• ERR_OPENCL_SET_KERNEL_PARAMETER —— OpenCL内部错误
# 29.12 CLSetKernelArgMem
设置一个OpenCL缓冲作为OpenCL函数的参数。
bool CLSetKernelArgMem(
int kernel, // 处理一个OpenCL程序的内核
uint arg_index, // OpenCL函数参数的数量
int cl_mem_handle // 处理一个OpenCL缓冲
);
2
3
4
5
参数
kernel
[in] 处理一个OpenCL程序的内核。
arg_index
[in] 函数参数的编号,编号从零开始。
cl_mem_handle
[in] 一个OpenCL缓冲处理句柄。
返回值
如果成功则返回true, 否则返回false。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
# 29.13 CLSetKernelArgMemLocal
设置本地缓冲区作为内核函数参数。
bool CLSetKernelArgMemLocal(
int kernel, // 处理OpenCL程序的内核
uint arg_index, // OpenCL函数参数的数值
ulong local_mem_size // 缓冲区大小
);
2
3
4
5
参数
kernel
[in] 处理OpenCL程序的内核。
arg_index
[in] 函数参数的编号,编号从零开始。
local_mem_size
[in] 缓冲区的字节大小。
返回值
如果成功返回true,否则返回false。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
# 29.14 CLBufferCreate
创建一个OpenCL缓冲并返回其句柄。
int CLBufferCreate(
int context, // 一个OpenCL 上下文句柄
uint size, // 缓冲大小
uint flags // 指定OpenCL缓冲性能的标志组合
);
2
3
4
5
参数
context
[in] 一个OpenCL 上下文句柄。
size
[in] 缓冲字节大小。
flags
[in] 使用组合标帜的缓冲性能: CL_MEM_READ_WRITE, CL_MEM_WRITE_ONLY, CL_MEM_READ_ONLY, CL_MEM_ALLOC_HOST_PTR。
返回值
如果成功,则返回OpenCL缓冲的句柄。 一旦出错 返回-1。想要获取有关错误的详细信息,请调用 GetLastError() 函数.
注意
目前,以下错误代码正在使用:
• ERR_OPENCL_INVALID_HANDLE —— 无效的OpenCL上下文句柄。
• ERR_NOT_ENOUGH_MEMORY —— 内存不足。
• ERR_OPENCL_BUFFER_CREATE —— 创建缓冲区时发生内部错误。
# 29.15 CLBufferFree
删除OpenCL缓冲。
void CLBufferFree(
int buffer // 一个OpenCL缓冲句柄
);
2
3
参数
buffer
[in] 一个OpenCL缓冲句柄。
返回值
无。 在内部错误情况下 _LastError 的值会改变。想要获取有关错误的详细信息,请调用 GetLastError() 函数。
# 28.16 CLBufferWrite
写入OpenCL缓冲区并返回写入元素的数量。
uint CLBufferWrite(
int buffer, // 一个OpenCL缓冲句柄
const void& data[], // 值的数组
uint buffer_offset=0, // 字节区偏移的OpenCL缓冲,默认为0
uint data_offset=0, // 元素中数组的偏移,默认为0
uint data_count=WHOLE_ARRAY // 从阵列写入的值的数量,默认整个阵列
);
2
3
4
5
6
7
参数
buffer
[in] 一个OpenCL缓冲句柄。
data[]
[in] 应该在OpenCL缓冲区中写入的值数组。 通过引用传递。
buffer_offset
[in] OpenCL缓冲区中的偏移量,以字节为单位,从中开始写入。默认情况下,从缓冲区的开头开始写入。
data_offset
[in] 第一个数组元素的索引,从数组中的值开始写入OpenCL缓冲区。默认情况下,从数组的最开始处获取值。
data_count
[in] 应写入的值的数量。 默认情况下,为该数组的所有值。
返回值
写入元素的数量. 错误情况下返回0。想要获取有关错误的详细信息,请调用 GetLastError() 函数。
注意
对于一维数组,在读取数据用于写入OpenCL缓冲区的元素时,计算的数量,是在考虑了AS_SERIES标帜的情况下得出的。
一个二维或多维的数组被表示为一维时。在这种情况下,data_offset是表示中应该跳过的元素数量,而不是第一个维度中的元素数量。
# 29.17 CLBufferRead
读取OpenCL缓冲区并返回写入元素的数量。
uint CLBufferRead(
int buffer, // 处理一个OpenCL缓冲
const void& data[], // 一个值的数组
uint buffer_offset=0, // 在字节缓冲区偏移的OpenCL,默认为0
uint data_offset=0, // 在元素的数组的偏移量,默认为0
uint data_count=WHOLE_ARRAY // 用于读取的缓冲值的个数,默认为整个缓冲区
);
2
3
4
5
6
7
参数
buffer
[in] 一个OpenCL缓冲句柄。
data[]
[in] 用于从OpenCL缓冲区接收值的数组。 通过引用传递。
buffer_offset
[in] OpenCL缓冲区中的偏移量,以字节为单位,从中开始写入。默认情况下,从缓冲区的开头开始写入。
data_offset
[in] 第一个数组元素的索引,从数组中的值开始写入OpenCL缓冲区。默认情况下,从数组的最开始处获取值。
data_count
[in] 应读取的值的数量。默认情况下,为该数组的所有值。
返回值
读取元素的数量. 错误情况下返回0。想要获取有关错误的详细信息,请调用 GetLastError() 函数。
注意
对于一维数组,在读取数据用于写入OpenCL缓冲区的元素时,计算的数量,是在考虑了AS_SERIES标帜的情况下得出的。
一个二维或多维的数组被表示为一维时。在这种情况下,data_offset是表示中应该跳过的元素数量,而不是第一个维度中的元素数量。
# 29.18 CLExecute
此函数运行一个 OpenCL程序。有三个版本的变体:
- 用一个内核启动内核函数
bool CLExecute(
int kernel // 处理一个OpenCL程序的内核
);
2
3
- 启动几个内核副本 (OpenCL函数) 与任务空间描述
bool CLExecute(
int kernel, // 处理一个OpenCL程序的内核
uint work_dim, // 任务空间维度
const uint& global_work_offset[], // 在任务空间初始偏移
const uint& global_work_size[] // 任务总数
);
2
3
4
5
6
- 启动几个内核副本 (OpenCL函数) 与任务空间描述以及集合本地任务子集大小规范
bool CLExecute(
int kernel, // 处理一个OpenCL程序的内核
uint work_dim, // 任务空间维度
const uint& global_work_offset[], // 在任务空间初始偏移
const uint& global_work_size[], // 任务总数
const uint& local_work_size[] // 在本地集合中的任务数
);
2
3
4
5
6
7
参数
kernel
[in] 处理OpenCL内核。
work_dim
[in] 任务空间维度。
global_work_offset[]
[in] 在任务空间初始偏移。
global_work_size[]
[in] 任务子集大小。
local_work_size[]
[in] 集合本地任务子集大小。
返回值
如果成功则返回true, 否则返回false。 想要获取有关错误的详细信息,请调用 GetLastError() 函数。
注意
以下例子中要考虑参数的使用:
• work_dim 指定 work_items[] 描述任务的阵列维度. 如果work_dim=3,三维阵列work_items[N1, N2, N3] 将被使用。
• global_work_size[] 包含设定 work_items[]阵列大小的值. 如果 work_dim=3, global_work_size[3] 阵列将是 {40, 100, 320}。 然后我们将得到 work_items[40, 100, 320]。 所以,任务总数是 is 40 x 100 x 320 = 1 280 000。
• local_work_size[] 设定任务的子集将由OpenCL程序的指定内核来执行。 其维度等于 work_items[] 维度并允许共同任务子集精确分割成更小的子集。 事实上,应当选择 local_work_size[] 从而使全局任务设定分割成更小的子集。
local_work_size[3]={10, 10, 10} 将适合当前的例子, 比如work_items[40, 100, 320] 能被 local_items[10, 10, 10] 阵列毫无剩余的收集起来。
# 29.19 CLExecutionStatus
返回OpenCL程序执行状态。
bool CLExecutionStatus(
int kernel // 处理OpenCL程序的内核
);
2
3
参数
kernel
[in] 处理OpenCL程序的内核。
返回值
返回OpenCL程序状态。该值可以是以下值的其中一个:
• CL_COMPLETE=0 —— 程序完成,
• CL_RUNNING=1 —— 运行,
• CL_SUBMITTED=2 —— 提交执行,
• CL_QUEUED=3 —— 排队,
• -1 (负1) —— 执行CLExecutionStatus()时出现错误。