Вопрос

На данный момент я пытаюсь создать Java-приложение, которое использует CUDA-функциональность.Соединение между CUDA и Java работает нормально, но у меня возникла другая проблема, и я хотел спросить, верны ли мои мысли по этому поводу.

Когда я вызываю собственную функцию из Java, я передаю ей некоторые данные, функции что-то вычисляют и возвращают результат.Возможно ли позволить первой функции возвращать ссылку (указатель) на этот результат, который я могу передать в JNI и вызвать другую функцию, которая выполняет дальнейшие вычисления с результатом?

Моя идея состояла в том, чтобы уменьшить накладные расходы, связанные с копированием данных в графический процессор и из него, оставив данные в памяти графического процессора и просто передав ссылку на них, чтобы другие функции могли их использовать.

Попробовав некоторое время, я подумал для себя, что это не должно быть возможно, потому что указатели удаляются после завершения работы приложения (в данном случае, когда завершается C-функция).Правильно ли это?Или я просто плохо разбираюсь в C, чтобы увидеть решение?

Редактировать:Ну, чтобы немного расширить вопрос (или сделать его более понятным):Освобождается ли память, выделяемая собственными функциями JNI, когда функция завершается?Или я все еще могу получить к нему доступ до тех пор, пока приложение JNI не завершит работу или пока я не освобожу его вручную?

Спасибо за ваш вклад :)

Это было полезно?

Решение

Я использовал следующий подход:

в вашем коде JNI создайте структуру, которая содержала бы ссылки на нужные вам объекты.Когда вы впервые создаете эту структуру, верните ее указатель на java в виде long.Затем из java вы просто вызываете любой метод с помощью этого long в качестве параметра, а в C преобразуйте его в указатель на вашу структуру.

Структура будет находиться в куче, поэтому она не будет очищаться между различными вызовами JNI.

Редактировать:Я не думаю, что вы можете использовать длинный ptr = (long)&address; поскольку address - это статическая переменная.Используйте его так, как предложил Gunslinger47, т.е.создайте новый экземпляр класса или структуры (используя new или malloc) и передайте его указатель.

Другие советы

В C ++ вы можете использовать любой механизм, который вы хотите выделить / освободить память:стек, malloc / free, создать / удалить или любая другая пользовательская реализация.Единственное требование заключается в том, что если вы выделили блок памяти с помощью одного механизма, вы должны освободить его с помощью того же механизма, поэтому вы не можете вызвать free в переменной стека, и вы не можете вызвать delete на mallocотредактированная память.

JNI имеет свои собственные механизмы для выделения / освобождения памяти JVM:

  • newObject/Удалить localref
  • Создать глобальную ссылку /Удалить глобальную ссылку
  • Новая слабая глобальная ссылка / Удалить Слабую глобальную ссылку

Они следуют одному и тому же правилу, единственная загвоздка заключается в том, что локальные ссылки могут быть удалены "в массовом порядке" либо явно, с PopLocalFrame, или неявно, когда собственный метод завершает работу.

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

Тогда единственная проблема заключается в том, как получить доступ к памяти при последующих вызовах, и тогда вы можете воспользоваться предложением Gunslinger47:

JNIEXPORT jlong JNICALL Java_MyJavaClass_Function1() {
    MyClass* pObject = new MyClass(...);
    return (long)pObject;
}

JNIEXPORT void JNICALL Java_MyJavaClass_Function2(jlong lp) {
    MyClass* pObject = (MyClass*)lp;
    ...
}

Java не будет знать, что делать с указателем, но она должна быть способна сохранять указатель из возвращаемого значения собственной функции, а затем передавать его другой собственной функции для обработки.Указатели C - это не что иное, как числовые значения в ядре.

Другой участник должен был бы сообщить вам, будет ли указанная на графическую память очищена между вызовами JNI и будут ли какие-либо обходные пути.

Я знаю, что на этот вопрос уже был дан официальный ответ, но я хотел бы добавить свое решение:Вместо того чтобы пытаться передать указатель, поместите указатель в массив Java (с индексом 0) и передайте его в JNI.Код JNI может получить и установить элемент массива, используя GetIntArrayRegion/SetIntArrayRegion.

В моем коде мне нужен собственный уровень для управления файловым дескриптором (открытым сокетом).Класс Java содержит int[1] массив и передает его собственной функции.Собственная функция может делать с ним все, что угодно (get / set) и возвращать результат в массив.

Если вы выделяете память динамически (в куче) внутри собственной функции, она не удаляется.Другими словами, вы можете сохранять состояние между различными вызовами собственных функций, используя указатели, статические переменные и т.д.

Подумайте об этом по-другому:что вы могли бы безопасно сохранить в вызове функции, вызываемой из другой программы на C ++?То же самое применимо и здесь.Когда функция завершается, все, что находится в стеке для вызова этой функции, уничтожается;но все, что находится в куче, сохраняется, если вы явно не удалите это.

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

Хотя принятый ответ от @denis-tulskiy действительно имеет смысл, я лично следовал предложениям от здесь.

Таким образом, вместо использования типа псевдо-указателя, такого как jlong (или jint если вы хотите сэкономить немного места на 32-битной arch), используйте вместо ByteBuffer.Например:

MyNativeStruct* data; // Initialized elsewhere.
jobject bb = (*env)->NewDirectByteBuffer(env, (void*) data, sizeof(MyNativeStruct));

который вы можете позже повторно использовать с:

jobject bb; // Initialized elsewhere.
MyNativeStruct* data = (MyNativeStruct*) (*env)->GetDirectBufferAddress(env, bb);

Для очень простых случаев это решение очень простое в использовании.Предположим, у вас есть:

struct {
  int exampleInt;
  short exampleShort;
} MyNativeStruct;

На стороне Java вам просто нужно сделать:

public int getExampleInt() {
  return bb.getInt(0);
}

public short getExampleShort() {
  return bb.getShort(4);
}

Что избавляет вас от необходимости писать Лоты из шаблонного кода !Однако следует обратить внимание на порядок байтов, как объяснено здесь.

Лучше всего делать это именно так, как это делает Unsafe.allocateMemory.

Создайте свой объект, затем введите его в (uintptr_t), который представляет собой 32/64-разрядное целое число без знака.

return (uintptr_t) malloc(50);

void * f = (uintptr_t) jlong;

Это единственный правильный способ сделать это.

Вот проверка работоспособности Unsafe.allocateMemory выполняется.

inline jlong addr_to_java(void* p) {
  assert(p == (void*)(uintptr_t)p, "must not be odd high bits");
  return (uintptr_t)p;
}

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory(JNIEnv *env, jobject unsafe, jlong size))
  UnsafeWrapper("Unsafe_AllocateMemory");
  size_t sz = (size_t)size;
  if (sz != (julong)size || size < 0) {
    THROW_0(vmSymbols::java_lang_IllegalArgumentException());
  }
  if (sz == 0) {
    return 0;
  }
  sz = round_to(sz, HeapWordSize);
  void* x = os::malloc(sz, mtInternal);
  if (x == NULL) {
    THROW_0(vmSymbols::java_lang_OutOfMemoryError());
  }
  //Copy::fill_to_words((HeapWord*)x, sz / HeapWordSize);
  return addr_to_java(x);
UNSAFE_END
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top