【高性能计算】opencl语法及相关概念(二):索引,队列,核函数

news2024/11/27 22:33:44

目录

  • 数据并行及任务并行
  • 异构编程语言的共性
  • opencl的划分方式
  • opencl上下文定义
  • 以字符串为主的程序对象
  • 同一设备,多个命令队列
  • 同时执行多个核函数的示例

数据并行及任务并行

数据并行是将大规模的计算任务划分为多个子任务,并将这些子任务同时应用于不同的数据集上。每个子任务在独立的处理器上执行,通过对不同的数据集进行并行处理来提高计算性能。数据并行特别适用于对大规模数据集进行相同操作的情况,例如矩阵乘法、图像处理等。

任务并行是将计算任务划分为多个子任务,每个子任务执行不同的操作或指令序列。不同的子任务在不同的处理器上同时执行,通过并行执行多个不同的操作来提高计算性能。任务并行特别适用于复杂的计算任务,其中不同的子任务之间存在依赖关系,需要协同工作完成。

异构编程语言的共性

在这里插入图片描述在高性能计算中,"内核"是指计算任务的最小执行单元或最小计算单元。它代表了一系列指令的集合,可以在处理器上执行,完成特定的计算或操作。

内核通常是针对某个特定的计算问题或算法进行优化的实现。它可以包含许多计算和数据处理操作,如矩阵乘法、向量运算、排序算法等。内核的设计旨在充分利用硬件资源(如处理器、内存等),通过并行执行多个内核实例,以提高计算性能和效率。

内核执行时通常独立于主程序运行,并且可以在多个处理器上并行执行。内核的优化包括利用数据局部性、向量化指令、缓存优化等技术,以最大程度地利用处理器的计算能力。

在高性能计算中,通过合理地划分和管理任务的内核,可以充分发挥并行计算的潜力,提高程序的执行效率和整体性能。内核的优化是高性能计算中关键的一环。
在这里插入图片描述

opencl的划分方式

在这里插入图片描述
(1)全局索引:对图示整个方阵而言,坐标位置在(6,5)。
(2)工作组(localSize),是对全局工作项的二次粗划分。
(3)局部索引,是工作项在工作组内部的索引。
在核函数内使用工作组,可以通过获取每个工作项的全局和局部索引来访问数据。索引的计算和访问逻辑如下:

  1. 获取全局索引:使用内置变量get_global_id(dim)可以获取当前工作项在维度dim上的全局索引。dim的值通常是0,表示第一个维度。

  2. 获取局部索引:使用内置变量get_local_id(dim)可以获取当前工作项在维度dim上的局部索引。dim的值通常是0,表示第一个维度。

  3. 获取全局范围:使用内置变量get_global_size(dim)可以获取整个执行范围(全局工作项数量)在维度dim上的大小。dim的值通常是0,表示第一个维度。

  4. 获取局部范围:使用内置变量get_local_size(dim)可以获取工作组在维度dim上的大小。dim的值通常是0,表示第一个维度。

通过这些索引和范围,您可以根据需要访问全局和局部内存中的数据。下面是一个简单的示例:

__kernel void myKernel(__global float* input, __global float* output)
{
    // 获取全局索引和局部索引
    size_t globalId = get_global_id(0);
    size_t localId = get_local_id(0);

    // 获取全局范围和局部范围
    size_t globalSize = get_global_size(0);
    size_t localSize = get_local_size(0);

    // 计算对应全局索引的输入和输出数据索引
    size_t inputIndex = globalId;
    size_t outputIndex = globalId;

    // 计算对应局部索引的输入和输出数据索引
    size_t localInputIndex = localId;
    size_t localOutputIndex = localId;

    // 在此处使用索引访问输入和输出数据,并进行计算
    output[outputIndex] = input[inputIndex] + localInput[localInputIndex];

    // 等待所有工作项完成局部计算
    barrier(CLK_LOCAL_MEM_FENCE);

    // 在此处进行工作组级别的计算或协作

}

在这个示例中,我们在核函数myKernel中获取了全局索引和局部索引。使用这些索引,我们可以计算出要访问的输入和输出数组的索引,并在核函数内执行相应的计算操作。还可以使用局部索引进行工作组级别的计算或者协作(例如使用局部内存进行数据共享)。最后的barrier函数用于等待所有工作项完成局部计算,以确保所有工作项能够同步执行。

opencl上下文定义

设备(device):宿主机使用的 OpenCL设备集合。
内核 (kernel):在 OpenCL 设备上运行的 OpenCL 函数。
程序对象 (program object):实现内核的程序源代码和可执行文件。
内存对象(memory object):内存中对OpenCL设备可见的组对象,包含可以由内核实
例处理的值。

以字符串为主的程序对象

上下文中还包括一个或多个程序对象 (program object),程序对象包含内核的代码。程序对
象这个名字的选择容易让人混淆。最好把它想象成一个动态库,可以从中取出内核使用的函
数。程序对象会在运行时由宿主机程序构建。对于不是从事图形领域的程序员来说,这看起来
可能有些奇怪。可以考虑一下 OpenCL程序员面对的挑战。他编写了 OpenCL应用程序并把这个
应用程序交给最终用户,但是这些用户可能选择在其他地方运行这个应用程序。程序员根本无
法控制最终用户在哪里运行应用程序 (可能是 CPU、CPU 或者其他芯片)。OpenCL 程序员所知
道的只是目标平台符合OpenCL规范。
对于这个问题,解决方法就是在运行时从源代码构建程序对象。宿主机程序定义上下文中
的设备。只有那时才有可能知道如何编译程序源代码来创建内核代码。对于源代码本身
OpenCL 在形式上相当灵活。在很多情况下,这会是一个常规的字符串,可以在宿主机程序中静
态定义,或者在运行时从一个文件加载,也可能在宿主机程序中动态生成。

同一设备,多个命令队列

在OpenCL中,您可以在一个设备上创建多个命令队列。这样做主要有以下几个优点:

1. 并发执行:通过使用多个命令队列,您可以并行地执行多个内核或命令,从而提高程序的性能和效率。

2. 独立管理:每个命令队列都有自己的内核执行顺序和状态,因此您可以更灵活地管理和调度不同的内核和任务。

3. 异步操作:使用多个命令队列可以实现异步操作。您可以将多个命令添加到不同的队列中,并在需要时进行同步。

然而,创建多个命令队列也可能有一些注意事项:

1. 设备限制:每个设备的命令队列数量是有限的,具体取决于硬件和驱动程序,超出限制可能导致错误。可以使用`clGetDeviceInfo`函数查询设备支持的最大命令队列数。

2. 内存和资源管理:每个命令队列都有独立的内存和资源管理,因此需要确保系统中的总资源使用不超过设备的限制。

3. 同步和数据共享:不同命令队列之间的同步和数据共享可能需要额外的机制,如事件或内存对象的共享。

为了示范在同一个设备上创建多个命令队列,下面是一个简单的代码片段:

```cpp
cl_device_id device;
cl_context context;
cl_command_queue queue1, queue2;
  
// 创建设备和上下文
clGetDeviceIDs(NULL, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
context = clCreateContext(NULL, 1, &device, NULL, NULL, NULL);

// 创建命令队列
queue1 = clCreateCommandQueue(context, device, 0, NULL);
queue2 = clCreateCommandQueue(context, device, 0, NULL);

// 在命令队列1中提交命令
clEnqueueNDRangeKernel(queue1, kernel1, ...);

// 在命令队列2中提交命令
clEnqueueNDRangeKernel(queue2, kernel2, ...);

在这个示例中,我们通过调用clCreateCommandQueue函数两次来创建了两个命令队列:queue1queue2。然后,我们可以在这两个命令队列中分别提交不同的内核或任务。

需要注意的是,不同命令队列之间的执行顺序是不确定的,除非您使用额外的同步机制,如事件或屏障,来确保它们的顺序和同步。另外,多个命令队列的使用可能需要更复杂的任务和数据管理,以确保正确的同步和协调。



```cpp
queue:命令队列对象,用于提交命令。

num_events_in_wait_list:等待事件列表中事件的数量。通常为0,表示没有等待的事件。

event_wait_list:指向等待事件列表的指针,用于指定在执行标记之前需要等待的事件。通常为NULL,表示没有等待的事件。

event:返回的事件对象,用于标识标记命令的事件。在事件完成之前,可以使用该事件来等待或查询内核的执行状态。

同时执行多个核函数的示例

#include <CL/cl.h>
#include <iostream>

#define NUM_ELEMENTS 1000
// 在这段代码中,R"(...)" 是一种称为原始字符串字面量(raw string literals)的C++11特性。这种特性允许在字符串中包含特殊字符而无需进行转义。
// 在您给出的代码中,R"( 和 ")" 包围的部分是一个原始字符串字面量,其中包含了一个内核函数的定义。
// 使用原始字符串字面量的好处是,您可以在字符串中直接使用特殊字符,例如反斜杠和双引号,而无需对它们进行转义。这在编写包含许多转义字符的长字符串时特别方便。
const char* kernelSource[] = {
    R"(
    __kernel void copy(__global int* input, __global int* output) {
        int gid = get_global_id(0);
        output[gid] = input[gid];
    }
    )",

    R"(
    __kernel void doublevalue(__global int* output) {
        int gid = get_global_id(0);
        output[gid] = output[gid]+1;
    }
    )"
};

int main() {
    cl_platform_id platform;
    cl_device_id device;
    cl_context context;
    cl_command_queue queue;
    cl_program program;
    cl_kernel kernel;
    cl_kernel kernel2;
    cl_mem inputBuffer, outputBuffer;
    cl_event syncEvent;
    int input[NUM_ELEMENTS];
    int output[NUM_ELEMENTS];

    // 初始化输入数据
    for (int i = 0; i < NUM_ELEMENTS; i++) {
        input[i] = i;
    }

    // 创建平台、设备和上下文
    clGetPlatformIDs(1, &platform, NULL);
    clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, 1, &device, NULL);
    context = clCreateContext(NULL, 1, &device, NULL, NULL, NULL);
    queue = clCreateCommandQueue(context, device, 0, NULL);

    // 创建内存对象
    inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(int) * NUM_ELEMENTS, input, NULL);
    outputBuffer = clCreateBuffer(context,  CL_MEM_READ_WRITE, sizeof(int) * NUM_ELEMENTS, NULL, NULL);
    cl_int err;
    // 创建内核程序并构建
    program = clCreateProgramWithSource(context, 2, kernelSource, NULL, &err);
    if (program == NULL || err != CL_SUCCESS) {
        std::cout << "Failed to create program object." << std::endl;
    }
    //1即num_devices:构建程序的设备数量。
    clBuildProgram(program, 1, &device, NULL, NULL, NULL);
    // 创建内核,第2个参数需要对应核函数名
    kernel = clCreateKernel(program, "copy", NULL);
    kernel2 = clCreateKernel(program, "doublevalue", NULL);

    // 设置内核参数
    clSetKernelArg(kernel, 0, sizeof(cl_mem), &inputBuffer);
    clSetKernelArg(kernel, 1, sizeof(cl_mem), &outputBuffer);
    clSetKernelArg(kernel2, 0, sizeof(cl_mem), &outputBuffer);
    // 创建同步事件
    // syncEvent = clCreateUserEvent(context, NULL);
    size_t globalSize[1]={NUM_ELEMENTS};
    //注意:在同一个命令队列中,对于相同的命令队列上的内核函数,它们通常是按顺序执行的。
    //这意味着在命令队列中提交的内核函数将按照它们被插入的顺序进行执行。
    //执行第一个内核
    clEnqueueNDRangeKernel(queue, kernel, 1, NULL, globalSize, NULL, 0, NULL, NULL);

    // 执行第二个内核(在第一个内核完成后执行)第二个1为在内核程序执行前等待的数量
    clEnqueueNDRangeKernel(queue, kernel2, 1, NULL, globalSize, NULL, 0, NULL, NULL);

    // 读取结果
    clEnqueueReadBuffer(queue, outputBuffer, CL_TRUE, 0, sizeof(int) * NUM_ELEMENTS, output, 0, NULL, NULL);

    // 打印结果
    for (int i = 0; i < NUM_ELEMENTS; i++) {
        std::cout << "Output[" << i << "] = " << output[i] << std::endl;
    }

    // 释放资源
    clReleaseMemObject(inputBuffer);
    clReleaseMemObject(outputBuffer);
    clReleaseKernel(kernel);
    clReleaseKernel(kernel2);
    clReleaseProgram(program);
    clReleaseCommandQueue(queue);
    clReleaseContext(context);
    return 0;
}

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

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

相关文章

十二、外观模式

一、什么是外观模式 外观&#xff08;Facade&#xff09;模式又叫作门面模式&#xff0c;是一种通过为多个复杂的子系统提供一个一致的接口&#xff0c;而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口&#xff0c;外部应用程序不用关心内部子系统的具体细节&am…

【ES】笔记-Set集合实践

JS <script>let arr[1,2,3,4,5,4,3,2,1];//1.数组去重let result0[...new Set(arr)];console.log(数组去重${result0});//2.交集let arr2[4,5,6,5,6];let result[...new Set(arr)].filter(item>{let s2new Set(arr2);//4 5 6if(s2.has(item)){return true;}else{retur…

论文阅读_医疗知识图谱_GraphCare

英文名称: GraphCare: Enhancing Healthcare Predictions with Open-World Personalized Knowledge Graphs 中文名称: GraphCare&#xff1a;通过开放世界的个性化知识图增强医疗保健预测 文章: http://arxiv.org/abs/2305.12788 代码: https://github.com/pat-jj/GraphCare 作…

ELK安装、部署、调试(六) logstash的安装和配置

1.介绍 Logstash是具有实时流水线能力的开源的数据收集引擎。Logstash可以动态统一不同来源的数据&#xff0c;并将数据标准化到您选择的目标输出。它提供了大量插件&#xff0c;可帮助我们解析&#xff0c;丰富&#xff0c;转换和缓冲任何类型的数据。 管道&#xff08;Logs…

WebGIS的一些学习笔记

一、简述计算机网络的Internet 概念、网络类型分类、基本特征和功用是什么 计算机网络的Internet 概念 计算机网络是地理上分散的多台独立自主的计算机遵循约定的通讯协议&#xff0c;通过软、硬件互连以实现交互通信、资源共享、信息交换、协同工作以及在线处理等功能的系统…

基于深度学习的AI生成式人脸图像鉴别

AIGC&#xff08;AI内容生成&#xff09;技术的快速发展确实为创作者提供了高效生产力工具&#xff0c;但同时也引发了一些问题和挑战。这些技术可以生成以假乱真的图像、视频换脸等&#xff0c;给不法分子提供了滥用的机会。其中&#xff0c;一些不法分子可能利用AIGC技术制造…

JZ13 机器人的运动范围

题目描述&#xff1a; 思路&#xff1a;使用深度优先&#xff08;dfs&#xff09;搜索方法 从[0,0]开始&#xff0c;每次选择一个方向开始检查能否访问&#xff0c;如果能访问进入该节点&#xff0c;该节点作为子问题&#xff0c;继续按照这个思路访问&#xff0c;一条路走到…

verilator的安装

出现libgz安装包找不到的问题

官方推荐使用的OkHttp4网络请求库全面解析(Android篇)

作者&#xff1a;cofbro 前言 现在谈起网络请求&#xff0c;大家肯定下意识想到的就是 okhttp 或者 retrofit 这样的三方请求库。诚然&#xff0c;现在有越来越多的三方库帮助着我们快速开发&#xff0c;但是对于现在的程序员来说&#xff0c;我们不仅要学会如何去用&#xff…

(牛客周赛 9)C.小美的01串翻转

题目&#xff1a; 样例&#xff1a; 输入 10001 输出 8 思路&#xff1a; 这里是连续的找子串&#xff0c;权值的意思是 我们取反操作了多少次&#xff0c; 我们有假设长度是 5 &#xff0c;字符串是 10001 那么相邻不一样的字符串有两种情况 01010 或者 10101&#xf…

【项目】Reactor模式的服务器

目录 Reactor完整代码连接 前置知识&#xff1a; 1.普通的epoll读写有什么问题&#xff1f; 2.Connection内的回调函数是什么 3.服务器的初始化&#xff08;Connection只是使用的一个结构体&#xff09; 4.等待就绪事件&#xff1a;有事件就绪&#xff0c;对使用Connectio…

MATLAB实现AHP层次分析法——以情人节选取礼物为例

问题背景&#xff1a; 情人节来临之际&#xff0c;广大直男&#xff08;女&#xff09;同胞在给异性朋友选购礼物时会遇到难题——什么才是礼物好坏最重要的标准&#xff1f;基于层次分析法AHP进行计算&#xff0c;得出最高权重的指标&#xff0c;给出各位朋友选购礼物的一种思…

Vector<T> 动态数组(模板语法)

C数据结构与算法 目录 本文前驱课程 1 C自学精简教程 目录(必读) 2 动态数组 Vector&#xff08;难度1&#xff09; 其中&#xff0c;2 是 1 中的一个作业。2 中详细讲解了动态数组实现的基本原理。 本文目标 1 学会写基本的C类模板语法&#xff1b; 2 为以后熟练使用 S…

java.lang.classnotfoundexception: com.android.tools.lint.client.api.vendor

Unity Android studio打包报错修复 解决方式 java.lang.classnotfoundexception: com.android.tools.lint.client.api.vendor 解决方式 在 launcherTemplate 目录下找到 Android/lintOptions 选项 加上 checkReleaseBuilds false lintOptions { abortOnError false checkRelea…

以GitFlow分支模型为基准的Git版本分支管理流程

以GitFlow分支模型为基准的Git版本分支管理流程 文章目录 以GitFlow分支模型为基准的Git版本分支管理流程GitFlow分支模型中的主要概念GitFlow的分支管理流程图版本号说明借助插件Git Flow Integration Plus实现分支模型管理其他模型TBD模型阿里AoneFlow模型 GitFlow分支模型中…

设计模式的使用——建造者模式+适配器模式

项目代码地址 一、需求介绍 现公司数据库有一张表中的数据&#xff0c;需要通过外部接口将数据推送到别人的系统中。现有的问题是&#xff1a; 数据字段太多&#xff0c;而且双方系统实体字段不一致&#xff0c;每次都要通过get、set方法去对数据取值然后重新赋值。如果后期需…

使用php实现微信登录其实并不难,可以简单地分为三步进行

使用php实现微信登录其实并不难&#xff0c;可以简单地分为三步进行。 第一步&#xff1a;用户同意授权&#xff0c;获取code //微信登录public function wxlogin(){$appid "";$secret "";$str"http://***.***.com/getToken";$redirect_uriu…

家政电子邮件营销怎么做?邮件营销的方案?

家政电子邮件营销的作用&#xff1f;企业如何利用营销邮件拓客&#xff1f; 随着科技的不断发展&#xff0c;家政服务行业也逐渐融入了电子邮件营销的方式&#xff0c;这为家政企业提供了与客户更紧密互动的机会。在本文中&#xff0c;我们将探讨家政电子邮件营销的几个关键步…

OLED透明屏显示技术:未来显示科技的领航者

OLED透明屏显示技术是一种创新性的显示技术&#xff0c;它的特殊性质使其成为未来显示科技的领航者。 OLED透明屏具有高对比度、快速响应时间、广视角和低功耗等优势&#xff0c;同时&#xff0c;其透明度、柔性和薄型设计使其成为创新设计的理想选择。 本文将深入探讨OLED透…

从零做软件开发项目系列之九——项目结项

前言 一个项目&#xff0c;经过前期的需求调研分析&#xff0c;软件设计&#xff0c;程序开发&#xff0c;软件测试、系统部署、试运行系统调试等过程&#xff0c;最后到了项目的验收阶段&#xff0c;也就是项目生命周期的最后一个阶段&#xff0c;即项目结项&#xff0c;它涉…