使用 JNI、多线程从 Fortran 调用 Java
Calling Java from Fortran using JNI, multithreading
我正在尝试将数组从 Fortran 传递到 Java,在 Java 中进行一些计算,并将值 return 传递给我的 Fortran 程序。我正在使用 JNI 从 Fortran 调用 Java。
我写了一个示例程序,我的代码如下:
main.f95:
PROGRAM MAIN
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE
USE array_example
IMPLICIT NONE
INTERFACE
FUNCTION obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
TYPE(C_PTR), INTENT(IN), VALUE :: c
INTEGER(C_INT), INTENT(IN):: c_size
REAL(C_DOUBLE):: f
END FUNCTION
END INTERFACE
INTEGER :: x, y
x=5
CALL example(obj,x)
END PROGRAM MAIN
mod.f95:
MODULE array_example
CONTAINS
SUBROUTINE example(obj, N)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE, C_LOC
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
REAL(C_DOUBLE) :: res
INTEGER :: j
INTEGER(C_INT) :: flag
INTERFACE
FUNCTION obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
IMPLICIT NONE
TYPE(C_PTR), INTENT(IN), VALUE :: c
INTEGER(C_INT), INTENT(IN):: c_size
REAL(C_DOUBLE):: f
END
END INTERFACE
REAL(C_DOUBLE), TARGET :: c_array(1:N)
TYPE(C_PTR) :: cptr
cptr = c_loc(c_array(1))
c_array(1:N)=(/2.6179917, 1.570795, 1.570795, 1.570795, 1.570795/)
res = obj(cptr, N)
PRINT *, res
END SUBROUTINE
END MODULE
objFuncC.c
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
extern double obj(double *, int *);
JNIEnv* create_vm(JavaVM **jvm)
{
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options;
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options.optionString = "-Djava.class.path=./";
args.options = &options;
args.ignoreUnrecognized = 0;
int rv;
rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
if (rv < 0 || !env)
printf("Unable to Launch JVM %d\n",rv);
else
printf("Launched JVM successfully\n");
return env;
}
double obj(double *ptr, int *c_size)
{
JavaVM *jvm;
JNIEnv *env;
jdouble *dptr;
jdoubleArray newArray;
jint size;
double result;
size = *c_size;
dptr = (double *)ptr;
env = create_vm(&jvm);
if(env == NULL)
return 1;
newArray = (*env)->NewDoubleArray(env, size);
(*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr);
jclass objFuncJ_class;
jmethodID main_method;
jmethodID evaluate_method;
objFuncJ_class = (*env)->FindClass(env, "objFuncJ");
evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D");
result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);
printf("Result = %f\n",result);
return result;
}
objFunc.java
import java.io.*;
import java.lang.*;
public class objFuncJ
{
public static double evaluate(double[] position)
{
int m = 10;
double sum = 0.0;
for (int i = 1; i <= position.length; i++)
{
double xi = position[(i - 1)];
double pow = 1.0;
double xiPow = Math.sin(i * (xi * xi) / Math.PI);
for (int j = 1; j <= (2 * m); j++)
{
pow *= xiPow;
}
sum += Math.sin(xi) * pow;
}
return -sum;
}
}
我得到以下输出:
Launched JVM successfully
Result = -1.011206
Unable to Launch JVM -5
Unable to Launch JVM -5
Unable to Launch JVM -5
1.0000000000000000
'Result'中的值是正确的输出。但是,多次执行失败。如何更改我的代码以处理函数的多次评估并仅创建一次 Java JVM?
evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D");
不同意
public static double evaluate(double[] position, int flg)
寓意:不要尝试自己编写 JNI 方法签名。使用 javap -s
的输出。永远不会错。
好吧,我知道我错在哪里了。我将 objFuncC.c 更改如下:
extern double obj(double *, int *);
extern int init();
static JavaVM *jvm;
JNIEnv* create_vm(JavaVM **jvm)
{
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options;
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options.optionString = "-Djava.class.path=./";
args.options = &options;
args.ignoreUnrecognized = 0;
int rv;
rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
if (rv < 0 || !env)
printf("Unable to Launch JVM %d\n",rv);
else
printf("Launched JVM successfully\n");
return env;
}
int init()
{
JNIEnv *env_init;
env_init = create_vm(&jvm);
if(env_init == NULL)
return 1;
}
double obj(double *ptr, int *c_size)
{
JNIEnv *env;
jdouble *dptr;
jdoubleArray newArray;
jint size;
double result;
size = *c_size;
dptr = (double *)ptr;
jint rs = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
newArray = (*env)->NewDoubleArray(env, size);
(*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr);
jclass objFuncJ_class;
jmethodID main_method;
jmethodID evaluate_method;
objFuncJ_class = (*env)->FindClass(env, "objFuncJ");
evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D");
result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);
printf("Result = %f\n",result);
return result;
}
我在我的 Fortran 程序 (mod.f95) 中为函数 init 添加了一个接口,并在调用 obj(double *, int *) 之前调用了 init()。它工作正常,我得到以下输出:
Launched JVM successfully
Result = -1.011206
Result = -1.011206
Result = -1.011206
Result = -1.011206
-1.0112061116510644
感谢您的帮助:)
我正在尝试将数组从 Fortran 传递到 Java,在 Java 中进行一些计算,并将值 return 传递给我的 Fortran 程序。我正在使用 JNI 从 Fortran 调用 Java。 我写了一个示例程序,我的代码如下:
main.f95:
PROGRAM MAIN
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE
USE array_example
IMPLICIT NONE
INTERFACE
FUNCTION obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
TYPE(C_PTR), INTENT(IN), VALUE :: c
INTEGER(C_INT), INTENT(IN):: c_size
REAL(C_DOUBLE):: f
END FUNCTION
END INTERFACE
INTEGER :: x, y
x=5
CALL example(obj,x)
END PROGRAM MAIN
mod.f95:
MODULE array_example
CONTAINS
SUBROUTINE example(obj, N)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_INT, C_DOUBLE, C_LOC
IMPLICIT NONE
INTEGER, INTENT(IN) :: N
REAL(C_DOUBLE) :: res
INTEGER :: j
INTEGER(C_INT) :: flag
INTERFACE
FUNCTION obj(c, c_size) RESULT(f) BIND(C, NAME='obj')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_DOUBLE, C_PTR, C_INT
IMPLICIT NONE
TYPE(C_PTR), INTENT(IN), VALUE :: c
INTEGER(C_INT), INTENT(IN):: c_size
REAL(C_DOUBLE):: f
END
END INTERFACE
REAL(C_DOUBLE), TARGET :: c_array(1:N)
TYPE(C_PTR) :: cptr
cptr = c_loc(c_array(1))
c_array(1:N)=(/2.6179917, 1.570795, 1.570795, 1.570795, 1.570795/)
res = obj(cptr, N)
PRINT *, res
END SUBROUTINE
END MODULE
objFuncC.c
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
extern double obj(double *, int *);
JNIEnv* create_vm(JavaVM **jvm)
{
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options;
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options.optionString = "-Djava.class.path=./";
args.options = &options;
args.ignoreUnrecognized = 0;
int rv;
rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
if (rv < 0 || !env)
printf("Unable to Launch JVM %d\n",rv);
else
printf("Launched JVM successfully\n");
return env;
}
double obj(double *ptr, int *c_size)
{
JavaVM *jvm;
JNIEnv *env;
jdouble *dptr;
jdoubleArray newArray;
jint size;
double result;
size = *c_size;
dptr = (double *)ptr;
env = create_vm(&jvm);
if(env == NULL)
return 1;
newArray = (*env)->NewDoubleArray(env, size);
(*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr);
jclass objFuncJ_class;
jmethodID main_method;
jmethodID evaluate_method;
objFuncJ_class = (*env)->FindClass(env, "objFuncJ");
evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D");
result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);
printf("Result = %f\n",result);
return result;
}
objFunc.java
import java.io.*;
import java.lang.*;
public class objFuncJ
{
public static double evaluate(double[] position)
{
int m = 10;
double sum = 0.0;
for (int i = 1; i <= position.length; i++)
{
double xi = position[(i - 1)];
double pow = 1.0;
double xiPow = Math.sin(i * (xi * xi) / Math.PI);
for (int j = 1; j <= (2 * m); j++)
{
pow *= xiPow;
}
sum += Math.sin(xi) * pow;
}
return -sum;
}
}
我得到以下输出:
Launched JVM successfully
Result = -1.011206
Unable to Launch JVM -5
Unable to Launch JVM -5
Unable to Launch JVM -5
1.0000000000000000
'Result'中的值是正确的输出。但是,多次执行失败。如何更改我的代码以处理函数的多次评估并仅创建一次 Java JVM?
evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D");
不同意
public static double evaluate(double[] position, int flg)
寓意:不要尝试自己编写 JNI 方法签名。使用 javap -s
的输出。永远不会错。
好吧,我知道我错在哪里了。我将 objFuncC.c 更改如下:
extern double obj(double *, int *);
extern int init();
static JavaVM *jvm;
JNIEnv* create_vm(JavaVM **jvm)
{
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options;
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options.optionString = "-Djava.class.path=./";
args.options = &options;
args.ignoreUnrecognized = 0;
int rv;
rv = JNI_CreateJavaVM(jvm, (void**)&env, &args);
if (rv < 0 || !env)
printf("Unable to Launch JVM %d\n",rv);
else
printf("Launched JVM successfully\n");
return env;
}
int init()
{
JNIEnv *env_init;
env_init = create_vm(&jvm);
if(env_init == NULL)
return 1;
}
double obj(double *ptr, int *c_size)
{
JNIEnv *env;
jdouble *dptr;
jdoubleArray newArray;
jint size;
double result;
size = *c_size;
dptr = (double *)ptr;
jint rs = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
newArray = (*env)->NewDoubleArray(env, size);
(*env)->SetDoubleArrayRegion(env, newArray, 0, size, (const jdouble*)dptr);
jclass objFuncJ_class;
jmethodID main_method;
jmethodID evaluate_method;
objFuncJ_class = (*env)->FindClass(env, "objFuncJ");
evaluate_method = (*env)->GetStaticMethodID(env, objFuncJ_class, "evaluate", "([D)D");
result = (*env)->CallStaticDoubleMethod(env, objFuncJ_class, evaluate_method, newArray);
printf("Result = %f\n",result);
return result;
}
我在我的 Fortran 程序 (mod.f95) 中为函数 init 添加了一个接口,并在调用 obj(double *, int *) 之前调用了 init()。它工作正常,我得到以下输出:
Launched JVM successfully
Result = -1.011206
Result = -1.011206
Result = -1.011206
Result = -1.011206
-1.0112061116510644
感谢您的帮助:)