介绍

由于OpenCL是为各类处理器设备而打造的开发标准的计算语言。因此跟CUDA不太一样的是,其对设备特征查询的项更上层,而没有提供一些更为底层的特征查询。比如,你用OpenCL的设备查询API只能获取最大work group size,但无法获取到最小线程并行粒度。

但是,由于最小线程并行粒度对于OpenCL应用领域最广的GPU而言确实是一个比较重要的参数。如果你的work group的work item的个数是最小线程并行粒度的倍数,那么你的OpenCL kernel程序往往会达到很高的计算效率,同时也能基于这个模型来做一些Memory Bank Confliction的避免措施。因此,我这里提供了一个比较简单的OpenCL kernel来获取当前GPU或其它处理器的最小线程并行粒度。

基本理论

我们知道,一个计算设备由若干个Compute Unit构i成,而一个Compute Unit中包含了多个Processing Element,一个Compute Unit中的所有Processing Element对于一条算术逻辑指令而言是同时进行操作的。而不同的Compute Unit之间也可以是同时进行操作。因此,GPU的并行可以划分为两个层次——一层是Compute Unit内的所有Processing Element的并行操作;另一层是各个Compute Unit的并行操作。

上面是物理层面,如果对于OpenCL逻辑层面,我们可以认为,一个work group的最大work item个数是指一个compute unit最多能调度、分配的线程数。这个数值一般就是一个CU内所包含的PE的个数的倍数。比如,如果一个GPU有2个CU,每个CU含有8个PE,而Max work group size是512,那么说明一个CU至少可以分配供512个线程并发操作所需要的各种资源。由于一个GPU根据一条算术逻辑指令能对所有PE发射若干次作为一个“原子的”发射操作,因此,这一个对程序员而言作为“原子的”发射操作启动了多少个线程,那么我们就可以认为是该GPU的最小并行线程数。如果一款GPU的最小线程并行数是32,那么该GPU将以32个线程作为一组原子的线程组。这意味着,如果遇到分支,那么一组32个线程组中的所有线程都将介入这个分支,对于不满足条件的线程,则会等到这32个线程中其它线程都完成分支处理之后再一起执行下面的指令。

如果我将work group size指定为64,并且在kernel程序里加一个判断,如果pid小于32做操作A,否则做操作B,那么pid为0~31的线程组会执行操作A,而pid为32到63的线程组不会受到阻塞,而会立马执行操作B。此时,两组线程将并发操作(注意,这里是并发,而不是并行。因为上面讲过,GPU一次发射32个线程的话,那么对于多个32线程组将会调度发射指令)。

根据这个特性,我们就可以写一个OpenCL kernel程序来判别当前GPU的最小并行线程粒度。

我们首先会将work group size定为最大能接受的尺寸。然后,我们将这个work group平均划分为两组,对它们进行测试。我们在中间定义了一个local memory的变量,每个线程都能访问它,不过我们只让pid为0以及pid为[max_work_group_size / 2]的线程去访问它,以不受太多干扰。如果这个标志在线程组0执行时被线程组1改变,那么我们就知道这个粒度并非是最小的,然后对前一组再平均划分为2,递归操作。如果在执行线程组0之后标志没有被更改,那么说明这整个线程组是一个原子的线程组,也就是我们所要的最小并行的线程粒度。

在内核程序中,我们还传了一个用于延迟的循环次数,使得非原子的线程组能够被并发执行。

OpenCL代码片断

执行环境为:Windows 7 64bit  AMD-APU A6-3420M    Visual Studio 2008  ATI Strom APP SDK.

主机端的部分代码片断:

        cl_context context = nullptr;       // OpenCL contextcl_command_queue commandQueue = nullptr;cl_program program = nullptr;       // OpenCL kernel program object that'll be running on the compute devicecl_mem outputMemObj = nullptr;      // output memory object for outputcl_kernel kernel = nullptr;         // kernel objectconst int deviceIndex = 0;context = clCreateContext(NULL,1, &devices[deviceIndex],NULL,NULL,NULL);commandQueue = clCreateCommandQueue(context, devices[deviceIndex], 0, NULL);// Read the kernel code to the bufferFILE *fp = fopen("cl_kernel.cl", "rb");if(fp == nullptr){puts("The kernel file not found!");goto RELEASE_RESOURCES;}fseek(fp, 0, SEEK_END);size_t kernelLength = ftell(fp);fseek(fp, 0, SEEK_SET);char *kernelCodeBuffer = (char*)malloc(kernelLength + 1);fread(kernelCodeBuffer, 1, kernelLength, fp);kernelCodeBuffer[kernelLength] = '\0';fclose(fp);const char *aSource = kernelCodeBuffer;program = clCreateProgramWithSource(context, 1, &aSource, &kernelLength, NULL);status = clBuildProgram(program, 1, &devices[deviceIndex], NULL, NULL, NULL);cl_int outputArg = 0;outputMemObj = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(outputArg), NULL, NULL);kernel = clCreateKernel(program,"QueryMinimumGranularity", NULL);cl_int inputArg = 1000;status = clSetKernelArg(kernel, 0, sizeof(inputArg), &inputArg);status = clSetKernelArg(kernel, 1, sizeof(outputMemObj), &outputMemObj);size_t groupSize;clGetDeviceInfo(devices[deviceIndex], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(groupSize), &groupSize, NULL);size_t global_work_size[1] = { groupSize };size_t local_work_size[1] = { groupSize };status = clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);clFinish(commandQueue);     // Force wait until the OpenCL kernel is completedstatus = clEnqueueReadBuffer(commandQueue, outputMemObj, CL_TRUE, 0, sizeof(outputArg), &outputArg, 0, NULL, NULL);char chBuffer[256];wchar_t wsBuffer[256];sprintf(chBuffer, "The minimum granularity is: %d", outputArg);MBString2WCString(wsBuffer, chBuffer, false);MessageBox(hWnd, wsBuffer, L"Notice", MB_OK);

kernel代码:

__kernel void QueryMinimumGranularity(int nLoop, __global int *pOut)
{__local volatile int flag;int index = get_global_id(0);int totalItems = get_global_size(0);do{int halfIndex = totalItems / 2;if(index == 0)flag = 1;barrier(CLK_LOCAL_MEM_FENCE);if(index < halfIndex){for(int i = 0; i < nLoop; i++){if(flag == -1)break;}if(flag != -1){if(index == 0){*pOut = totalItems;flag = 2;}}}else{if(index == halfIndex){if(flag != 2){//while(flag != 1);flag = -1;}}}barrier(CLK_LOCAL_MEM_FENCE);if(flag == 2)break;totalItems /= 2;}while(totalItems > 0);
}

关于Image Engineering & Computer Vision的更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.


OpenCL最小线程并行粒度相关推荐

  1. dotnet开发使用runtimeconfig.json设置程序默认的最小线程数

    默认最小线程数:用于突发线程爆增时,可秒开设置了最小线程数的线程: 当程序的线程总数大于最小线程数时,dotnet线程池以0.5秒的速度新增加一个线程(会造成线程饿死): 如果未设置默认最小线程,会以 ...

  2. Android平台利用OpenCL框架实现并行开发初试

    在我们熟知的桌面平台,GPU得到了极为广泛的应用,小到各种电子游戏,大到高性能计算,多核心.高并行化的GPU成为我们日常娱乐和科学研究必不可少的"利器".同样,在近些年兴起的移动平 ...

  3. 思考如何保证线程并行的数据安全性

    我们可以思考一下,问题的本质在于共享数据存在并发访问.如果我们能够有一种方法使得线程的并行变成串行,那是不是就不存在这个问题呢? 按照大家已有的知识,最先想到的应该就是锁吧. 毕竟这个场景并不模式,我 ...

  4. 悟纤和师傅去女儿国「线程并行变为串行,Thread你好牛」- 第296篇

    相关历史文章(阅读本文之前,您可能需要先看下之前的系列

  5. 《我要进大厂》- Java并发 夺命连环10问,你能坚持到第几问?(进程线程 | 并行并发 | 上下文切换 | 线程死锁 | 线程创建)

  6. 将帐户从Quickbooks迁移到Xero

    As I mentioned in my last post, last year we moved the business accounts for edgeofmyseat.com from Q ...

  7. GPU通用计算调研报告

    摘要:NVIDIA公司在1999年发布GeForce256时首先提出GPU(图形处理器)的概念,随后大量复杂的应用需求促使整个产业蓬勃发展至今.GPU在这十多年的演变过程中,我们看到GPU从最初帮助C ...

  8. 10年GPU通用计算回顾

    硬件T&L单元催生GPU诞生 前言:从世界上第一款GPU横空出世到今天,显卡已经走过了10年历史.GPU在这10年演变过程中,我们看到GPU从最初帮助CPU分担几何吞吐量,到Shader单元初 ...

  9. 线程VS进程,多线程VS多进程,并行VS并发,单核cpuVS多核cpu

    目录 概论 进程VS线程 并发VS并行 多线程VS多进程 总结 概论 程序是为完成特定任务.用某种语言编写的组指令的集合.即指一段静态的代码,静态对象. 进程是程序的次执行过程, 或是正在运行的 一个 ...

最新文章

  1. Spring MVC 4
  2. 微软好绝情:Windows 7再无重大更新!
  3. ipad2“新瓶装老酒” 苹果创新乏力?
  4. 《中国编程挑战赛--资格赛》赛题及解答
  5. AI顶会直播丨深度学习顶级会议ICLR 2021中国预讲会明天召开,为期三天五大论坛...
  6. 8个高效的Python爬虫框架分享
  7. Jmeter笔记(Ⅱ)使用Jmeter实现轻量级的接口自动化测试
  8. WP与IOS与Android的后台机制相同与不同
  9. Java面试题:2021大厂最全Java面试资料
  10. js实现数组翻转(倒序输出)
  11. LightOJ 1406 Assassin`s Creed
  12. Spark3 读写 S3 Parquet, Hive, Hudi
  13. CSS实现DIV块的阴影效果
  14. HDOJ(HDU)1000A + B Problem Java题解
  15. 尤雨溪大大在 6 月 4 日的 Vue3.0 技术分享
  16. 2019淘宝双十一活动前期营销分析
  17. 《纸牌屋》:大数据只是噱头
  18. shell文本编辑之awk
  19. 计算机网络名词缩写,计算机网络名词 英文缩写解释大全
  20. android 8.1 Launcher3 去掉抽屉式,显示所有 app

热门文章

  1. 延迟渲染(Deferred Rendering)
  2. 005 GO-高级数据类型(结构体和方法)
  3. 西瓜大战java_让“西瓜大战”来得更加猛烈些
  4. 【程序人生】那些争议最大的编程观点
  5. 浑天码部件代码表说明
  6. 两台电脑通过trunk口互通,修改trunk 接口pvid后不通了 <1>
  7. 《推荐几个常用的H5+APP制作开发工具》
  8. 解决flask框架本地代码已经刷新,但是端口内容不变的问题
  9. Oracle 11gR2 RAC 单网卡 转 双网卡绑定 配置步骤
  10. Navicat for Mysql建立本地连接出现 1044或1045的问题并如何解决。