当前位置: 首页 > news >正文

OpenCL study - code02

在实际工程开发中,我们经常会遇到性能瓶颈的问题,尤其是在图像处理、信号处理或者机器学习前处理等场景中,海量数据的逐元素计算如果全部由 CPU 承担,效率往往无法满足实时需求。此时,利用 GPU 的并行计算能力就成了提升性能的关键手段。
OpenCL(Open Computing Language)是一个开放的、跨平台的并行计算框架,它支持在多种设备(CPU、GPU、DSP 甚至 FPGA)上运行并行程序。相比 CUDA 只支持 NVIDIA 平台,OpenCL 更加通用,适合在多厂商硬件上开发。
本文通过一个最基础的向量加法示例,介绍如何使用 OpenCL 将计算任务从 CPU 下发到 GPU,并进一步封装通用流程,做到后续只需关注**“算子本身”**的开发,大大提高算子迭代效率。无论你是 OpenCL 新手,还是在做异构计算优化的工程师,这篇文章都能为你提供实用的参考。

// opencl_helper.h
#ifndef OPENCL_HELPER_H
#define OPENCL_HELPER_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <OpenCL/opencl.h>#define CHECK_ERROR(err, msg) \if (err != CL_SUCCESS) { \fprintf(stderr, "%s failed with error %d\n", msg, err); \exit(1); \}typedef struct {cl_platform_id platform;cl_device_id device;cl_context context;cl_command_queue queue;cl_program program;cl_kernel kernel;
} OpenCLObjects;// 加载内核源码
char *read_source(const char *filename) {FILE *fp = fopen(filename, "r");if (!fp) {perror("Failed to open kernel file");exit(1);}fseek(fp, 0, SEEK_END);size_t size = ftell(fp);rewind(fp);char *source = (char *)malloc(size + 1);fread(source, 1, size, fp);source[size] = '\0';fclose(fp);return source;
}// 初始化 OpenCL,并构建 kernel
OpenCLObjects init_opencl(const char *source_file, const char *kernel_name) {OpenCLObjects ocl;cl_int err;err = clGetPlatformIDs(1, &ocl.platform, NULL);CHECK_ERROR(err, "clGetPlatformIDs");err = clGetDeviceIDs(ocl.platform, CL_DEVICE_TYPE_DEFAULT, 1, &ocl.device, NULL);CHECK_ERROR(err, "clGetDeviceIDs");ocl.context = clCreateContext(NULL, 1, &ocl.device, NULL, NULL, &err);CHECK_ERROR(err, "clCreateContext");ocl.queue = clCreateCommandQueue(ocl.context, ocl.device, 0, &err);CHECK_ERROR(err, "clCreateCommandQueue");char *source = read_source(source_file);ocl.program = clCreateProgramWithSource(ocl.context, 1, (const char **)&source, NULL, &err);CHECK_ERROR(err, "clCreateProgramWithSource");err = clBuildProgram(ocl.program, 1, &ocl.device, NULL, NULL, NULL);if (err != CL_SUCCESS) {char log[4096];clGetProgramBuildInfo(ocl.program, ocl.device, CL_PROGRAM_BUILD_LOG, sizeof(log), log, NULL);fprintf(stderr, "Build Error:\n%s\n", log);exit(1);}ocl.kernel = clCreateKernel(ocl.program, kernel_name, &err);CHECK_ERROR(err, "clCreateKernel");free(source);return ocl;
}void release_opencl(OpenCLObjects *ocl) {clReleaseKernel(ocl->kernel);clReleaseProgram(ocl->program);clReleaseCommandQueue(ocl->queue);clReleaseContext(ocl->context);
}#endif
__kernel void vector_add(__global const float* A,__global const float* B,__global float* C) {int id = get_global_id(0);C[id] = A[id] + B[id];
}
// main.cpp
// this code compiles with the following command:
// g++ main.cpp -framework OpenCL -o vector_add#include <stdio.h>
#include <stdlib.h>
#include "opencl_helper.h"#define ARRAY_SIZE 1024int main() {float *A = (float *)malloc(sizeof(float) * ARRAY_SIZE);float *B = (float *)malloc(sizeof(float) * ARRAY_SIZE);float *C = (float *)malloc(sizeof(float) * ARRAY_SIZE);for (int i = 0; i < ARRAY_SIZE; i++) {A[i] = (float)i;B[i] = (float)(i * 2);}// 初始化 OpenCLOpenCLObjects ocl = init_opencl("vector_add.cl", "vector_add");cl_int err;// 创建缓冲区cl_mem bufferA = clCreateBuffer(ocl.context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * ARRAY_SIZE, A, &err);CHECK_ERROR(err, "clCreateBuffer A");cl_mem bufferB = clCreateBuffer(ocl.context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,sizeof(float) * ARRAY_SIZE, B, &err);CHECK_ERROR(err, "clCreateBuffer B");cl_mem bufferC = clCreateBuffer(ocl.context, CL_MEM_WRITE_ONLY,sizeof(float) * ARRAY_SIZE, NULL, &err);CHECK_ERROR(err, "clCreateBuffer C");// 设置参数clSetKernelArg(ocl.kernel, 0, sizeof(cl_mem), &bufferA);clSetKernelArg(ocl.kernel, 1, sizeof(cl_mem), &bufferB);clSetKernelArg(ocl.kernel, 2, sizeof(cl_mem), &bufferC);size_t global_size = ARRAY_SIZE;err = clEnqueueNDRangeKernel(ocl.queue, ocl.kernel, 1, NULL, &global_size, NULL, 0, NULL, NULL);CHECK_ERROR(err, "clEnqueueNDRangeKernel");clFinish(ocl.queue);// 读取结果err = clEnqueueReadBuffer(ocl.queue, bufferC, CL_TRUE, 0, sizeof(float) * ARRAY_SIZE, C, 0, NULL, NULL);CHECK_ERROR(err, "clEnqueueReadBuffer");for (int i = 0; i < 10; i++) {printf("Result[%d]: %.1f + %.1f = %.1f\n", i, A[i], B[i], C[i]);}// 清理clReleaseMemObject(bufferA);clReleaseMemObject(bufferB);clReleaseMemObject(bufferC);release_opencl(&ocl);free(A);free(B);free(C);return 0;
}
  • 代码结构:
    在这里插入图片描述
  • 编译命令:
g++ main.cpp -framework OpenCL -o vector_add
  • 运行
./vector_add

在这里插入图片描述

http://www.dtcms.com/a/275276.html

相关文章:

  • 箭头函数(Arrow Functions)和普通函数(Regular Functions)
  • 7. 负载均衡:流量调度引擎
  • 8-day06预训练模型
  • 一个中层管理者应该看什么书籍?
  • 从就绪到终止:操作系统进程状态转换指南
  • 智能文本抽取在合同管理实战应用
  • 人事系统选型与应用全攻略:从痛点解决到效率跃升的实战指南
  • Datawhale AI夏令营:基于带货视频评论的用户洞察挑战赛上分全攻略
  • 自动驾驶线控系统与动力电池系统
  • 【天坑记录】cursor jsx文件保存时错误格式化了
  • K230摄像头配置与显示配置误解:而这根本没关系
  • 【驱动】移植CH340驱动,设置 udev 规则,解决和 BRLTTY 的冲突
  • 软件测试面试200问(附30W字面试文档)
  • 跟着Carl学算法--二叉树【3】
  • 静态路由技术
  • DeepSeek模型分析及其在AI辅助蛋白质工程中的应用-文献精读148
  • [electron]升级功能
  • CSS Grid布局和Flexbox有什么区别?
  • C语言文件读写操作详解:fgetc与feof函数的应用
  • 经典同步问题详解
  • 使用 lstrip() 和 rstrip() 方法
  • java集合类
  • 【牛客刷题】吃糖果----糖果甜度问题(贪心策略详解)
  • 机器学习详解
  • Windows删除文件或者拔出U盘显示正在使用/占用解决办法
  • Android tombstones memory map分析
  • HarmonyOS从入门到精通:动画设计与实现之四 - 转场动画设计与流畅交互体验
  • 优选算法 --(双指针算法 1~8)
  • The Practice of Programming
  • 深入解码 Docker 镜像与容器的奇妙世界