首页 > 编程语言 >java中使用opencl操作GPU

java中使用opencl操作GPU

时间:2024-02-09 19:11:25浏览次数:35  
标签:CL java cl int opencl 矩阵 内核 GPU null

  需要管理GPU资源,使用java编写,选用opencl框架,并且选择org.jocl包(<dependency><groupId>org.jocl</groupId><artifactId>jocl</artifactId><version>2.0.5</version></dependency>)。具体opencl原理此处不涉及,仅记录使用java该如何做基本操作。

  最少要以下几步,详细可以参看:https://blog.51cto.com/u_16175492/6735362和https://www.cnblogs.com/jzhoucdc/p/14346418.html

1、获取平台(cl_platform_id)信息

        // 定义一个一维、一位的整型数组,用于下一步接收平台数量。因opencl函数调用需要用指针,java中用数组的引用来实现
        int numberOfClPlatforms[] = new int[1];
        // 第一次调用获取平台数量:num_entries传0、platforms传null,将在numberOfClPlatforms[0]中返回平台数量
        CL.clGetPlatformIDs(0, null, numberOfClPlatforms);
        // 根据平台数量生成平台列表数组,用于下一步接收平台id的清单
        cl_platform_id[] clPlaforms = new cl_platform_id[numberOfClPlatforms[0]];
        // 再次调用clGetPlatformIDs,获取平台id的列表,platforms中是所有平台的编码,第一个参数num_entries存放参数clPlatforms中最多可以接收的平台数量,clPlatforms用于接收平台列表(可以理解为硬件编码,不会变化),numberOfClPlatforms[0]用于接收平台数量
        CL.clGetPlatformIDs(clPlaforms.length, clPlaforms, numberOfClPlatforms);
        log.info("got {} platforms", numberOfClPlatforms[0]);

2、根据平台信息获取设备(cl_device_id)信息

        // 定义一个一维、一位的整型数组,用于下一步接收设备(显卡)数量。因opencl函数调用需要用指针,java中用数组的引用来实现
        int[] numberOfClDevices={0};
        // 第一次调用获取设备数量:num_entries传0、devices传null,将在numberOfClDevices[0]中返回设备数量
        CL.clGetDeviceIDs(clPlaforms[0],CL.CL_DEVICE_TYPE_GPU,0,null,numberOfClDevices);
        // 根据设备数量初始化设备清单数组,用于下一步接收设备id的清单
        cl_device_id[] clDevices = new cl_device_id[numberOfClDevices[0]];
        // 再次调用clGetDeviceIDs,获取设备id列表,clDevices中是所有设备的编码,num_entries存放clDevices中最多可以接收的设备数量,clDevices用于接收设备列表(可以理解为硬件编码,不会变化),counts[0]用于接收设备数量
        CL.clGetDeviceIDs(clPlaforms[0],CL.CL_DEVICE_TYPE_GPU,numberOfClDevices[0],clDevices,numberOfClDevices);
        log.info("got {} GPUs", numberOfClDevices[0]);

3、创建设备的上下文(cl_context)

        cl_context_properties contextProperties = new cl_context_properties();
        contextProperties.addProperty(CL.CL_CONTEXT_PLATFORM, clPlatforms[0]);
        cl_context clContext = CL.clCreateContext(contextProperties, clDevices.length, clDevices, null, null, null);

4、根据上下文创建队列(cl_command_queue)

cl_command_queue clCommandQueues = CL.clCreateCommandQueueWithProperties(clContexts[i], clDevices[i], null, null);

5、根据上下文准备程序(OpenCL C语言,类C99,cl_program)

            // 定义C99语法的内核程序源码
            String clSource = "__kernel void runDefault() { for(long a=0;a<10;a++);}\n" +
            "__kernel void runTillDeath(){while(1);}\n" +
            "__kernel void matrixMultiple(__global float* A, __global float* B, __global float* C," +
            "                                  int M, int N, int K)" +
            "{" +
            "    int row = get_global_id(0);" +
            "    int col = get_global_id(1);" +
            "    if (row < M && col < N) {" +
            "        float sum = 0.0f;" +
            "        for (int k = 0; k < K; ++k) {" +
            "            sum += A[row * K + k] * B[k * N + col];" +
            "        }" +
            "        C[row * N + col] = sum;" +
            "    }" +
            "}\n";
            // 创建程序
            cl_program clPrograms[i] = CL.clCreateProgramWithSource(clContexts[i], 1, new String[]{clSource}, null, null);
            // 编译生成内核可执行代码(很快)
            CL.clBuildProgram(clPrograms[i], 0, null, null, null, null);

6、根据程序创建内核(cl_kernel)

        // 用指定内核函数创建内核,需要单独定义每个出入参,此处以矩阵乘法为例
        /**
         * 矩阵乘法:第一个矩阵的列数与第二个矩阵的行数相等才能进行合法的乘法运算,即 A 的维度是 MxK,B 的维度是 KxN,操作如下:A (M x K) * B (K x N) = C (M x N)
         */
        // 第一个矩阵(通常记为 A)的行数,也是结果矩阵(记为 C)的行数
        int M = 8000;
        // 第二个矩阵(通常记为 B)的列数,也是结果矩阵(记为 C)的列数
        int N = 8000;
        // 第二个矩阵(B)的行数,同时也是第一个矩阵(A)的列数
        int K = 8000;
        // 创建矩阵乘法所需出入参的内存对象,包括放置2个输入矩阵以及1个输出结果矩阵数据缓存
        cl_mem inBufferA = CL.clCreateBuffer(clContext, CL.CL_MEM_READ_ONLY | CL.CL_MEM_COPY_HOST_PTR, Sizeof.cl_float * M * K, Pointer.to(A), null);
        cl_mem inBufferB = CL.clCreateBuffer(clContext, CL.CL_MEM_READ_ONLY | CL.CL_MEM_COPY_HOST_PTR, Sizeof.cl_float * K * N, Pointer.to(B), null);
        cl_mem outBufferC = CL.clCreateBuffer(clContext, CL.CL_MEM_WRITE_ONLY | CL.CL_MEM_COPY_HOST_PTR, Sizeof.cl_float * M * N, Pointer.to(C), null);
        // 创建内核
        clKernel = CL.clCreateKernel(clPrograms[i], "matrixMultiple", null);
        // 设置内核运行所需的参数,共6个,3个矩阵缓存区(cl_mem类型),3个矩阵定义(整形),java传递指针给opencl比较麻烦,需要用到opencl封装出来的几个方法
        IntBuffer intValueBufferM = IntBuffer.allocate(1);
        intValueBufferM.put(M);
        intValueBufferM.flip();
        IntBuffer intValueBufferN = IntBuffer.allocate(1);
        intValueBufferN.put(N);
        intValueBufferN.flip();
        IntBuffer intValueBufferK = IntBuffer.allocate(1);
        intValueBufferK.put(K);
        intValueBufferK.flip();
        CL.clSetKernelArg(clKernel, 0, Sizeof.cl_mem, Pointer.to(inBuffersA[devId]));
        CL.clSetKernelArg(clKernel, 1, Sizeof.cl_mem, Pointer.to(inBuffersB[devId]));
        CL.clSetKernelArg(clKernel, 2, Sizeof.cl_mem, Pointer.to(outBuffersC[devId]));
        CL.clSetKernelArg(clKernel, 3, Sizeof.cl_int, Pointer.to(intValueBufferM));
        CL.clSetKernelArg(clKernel, 4, Sizeof.cl_int, Pointer.to(intValueBufferN));
        CL.clSetKernelArg(clKernel, 5, Sizeof.cl_int, Pointer.to(intValueBufferK));

7、让设备执行内核代码

    // 定义输入输出数据
    private  float A[] = new float[M * K];
    private  float B[] = new float[K * N];
    private  float C[] = new float[M * N];
    // 定义工作空间
    long global_work_size[] = new long[]{M, N};
    long local_work_size[] = new long[]{8, 8}; // 假设选择8x8的工作组尺寸
    // 指定队列(设备),运行内核
    CL.clEnqueueNDRangeKernel(clCommandQueue, clKernel, 1, global_work_size, local_work_size, null, 0, null, null);
    // 等待内核执行完成。此时线程会被挂起,直到内核运行完成并退出。需要注意的是,设备在执行内核程序过程中,主机线程无法直接干预该过程。主机进程退出时,操作系统会终止设备上的内核执行。
    int status = CL.clFinish(clCommandQueue);
    if (status != CL.CL_SUCCESS) {
        // 执行错误处理
    }

8、等待设备内核执行完(内核一旦开始执行,主机进程无法直接干预该过程)

9、释放内核、程序、队列、上下文对象

    CL.clReleaseMemObject(inBufferA)
    ...
    CL.clReleaseKernel(clKernel);
    CL.clReleaseDevice(clDevice);
    CL.clReleaseCommandQueue(clCommandQueue);
    CL.clReleaseProgram(clProgram);
    CL.clReleaseContext(clContext);

标签:CL,java,cl,int,opencl,矩阵,内核,GPU,null
From: https://www.cnblogs.com/badwood316/p/18010152

相关文章

  • JAVA零钱通面向过程和oop
    编程思想1.每一个小功能对应一个代码块,高内聚低耦合。2.建议先排除不正确情况,而不是对每一个正确情况做一些操作。编程效果源码实现面向过程点击查看代码packagelingqiantong.chuantong;importjava.text.SimpleDateFormat;importjava.util.Date;importjava.uti......
  • Java中String、StringBuffer、StringBuilder的区别以及使用场景总结
    Java中,String、StringBuffer和StringBuilder都用于处理字符串,但在功能和性能上有显著的区别。了解这些区别有助于选择最适合特定情境的类型。在选择使用String、StringBuffer或StringBuilder时,应根据字符串操作的性能需求和线程安全要求来做出决定。1、String、StringBuffer、......
  • Java break、continue 详解与数组深入解析:单维数组和多维数组详细教程
    JavaBreak和ContinueJavaBreak:break语句用于跳出循环或switch语句。在循环中使用break语句可以立即终止循环,并继续执行循环后面的代码。在switch语句中使用break语句可以跳出当前case,并继续执行下一个case。示例://循环示例for(inti=0;i<10;i++......
  • Java并发编程-CompletableFuture(上)
    大家好,我是小高先生,这篇文章我将和大家一起学习Java并发编程中很重要的一个类-CompletableFuture。 在Java的并发编程领域,Future接口一直扮演着关键的角色,它定义了一组与异步任务执行相关的方法,包括获取异步任务的结果、取消任务执行以及检查任务是否已完成等。然而,随着业务场......
  • Java并发编程-CompletableFuture(下)
    大家好,我是小高先生,书接上文,我们继续来学习CompletableFuture。上文我们讲了基础装Future是如何升级为神装CompletableFuture以及如何购买CompletableFuture,接下来我们一起来学习如何在战斗中使用CompletableFuture。CompletableFuture的基本使用CompletableFuture的实战案例C......
  • 多版本Java 配置记录
    目录结构Java总目录下放置多个jdk目录(jdk16.0.2,jkd21.0.2...)借本操作从OracleJava等下载目标jdk(感觉直接下zip解压最清爽)若下载的jdk中没有jre目录,则手动构建(好像1.8之后就如此)进入jdkdir(jdk-xx.x.x/)使用bin/中的jlink构建jre/(运行时环境)--module-pathjmod指......
  • mysql插入数据出现java.sql.SQLException Create breakpoint : Incorrect string valu
    问题图片如下:  如果出现这个问题,就是当前的mysql设置的字符集和当前业务的需求符合;前:当前我需要在mysql中存入的内容中包括了表情等信息,如下:问题分析:因为我在docker中搭建的mysql设置的默认编码为utf-8,下面是我的my.cnf文件[client]default_character_set=utf8[my......
  • Java后端项目实现无限级树 - 案例:部门树 - Department实体类
    privateList<Department>getDeptTree(){   //1.一次性查询出所有数据,放到内存中,可以使用map - 做缓存用   //2.从所有数据中找出第一层级/顶级部门:这就是我们需要返回的数据   //3.遍历所有部门只要不是第一层级的,找自己的上级,并将自己放入上级的childre......
  • 如何使用graalvm为带有反射功能的java代码生成native image
    译自ConfigureNativeImagewiththeTracingAgentgraal官方文档,以下所有命令需要在linux环境下操作,graalvm也支持windows。要为使用Java反射、动态代理对象、JNI或类路径资源的Java应用程序构建本机可执行文件,应为native-image工具提供JSON格式的配置文件或在代......
  • java11下载安装
    https://www.oracle.com/in/java/technologies/javase/jdk11-archive-downloads.html https://jdk.java.net/java-se-ri/11-MR2 ......