通过OpenCL内核代码猜测设备寄存器个数
在OpenCL标准中,没有给出查看计算设备一共有多少寄存器,至少能分配给每个work-item多少寄存器使用的特征查询。而由于一个段内核代码是否因寄存器紧缺而导致性能严重下降也是一个比较重要的因素,因此我这边提供一个比较基本的方法来猜测当前计算设备至少能为每个work-item分配多少可用的寄存器。
这个方法的思路是,先定义四个临时变量,然后在一个大规模循环里面做一定规模的计算。然后把时间统计出来。随后,再定义八个临时变量,仍然,在与前者相同次数的循环里做一定规模的计算,再把时间统计出来。一般,如果寄存器不爆,或者由于Cache的缘故,性能影响不大的话,两者消耗时间一般在2倍左右。如果后者比前者超了2.2倍以上,那么我们即可认为寄存器爆了~
这个方法对于一般的GPU更有用些。由于CPU往往拥有L1 Data Cache,当寄存器不够用的时候,编译器会将不太常用的数据放到栈中,而栈在此时往往能获得高命中率的Cache访问,因此性能不会过受影响。而GPU端当寄存器不够用时,编译器往往会采取将不常用数据直接存放到VRAM中,而对外部VRAM的访问往往是比较慢的,因此,如果临时变量太多,使得频繁访问外部存储器,会使得整体计算性能大幅下降。当然,现在不少GPU也有了L1 Cache,但是空间也十分有限。因此,这里用“猜”这个词,呵呵~
下面先提供四个临时变量的kernel代码:
__kernel void QueryRegisterCount(__global int *pInOut)
{
int index = get_global_id(); int i0 = pInOut[(index * + ) * ];
int i1 = pInOut[(index * + ) * ];
int i2 = pInOut[(index * + ) * ];
int i3 = pInOut[(index * + ) * ]; for(int i = ; i < ; i++)
{
i1 += i0 << ;
i2 += i1 << ;
i3 += i2 << ;
i0 += i3 << ; i1 += i0 >> ;
i2 += i1 >> ;
i3 += i2 >> ;
i0 += i3 >> ; i1 += i0 >> ;
i2 += i1 >> ;
i3 += i2 >> ;
i0 += i3 >> ; i1 += i0 >> ;
i2 += i1 >> ;
i3 += i2 >> ;
i0 += i3 >> ;
} pInOut[(index * + ) * ] = i0;
pInOut[(index * + ) * ] = i1;
pInOut[(index * + ) * ] = i2;
pInOut[(index * + ) * ] = i3;
}
再提供八个临时变量的kernel代码:
__kernel void QueryRegisterCount(__global int *pInOut)
{
int index = get_global_id(); int i0 = pInOut[(index * + ) * ];
int i1 = pInOut[(index * + ) * ];
int i2 = pInOut[(index * + ) * ];
int i3 = pInOut[(index * + ) * ];
int i4 = pInOut[(index * + ) * ];
int i5 = pInOut[(index * + ) * ];
int i6 = pInOut[(index * + ) * ];
int i7 = pInOut[(index * + ) * ]; for(int i = ; i < ; i++)
{
i1 += i0 << ;
i2 += i1 << ;
i3 += i2 << ;
i4 += i3 << ;
i5 += i4 << ;
i6 += i5 << ;
i7 += i6 << ;
i0 += i7 << ; i1 += i0 >> ;
i2 += i1 >> ;
i3 += i2 >> ;
i4 += i3 >> ;
i5 += i4 >> ;
i6 += i5 >> ;
i7 += i6 >> ;
i0 += i7 >> ; i1 += i0 >> ;
i2 += i1 >> ;
i3 += i2 >> ;
i4 += i3 >> ;
i5 += i4 >> ;
i6 += i5 >> ;
i7 += i6 >> ;
i0 += i7 >> ; i1 += i0 >> ;
i2 += i1 >> ;
i3 += i2 >> ;
i4 += i3 >> ;
i5 += i4 >> ;
i6 += i5 >> ;
i7 += i6 >> ;
i0 += i7 >> ;
} pInOut[(index * + ) * ] = i0;
pInOut[(index * + ) * ] = i1;
pInOut[(index * + ) * ] = i2;
pInOut[(index * + ) * ] = i3;
pInOut[(index * + ) * ] = i4;
pInOut[(index * + ) * ] = i5;
pInOut[(index * + ) * ] = i6;
pInOut[(index * + ) * ] = i7;
}
而像16个、32个临时变量的方法依此类推~
然后,给出主机端代码:
/** Prepare for running an OpenCL kernel program to get register count */ /*Step 4: Creating command queue associate with the context.*/
commandQueue = clCreateCommandQueue(context, device, CL_QUEUE_PROFILING_ENABLE, NULL); /*Step 5: Create program object */
// Read the kernel code to the buffer
kernelPath = [[NSBundle mainBundle] pathForResource:@"reg" ofType:@"ocl"];
aSource = [[NSString stringWithContentsOfFile:kernelPath encoding:NSUTF8StringEncoding error:nil] UTF8String];
kernelLength = strlen(aSource);
program = clCreateProgramWithSource(context, , &aSource, &kernelLength, NULL); /*Step 6: Build program. */
status = clBuildProgram(program, , &device, NULL, NULL, NULL); /*Step 7: Initial inputs and output for the host and create memory objects for the kernel*/
const size_t memSize = global_work_size[] * * * ;
cl_int *orgBufer = (cl_int*)malloc(memSize);
memset(orgBufer, , memSize);
outputMemObj = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, memSize, orgBufer, NULL); /*Step 8: Create kernel object */
kernel = clCreateKernel(program, "QueryRegisterCount", NULL); /*Step 9: Sets Kernel arguments.*/
status |= clSetKernelArg(kernel, , sizeof(outputMemObj), &outputMemObj); /*Step 10: Running the kernel.*/
for(int i = ; i < ; i++)
{
NSTimeInterval beginTime = [[NSProcessInfo processInfo] systemUptime];
status |= clEnqueueNDRangeKernel(commandQueue, kernel, , NULL, global_work_size, local_work_size, , NULL, NULL);
clFinish(commandQueue);
NSTimeInterval endTime = [[NSProcessInfo processInfo] systemUptime]; NSLog(@"Time spent: %f", endTime - beginTime);
} free(orgBufer); if(status != CL_SUCCESS)
{
NSLog(@"Program built failed!");
return;
} clReleaseMemObject(outputMemObj);
clReleaseProgram(program);
clReleaseKernel(kernel);
clReleaseCommandQueue(commandQueue); clReleaseContext(context);
以上由于是在OS X下开发的,因此直接用Objective-C文件读写更方便些。但是大部分都是C代码,很容易读懂。
其中,最后一断代码中,我们做5次循环,统计时间。我们比较的时候往往选出5次执行时间中最小耗费的时间进行比较。
在2013年的MacBook Air中的Intel HD 5000中的测试结果为:
四个临时变量耗费:0.061020秒
八个临时变量耗费:0.121868秒
十六个临时变量耗费:0.243470秒
三十二个临时变量耗费:0.719506秒
很显然,我们可以猜得,Intel HD Graphics 5000至少可以为每个work-item分配16个寄存器。
我们如果要用在实际应用场合,可以通过动态生成kernel字符串依次执行进行检测,直到相邻两段kernel的执行时间超过2.2倍,那么我们即可终止。
通过OpenCL内核代码猜测设备寄存器个数的更多相关文章
- linux内核代码注释 赵炯 第三章引导启动程序
linux内核代码注释 第三章引导启动程序 boot目录中的三个汇编代码文件 bootsect.s和setup.s采用近似intel的汇编语法,需要8086汇编器连接器as86和ld86 head ...
- 【自制操作系统06】终于开始用 C 语言了,第一行内核代码!
一.整理下到目前为止的流程图 写到这,终于才把一些苦力活都干完了,也终于到了我们的内核代码部分,也终于开始第一次用 c 语言写代码了!为了这个阶段性的胜利,以及更好地进入内核部分,下图贴一张到目前为止 ...
- Linux内核分析—完成一个简单的时间片轮转多道程序内核代码
---恢复内容开始--- 20135125陈智威 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-10 ...
- [转] Linux内核代码风格 CodingStyle [CH]
from:http://blog.csdn.net/jiang_dlut/article/details/8163731 中文版维护者: 张乐 Zhang Le <r0bertz@gentoo. ...
- 从linux内核代码分析操作系统启动过程
朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 在本次的实验中, ...
- 第三次阅读赵炯博士的《linux内核代码完全注释》:序
这是我第三次阅读linux内核代码完全注释了,当然前两次也没有读完,第一次读到第五章,第二次第七章. 所以说,赵炯博士对我最大的帮助时介绍了intel386的结构,以及内核编程的方法. 至于真正的内核 ...
- Linux学习笔记:【004】Linux内核代码风格
Chinese translated version of Documentation/CodingStyle If you have any comment or update to the c ...
- [11]Windows内核情景分析---设备驱动
设备驱动 设备栈:从上层到下层的顺序依次是:过滤设备.类设备.过滤设备.小端口设备[过.类.过滤.小端口] 驱动栈:因设备堆栈原因而建立起来的一种堆栈 老式驱动:指不提供AddDevice的驱动,又叫 ...
- [转] LINUX内核代码编程规范
这是一个简短的文档,描述了linux内核的首选代码风格.代码风格是因人而异的,而且我 不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风格, 并且我也希望绝大多数其他代码也能 ...
随机推荐
- 【JavaWeb】通过邮件找回密码
前言 本文将介绍忘记密码时通过发送重置密码邮件找回密码的实现思路.整个实现过程中最重要的就是以下三点: 如何发送邮件到用户指定邮箱 邮件中的重置密码链接构成是怎么样的 验证重置密码链接的合法性(是否过 ...
- 关于Linux、python的PDF书籍整理(附带亲测的 IT 电子书网站)
[18.1.3][在博客园发的文章不是很多呢,接下来的博客会转移到独立的个人博客网站上去了,具体的学习笔记和内容都会在独立网站上发布,后期还会有博主的个人资源库和教程还有独立网盘存储(可以关注一波哈) ...
- MySQL在使用group_concat()函数数据被截取
遇到问题: 项目中有个需求,MySQL中存储的是树状的数据.现在给出一个节点,需要从Mysql数据库中取出这个节点下所有的节点.采用MySQL的函数. 函数如下: CREATE DEFINER=`ro ...
- 极光推送出现 超时问题:Connect timeout. Please retry later. Error:7
检查之后均没有什么太大的问题, 最后发现出现77这种错误码,有一种可能就是系统的ca包没有更新 包名为 ca-certificates 使用命令 yum install ca-certificates ...
- duilib学习领悟(4)
使用duilib创建的主窗口绘制工作全都发生在一个 真实存在的主窗口句柄当中,这个绘制过程稍稍有些复杂,但再复杂也逃不过WM_PAINT消息,所有的绘制工作也都由这个WM_PAINT消息完成.在上几篇 ...
- django post请求 403错误解决方法
--摘 第一次用Django做项目,遇到了很多问题. 今天遇到的问题是Django在处理post请求时多次出现403错误. 我先描述一下问题出现的环境:我用Django写了一个web服务端,姑且称它为 ...
- ES6-3 变量的解构赋值
1.数组的解构赋值 数组的解构赋值其实是=左右进行“模式匹配”. ❗️❗️❗️=右侧是具体的数值,不是变量!,=左侧的是变量!如果右侧是变量形式,需要先计算出具体的数值!! let [a,[b],c] ...
- HashMap判断键是否为null
用containsKey(),而不用get(): HashMap中,null可以作为键,这样的键只有一个:可以有一个或多个键所对应的值为null.当get()方法返回null值时,即可以表示HashM ...
- Linux下的电路设计辅助软件
造冰箱的大熊猫@cnblogs 2019/8/21 最近调研了以下开源的电路设计辅助软件(EDA),汇总如下 1.gEDA 官网:geda-project.org 老牌的开源EDA软件,诞生于上世纪9 ...
- 费马小定理 x
费马小定理(Fermat Theory) 是数论中的一个重要定理,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a(p-1)≡1(mod p).即:假如a是整数,p是质数,且a,p互质(即两 ...