Отправка int [] s между Java и C
У меня есть код обработки изображений Java на Android, который действует на два больших массива int. В большинстве случаев Java достаточно быстро, но мне нужно использовать C через JNI и NDK для ускорения нескольких операций.
Единственный способ, которым я знаю, что я могу передать данные из массивов int в C, - использовать ByteBuffer.allocateDirect для создания нового буфера, скопировать данные на него и затем сделать код C действующим в буфере.
Однако я не могу видеть, как я могу манипулировать данными в этом буфере в Java, как если бы буфер был int [] или байтом []. Например, вызов ByteBuffer.array() завершится неудачно во вновь созданном буфере. Есть ли способ сделать эту работу?
У меня ограниченная память и вы хотите уменьшить количество массивов/буферов, которые мне нужны. Например, было бы неплохо, если бы я мог использовать IntBuffer.wrap(new int [...]) для создания буфера, а затем манипулировать массивом, поддерживающим буфер непосредственно на Java, но я не могу этого сделать, потому что единственное, что кажется работа здесь для JNI - ByteBuffer.allocateDirect.
Существуют ли другие способы отправки данных между C и Java? Могу ли я как-то выделить память на стороне С и отправить Java прямо туда?
Изменить: сравнительный пример сравнения использования буфера с int [] use:
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 секунды.
Ответы
Ответ 1
Из http://java.sun.com/docs/books/jni/html/objtypes.html используйте JNI Get/Release<TYPE>ArrayElements(...)
В этом примере я передам массив (для параметра sake, it 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 up to you
return 0;
}
Раздел 3.3 изучения и, в частности, 3.3.2 - это позволит вам получить указатель на массив в java-памяти, изменить его и выпустить, что фактически позволяет вам изменять массив в собственном коде.
Я только что использовал его в своем собственном проекте (с короткими массивами), и он отлично работает:)
Ответ 2
Если вы используете прямые выделенные буферы, вы можете получить доступ к базовому массиву непосредственно из C, используя функцию GetDirectBufferAddress
. Это предотвращает возможность копирования областей области.
Вы можете работать с возвращенным адресом напрямую, как и обычный массив C, и он будет напрямую модифицировать буфер с прямым распределением Java.
Затем, в качестве эфемерных состояний, вы можете использовать ByteBuffer.asIntBuffer() и family для доступа к буфере таким образом, который эмулирует массивы различных Java-примитивов.
http://download.oracle.com/javase/1.4.2/docs/guide/jni/jni-14.html
Ответ 3
Вы можете использовать обратный вызов для отправки данных из собственного уровня в 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 */
...
}