随着深度学习、区块链的发展,人类对计算量的需求越来越高,在传统的计算模式下,压榨GPU的计算能力一直是重点。

NV系列的显卡在这方面走的比较快,CUDA框架已经普及到了高性能计算的各个方面,比如Google的TensorFlow深度学习框架,默认内置了支持CUDA的GPU计算。

AMD(ATI)及其它显卡在这方面似乎一直不够给力,在CUDA退出后仓促应对,使用了开放式的OPENCL架构,其中对CUDA应当说有不少的模仿。开放架构本来是一件好事,但OPENCL的发展一直不尽人意。而且为了兼容更多的显卡,程序中通用层导致的效率损失一直比较大。而实际上,现在的高性能显卡其实也就剩下了NV/AMD两家的竞争,这样基本没什么意义的性能损失不能不说让人纠结。所以在个人工作站和个人装机市场,通常的选择都是NV系列的显卡。

mac电脑在这方面是比较尴尬的,当前的高端系列是MacPro垃圾桶。至少新款的一体机MacPro量产之前,垃圾桶仍然是mac家性能的扛鼎产品。然而其内置的显卡就是AMD,只能使用OPENCL通用计算框架了。

下面是苹果官方给出的一个OPENCL的入门例子,结构很清晰,展示了使用显卡进行高性能计算的一般结构,我在注释中增加了中文的说明,相信可以让你更容易的上手OPENCL显卡计算。

  1. //
  2. // File: hello.c
  3. //
  4. // Abstract: A simple "Hello World" compute example showing basic usage of OpenCL which
  5. // calculates the mathematical square (X[i] = pow(X[i],2)) for a buffer of
  6. // floating point values.
  7. //
  8. //
  9. // Version: <1.0>
  10. //
  11. // Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple")
  12. // in consideration of your agreement to the following terms, and your use,
  13. // installation, modification or redistribution of this Apple software
  14. // constitutes acceptance of these terms. If you do not agree with these
  15. // terms, please do not use, install, modify or redistribute this Apple
  16. // software.
  17. //
  18. // In consideration of your agreement to abide by the following terms, and
  19. // subject to these terms, Apple grants you a personal, non - exclusive
  20. // license, under Apple's copyrights in this original Apple software ( the
  21. // "Apple Software" ), to use, reproduce, modify and redistribute the Apple
  22. // Software, with or without modifications, in source and / or binary forms;
  23. // provided that if you redistribute the Apple Software in its entirety and
  24. // without modifications, you must retain this notice and the following text
  25. // and disclaimers in all such redistributions of the Apple Software. Neither
  26. // the name, trademarks, service marks or logos of Apple Inc. may be used to
  27. // endorse or promote products derived from the Apple Software without specific
  28. // prior written permission from Apple. Except as expressly stated in this
  29. // notice, no other rights or licenses, express or implied, are granted by
  30. // Apple herein, including but not limited to any patent rights that may be
  31. // infringed by your derivative works or by other works in which the Apple
  32. // Software may be incorporated.
  33. //
  34. // The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
  35. // WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  36. // WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
  37. // PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION
  38. // ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
  39. //
  40. // IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  41. // CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  42. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  43. // INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
  44. // AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER
  45. // UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR
  46. // OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  47. //
  48. // Copyright ( C ) 2008 Apple Inc. All Rights Reserved.
  49. //
  50. ////////////////////////////////////////////////////////////////////////////////
  51. #include <fcntl.h>
  52. #include <stdio.h>
  53. #include <stdlib.h>
  54. #include <string.h>
  55. #include <math.h>
  56. #include <unistd.h>
  57. #include <sys/types.h>
  58. #include <sys/stat.h>
  59. #include <OpenCL/opencl.h>
  60. ////////////////////////////////////////////////////////////////////////////////
  61. // Use a static data size for simplicity
  62. //
  63. #define DATA_SIZE (1024)
  64. ////////////////////////////////////////////////////////////////////////////////
  65. // Simple compute kernel which computes the square of an input array
  66. // 这是OPENCL用于计算的内核部分源码,跟C相同的语法格式,通过编译后将发布到GPU设备
  67. //(或者将来专用的计算设备)上面去执行。因为显卡通常有几十、上百个内核,所以这部分
  68. // 需要设计成可并发的程序逻辑。
  69. //
  70. const char *KernelSource = "\n" \
  71. "__kernel void square( \n" \
  72. " __global float* input, \n" \
  73. " __global float* output, \n" \
  74. " const unsigned int count) \n" \
  75. "{ \n" \
  76. // 并发逻辑主要是在下面这一行体现的,i的初始值获取当前内核的id(整数),根据id计算自己的那一小块任务
  77. " int i = get_global_id(0); \n" \
  78. " if(i < count) \n" \
  79. " output[i] = input[i] * input[i]; \n" \
  80. "} \n" \
  81. "\n";
  82. ////////////////////////////////////////////////////////////////////////////////
  83. int main(int argc, char** argv)
  84. {
  85. int err; // error code returned from api calls
  86. float data[DATA_SIZE]; // original data set given to device
  87. float results[DATA_SIZE]; // results returned from device
  88. unsigned int correct; // number of correct results returned
  89. size_t global; // global domain size for our calculation
  90. size_t local; // local domain size for our calculation
  91. cl_device_id device_id; // compute device id
  92. cl_context context; // compute context
  93. cl_command_queue commands; // compute command queue
  94. cl_program program; // compute program
  95. cl_kernel kernel; // compute kernel
  96. cl_mem input; // device memory used for the input array
  97. cl_mem output; // device memory used for the output array
  98. // Fill our data set with random float values
  99. //
  100. int i = 0;
  101. unsigned int count = DATA_SIZE;
  102. //随机产生一组浮点数据,用于给GPU进行计算
  103. for(i = 0; i < count; i++)
  104. data[i] = rand() / (float)RAND_MAX;
  105. // Connect to a compute device
  106. //
  107. int gpu = 1;
  108. // 获取GPU设备,OPENCL的优势是可以使用CPU进行模拟,当然这种功能只是为了在没有GPU设备上进行调试
  109. // 如果上面变量gpu=0的话,则使用CPU模拟
  110. err = clGetDeviceIDs(NULL, gpu ? CL_DEVICE_TYPE_GPU : CL_DEVICE_TYPE_CPU, 1, &device_id, NULL);
  111. if (err != CL_SUCCESS)
  112. {
  113. printf("Error: Failed to create a device group!\n");
  114. return EXIT_FAILURE;
  115. }
  116. // Create a compute context
  117. // 建立一个GPU计算的上下文环境,一组上下文环境保存一组相关的状态、内存等资源
  118. context = clCreateContext(0, 1, &device_id, NULL, NULL, &err);
  119. if (!context)
  120. {
  121. printf("Error: Failed to create a compute context!\n");
  122. return EXIT_FAILURE;
  123. }
  124. // Create a command commands
  125. //使用获取到的GPU设备和上下文环境监理一个命令队列,其实就是给GPU的任务队列
  126. commands = clCreateCommandQueue(context, device_id, 0, &err);
  127. if (!commands)
  128. {
  129. printf("Error: Failed to create a command commands!\n");
  130. return EXIT_FAILURE;
  131. }
  132. // Create the compute program from the source buffer
  133. //将内核程序的字符串加载到上下文环境
  134. program = clCreateProgramWithSource(context, 1, (const char **) & KernelSource, NULL, &err);
  135. if (!program)
  136. {
  137. printf("Error: Failed to create compute program!\n");
  138. return EXIT_FAILURE;
  139. }
  140. // Build the program executable
  141. //根据所使用的设备,将程序编译成目标机器语言代码,跟通常的编译类似,
  142. //内核程序的语法类错误信息都会在这里出现,所以一般尽可能打印完整从而帮助判断。
  143. err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
  144. if (err != CL_SUCCESS)
  145. {
  146. size_t len;
  147. char buffer[2048];
  148. printf("Error: Failed to build program executable!\n");
  149. clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(buffer), buffer, &len);
  150. printf("%s\n", buffer);
  151. exit(1);
  152. }
  153. // Create the compute kernel in the program we wish to run
  154. //使用内核程序的函数名建立一个计算内核
  155. kernel = clCreateKernel(program, "square", &err);
  156. if (!kernel || err != CL_SUCCESS)
  157. {
  158. printf("Error: Failed to create compute kernel!\n");
  159. exit(1);
  160. }
  161. // Create the input and output arrays in device memory for our calculation
  162. // 建立GPU的输入缓冲区,注意READ_ONLY是对GPU而言的,这个缓冲区是建立在显卡显存中的
  163. input = clCreateBuffer(context, CL_MEM_READ_ONLY, sizeof(float) * count, NULL, NULL);
  164. // 建立GPU的输出缓冲区,用于输出计算结果
  165. output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(float) * count, NULL, NULL);
  166. if (!input || !output)
  167. {
  168. printf("Error: Failed to allocate device memory!\n");
  169. exit(1);
  170. }
  171. // Write our data set into the input array in device memory
  172. // 将CPU内存中的数据,写入到GPU显卡内存(内核函数的input部分)
  173. err = clEnqueueWriteBuffer(commands, input, CL_TRUE, 0, sizeof(float) * count, data, 0, NULL, NULL);
  174. if (err != CL_SUCCESS)
  175. {
  176. printf("Error: Failed to write to source array!\n");
  177. exit(1);
  178. }
  179. // Set the arguments to our compute kernel
  180. // 设定内核函数中的三个参数
  181. err = 0;
  182. err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &input);
  183. err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &output);
  184. err |= clSetKernelArg(kernel, 2, sizeof(unsigned int), &count);
  185. if (err != CL_SUCCESS)
  186. {
  187. printf("Error: Failed to set kernel arguments! %d\n", err);
  188. exit(1);
  189. }
  190. // Get the maximum work group size for executing the kernel on the device
  191. //获取GPU可用的计算核心数量
  192. err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(local), &local, NULL);
  193. if (err != CL_SUCCESS)
  194. {
  195. printf("Error: Failed to retrieve kernel work group info! %d\n", err);
  196. exit(1);
  197. }
  198. // Execute the kernel over the entire range of our 1d input data set
  199. // using the maximum number of work group items for this device
  200. // 这是真正的计算部分,计算启动的时候采用队列的方式,因为一般计算任务的数量都会远远大于可用的内核数量,
  201. // 在下面函数中,local是可用的内核数,global是要计算的数量,OPENCL会自动执行队列,完成所有的计算
  202. // 所以在前面强调了,内核程序的设计要考虑、并尽力利用这种并发特征
  203. global = count;
  204. err = clEnqueueNDRangeKernel(commands, kernel, 1, NULL, &global, &local, 0, NULL, NULL);
  205. if (err)
  206. {
  207. printf("Error: Failed to execute kernel!\n");
  208. return EXIT_FAILURE;
  209. }
  210. // Wait for the command commands to get serviced before reading back results
  211. // 阻塞直到OPENCL完成所有的计算任务
  212. clFinish(commands);
  213. // Read back the results from the device to verify the output
  214. // 从GPU显存中把计算的结果复制到CPU内存
  215. err = clEnqueueReadBuffer( commands, output, CL_TRUE, 0, sizeof(float) * count, results, 0, NULL, NULL );
  216. if (err != CL_SUCCESS)
  217. {
  218. printf("Error: Failed to read output array! %d\n", err);
  219. exit(1);
  220. }
  221. // Validate our results
  222. // 下面是使用CPU计算来验证OPENCL计算结果是否正确
  223. correct = 0;
  224. for(i = 0; i < count; i++)
  225. {
  226. if(results[i] == data[i] * data[i])
  227. correct++;
  228. }
  229. // Print a brief summary detailing the results
  230. // 显示验证的结果
  231. printf("Computed '%d/%d' correct values!\n", correct, count);
  232. // Shutdown and cleanup
  233. // 清理各类对象及关闭OPENCL环境
  234. clReleaseMemObject(input);
  235. clReleaseMemObject(output);
  236. clReleaseProgram(program);
  237. clReleaseKernel(kernel);
  238. clReleaseCommandQueue(commands);
  239. clReleaseContext(context);
  240. return 0;
  241. }

因为使用了mac的OPENCL框架,所以编译的时候要加上对框架的引用,如下所示:

  1. gcc -o hello hello.c -framework OpenCL

macOS的OpenCL高性能计算的更多相关文章

  1. python scrapy 入门,10分钟完成一个爬虫

    在TensorFlow热起来之前,很多人学习python的原因是因为想写爬虫.的确,有着丰富第三方库的python很适合干这种工作. Scrapy是一个易学易用的爬虫框架,尽管因为互联网多变的复杂性仍 ...

  2. 【记录一个问题】macos下使用opencl, clSetEventCallback不生效

    一开始的调用顺序是这样: enqueueWriteBuffer enqueueNDRangeKernel enqueueReadBuffer SetEventCallback 执行后主程序用getch ...

  3. macos下命令行通过ndk编译android下可以执行的ELF程序(并验证opencl的调用)

    源码如下,实现把一个JPG保存成灰度图格式的BMP 1 //jpg2bmp.cpp 2 #include <stdio.h> 3 #include <inttypes.h> 4 ...

  4. OpenCL、OpenGL、OpenAL

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

  5. C#在高性能计算领域为什么性能却如此不尽人意

    C#的优雅,强大IDE的支持,.net下各语言的二进制兼容,自从第一眼看到C#就被其良好的设计吸引.一直希望将其应用于高性能计算领域,长时间努力却效果却不尽如人意. 对于小的测试代码用例而言,C#用2 ...

  6. 《OpenCL异构计算》新版中译本派送中!

    <OpenCL异构计算1.2>新鲜出炉,目前市面上仍一书难求!我们已向清华出版社订购到第一批新书.关注异构开发社区,积极参与,就有可能免费获取新书! 1.如果您异构社区的老朋友,请关注:1 ...

  7. 【异构计算】在Windows下使用OpenCL配置

    前言 目前,NVIDIA 和 AMD 的 Windows driver 均有支持OpenCL(NVIDIA 的正式版 driver 是从自195.62 版开始,而 AMD则是从9.11 版开始).NV ...

  8. OpenCL与CUDA,CPU与GPU

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

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

    http://www.cnblogs.com/lifan3a/articles/4607659.html 在我们熟知的桌面平台,GPU得到了极为广泛的应用,小到各种电子游戏,大到高性能计算,多核心.高 ...

随机推荐

  1. docker使用教程

    Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Li ...

  2. vue 调用摄像头拍照以及获取相片本地路径(实测有效)

    在学习这个的时候有一点前提:这是针对手机功能的,所以最重要的是要用手机进行实时调试 包含图片的增加和删除功能 <template> <div> <!--照片区域--> ...

  3. 学习随笔:Vue.js与Django交互以及Ajax和axios

    1. Vue.js地址 Staticfile CDN(国内): https://cdn.staticfile.org/vue/2.2.2/vue.min.js unpkg:会保持和npm发布的最新的版 ...

  4. APM(pixhawk)飞控疑难杂症解决方法汇总(持续更新)

    本文转自下面博主 https://blog.csdn.net/junzixing/article/details/79310159 APM/Pixhawk常用链接汇总(持续更新) https://bl ...

  5. 如何在HTML表格里定位到一行数据

    业务需求: 在这样一个表格里,通过点击"确认"按钮,收集该行数据,向后台发送请求 解决办法 以该button为锚获取父节点,再由父节点获取各个元素的值 获取子元素又有很多办法,包括 ...

  6. Git SSL公钥密钥生成

    下面教大家简单易懂的五步配置好密钥 第一次配置ssh 和ssl git config --global --list 查看git的配置 步骤: 1. git config --global user. ...

  7. vue Error: No PostCSS Config found in

    最近在做一个vue的移动端的项目,遇到一个问题,我本地的项目运行正常,可是上传到github上的一启动就报错,就是标题上的错误,找了很久,刚开始以为是某个css没有配置,就把本地的复制过去还是报错,无 ...

  8. 封装redis

    封装redis import redis # r = redis.Redis() class MyRedis(): def __init__(self,ip,password,port=6379,db ...

  9. 在Linux上搭建测试环境常用命令(转自-测试小柚子)

    一.搭建测试环境: 二.查看应用日志: (1)vivi/vim 原本是指修改文件,同时可以使用vi 日志文件名,打开日志文件(2)lessless命令是查看日志最常用的命令.用法:less 日志文件名 ...

  10. h5唤起APP并检查是否成功

    // 检查app是否打开 function checkOpen(cb) { const clickTime = +(new Date()); function check(elsTime) { if ...