以上一篇《OpenCL入门测试》为基础,将函数封装到类中,方便调用。
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <cassert>
#include <windows.h>
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS // 定义使用OpenCL 1.2
#include <CL/cl.h>
using namespace std;class COpenCL
{public://const int Size = 38888888;//大小和内存有关,仅作示例const int Size = 2073600;//一帧高清点数float* nums1_h = new float[Size];//动态创建 nums1_h 数组float* nums2_h = new float[Size];//动态创建 nums2_h 数组float* sum_h = new float[Size]; //动态创建 sum_h 数组float* gpu_sum = new float[Size];void Init(void); // 初始化void Close(void);// 关闭,释放资源void CreateBuffer(void);// 创建缓冲区void CreateProgramSource(void);// 创建异构源代码void SetKernelArg(void);// 设置核参数void RunGPU(void);// 运行GPUvoid RunAsCpu(const float *nums1, const float *nums2, float* sum, const int num);// CPU运行函数private:cl_mem nums1_d,nums2_d,sum_d;const int mem_size = sizeof(float) * Size;//计算设备所需存储器size_t global_work_size = Size;//设备需要工作项(线程)cl_int Err;cl_platform_id Selected_Platform_ID; // 已选择平台的IDcl_device_id DevicesID; // GPU设备cl_context Context; // 设备管理cl_command_queue CommandQueue; // 命令队列cl_program Program; // 程序对象cl_kernel Kernel; // 内核对象cl_kernel RunAsGpu;//核函数void SelectedPlatform(void);//选择平台void CreateDevice(void);// 创建GPU设备void CreateContext(void);// 创建设备管理void CreateCommandQueue(void);// 创建命令队列void GetProgramBuildInfo(void);// 获取异构(设备)编译程序信息
};// 全局变量
_LARGE_INTEGER g_iSysFrequency,// 系统频率iStartTestTime; // 开始测试时间// 错误检查宏
#define CheckErr(Err, PrintStr) \
if(Err != CL_SUCCESS) \{ \printf("\n\n"); \printf(" ");\printf(PrintStr); \printf("\n\n"); \system("pause"); \exit(1); \}
//---------------------------------------------------------------------------// 核函数源码字符串
const char *RunAsGpu_Source =
"__kernel void RunAsGpu_Source(__global const float *nums1, __global const float *nums2, __global float* sum)\n"
"{\n"
"int id = get_global_id(0);\n"
"sum[id] = nums1[id] + nums2[id];\n"
"}\n";
//---------------------------------------------------------------------------void COpenCL::RunAsCpu(const float *nums1, const float *nums2, float* sum, const int num)// CPU运行函数
{for (int i = 0; i < num; i++){sum[i] = nums1[i] + nums2[i];}
}void StartTestTime(void)// 开始测量耗时
{QueryPerformanceCounter(&iStartTestTime);//开始计时
}double StopTestTime(int iTimeUnit)// 测量耗时
{_LARGE_INTEGER iStopTime; double fRetTime;QueryPerformanceCounter(&iStopTime);// 读停止时间switch (iTimeUnit){case 0: fRetTime = (double)(iStopTime.QuadPart - iStartTestTime.QuadPart); // nsbreak;case 1: fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / (g_iSysFrequency.QuadPart / 1000000)); // usbreak;case 2: fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / (g_iSysFrequency.QuadPart / 1000)); // msbreak;case 3: fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / g_iSysFrequency.QuadPart); // Sbreak;}return fRetTime;
}void COpenCL::SelectedPlatform(void)//选择平台
{cl_uint PlatformCount; //平台数cl_platform_id *pTotalPlatformtID;//所有平台数ID// 获取平台数目Err = clGetPlatformIDs(0, 0, &PlatformCount);// 获取平台数CheckErr(Err, "错误: OpenCL获取平台数错误!");if (PlatformCount > 0){cout << "可用平台数量: " << PlatformCount << endl;}else{printf("\n\n 错误: 没有可用OpenCL平台!\n\n");system("pause");exit(0);}// 获取所有平台IDpTotalPlatformtID = new cl_platform_id[PlatformCount];//动态创建所有平台ID数组Err = clGetPlatformIDs(PlatformCount, pTotalPlatformtID, NULL);//获取所有平台ID(列表)CheckErr(Err, "错误: OpenCL获取所有平台ID错误。\n");// 列出所有平台名称printf("\n");cout << "所有平台的名称: \n\n";for (cl_uint i = 0; i < PlatformCount; ++i){// 获取平台名称的长度size_t Platform_Name_Length;Err = clGetPlatformInfo(pTotalPlatformtID[i], CL_PLATFORM_NAME, 0, 0, &Platform_Name_Length);// 获取平台名称字符长度CheckErr(Err, "获取OpenCL平台名称长度错误。\n");// 获取平台名称char *Platform_Name = new char[Platform_Name_Length];//动态创建各平台名称字符串数组的长度Err = clGetPlatformInfo(pTotalPlatformtID[i], CL_PLATFORM_NAME, Platform_Name_Length, Platform_Name, 0);// 获取平台名称CheckErr(Err, "错误: 获取平台名称失败。\n");cout << " [" << i << "] " << Platform_Name << "\n";// 输出平台名称Selected_Platform_ID = pTotalPlatformtID[0];//总是选第一个delete[] Platform_Name;}delete[] pTotalPlatformtID;
}void COpenCL::CreateDevice(void)// 创建GPU设备
{Err = clGetDeviceIDs(Selected_Platform_ID, CL_DEVICE_TYPE_GPU, 1, &DevicesID, NULL);//获得GPU设备数量CheckErr(Err, "错误: OpenCL创建GPU设备失败!");
}void COpenCL::CreateContext(void)// 创建设备管理
{Context = clCreateContext(0, 1, &DevicesID, NULL, NULL, &Err);CheckErr(Err, "错误: OpenCL创建设备环境失败!");
}void COpenCL::CreateCommandQueue(void)// 创建命令队列
{CommandQueue = clCreateCommandQueue(Context, DevicesID, 0, &Err);CheckErr(Err, "错误: OpenCL创建命令队列失败!");
}void COpenCL::GetProgramBuildInfo(void)// 获取异构(设备)编译程序信息
{char* build_log; size_t log_size;clGetProgramBuildInfo(Program, DevicesID, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);// 获取编译信息长度build_log = new char[log_size + 1];clGetProgramBuildInfo(Program, DevicesID, CL_PROGRAM_BUILD_LOG, log_size, build_log, NULL);// 查询编译信息build_log[log_size] = '\0';printf("\n异构(设备)编译信息:\n\n");cout << build_log << endl;delete[] build_log;
}
//---------------------------------------------------------------------------void COpenCL::Init(void)// 初始化
{SelectedPlatform();// 选择平台CreateDevice();// 创建GPU设备CreateContext();// 创建设备管理CreateCommandQueue();// 创建命令队列
}void COpenCL::CreateBuffer(void)// 创建缓冲区
{for (int i = 0; i < Size; i++)//初始化测试数据 {nums1_h[i] = nums2_h[i] = (float)i;}//创建设备缓冲区StartTestTime();nums1_d = clCreateBuffer(Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, mem_size, nums1_h, &Err);//nums1_d设备输入nums2_d = clCreateBuffer(Context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, mem_size, nums2_h, &Err);//nums2_d设备输入sum_d = clCreateBuffer(Context, CL_MEM_WRITE_ONLY, mem_size, NULL, &Err);//sum_d设备输出cout << "\nCPU:传输数据到GPU耗时: " << StopTestTime(2) << " ms" << endl;if (nums1_d == 0 || nums2_d == 0 || sum_d == 0){delete[] nums1_h;delete[] nums2_h;delete[] sum_h;clReleaseMemObject(nums1_d);clReleaseMemObject(nums2_d);clReleaseMemObject(sum_d);clReleaseCommandQueue(CommandQueue);clReleaseContext(Context);printf("\n错误:OpenCL创建设备缓冲区失败!\n\n");system("pause");exit(1);//退出}
}void COpenCL::CreateProgramSource(void)// 创建异构源代码
{size_t Src_size[] = { strlen(RunAsGpu_Source) };//读入源代码数组Program = clCreateProgramWithSource(Context, 1, &RunAsGpu_Source, Src_size, &Err);// 输入设备源程序CheckErr(Err, "错误: OpenCL输入设备源程序失败!");// 编译程序对象(编译异构源代码)Err = clBuildProgram(Program, 1, &DevicesID, NULL, NULL, NULL);// 编译设备源程序CheckErr(Err, "错误: OpenCL编译设备源程序失败!");// 创建设备(核)程序函数 RunAsGpuRunAsGpu = clCreateKernel(Program, "RunAsGpu_Source", &Err);// 创建核函数if (Err != CL_SUCCESS){delete[] nums1_h;delete[] nums2_h;delete[] sum_h;clReleaseMemObject(nums1_d);clReleaseMemObject(nums2_d);clReleaseMemObject(sum_d);clReleaseCommandQueue(CommandQueue);clReleaseContext(Context);clReleaseKernel(RunAsGpu);printf("\n错误:OpenCL创建核函数失败!\n\n");system("pause");exit(1);//退出}GetProgramBuildInfo();// 获取异构(设备)编译程序信息
}void COpenCL::SetKernelArg(void)// 设置核参数
{Err = clSetKernelArg(RunAsGpu, 0, sizeof(cl_mem), &nums1_d);Err |= clSetKernelArg(RunAsGpu, 1, sizeof(cl_mem), &nums2_d);Err |= clSetKernelArg(RunAsGpu, 2, sizeof(cl_mem), &sum_d);CheckErr(Err, "错误: OpenCL输入设备(核)程序函数 RunAsGpu 形参失败!");
}void COpenCL::RunGPU(void)// 运行GPU
{StartTestTime();Err = clEnqueueNDRangeKernel(CommandQueue, RunAsGpu, 1, NULL, &global_work_size, NULL, 0, NULL, NULL);//运行核函数CheckErr(Err, "错误: OpenCL核运算失败!");cout << "GPU 计算耗时: " << StopTestTime(0) << " ns" << endl;StartTestTime();clEnqueueReadBuffer(CommandQueue, sum_d, CL_TRUE, 0, mem_size, gpu_sum, 0, NULL, NULL);//读设备缓冲区cout << "CPU 读回数据耗时: " << StopTestTime(2) << " ms" << endl;
}void COpenCL::Close(void)// 关闭,释放资源
{delete[] gpu_sum;delete[] nums1_h;delete[] nums2_h;delete[] sum_h;clReleaseMemObject(nums1_d);clReleaseMemObject(nums2_d);clReleaseMemObject(sum_d);clReleaseCommandQueue(CommandQueue);clReleaseContext(Context);clReleaseKernel(RunAsGpu);
}
//---------------------------------------------------------------------------int main()
{COpenCL OpenCL;QueryPerformanceFrequency(&g_iSysFrequency);//读系统频率OpenCL.Init();OpenCL.CreateBuffer();OpenCL.CreateProgramSource();OpenCL.SetKernelArg();OpenCL.RunGPU();StartTestTime();OpenCL.RunAsCpu(OpenCL.nums1_h, OpenCL.nums2_h, OpenCL.sum_h, OpenCL.Size);// 运行CPU函数cout << "\nCPU 计算耗时: " << StopTestTime(2) << " ms" << endl;if (memcmp(OpenCL.sum_h, OpenCL.gpu_sum, OpenCL.Size * sizeof(float)) == 0)// 比较结果,数值比较{printf("\n比较GPU和CPU计算数值正确。\n");}else{printf("\n比较GPU和CPU计算数值错误!\n");system("pause");exit(1);//退出 }OpenCL.Close();// 关闭,释放资源// 殿后处理printf("\n");printf("运行成功!\n");printf("\n");system("pause");
}
在 Microsoft Visual C++ 2017 控制台调试通过。