通过JNI调用cuda程序

本文介绍如何通过Java Native Interface (JNI) 调用CUDA本地程序,将计算密集型任务分配到GPU中以提升性能。教程涵盖了从创建JNI调用类,配置CUDA环境,编写及编译CUDA程序,到Java项目中引入DLL文件的全过程。

#前言
通过使用JNI(Java Native Interface)来调用cuda本地程序,实现把计算密集型任务分配到gpu中以便取得更好的性能。本文主要提供一个实例教程,方便笔者日后查看同时也方便读者学习相关知识。

#环境
本文采用cuda9.0+vs2017社区版+Intellij2016.1.3+jdk1.8+win10

#步骤
##1 建立jni调用类

package jni;

/**
 * Created by lu on 2017/10/24.
 */
public class JniTest {

    public native int printHelloWorldByGpu();


}

注意点:

  1. jni修饰符为native

##2 到该java文件目录下,执行javah jni.JniTest
这里写图片描述
产生的h文件会在执行该命令的目录中生成。
本文项目结构:
这里写图片描述
注意点:

  1. 如果带有包名,需要到包所在目录执行jni.JniTest。在本文中为java目录。如果在jni目录下执行会提示找不到类文件错误

##3 cuda项目
在此,笔者建议建立cuda模板项目。cuda模板项目可以测试cuda程序是否能正常运行,而且不需要额外的cuda配置。如果建立win32库工程,则需要加入cuda环境,配置相对复杂

###3.1 新建cuda项目
这里写图片描述

###3.2 复制头文件到项目中,并编写cuda程序
这里写图片描述

详细的cuda程序源代码:


#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <malloc.h> 
#include <stdio.h>
#include "jni_JniTest.h"
static int k = 0;
__global__ void helloFromGPU() {
	printf("Hello World from GPU! this is block (%d,%d,%d) thread (%d,%d,%d)\n",
		blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, threadIdx.z);
}

__global__ void printMem(int * p) {
	printf("Hello World from GPU!");
	for (int i = 0; i < 10; i++) {
		printf("this is %d\n", p[i]);
	}
}
__global__ void printID() {
	printf("Hello World from GPU! this is thread %d ");
}
int printHelloWorld(void) {

	int * d_data = 0;
	int h_data[] = { 1,2,3,4,5,6,7,8,9,10 };
	cudaError_t cudaStatus = cudaMalloc((void**)&d_data, 10 * sizeof(int));
	cudaStatus = cudaMemset(&d_data, 3, 10 * sizeof(int));
	helloFromGPU << <2, 5 >> >();
	cudaDeviceReset();
	char z;
	scanf("%c", &z);
Error:
	return 0;
	return 0;
}
JNIEXPORT jint JNICALL Java_jni_JniTest_printHelloWorldByGpu
(JNIEnv *, jobject) {
	printHelloWorld();
	return 1;
}

这源代码是从《CUDA C编程权威指南》第一章程序改编过来的,详细可以参考原著。另外需要注意的是笔者在方法的最后增加了一个scanf的输入,原意为可以在程序结束前暂停来看输出。
注意点:

  1. 复制后改不改名为.cuh在本样例程序中都可以执行,但笔者认为最好还是改名字,以区分不同的项目。

###3.3 配置环境
编写好后,会发现jni.h找不到,这是由于cuda项目中默认VC环境没有配置jni库,所以要手动添加。
这里写图片描述

这里写图片描述

在红框处添加
D:\Program Files\Java\jdk1.8.0_144\include\win32
D:\Program Files\Java\jdk1.8.0_144\include\

###3.4 更改输出文件类型
由于需要的是dll文件,所以要修改项目生成的文件类型
这里写图片描述

###3.5 生成并获取dll文件
点击项目进行生成,然后到输出目录获取dll文件。复制到java项目中。
此为本文dll文件生成的目录
这里写图片描述

复制到java项目中
这里写图片描述

##4 java项目配置
方法有两种:

  1. 把dll文件作为库文件配置在libraries

这里写图片描述

这里写图片描述

  1. 在JVM中配置添加库文件路径相关配置
    这里写图片描述

笔者认为在这里采用方法1比较好,配置方便直观。

##5 测试程序
编写简单的main函数进行测试

package jni;

/**
 * Created by lu on 2017/10/24.
 */
public class JniMainTest {

    static{
        System.loadLibrary("cudaExport");
    }

    public static void main(String[] args) throws InterruptedException {
        JniTest jni = new JniTest();
        jni.printHelloWorldByGpu();
    }
}

主函数调用System.loadLibrary对cudaExport进行库加载。这里注意,是cudaExport不是cudaExport.dll
此处注意,由于cuda程序中最后有一个scanf,所以printHelloWorldByGpu输出之后会等待输入。
##6 结语
由于cuda程序是c/c++程序的扩展,所以使用jni程序也能调用。这样就可以实现构建cuda在java方面的应用。笔者认为这样在数据处理,矩阵计算等计算密集型任务可以通过以这样的方式分发到cuda,使用GPU来执行,以此可以提高设备的使用效率。
文中如有不妥之处,还望指出。
赤月幼狼,201710251117。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赤月幼狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值