Отправка int [] между Java и C - PullRequest
       11

Отправка int [] между Java и C

13 голосов
/ 30 января 2011

У меня есть некоторый код Java для обработки изображений в Android, который работает с двумя большими массивами int. В большинстве случаев Java достаточно быстр, но мне нужно использовать C через JNI и NDK, чтобы ускорить несколько операций.

Единственный способ узнать, что я могу передать данные из массивов int в C, - это использовать ByteBuffer.allocateDirect, чтобы создать новый буфер, скопировать в него данные и затем заставить код C воздействовать на буфер.

Однако я не вижу способа манипулировать данными в этом буфере в Java, как если бы буфер был int [] или byte []. Например, вызов ByteBuffer.array () завершится неудачно для вновь созданного буфера. Есть ли способ заставить эту работу?

У меня ограниченная память, и я хочу уменьшить количество необходимых мне массивов / буферов. Например, было бы неплохо, если бы я мог использовать IntBuffer.wrap (new int [...]) для создания буфера, а затем манипулировать массивом, поддерживающим буфер непосредственно в Java, но я не могу этого сделать, потому что единственное, что кажется здесь для JNI работает ByteBuffer.allocateDirect.

Существуют ли другие способы отправки данных между C и Java? Можно ли как-то выделить память на стороне C и сделать так, чтобы Java отправляла туда данные напрямую?

Редактировать: бенчмарк, сравнивающий использование буфера с использованием int []:

int size = 1000;
IntBuffer allocateDirect = java.nio.ByteBuffer.allocateDirect(4 * size).asIntBuffer();
for (int i = 0; i < 100; ++i)
{
  for (int x = 0; x < size; ++x)
  {
    int v = allocateDirect.get(x);
    allocateDirect.put(x, v + 1);
  }
}

int[] intArray = new int[size];
for (int i = 0; i < 100; ++i)
{
  for (int x = 0; x < size; ++x)
  {
    int v = intArray[x];
    intArray[x] = v + 1;
  }
}

На телефоне Droid для завершения версии буфера требуется ~ 10 секунд, а для версии массива ~ 0,01 секунды.

Ответы [ 3 ]

17 голосов
/ 31 января 2011

С http://java.sun.com/docs/books/jni/html/objtypes.html, используйте JNI's Get/Release<TYPE>ArrayElements(...)

В этом примере я передам массив (для аргумента, это int array = new int[10], а затем заполню его 0-9

 JNIEXPORT jint JNICALL 
 Java_IntArray_doStuffArray(JNIEnv *env, jobject obj, jintArray arr)
 {

     // initializations, declarations, etc
     jint *c_array;
     jint i = 0;

     // get a pointer to the array
     c_array = (*env)->GetIntArrayElements(env, arr, NULL);

     // do some exception checking
     if (c_array == NULL) {
         return -1; /* exception occurred */
     }

     // do stuff to the array
     for (i=0; i<10; i++) {
         c_array[i] = i;
     }

     // release the memory so java can have it again
     (*env)->ReleaseIntArrayElements(env, arr, c_array, 0);

     // return something, or not.. it's up to you
     return 0;
 }

Изучите раздел 3.3, в частности 3.3.2, - это позволит вам получить указатель на массив в памяти java, изменить его и освободить, что фактически позволит вам изменить массив в собственном коде. *

Я только что использовал его в своем собственном проекте (с короткими массивами), и он прекрасно работает:)

5 голосов
/ 31 января 2011

Если вы используете буферы с прямым выделением, вы можете получить доступ к массиву поддержки напрямую из C, используя функцию GetDirectBufferAddress. Это предотвращает возможность копирования областей области.

Вы можете работать с возвращаемым адресом напрямую, как с обычным массивом C, и он будет напрямую изменять буфер прямого назначения Java.

Затем, в качестве эфемерных состояний, вы можете использовать ByteBuffer.asIntBuffer () и семейство для доступа к буферу способом, который эмулирует массивы различных примитивов Java.

http://download.oracle.com/javase/1.4.2/docs/guide/jni/jni-14.html

4 голосов
/ 30 января 2011

Вы можете использовать обратный вызов для отправки данных из собственного уровня в Java.

На уровне Java: в моем родном классе у меня есть следующие методы:

//Native method
public native String getStrData(int size);

//Callback method
public void addData(char[] native_data, int size) {

    ...

}

В родном слое: в моей нативной реализации:

JNIEXPORT jstring JNICALL Java_com_pkg_NativeClass_getStrData 
   (JNIEnv *env, jobject obj, jint size) {
     ...

     jclass native_class;           /* Callback: native class */
     jmethodID native_method_id;    /* Callback: native method id */
     jcharArray row;                /* Callback: native data */

     ...

    /* Start Callback: Native to Java  */   
    native_class = (*env)->GetObjectClass(env, obj);
    native_method_id = (*env)->GetMethodID(env, native_class, "addData", "([CI)V");
    if (native_method_id == 0) {
        return (jstring)ERR_NATIVE_METHODID;
    }
    row = (jcharArray)(*env)->NewCharArray(env, size);
    /* jc has the data to be sent to Java */
    (*env)->SetCharArrayRegion(env, (jcharArray)row, (jsize)0, size, (jchar *)jc);

    (*env)->CallVoidMethod(env, obj, native_method_id, row, size);
    /* End Callback */

     ...
}
...