GPU并行编程基础与Aparapi入门实战

0. 绪

最近项目需要使用GPU进行并行计算,作为不会C++的垃圾只能找Java语言操作的GPU api,目前亲测可使用的有JCuda、JOCL和Aparapi,其中JCuda中支持Nvidia的显卡上,JOCL使用相对Aparapi更麻烦,所以选择了Aparapi…
引用:
CUDA:https://www.jianshu.com/p/34a504af8d51
OpenCL:https://www.cnblogs.com/wangshide/archive/2012/01/07/2315830.html
Aparapi官网:http://aparapi.com/

1. GPU相关基础概念

CPU:中央处理器,是计算机的运算核心和控制核心,主要由运算器(ALU)、控制单元(CU)、寄存器(Register)和高速缓存器(Cache)组成,它的功能主要是解释计算机指令以及处理计算机软件中的数据。

  • CPU遵循的是冯诺伊曼架构,核心就是:存储程序,顺序执行;
  • 在CPU结构中,缓存Cache和控制单元CU占据了大部分空间,计算单元占比很少,所以它在大规模并行计算能力上极受限制,更擅长逻辑控制。

GPU:图形处理器,结构与CPU类似,由通用计算单元、控制器和寄存器组成,相比CPU,GPU有更多的ALU用于数据处理,适合密集型数据进行并行处理。

  • GPU相对构成简单,有数量较多的计算单元和超长的流水线,适合处理大量的类型统一的数据。
  • GPU的工作计算量大,但没有什么技术含量,需要重复很多次。
  • GPU无法单独工作,需要由CPU进行控制调用才能工作。CPU可单独作用,处理复杂的逻辑运算和不同的数据类型,当需要处理大量的类型统一的数据时,则调用GPU进行并行计算。
  • GPU中有很多的运算器ALU和很少的缓存cache,缓存的目的不是保存后面需要访问的数据的,这点和CPU不同,而是为线程thread提高服务的。如果有很多线程需要访问同一个相同的数据,缓存会合并这些访问,然后再去访问。

比如下图:

在这里插入图片描述
其他概念:

  1. 内存:内存是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的。内存(Memory)也被称为内存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。
  2. 显存:显存,也被叫做帧缓存,它的作用是用来存储显卡芯片处理过或者即将提取的渲染数据。如同计算机的内存一样,显存是用来存储要处理的图形信息的部件。

2.OpenCL编程基础

OpenCL编程中重要的概念

  1. Host:CPU以及系统的内存统称为Host,我们在Host上定义控制OpenCl的环境与执行逻辑。
  2. Device:将GPU以及GPU本身的显示内存称为Device,在Host上定义的计算逻辑最终要GPU进行计算。
  3. Kernel:OpenCl程序分为Host(CPU)和Device(GPU)两个部分,主程序由CPU执行,当需要并行计算数据时,OpenCL就会将程序编译成GPU能执行的程序,发送给GPU,这个程序就是Kernel。Kernel是Device程序执行的入口点。
  4. SIMT:单指令多线程(SINGLE INSTRUCTION MULTI THREAD)的简写,相同的代码在不同线程中并行执行,每个线程使用不同的数据来执行同一段代码。
  5. Work-item:工作项,与CUDA中的Thread是一样的,是最小执行单元。一个GPU并行程序会被许多个threads来执行。
  6. Work-group:工作组,与CUDA中Block是一样的。Work-group的存在是为了允许work-item之间的通信与协作。数个Work-item会被群组成一个Work-group,同一个Work-group中的Work-item可以同步并通过shared memory通信。(work-group是以N维网格形式组织的,N=1,2或3)
  7. ND-Range:ND-Range是Work-group下一个组织级别,定义了work-group的组织形式,与CUDA中的grid是一样的。(ND-Rang以N维网格形式组织的,N=1,2或3)
    在这里插入图片描述
  8. Global memory:通俗意义上的设备内存。
  9. Constant memory:位于设备内存,常量内存用于保存在核函数执行期间不会发生变化的数据。变量的访问限制为只读。常量内存采取了不同于标准全局内存的处理方式。在某些情况下,用常量内存替换全局内存能有效地减少内存带宽,从而提升性能。
  10. Local memory:局部内存,隶属于一个工作组的内存区域。它可以用来分配一些变量, 这些变量由此工作组中的所有工作项共享。在OpenCL设备上,可能会将 其实现成一块专有的内存区域,也可能将其映射到全局内存中。
  11. Private memory:私有内存,隶属于一个工作项的内存区域。 一个工作项的私有内存中所定义的变量对另外一个工作项来说是不可见的
    在这里插入图片描述

注意点:

  • 核函数是以Work-group为单位执行
  • Host程序中包括:Plantform(平台)、Context(上下文,即OpenCl环境,包括OpenCL kernel、设备、内存管理、命令队列等)、Command-Queue(指令队列,多个指令队列允许应用程序在不需要同步的情况下执行多条无关联的指令)
  • Work-item、Work-group和ND-range都使用一维、二维和三维的索引进行标识

3. Aparapi编程入门

Aparapi是运行在GPU上的并行api,支持将java代码动态转换成OpenCl Kernel。支持所有对OpenCL兼容的显卡。
Aparapi开箱即用,前提显卡驱动需要支持OpenCl或自定义下载对应显卡驱动的OpenCL SDK


<dependency>
    <groupId>com.aparapi</groupId>
    <artifactId>aparapi</artifactId>
    <version>2.0.0</version>
</dependency>

Demo:元素相加

  1. 创建Kernel对象,重写run方法,定义计算规则
  2. 注意:Aparapi仅支持Java基本数据类型boolean,byte,short,int,long和float,以及这些基本数据类型的一维数组。Double类型取决于你的显卡。
  3. kernel.execute(Range):表示核执行范围,可定义一维、二维或者三维
  4. kernel.dispose();使用完毕以后,调用dispose释放资源
  5. getGlobalId();获取每个执行线程的索引
final int size = 512;

   final float[] a = new float[size];
   final float[] b = new float[size];

   for (int i = 0; i < size; i++) {
      a[i] = (float) (Math.random() * 100);
      b[i] = (float) (Math.random() * 100);
   }

   final float[] sum = new float[size];

   Kernel kernel = new Kernel(){
      @Override public void run() {
         int gid = getGlobalId();
         sum[gid] = a[gid] + b[gid];
      }
   };

   kernel.execute(Range.create(size));

   for (int i = 0; i < size; i++) {
      System.out.printf("%6.2f + %6.2f = %8.2f\n", a[i], b[i], sum[i]);
   }

   kernel.dispose();
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值