linux | 苹果OpenCL(提高应用软件如游戏、娱乐以及科研和医疗软件的运行速度和响应)

news2024/9/24 1:26:31

点击上方"蓝字"关注我们

01、引言

>>>

OpenCL 1.0 于 2008 年 11 月发布。

OpenCL 是为个人电脑、服务器、移动设备以及嵌入式设备的多核系统提供并行编程开发的底层 API。OpenCL 的编程语言类似于 C 语言。其可以用于包含 CPU、GPU 以及来自主流制造商如 NXP®、NVIDIA®、Intel®、AMD、IBM 等的处理器的异构平台。OpenCL 旨在提高应用软件如游戏、娱乐以及科研和医疗软件的运行速度和响应。

我们使用 Apalis iMX6Q 系统模块测试 OpenCL,对比两个应用 - 一个运行在 GPU 上,另一个则在 CPU。最后我们将分享本次测试的结果。

02、Toradex Embedded Linux 镜像中添加 OpenCL

>>>

假设你已经具有能够编译 Apalis iMX6 镜像的 OpenEmbedded 编译环境。你可以参考我们 OpenEmbedded (core) 【http://developer.toradex.com/knowledge-base/board-support-package/openembedded-(core)】文章。

为编译支持 OpenCL 以及相关库文件的嵌入式 Linux 镜像,需要采取以下步骤:

首先,修改下面目录中的文件。

/meta-toradex/recipes-fsl/packagegroups/packagegroup-fsl-tools-gpu.bbappend


添加如下内容:

SOC_TOOLS_GPU_append_mx6 = " \
    libopencl-mx6 \
    libgles-mx6 \
"

并在 local.conf 文件中添加 imx-gpu-viv

IMAGE_INSTALL_append = "imx-gpu-viv"


现在就可以编译镜像:

bitbake angstrom-lxde-image

03、GPU 和 CPU 代码

>>>

所有的代码可以从 GitHub【https://github.com/giobauermeister/OpenCL-test-apps】上下载。

我们使用数列求和应用作为基本的演示例程。第一部分代码运行在 GPU 上,第二部分则在 CPU 上。应用执行完毕后打印其所消耗的时间。使用 OpenCL 所需的头文件是 cl.h,位于文件系统的 /usr/include/CL 目录。链接程序所需的库文件是 libGAL.so 和 libOpenCL.so,位于 /usr/lib 目录。

为了计算消耗的时间,我们创建带分析功能的队列,在结束的时候获取分析的结果。

下面是 OpenCL 代码:

//************************************************************
// Demo OpenCL application to compute a simple vector addition
// computation between 2 arrays on the GPU
// ************************************************************
#include 
#include 
#include 
#include <CL/cl.h>
//
// OpenCL source code
const char* OpenCLSource[] = {
"__kernel void VectorAdd(__global int* c, __global int* a,__global int* b)",
"{",
" // Index of the elements to add \n",
" unsigned int n = get_global_id(0);",
" // Sum the nth element of vectors a and b and store in c \n",
" c[n] = a[n] + b[n];",
"}"
};
// Some interesting data for the vectors
Int InitialData1[80] = {37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17,37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17,37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17,37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17};
int InitialData2[80] = {35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15,35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15,35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15,35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15};
// Number of elements in the vectors to be added
#define SIZE 600000
// Main function
// ************************************************************
int main(int argc, char **argv)
{ 
     // Two integer source vectors in Host memory
     int HostVector1[SIZE], HostVector2[SIZE];
     //Output Vector
     int HostOutputVector[SIZE];
     // Initialize with some interesting repeating data
     for(int c = 0; c < SIZE; c++)
     {
          HostVector1[c] = InitialData1[c%20];
          HostVector2[c] = InitialData2[c%20];
          HostOutputVector[c] = 0;
     }
     //Get an OpenCL platform
     cl_platform_id cpPlatform;
     clGetPlatformIDs(1, &amp;cpPlatform, NULL);
     // Get a GPU device
     cl_device_id cdDevice;
     clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 1, &amp;cdDevice, NULL);
     char cBuffer[1024];
     clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(cBuffer), &amp;cBuffer, NULL);
     printf("CL_DEVICE_NAME: %s\n", cBuffer);
     clGetDeviceInfo(cdDevice, CL_DRIVER_VERSION, sizeof(cBuffer), &amp;cBuffer, NULL);
     printf("CL_DRIVER_VERSION: %s\n\n", cBuffer);
     // Create a context to run OpenCL enabled GPU
     cl_context GPUContext = clCreateContextFromType(0, CL_DEVICE_TYPE_GPU, NULL, NULL, NULL);     
     // Create a command-queue on the GPU device
     cl_command_queue cqCommandQueue = clCreateCommandQueue(GPUContext, cdDevice, CL_QUEUE_PROFILING_ENABLE, NULL);
     // Allocate GPU memory for source vectors AND initialize from CPU memory
     cl_mem GPUVector1 = clCreateBuffer(GPUContext, CL_MEM_READ_ONLY |
     CL_MEM_COPY_HOST_PTR, sizeof(int) * SIZE, HostVector1, NULL);
     cl_mem GPUVector2 = clCreateBuffer(GPUContext, CL_MEM_READ_ONLY |
     CL_MEM_COPY_HOST_PTR, sizeof(int) * SIZE, HostVector2, NULL);
     // Allocate output memory on GPU
     cl_mem GPUOutputVector = clCreateBuffer(GPUContext, CL_MEM_WRITE_ONLY,
     sizeof(int) * SIZE, NULL, NULL);
     // Create OpenCL program with source code
     cl_program OpenCLProgram = clCreateProgramWithSource(GPUContext, 7, OpenCLSource, NULL, NULL);
     // Build the program (OpenCL JIT compilation)
     clBuildProgram(OpenCLProgram, 0, NULL, NULL, NULL, NULL);
     // Create a handle to the compiled OpenCL function (Kernel)
     cl_kernel OpenCLVectorAdd = clCreateKernel(OpenCLProgram, "VectorAdd", NULL);
     // In the next step we associate the GPU memory with the Kernel arguments
     clSetKernelArg(OpenCLVectorAdd, 0, sizeof(cl_mem), (void*)&amp;GPUOutputVector);
     clSetKernelArg(OpenCLVectorAdd, 1, sizeof(cl_mem), (void*)&amp;GPUVector1);
     clSetKernelArg(OpenCLVectorAdd, 2, sizeof(cl_mem), (void*)&amp;GPUVector2);
 
     //create event
     cl_event event = clCreateUserEvent(GPUContext, NULL);
 
     // Launch the Kernel on the GPU
     // This kernel only uses global data
     size_t WorkSize[1] = {SIZE}; // one dimensional Range
     clEnqueueNDRangeKernel(cqCommandQueue, OpenCLVectorAdd, 1, NULL, WorkSize, NULL, 0, NULL, &amp;event);
     // Copy the output in GPU memory back to CPU memory
     clEnqueueReadBuffer(cqCommandQueue, GPUOutputVector, CL_TRUE, 0,
     SIZE * sizeof(int), HostOutputVector, 0, NULL, NULL);
     // Cleanup
     clReleaseKernel(OpenCLVectorAdd);
     clReleaseProgram(OpenCLProgram);
     clReleaseCommandQueue(cqCommandQueue);
     clReleaseContext(GPUContext);
     clReleaseMemObject(GPUVector1);
     clReleaseMemObject(GPUVector2);
     clReleaseMemObject(GPUOutputVector);    
 
     clWaitForEvents(1, &amp;event);
     cl_ulong start = 0, end = 0;
     double total_time;     
 
     clGetEventProfilingInfo(event, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &amp;start, NULL);
     clGetEventProfilingInfo(event, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &amp;end, NULL);
 
     total_time = end - start;     
 
     printf("\nExecution time in milliseconds = %0.3f ms", (total_time / 1000000.0) );
     printf("\nExecution time in seconds = %0.3f s\n\n", ((total_time / 1000000.0))/1000 );          
 
     return 0;
}

CPU 代码是简单的 C 程序,和上面一样计算同样的队列求和。为了计算消耗的时间,我们使用 time.h中的库。代码如下:

#include 
#include 
#include  
 
int InitialData1[80] = {37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17,37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17,37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17,37,50,54,50,56,0,43,43,74,71,32,36,16,43,56,100,50,25,15,17};
int InitialData2[80] = {35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15,35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15,35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15,35,51,54,58,55,32,36,69,27,39,35,40,16,44,55,14,58,75,18,15};
 
#define SIZE 600000
 
int main(int argc, char **argv)
{
time_t start, stop;
clock_t ticks;
 
time(&amp;start);    
// Two integer source vectors in Host memory
int HostVector1[SIZE], HostVector2[SIZE];
//Output Vector
int HostOutputVector[SIZE];
// Initialize with some interesting repeating data
//int n;
for(int c = 0; c < SIZE; c++)
{
HostVector1[c] = InitialData1[c%20];
HostVector2[c] = InitialData2[c%20];
HostOutputVector[c] = 0;
}
 
for(int i = 0; i < SIZE; i++)
{
        HostOutputVector[i] = HostVector1[i] + HostVector2[i];
        ticks = clock();
}     
 
time(&amp;stop);
 
printf("\nExecution time in miliseconds = %0.3f ms",((double)ticks/CLOCKS_PER_SEC)*1000);
 
printf("\nExecution time in seconds = %0.3f s\n\n", (double)ticks/CLOCKS_PER_SEC);
 
return 0;
}

04、交叉编译应用

>>>

同一个 Makefile 可以用于交叉编译 GPU 和 CPU 应用。你需要注意下面的三个变量。根据你的系统做相应的调整:

  • ROOTFS_DIR -> Apalis iMX6 文件系统路径

  • APPNAME -> 应用的名字

  • TOOLCHAIN -> 交叉编译工具的路径

export ARCH=arm
export ROOTFS_DIR=/usr/local/toradex-linux-v2.5/oe-core/build/out-glibc/sysroots/apalis-imx6
 
APPNAME = proc_sample
TOOLCHAIN = /home/prjs/toolchain/gcc-linaro
 
CROSS_COMPILER = $(TOOLCHAIN)/bin/arm-linux-gnueabihf-
CC= $(CROSS_COMPILER)gcc
DEL_FILE = rm -rf
CP_FILE = cp -rf
TARGET_PATH_LIB = $(ROOTFS_DIR)/usr/lib
TARGET_PATH_INCLUDE = $(ROOTFS_DIR)/usr/include
CFLAGS = -DLINUX -DUSE_SOC_MX6 -Wall -std=c99 -O2 -fsigned-char -march=armv7-a -mfpu=neon -DEGL_API_FB -DGPU_TYPE_VIV -DGL_GLEXT_PROTOTYPES -DENABLE_GPU_RENDER_20 -I../include -I$(TARGET_PATH_INCLUDE)
LFLAGS = -Wl,--library-path=$(TARGET_PATH_LIB),-rpath-link=$(TARGET_PATH_LIB) -lm -lglib-2.0 -lOpenCL -lCLC -ldl -lpthread
OBJECTS = $(APPNAME).o
first: all
all: $(APPNAME)
$(APPNAME): $(OBJECTS)
$(CC) $(LFLAGS) -o $(APPNAME) $(OBJECTS)
$(APPNAME).o: $(APPNAME).c
$(CC) $(CFLAGS) -c -o $(APPNAME).o $(APPNAME).c
clean:
$(DEL_FILE) $(APPNAME)

在应用所在的目录中保持 Makefile 文件,然后运行 make。
将编译生成的文件复制到 Apalis iMX6 开发板上。

测试结果

在执行两个应用程序后,我们得到以下结果:

### Processor time
Execution time in miliseconds = 778.999 ms
Execution time in seconds = 0.779 s 
 
### GPU time 
Execution time in milliseconds = 12.324 ms
Execution time in seconds = 0.012 s

根据以上结果,我们可以很清楚地看到在 Apalis iMX6Q GPU 上使用 OpenCL 能够加速队列求和运算。

总结

>>>

借助 OpenCL,可以在不同设备从图形显卡到超级计算机以及嵌入式设备,运行代码。用户还可以进一步结合,例如在 OpenCV 中使用 OpenCL 提高计算机视觉的性能。

对于绝大多数嵌入式应用,Linux 是正确的选择。Linux 编译系统,例如 Buildroot 和 OpenEmbedded,能够创建定制化的 BSP,裁剪到任意的大小,并且提供丰富的应用和 SDK,从 gstreamer、Python 到 node.js 等。基于 OpenEmbedded/Yocto 的 Linux 是 Toradex 支持的默认发行版本,开发社区还提供多种开发语言环境和框架。

现在的 GUI 可以使用 Qt、HTML5 来开发,以至于有点难于选择。

你也可以使用 NDK 基于 C/C++ 和 C# 开发你的应用,使用 Qt 作为显示框架或者利用 Cordova 或者 React Native 框架用 Javascript 开发移动应用

故我在

点击下方卡片 关注我

↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2053764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于Hipe并发库中动态线程库DynamicThreadPond的一点解读(二)

文章目录 前提动态减少代码解读 动态增加线程池退出时发生了什么&#xff1f;总结附录 前提 我们在关于Hipe并发库中动态线程库DynamicThreadPond的一点解读(一)中介绍了DynamicThreadPond如何初始化&#xff0c;如何向任务队列中添加任务&#xff0c;线程池中的线程如何执行任…

机器学习——第十二章 计算学习理论

目录 12.1 基础知识 12.2 PAC学习 12.3 有限假设空间 12.3.1 可分情形 12.3.2 不可分情形 12.4 VC维 12.5 Rademacher复杂度 12.6 稳定性 12.1 基础知识 计算学习理论(computational learning theory)研究的是关于通过"计算"来进行"学习"的理论…

SAP无参考收货

其他收货 如果我们未参考其他凭证&#xff08;采购订单、生产订单或预留&#xff09;输入货物移动&#xff0c;则我们将讲到其他收货。因为在实际过帐之前&#xff0c;系统不会存储任何物料、数量、交货日期、接收工厂或来源方面的信息&#xff0c;所以此类收货属于计划外货物…

一文入门re 正则表达式

一、常用方法 &#xff08;一&#xff09;匹配 一般使用方法 第一个参数&#xff1a;正则模式 第二个参数&#xff1a;需要处理的字符串 第三个参数&#xff1a;附加处理方法result从任意位置开始匹配&#xff0c;返回match&#xff0c;没有匹配到返回None result re.searc…

MiniCPM-V: A GPT-4V Level MLLM on Your Phone论文阅读

大模型的趋势&#xff1a;模型性能越来越好&#xff0c;模型参数变小&#xff0c;端边设备计算能力变强。 MiniCPM-V优点 结果好、OCR能力突出、多分辨率、多语言、易于部署 模型结构 图片encoder适用vit。输入整体以及切片。切片使用自适应算法&#xff0c;通过计算分数&am…

揭秘图数据库:如何颠覆社交网络分析的游戏规则

在当今信息化时代&#xff0c;社交网络的数据量以指数级的速度增长&#xff0c;人们在社交网络中产生了海量的交互数据。如何从这些复杂的数据中提取有价值的信息&#xff0c;成为了数据科学和社交网络分析领域的一大挑战。图数据库作为一种有效的数据管理和分析工具&#xff0…

Anzo Capital视角下马丁格尔交易策略的利与弊

在Anzo Capital&#xff0c;我们深入探讨了马丁格尔交易策略的多面性。这种策略因其简单性而受到一些交易者的欢迎&#xff0c;但同时也存在着不可忽视的风险。本文将客观分析其优势和潜在缺陷&#xff0c;帮助投资者在Anzo Capital平台上做出更明智的决策。 马丁格尔策略的核…

JavaEE项目总结(1)

一、在vue项目中安装axios 由于需要使用axios框架进行异步请求&#xff0c;所以需要在vue项目中安装axios框架。在官方下载速度较慢&#xff0c;所以选择更换镜像源&#xff08;我使用的是华为云镜像&#xff09; 在项目终端中输入npm config set registry http://mirrors.…

MES系统:制造业转型升级的驱动力与效益源泉

制造业之所以需要并采纳MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;&#xff0c;主要是出于以下几个方面的考虑和需求。MES系统能够为企业带来显著的好处&#xff0c;具体体现在以下几个方面&#xff1a; 制造业需要MES系统的原因…

信贷风控架构一张图

记得刚入门风控时,由于做的模型岗,总有一种不能窥探风控全貌的疑惑。随着经验的积累,以及一些岗位的体验,慢慢对风控有了更清晰的认知。本文以一张图的形式简单地呈现信贷风控架构,让你能快速窥探信贷风控全貌。 文章目录 一、什么是风险管控?二、信贷风控架构一张图三、…

PCDN日常应用--公共资源利用

1、前言 PCDN 是通过在 CDN 的边缘节点上部署代理服务器&#xff0c;来实现主动调度和传输内容的方法。当用户请求内容时&#xff0c;PCDN 将根据各个节点的负载情况、距离、传输速度等一系列因素来动态选择最优的节点来提供内容。这样的调度方式使得 PCDN 在面对大规模请求时能…

健康管理系统解决方案

产品简介 脉购CRM健康管理系统是一款集会员健康管理以及会员数字化营销于一体的系统解决方案&#xff0c;旨在帮助企业更好地提供个性化的健康服务&#xff0c;维护好会员关系&#xff0c;通过有效的营销手段增加健康会员粘性和满意度&#xff0c;最终达到业绩增长的目的。 核…

云计算第三阶段---DBA Day2 -- Day4

DBA DAY2 一、常用函数、 常用函数 按使用方式分类按用途分类 ○单行函数 ○分组函数 ○字符函数 ○数学函数 ○日期函数 字符函数实例&#xff1a; ●LENGTH(str)&#xff1a;返字符串长度&#xff0c;以字节为单位 mysql> select length(abc); --------------- | l…

【NXP-MCXA153】TFT驱动移植

介绍 GC9A01是一款1.28寸的圆形TFT显示屏模块&#xff0c;分辨率为240x240&#xff0c;支持三种颜色格式&#xff1a;RGB444、RGB565、RGB666&#xff0c;本次实验将在NXP-MCXA153开发板上移植TFT LCD驱动 移植流程 ① 绑定TFT LCD上面的GPIO&#xff0c;如SCL_PIN、SDA_PIN…

Java - File、IO流

File类 File是java.io.包下的类&#xff0c;File类的对象&#xff0c;用于代表当前操作系统的文件&#xff08;可以是文件、文件夹&#xff09; File类只能对文件本身进行操作&#xff0c;不能读写文件里面存储的数据。 创建File类的对象&#xff1a; //创建file对象//1.具体…

House of Roman / malloc_printerr间接调用malloc函数执行malloc_hook

文章目录 House of Roman介绍&#xff1a;原理&#xff1a;例子&#xff1a;思路&#xff1a;分析&#xff1a;利用&#xff1a; House of Roman 介绍&#xff1a; House of Roman 这个技巧说简单点其实就是 fastbin attack 和 Unsortbin attack 结合的一个小 trick。该技术用…

python-字符串操作(赛氪OJ)

[题目描述] 给定长度为 n 的只有小写字母的字符串 s &#xff0c;进行 m 次操作&#xff0c;每次将 [l,r] 范围内所有 c1 字符改成 c2 &#xff0c;输出操作完的字符串。输入格式&#xff1a; 第一行两个数 n , m &#xff0c; 第二行一个字符串 s &#xff0c; 之后 m 行&…

Ubuntu修改命令提示符格式PS1

命令提示符组成 通过Xshell进入Ubuntu的默认提示符是这样的 rootDESKTOP:~# root: 当前登录用户DESKTOP&#xff1a;主机名~: 当前目录名&#xff08;即用户主目录&#xff09;#&#xff1a;超级用户权限(root用户显示)$: 普通用户权限(非root用户显示) 修改命令提示符 step1…

linux 环境下 配置jenkins前端服务

前期准备 如果没有&#xff0c;需要安装插件 <b>HH:mm:ss</b> <b>HH:mm:ss.S</b> $PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS! 连接服务器的密码 测试 前端部署 需要安装nodejs的插件 执行脚本 node -v npm install rm -rf ./dist…

未解决:不同类型的数据用memcpy然后打印(printf)出问题

引子&#xff1a; 本来是搜memcpy在拷贝两个数组时两数组下标类型不同的问题&#xff0c;即&#xff0c;若一个数组是很长&#xff0c;其下标用long型&#xff0c;要将此数组的一小段拷贝到另一个数组中&#xff0c;这另一个数组的下标只需要用int型&#xff0c;不确定会不会出…