2017-02-10 6 views
2

Я пытаюсь написать порт MINPACK-Fortran для Android, и я не могу понять правильное использование двойных массивов в JNI. Изучая вопрос на форумах, я понял, что JNI требует большого количества бесполезного кода при работе с массивами. Я хотел бы ошибаться, поэтому принесите код здесь.Лучшая практика использования двойных массивов

В этом примере предлагается вызов процедуры hybrd1_ (подставка Fortran, предоставленная MINPACK) для расчета систем нелинейных уравнений. Пользователь (сторона Java) предоставляет класс aSolver, который содержит метод solveStep. solveStep имеет два параметра (массивы двойных), где x - вектор параметров; f - вектор правой стороны в NLES. Проблема заключается в том, что я не знаю, какие векторы передаются по процедуре hybrd1_ в solveStep, в то время как итерация продолжается. Но я знаю, что эти векторы можно найти внутри x, fvec или wa (поля внутри объекта aSolver). hybrd1_ звонит rhs процедура (cdecl) - процедура, где много ужасных вещей, которые мне нужно сделать для переноса двух массивов на Java, а затем вернуть их на C++. Как вы можете видеть процедуру вызова rhs очень часто, и эти конверсии JNI разрушают всю процедуру.

C++ сторона

//struct which allow me to talk with Java from rhs procedure 
struct fake_n 
{ 
    int n; 
    jobject aSolver; 
    jmethodID meth; 
    JNIEnv* env; 
}; 

//iterator. the place where i need help 
void rhs(int * n, double * x, double * f, int * flag) 
{ 
    fake_n* f_n = (fake_n *) n; 
    // creating array parametres 
    jdoubleArray x_row = ((*f_n).env)->NewDoubleArray((*f_n).n); 
    jdoubleArray f_row = ((*f_n).env)->NewDoubleArray((*f_n).n); 
    // filling array parametres 
    ((*f_n).env)->SetDoubleArrayRegion(x_row, 0, (*f_n).n, x); 
    // launch object method 
    (*f_n).env->CallVoidMethod((*f_n).aSolver, (*f_n).meth, x_row, f_row); 
    // copy result back to c++ - why do i need? 
    jdouble *res = ((*f_n).env)->GetDoubleArrayElements(f_row, NULL); 
    for (int i =0; i< (*f_n).n; i++) { f[i] = res[i]; }   
} 

JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_hybrd_1NLES(JNIEnv* env, 
                  jclass clazz, 
                  jobject aSolver) 
{ 
    jclass cls = env->GetObjectClass(aSolver); 
    jmethodID solvestep = env->GetMethodID(cls, "solveStep", "([D[D)V"); 
    if (solvestep == 0) { 
     return; 
    } 

    jfieldID afields[4]; 
    char* atype[4] = {(char *) "[I", (char *)"[D", (char *)"[D", (char *)"[D"}; 
    char* aname[4] = {(char *)"n", (char *)"x", (char *)"fvec", (char *)"wa"}; 
    jobject objs[4]; 

    for (int i = 0; i < 4; i++) { 
     afields[i] = env->GetFieldID(cls, aname[i], atype[i]); 
     objs[i] = env->GetObjectField(aSolver, afields[i]); 
    } 
    jintArray *na = reinterpret_cast<jintArray *>(&(objs[0])); 
    jdoubleArray *xa = reinterpret_cast<jdoubleArray *>(&(objs[1])); 
    jdoubleArray *fveca = reinterpret_cast<jdoubleArray *>(&(objs[2])); 
    jdoubleArray *waa = reinterpret_cast<jdoubleArray *>(&(objs[3])); 

    int *n = env->GetIntArrayElements(*na, NULL); 
    double *x = env->GetDoubleArrayElements(*xa, NULL); 
    double *fvec = env->GetDoubleArrayElements(*fveca, NULL); 
    double *wa = env->GetDoubleArrayElements(*waa, NULL); 

    fake_n f_n; 
    f_n.n = *n; 
    f_n.aSolver = aSolver; 
    f_n.meth = solvestep; 
    f_n.env = env; 

    // tol, lwa, info - local variables 
    // this procedure calls the iterator (rhs) a lot of times 
    hybrd1_(rhs, (int *)&f_n, x, fvec, &tol, &info, wa, &lwa); 

    // should i ? 
    env->ReleaseIntArrayElements(*na, n, 0); 
    env->ReleaseDoubleArrayElements(*xa, x, 0); 
    env->ReleaseDoubleArrayElements(*fveca, fvec, 0); 
    env->ReleaseDoubleArrayElements(*waa, wa, 0); 
} 

А вот сторона JAVA

public class aSolver { 
    public double[] x; 
    public double[] fvec; 
    public double[] wa; 
    public int[] n; 
    public int rc; 

    public aSolver() { 
     n = new int[1]; 
     n[0] = 2; 
     x = new double[n[0]]; 
     fvec = new double[n[0]]; 
     wa = new double[n[0] * (3 * n[0] + 13)/2]; 
     x[0] = 0; 
     x[1] = 1; 
     rc = 0; 
     solveStep(x, fvec); 
    } 

    // method, which calling from iterator (rhs) 
    public void solveStep(double[] xv, double[] fv) { 
     // it is important: fv != fvec 
     // it is important: xv != x 
     fv[0] = 2.0 * xv[0] + 3.0 * xv[1] + 6.0; 
     fv[1] = 5.0 * xv[0] - 3.0 * xv[1] - 27.0; 
     rc++; 
    } 
} 

//---- 
aSolver s = new aSolver(); 
hybrd_NLES(s); 
//---- 

public native void hybrd_NLES(aSolver solver); 

Написанный код кажется громоздким и неэффективным. Кроме того, есть ощущение, что в до конца требуется ReleaseDoubleArrayElements, но я не понимаю, зачем он там нужен. Пожалуйста, помогите мне написать эффективную упаковку.

ответ

0

Прежде всего, код JNI действительно громоздкий по определению, поскольку вы должны использовать «env» для создания определенных переменных, и вам нужно сделать некоторую кодировку для получения данных, которые вам нужны, с кодом Java и для связи с этим. Я не могу сказать, как сделать код более эффективным, так как я действительно не понимаю, что вы хотели сделать. У вас может быть ненужное копирование, я не уверен. Если вы просмотрите свой код и по-прежнему думаете, что можете его улучшить, объясните немного дальше, какова ваша цель. Что касается вашего вопроса в разделе «ReleaseDoubleArrayElements» - NewDoubleArray создает только локальную ссылку, поэтому вам не нужно ее выпускать.

+0

Спасибо за объяснение о ReleaseDoubleArrayElements. Как насчет «ненужного копирования»? Я добавил дополнительные комментарии в код – user3811082

+0

. Перед вашим последним редактированием было похоже, что есть некоторые ненужные операции, но, глядя на него сейчас, это выглядит нормально. – yakobom

0

Я думаю, что нашел решение и поместил его здесь, если у кого-то есть аналогичная проблема. Я размещаю все рабочие массивы в одном и передаю его на C++ с Java. Затем внутри rhs Я вычислил смещения памяти к векторам x и f и приведу их в solveStep (теперь я отправляю на Java только смещения). Здесь изменяется код:

C++ сторона

struct fake_n 
{ 
    int n; 

    jobject aSolver; 
    jmethodID meth; 
    JNIEnv* env; 
    double* dbls; 
}; 

void rhs(int * n, double * x, double * f, int * flag) 
{ 
    fake_n* f_n = (fake_n *) n; 
    // calculate the offsets to x and f vectors 
    jint x_row = (jint)(x - (*f_n).dbls); 
    jint f_row = (jint)(f - (*f_n).dbls); 
    // and call the method of aSolver object 
    (*f_n).env->CallVoidMethod((*f_n).aSolver, (*f_n).meth, x_row, f_row); 
} 
JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_hybrd_1NLES(JNIEnv* env, 
                  jclass clazz, 
                  jobject aSolver) 
{ 
    jclass cls = env->GetObjectClass(aSolver); 
    jmethodID solvestep = env->GetMethodID(cls, "solveStep", "(II)V"); 
    if (solvestep == 0) { 
     return -1; 
    } 
    int info = 0; 

    //JNIlist - helper class to connect to JNI and to hold the variables data 
    //nothing unusual - jast a couple of simple wrappers 
    //original code is shown above 
    JNIlist * lJNI = new JNIlist(env, aSolver, cls); 
    lJNI->add(JNI_DT_INT,   (char *)"n"); 
    lJNI->add(JNI_DT_DOUBLE_ARRAY, (char *)"dbls"); 
    lJNI->connectJNI(); 

    int n = *((int*)lJNI->data(0)); 
    int lwa = n * (3 * n + 13)/2 + 1; 
    double tol = rel_TOL; 

    fake_n f_n; 
    f_n.n = n; 
    f_n.aSolver = aSolver; 
    f_n.meth = solvestep; 
    f_n.env = env; 
    f_n.dbls = (double*)(lJNI->data(1)); //pointer to the REAL memory section 
    // with the array of doubles "dbl" 
    //cause GetDoubleArrayElements with the iCopy parameter which equals JNI_FALSE 
    // gives access to the real memory section 

    hybrd1_(rhs, (int *)&f_n, f_n.dbls, f_n.dbls + n, &tol, &info, f_n.dbls + 2*n, &lwa); 

    delete lJNI; 

    return info; 
} 

Java сторона

public class aSolver { 
    public double[] dbls; 
    public int lwa; 
    public int n; 
    public int info; 
    public int rc; 
    private int xoffset, foffset; 

    public aSolver() { 
     n = 2; 
     lwa = (n*(3 * n + 13)/2 + 1); 
     dbls = new double[2 * n + lwa]; 
     info = 0; 
     dbls[0] = 0; 
     dbls[1] = 1; 
     rc = 0; 
     solveStep(0, 1); 
    } 

    public void setOffsets(int xv, int fv) { 
     xoffset = xv; 
     foffset = fv; 
    } 

    public void setf(int p, double val){ 
     dbls[foffset + p] = val; 
    } 

    public double x(int p){ 
     return dbls[xoffset + p]; 
    } 

    public void solveStep(int xv, int fv) { 
     setOffsets(xv, fv); 
     setf(0, 2.0 * x(0) + 3.0 * x(1) + 6.0); 
     setf(1, 5.0 * x(0) - 3.0 * x(1) - 27.0); 
     rc++; 
    } 
} 
Смежные вопросы