封装OpenCL类

以上一篇《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);// 运行GPU
	  void 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; // 已选择平台的ID
	  cl_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); // ns
		     break;
	case 1:  fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / (g_iSysFrequency.QuadPart / 1000000)); // us
		     break;
	case 2:  fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / (g_iSysFrequency.QuadPart / 1000)); // ms
		     break;
	case 3:  fRetTime = (double)((iStopTime.QuadPart - iStartTestTime.QuadPart) / g_iSysFrequency.QuadPart); // S
		     break;
	}

	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);
	}
	// 获取所有平台ID
	pTotalPlatformtID = 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编译设备源程序失败!");
	// 创建设备(核)程序函数 RunAsGpu
	RunAsGpu = 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 控制台调试通过。

  

原文地址:https://www.cnblogs.com/hbg200/p/10068515.html

时间: 2024-12-29 12:43:06

封装OpenCL类的相关文章

非专业码农 JAVA学习笔记 3 抽象、封装和类(1)

抽象.封装和类(1)待续 首先声明,今天接了太多个电话,两个小时看书被打断多次,缩减为一个小时.不管了,走马观花也要看完几个小节: (一)抽象与封装:抽象-抽取和研究实际性问题加以考察(抽象-尼玛抽出对象…) 封装:相关操作封存在命名空间.类等 (二)java的类: 1.系统定义的类(用于import中引入) 类名 作用 Java.lang 语言包,默认加载 Java.io, In out:文件数据流操作产生的输入输出流 Java.util 低级工具,如时间data类,变成数组vector类,h

jQuery Ajax封装通用类 (linjq)

jQuery Ajax封装通用类 (linjq) $(function(){ /** * ajax封装 * url 发送请求的地址 * data 发送到服务器的数据,数组存储,如:{"date": new Date().getTime(), "state": 1} * async 默认值: true.默认设置下,所有请求均为异步请求.如果需要发送同步请求,请将此选项设置为 false. * 注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行. * t

简易高重用的jdbcutils工具封装实现类以及简易连接池实现

由于现在发现做个小项目都是导入n多的依赖包,很烦琐,只想快点开发完一个个的小需求项目,这个时候真心不想用框架,只能自己写个jdbcutils,虽然网上有很多有apache的,阿里的,但是感觉用过后都不怎么顺手,然后自己花了点时间写个新的,不喜勿喷 1.我们要写个resultset集合转成bean的回调接口,这个用过spring jdbc的人都知道这玩意 package org.framework.mvc.jdbc.bean; import java.sql.ResultSet; import j

iOS开发—音频的播放的简单介绍和封装工具类

iOS开发—音频的播放的简单介绍和封装工具类 一.音效的播放简单介绍 简单来说,音频可以分为2种 (1)音效 又称“短音频”,通常在程序中的播放时长为1~2秒 在应用程序中起到点缀效果,提升整体用户体验 (2)音乐 比如游戏中的“背景音乐”,一般播放时间较长 框架:播放音频需要用到AVFoundation.framework框架 二.音效的播放 1.获得音效文件的路径 NSURL *url = [[NSBundle mainBundle] URLForResource:@"m_03.wav&qu

微信公众号开发系列-Http请求封装基类

HttpHelper请求封装基类,支持get请求和POS请求,方便微信开发接口交互,为后面接口交互做准备. 1.HttpHelper帮助基类 [csharp] view plaincopy using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Net; using System.Net.Security; namespa

Directx11学习笔记【二】 将HelloWin封装成类

我们把上一个教程的代码封装到一个类中来方便以后的使用. 首先新建一个空工程叫做MyHelloWin,添加一个main.cpp文件,然后新建一个类叫做MyWindow,将于窗体有关的操作封装到里面 MyWindow.h文件 1 /************************************************************************ 2 Directx11学习笔记[2] 将HelloWin封装成类 3 2016.01 by zhangbaochong 4 /

封装业务类

1.什么是业务类? 业务类:专门处理某项业务(事情) 2.业务类的作用? 把一些业务的业务逻辑封装起来,其它类需要处理这些业务的时候,直接调用业务类的方法就可以了 大大减少了其它类中的代码量,让代码看起来更整洁,可读性更好 3.业务类的规范 3.1 在类的上面,注明这个类的功能(作用) 让其它人一看到这个类就知道是干什么用的,减少沟通成本 1 // Created by XT on 16/7/31. 2 // Copyright © 2016年 XT. All rights reserved.

封装一个类搞定90%安卓客户端与服务器端交互

本实例封装了一个处理安卓客户端与服务器端交互的几个方法,对于中文乱码问题本实例也找到了解决方案.本例可以处理的场景如下: 1.与服务器端交互json数据. 2.Get方式与服务器端交互数据. 3.Post方式与服务器端交互数据. 4.HttpClient方式与服务器端交互数据. 5.上传文件到服务器端. 6.从服务器端下载文件. 7.从服务器端读取文本文件. 实例截图: 本篇文章将实例代码完整贴出,希望以本文作为一个交流的平台,大家集思广益封装出更好的处理类.交流地址: http://blog.

非专业码农 JAVA学习笔记 3 抽象、封装和类(2)

(2).静态域-放在内存公共存储单元,不放在特定的对象,用static修饰 (续上一篇<非专业码农 JAVA学习笔记 3 抽象.封装和类(1)>...) (3).静态初始器-由static引导的一对大括号括起来的语句组,作用跟构造函数相似 (4).最终域-final引导的,值在整个过程都不发生改变的 5.方法 (1)方法的定义:修饰词1 修饰词2…返回值类型 方法名(参数) throw[异常列表] 这里个人经验就是注意定义了返回值的方法,要在方法体里面增加return 该类型变量:此外遇到if