在OpenCL中,用__local(或local)修饰的变量会被存放在一个计算单元(Compute Unit)的共享存储器区域中。对于nVidia的GPU,一个CU可以被映射为物理上的一块SM(Stream Multiprocessor);而对于AMD-ATi的GPU可以被映射为物理上的一块SIMD。不管是SM也好,SIMD也罢,它们都有一个在本计算单元中被所有线程(OpenCL中称为Work Item)所共享的共享存储器。因此,在一个计算单元内,可以通过local shared memory来同步此计算单元内的所有工作项。

这里必须注意的是在计算单元之间的线程的通信只能通过全局存储器进行,因为每个计算单元之间是没有共享存储器的,呵呵。

下面我将证明Apple的OpenCL实现中,如果有两个Work Group(一个Work Group的处理交给一个计算单元执行),那么这两个Work Group正好能分别被映射到一个计算单元内。我用的是Mac Mini,GPU为GeForce 9400M,所有仅有两个SM,呵呵。

下面先给出kernel代码:

  1. __kernel void solve_sum(
  2. __global volatile unsigned buffer[512],
  3. __global unsigned dest[512]
  4. )
  5. {
  6. __local volatile int flag = 0;
  7. size_t gid = get_global_id(0);
  8. if(0 <= gid && gid < 32)
  9. {
  10. while(flag != 1);
  11. flag = 0;
  12. buffer[gid] = 0x1UL;
  13. //write_mem_fence(CLK_GLOBAL_MEM_FENCE);
  14. }
  15. else if(32 <= gid && gid < 64)
  16. {
  17. flag = 1;
  18. while(flag != 0);
  19. unsigned ret = buffer[31 + 32 - gid];
  20. dest[gid - 32] = ret;
  21. }
  22. }

__kernel void solve_sum(
__global volatile unsigned buffer[512],
__global unsigned dest[512]
)
{
__local volatile int flag = 0;

size_t gid = get_global_id(0);

if(0 <= gid && gid < 32)
{
while(flag != 1);
flag = 0;

buffer[gid] = 0x1UL;
//write_mem_fence(CLK_GLOBAL_MEM_FENCE);
}
else if(32 <= gid && gid < 64)
{
flag = 1;

while(flag != 0);
unsigned ret = buffer[31 + 32 - gid];
dest[gid - 32] = ret;
}
}

上面这个内核程序的配置为:分为两个工作组;每组32个工作项。这样,两个工作组能进不同的SM。各位在执行这段代码时会发生死循环。然后等2到3秒后程序会自动退出,这点不用担心,呵呵。原因就是两个SM的共享变量flag是各有各的一份。假定,线程0到线程31进SM0,那么SM0的所有线程共享这个flag变量;而线程32到线程63进SM1,那么SM1的flag将被SM1的所有线程共享。而如果企图把这个(其实是两个)共享变量用于两个SM之间的通信,显然是无法成功的,呵呵。尽管代码上只写了一个flag,但实际上却有两个副本。

下面提供主机端代码:

  1. #import <Foundation/Foundation.h>
  2. #include <OpenCL/opencl.h>
  3. static unsigned __attribute__((aligned(16))) buffer[512] = { 0 };    // original data set given to device
  4. static unsigned __attribute__((aligned(16))) dest[512] = { 0 };
  5. int opencl_execution(void)
  6. {
  7. int err;                            // error code returned from api calls
  8. size_t local;                       // local domain size for our calculation
  9. cl_platform_id  platform_id;        // added by zenny_chen
  10. cl_device_id device_id;             // compute device id
  11. cl_context context;                 // compute context
  12. cl_command_queue commands;          // compute command queue
  13. cl_program program;                 // compute program
  14. cl_kernel kernel;                   // compute kernel
  15. cl_mem memOrg, memDst;                      // device memory used for the input array
  16. // Create a platform
  17. err = clGetPlatformIDs(1, &platform_id, NULL);
  18. if (err != CL_SUCCESS)
  19. {
  20. printf("Error: Failed to create a platform!/n");
  21. return EXIT_FAILURE;
  22. }
  23. // Connect to a compute device
  24. //
  25. err = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL);
  26. if (err != CL_SUCCESS)
  27. {
  28. printf("Error: Failed to create a device group!/n");
  29. return EXIT_FAILURE;
  30. }
  31. // Create a compute context
  32. //
  33. context = clCreateContext((cl_context_properties[]){(cl_context_properties)CL_CONTEXT_PLATFORM, (cl_context_properties)platform_id, 0}, 1, &device_id, NULL, NULL, &err);
  34. if (!context)
  35. {
  36. printf("Error: Failed to create a compute context!/n");
  37. return EXIT_FAILURE;
  38. }
  39. // Create a command commands
  40. //
  41. commands = clCreateCommandQueue(context, device_id, 0, &err);
  42. if (!commands)
  43. {
  44. printf("Error: Failed to create a command commands!/n");
  45. return EXIT_FAILURE;
  46. }
  47. // Fetch kernel source
  48. NSString *filepath = [[NSBundle mainBundle] pathForResource:@"kernel" ofType:@"cl"];
  49. if(filepath == NULL)
  50. {
  51. puts("Source not found!");
  52. return EXIT_FAILURE;
  53. }
  54. const char *KernelSource = (const char*)[[NSString stringWithContentsOfFile:filepath encoding:NSUTF8StringEncoding error:nil] UTF8String];
  55. // Create the compute program from the source buffer
  56. //
  57. program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err);
  58. if (!program)
  59. {
  60. printf("Error: Failed to create compute program!/n");
  61. return EXIT_FAILURE;
  62. }
  63. // Build the program executable
  64. //
  65. err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
  66. if (err != CL_SUCCESS)
  67. {
  68. size_t len;
  69. char buffer[2048];
  70. printf("Error: Failed to build program executable!/n");
  71. clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len);
  72. printf("%s/n", buffer);
  73. exit(1);
  74. }
  75. // Create the compute kernel in the program we wish to run
  76. //
  77. kernel = clCreateKernel(program, "solve_sum", &err);
  78. if (!kernel || err != CL_SUCCESS)
  79. {
  80. printf("Error: Failed to create compute kernel!/n");
  81. exit(1);
  82. }
  83. // Create the input and output arrays in device memory for our calculation
  84. //
  85. memOrg = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(int) * 512, NULL, NULL);
  86. memDst = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(int) * 512, NULL, NULL);
  87. if (memOrg == NULL || memDst == NULL)
  88. {
  89. printf("Error: Failed to allocate device memory!/n");
  90. exit(1);
  91. }
  92. // Write our data set into the input array in device memory
  93. //
  94. err = clEnqueueWriteBuffer(commands, memOrg, CL_TRUE, 0, sizeof(int) * 512, buffer, 0, NULL, NULL);
  95. if (err != CL_SUCCESS)
  96. {
  97. printf("Error: Failed to write to source array!/n");
  98. exit(1);
  99. }
  100. // Set the arguments to our compute kernel
  101. //
  102. err = 0;
  103. err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &memOrg);
  104. err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &memDst);
  105. if (err != CL_SUCCESS)
  106. {
  107. printf("Error: Failed to set kernel arguments! %d/n", err);
  108. exit(1);
  109. }
  110. // Get the maximum work group size for executing the kernel on the device
  111. //
  112. err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(local), &local, NULL);
  113. if (err != CL_SUCCESS)
  114. {
  115. printf("Error: Failed to retrieve kernel work group info! %d/n", err);
  116. exit(1);
  117. }
  118. else
  119. printf("The number of work items in a work group is: %lu/r/n", local);
  120. // Execute the kernel over the entire range of our 1d input data set
  121. // using the maximum number of work group items for this device
  122. //
  123. err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, (size_t[]){ 64 }, (size_t[]){ 32 }, 0, NULL, NULL);
  124. if (err)
  125. {
  126. printf("Error: Failed to execute kernel!/n");
  127. return EXIT_FAILURE;
  128. }
  129. // Wait for the command commands to get serviced before reading back results
  130. //
  131. clFinish(commands);
  132. // Read back the results from the device to verify the output
  133. //
  134. err = clEnqueueReadBuffer(commands, memDst, CL_TRUE, 0, sizeof(int) * 512, dest, 0, NULL, NULL );
  135. if (err != CL_SUCCESS)
  136. {
  137. printf("Error: Failed to read output array! %d/n", err);
  138. exit(1);
  139. }
  140. // Validate our results
  141. //
  142. printf("The result is: 0x%.8X/n", dest[0]);
  143. // Shutdown and cleanup
  144. //
  145. clReleaseMemObject(memOrg);
  146. clReleaseMemObject(memDst);
  147. clReleaseProgram(program);
  148. clReleaseKernel(kernel);
  149. clReleaseCommandQueue(commands);
  150. clReleaseContext(context);
  151. return 0;
  152. }
  153. int main (int argc, const char * argv[]) {
  154. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  155. // insert code here...
  156. opencl_execution();
  157. [pool drain];
  158. return 0;
  159. }

#import <Foundation/Foundation.h>
#include <OpenCL/opencl.h>
static unsigned __attribute__((aligned(16))) buffer[512] = { 0 }; // original data set given to device
static unsigned __attribute__((aligned(16))) dest[512] = { 0 };
int opencl_execution(void)
{
int err; // error code returned from api calls

size_t local; // local domain size for our calculation

cl_platform_id platform_id; // added by zenny_chen
cl_device_id device_id; // compute device id
cl_context context; // compute context
cl_command_queue commands; // compute command queue
cl_program program; // compute program
cl_kernel kernel; // compute kernel

cl_mem memOrg, memDst; // device memory used for the input array

// Create a platform
err = clGetPlatformIDs(1, &platform_id, NULL);
if (err != CL_SUCCESS)
{
printf("Error: Failed to create a platform!/n");
return EXIT_FAILURE;
}

// Connect to a compute device
//
err = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_GPU, 1, &device_id, NULL);
if (err != CL_SUCCESS)
{
printf("Error: Failed to create a device group!/n");
return EXIT_FAILURE;
}

// Create a compute context
//
context = clCreateContext((cl_context_properties[]){(cl_context_properties)CL_CONTEXT_PLATFORM, (cl_context_properties)platform_id, 0}, 1, &device_id, NULL, NULL, &err);
if (!context)
{
printf("Error: Failed to create a compute context!/n");
return EXIT_FAILURE;
}

// Create a command commands
//
commands = clCreateCommandQueue(context, device_id, 0, &err);
if (!commands)
{
printf("Error: Failed to create a command commands!/n");
return EXIT_FAILURE;
}

// Fetch kernel source
NSString *filepath = [[NSBundle mainBundle] pathForResource:@"kernel" ofType:@"cl"];
if(filepath == NULL)
{
puts("Source not found!");
return EXIT_FAILURE;
}

const char *KernelSource = (const char*)[[NSString stringWithContentsOfFile:filepath encoding:NSUTF8StringEncoding error:nil] UTF8String];

// Create the compute program from the source buffer
//
program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err);
if (!program)
{
printf("Error: Failed to create compute program!/n");
return EXIT_FAILURE;
}

// Build the program executable
//
err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
if (err != CL_SUCCESS)
{
size_t len;
char buffer[2048];

printf("Error: Failed to build program executable!/n");
clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len);
printf("%s/n", buffer);
exit(1);
}

// Create the compute kernel in the program we wish to run
//
kernel = clCreateKernel(program, "solve_sum", &err);
if (!kernel || err != CL_SUCCESS)
{
printf("Error: Failed to create compute kernel!/n");
exit(1);
}

// Create the input and output arrays in device memory for our calculation
//
memOrg = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(int) * 512, NULL, NULL);
memDst = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(int) * 512, NULL, NULL);

if (memOrg == NULL || memDst == NULL)
{
printf("Error: Failed to allocate device memory!/n");
exit(1);
}

// Write our data set into the input array in device memory
//
err = clEnqueueWriteBuffer(commands, memOrg, CL_TRUE, 0, sizeof(int) * 512, buffer, 0, NULL, NULL);
if (err != CL_SUCCESS)
{
printf("Error: Failed to write to source array!/n");
exit(1);
}

// Set the arguments to our compute kernel
//
err = 0;
err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &memOrg);
err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &memDst);
if (err != CL_SUCCESS)
{
printf("Error: Failed to set kernel arguments! %d/n", err);
exit(1);
}

// Get the maximum work group size for executing the kernel on the device
//
err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(local), &local, NULL);
if (err != CL_SUCCESS)
{
printf("Error: Failed to retrieve kernel work group info! %d/n", err);
exit(1);
}
else
printf("The number of work items in a work group is: %lu/r/n", local);

// Execute the kernel over the entire range of our 1d input data set
// using the maximum number of work group items for this device
//

err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, (size_t[]){ 64 }, (size_t[]){ 32 }, 0, NULL, NULL);
if (err)
{
printf("Error: Failed to execute kernel!/n");
return EXIT_FAILURE;
}

// Wait for the command commands to get serviced before reading back results
//
clFinish(commands);

// Read back the results from the device to verify the output
//
err = clEnqueueReadBuffer(commands, memDst, CL_TRUE, 0, sizeof(int) * 512, dest, 0, NULL, NULL );
if (err != CL_SUCCESS)
{
printf("Error: Failed to read output array! %d/n", err);
exit(1);
}

// Validate our results
//
printf("The result is: 0x%.8X/n", dest[0]);

// Shutdown and cleanup
//
clReleaseMemObject(memOrg);
clReleaseMemObject(memDst);
clReleaseProgram(program);
clReleaseKernel(kernel);
clReleaseCommandQueue(commands);
clReleaseContext(context);

return 0;
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
opencl_execution();
[pool drain];
return 0;
}

见主机端代码第144行:

  1. err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, (size_t[]){ 64 }, (size_t[]){ 32 }, 0, NULL, NULL);

err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, (size_t[]){ 64 }, (size_t[]){ 32 }, 0, NULL, NULL);

这里,我们设定全局工作项个数为64,每个工作组有32个线程,那么这样一来就自然地被划分为两个工作组。如果我们把32改为64,这么一来就变为一个工作组,这样,在一个SM中通过一个共享变量进行通信完全OK,程序就能正常终止。

另外,如果想保持原来的2个Work Group,那么必须通过全局变量进行通信:

  1. __kernel void solve_sum(
  2. __global volatile unsigned buffer[512],
  3. __global unsigned dest[512]
  4. )
  5. {
  6. __local volatile int flag = 0;
  7. size_t gid = get_global_id(0);
  8. if(0 <= gid && gid < 32)
  9. {
  10. while(buffer[256] != 1);
  11. buffer[256] = 0;
  12. buffer[gid] = 0x1UL;
  13. //write_mem_fence(CLK_GLOBAL_MEM_FENCE);
  14. }
  15. else if(32 <= gid && gid < 64)
  16. {
  17. buffer[256] = 1;
  18. while(buffer[256] != 0);
  19. unsigned ret = buffer[31 + 32 - gid];
  20. dest[gid - 32] = ret;
  21. }
  22. }

__kernel void solve_sum(
__global volatile unsigned buffer[512],
__global unsigned dest[512]
)
{
__local volatile int flag = 0;

size_t gid = get_global_id(0);

if(0 <= gid && gid < 32)
{
while(buffer[256] != 1);
buffer[256] = 0;

buffer[gid] = 0x1UL;
//write_mem_fence(CLK_GLOBAL_MEM_FENCE);
}
else if(32 <= gid && gid < 64)
{
buffer[256] = 1;

while(buffer[256] != 0);
unsigned ret = buffer[31 + 32 - gid];
dest[gid - 32] = ret;
}
}

这里还要注意一点。用于通信的变量都必须加上volatile,否则,OpenCL内核编译器会把对全局变量的第二次访问全都优化为直接从寄存器取数据,从而外部对此变量的改变在当前线程内将无法看见。

【并行计算-CUDA开发】Apple's OpenCL——再谈Local Memory的更多相关文章

  1. 【并行计算-CUDA开发】GPGPU OpenCL/CUDA 高性能编程的10大注意事项

    GPGPU OpenCL/CUDA 高性能编程的10大注意事项 1.展开循环 如果提前知道了循环的次数,可以进行循环展开,这样省去了循环条件的比较次数.但是同时也不能使得kernel代码太大. 循环展 ...

  2. 【DSP开发】【并行计算-CUDA开发】TI OpenCL v01.01.xx

    TI OpenCL v01.01.xx TI OpenCL™ Runtime Documentation Contents: Introduction OpenCL 1.1 Reference Mat ...

  3. 【并行计算-CUDA开发】关于共享内存(shared memory)和存储体(bank)的事实和疑惑

    关于共享内存(shared memory)和存储体(bank)的事实和疑惑 主要是在研究访问共享内存会产生bank conflict时,自己产生的疑惑.对于这点疑惑,网上都没有相关描述, 不管是国内还 ...

  4. 【并行计算-CUDA开发】OpenCL、OpenGL和DirectX三者的区别

    什么是OpenCL? OpenCL全称Open Computing Language,是第一个面向异构系统通用目的并行编程的开放式.免费标准,也是一个统一的编程环境,便于软件开发人员为高性能计算服务器 ...

  5. 【并行计算-CUDA开发】OpenACC与OpenHMPP

    在西雅图超级计算大会(SC11)上发布了新的基于指令的加速器并行编程标准,既OpenACC.这个开发标准的目的是让更多的编程人员可以用到GPU计算,同时计算结果可以跨加速器使用,甚至能用在多核CPU上 ...

  6. 【并行计算-CUDA开发】GPU---并行计算利器

    1 GPU是什么 如图1所示,这台PC机与普通PC机不同的是这里插了7张显卡,左下角是显卡,在中间的就是GPU芯片.显卡的处理器称为图形处理器(GPU),它是显卡的"心脏",与CP ...

  7. 【并行计算-CUDA开发】CUDA存储器模型

    CUDA存储器模型 除了执行模型以外,CUDA也规定了存储器模型(如图2所示)和一系列用于主控CPU与GPU间通信的不同地址空间.图中红色的区域表示GPU片内的高速存储器,橙色区域表示DRAM中的的地 ...

  8. 【并行计算-CUDA开发】CUDA并行存储模型

    CUDA并行存储模型 CUDA将CPU作为主机(Host),GPU作为设备(Device).一个系统中可以有一个主机和多个设备.CPU负责逻辑性强的事务处理和串行计算,GPU专注于执行高度线程化的并行 ...

  9. 【并行计算-CUDA开发】从零开始学习OpenCL开发(一)架构

    多谢大家关注 转载本文请注明:http://blog.csdn.net/leonwei/article/details/8880012 本文将作为我<从零开始做OpenCL开发>系列文章的 ...

随机推荐

  1. kickstart批量装机脚本

    #!/bin/bash #安装必备的软件 yum -y install dhcp tftp-server tftp xinetd syslinux vsftpd yum -y install *kic ...

  2. MFC 画笔CPen、画刷CBrush

    新建单个文档的MFC应用程序,类视图——View项的属性——消息,WM_PAINT,创建OnPaint()函数 dc默认有一个画笔(实心1像素宽黑线). CPen画笔非实心线像素宽必须为1,否则膨胀接 ...

  3. BZOJ 2982 combination 脑子+组合数学

    可以发现,整个数列构成一个树形结构,并且是个完全二叉堆(小根堆). 并且这个堆的形态在给定$n$后是固定的,第1个位置上显然只能放1. 对子树的根来说,他自己是所分得的数集中最小的那个,所以从剩下$s ...

  4. nextAll([expr]) 查找当前元素之后所有的同辈元素。

    nextAll([expr]) 概述 查找当前元素之后所有的同辈元素. 可以用表达式过滤 参数 exprStringV1.2 用来过滤的表达式 示例 描述: 给第一个div之后的所有元素加个类直线电机 ...

  5. 丰桥运单打印windows/linux环境安装(原)

    Linux ①linux下安装jdk1.8,执行命令:yum -y install java ②创建文件夹sf-service将csim_waybill_print_service_V1.0.3.ja ...

  6. Palindrome Degree(CodeForces 7D)—— hash求回文

    学了kmp之后又学了hash来搞字符串.这东西很巧妙,且听娓娓道来. 这题的题意是:一个字符串如果是回文的,那么k值加1,如果前一半的串也是回文,k值再加1,以此类推,算出其k值.打个比方abaaba ...

  7. C#操作 Access 2013(.accdb)的方法

    使用的Microsoft.Jet.OLEDB.4.0,的方法并不能连接最新的Access 存储文件,而且Microsoft.Jet.OLEDB.4.0不能使用x64的方式生成,而且使用这个数据库引擎效 ...

  8. 在Windows下安装scrapy

    第一步: 安装pywin32 下载地址:https://sourceforge.net/projects/pywin32/files/pywin32/,下载对应版本的pywin32,直接双击安装即可 ...

  9. Q窗口操作函数(窗口最大化,全屏,隐藏最大化最小化按钮)

    //Qt主窗口没有最小化,最大化按钮且最大化显示  int main(int argc, char *argv[]) { QApplication a(argc, argv); TestQtForWi ...

  10. PHP-生产随机密码

    public function dd(){ error_reporting(E_ALL^E_NOTICE^E_WARNING);$arr1 = array(0,1,2,3,4,5,6,7,8,9);$ ...