Pergunta

No momento, eu estou tentando criar um Java-aplicação que utiliza CUDA-funcionalidade. A conexão entre CUDA e Java funciona bem, mas eu tenho um outro problema e queria perguntar, se os meus pensamentos sobre o assunto estão corretos.

Quando eu chamar uma função nativa do Java, eu passar alguns dados a ele, as funções calcula algo e retorna um resultado. É possível, para permitir que a primeira função retornar uma referência (ponteiro) para esse resultado que eu posso passar para JNI e chamar outra função que faz outros cálculos com o resultado?

A minha ideia era reduzir a sobrecarga que vem de copiar dados de e para a GPU, deixando os dados na memória da GPU e apenas passando uma referência a ele para outras funções podem usá-lo.

Depois de tentar algum tempo, eu pensei para mim mesmo, isso não deve ser possível, porque os ponteiros são apagados após o término da aplicação (neste caso, quando o termina-função C). Isso é correto? Ou estou apenas ao mau em C para ver a solução?

Edit: Bem, para expandir a questão um pouco (ou torná-lo mais claramente): memória alocada por funções nativas JNI é desalocada quando as extremidades de função? Ou eu ainda pode acessá-lo até fins de aplicação ou o JNI ou quando eu libertá-lo manualmente?

Obrigado pelo seu contributo:)

Foi útil?

Solução

Eu usei a seguinte abordagem:

em seu código JNI, criar uma estrutura que iria realizar referências a objetos que você precisa. Quando você primeiro criar esta estrutura, retornar a sua ponteiro para java como um long. Então, a partir java você acabou de chamar qualquer método com este long como um parâmetro, e em C lançá-lo para um ponteiro para a sua estrutura.

A estrutura será na pilha, por isso não vai ser apuradas entre as diferentes JNI.

EDIT: Eu não acho que você pode usar longo ptr = (long)&address; vez que o endereço é uma variável estática. Usá-lo da maneira Gunslinger47 sugeriu, ou seja, criar nova instância da classe ou um struct (usando novo ou malloc) e passar o ponteiro.

Outras dicas

Em C ++ você pode usar qualquer mecanismo que pretende atribuir / memória livre: a pilha, malloc / free, novo / exclusão ou qualquer outra implementação personalizada. A única exigência é que se você atribuído um bloco de memória com um mecanismo, você tem que libertá-la com o mesmo mecanismo, para que você não pode chamar free em uma variável de pilha e você não pode chamar delete na memória malloced.

JNI tem seus próprios mecanismos de alocação / liberação de memória JVM:

  • NewObject / DeleteLocalRef
  • NewGlobalRef / DeleteGlobalRef
  • NewWeakGlobalRef / DeleteWeakGlobalRef

Estes seguem a mesma regra, o único problema é que refs locais podem ser excluídos "em massa" de forma explícita, com PopLocalFrame, ou implicitamente, quando sai do método nativo.

JNI não sei como você definidos para sua memória, por isso não pode libertá-la quando suas saídas de função. variáveis ??de pilha irá, obviamente, ser destruído, porque você ainda está escrevendo C ++, mas a sua memória GPU permanecerá válido.

O único problema, então, é como acessar a memória em invocações subsequentes e, em seguida, você pode usar a sugestão de 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 não saberia o que fazer com um ponteiro, mas deve ser capaz de armazenar um ponteiro de valor de retorno de uma função nativa, em seguida, entregá-lo para outra função nativa para que possa lidar com eles. ponteiros C são nada mais do que os valores numéricos no núcleo.

Outra contibutor teria que dizer-lhe ou não o pontiagudo para gráficos memória seria apagada entre as chamadas JNI e se haveria quaisquer soluções alternativas.

Eu sei que esta pergunta já foi respondida oficialmente, mas eu gostaria de acrescentar a minha solução: Em vez de tentar passar um apontador, coloque o ponteiro em uma matriz Java (no índice 0) e passar isso para JNI. código JNI pode obter e definir o elemento matriz usando GetIntArrayRegion / SetIntArrayRegion.

No meu código, eu preciso da camada nativa para gerenciar um descritor de arquivo (uma tomada aberta). A classe Java contém uma matriz int[1] e passa para a função nativa. A função nativa pode fazer o que quer com ele (get / set) e colocar de volta o resultado na matriz.

Se você está alocando memória dinamicamente (na pilha) dentro da função nativa, este não é apagado. Em outras palavras, você é capaz de manter o estado entre diferentes chamadas para funções nativas, usando ponteiros, estático vars, etc.

Pense nisso uma maneira diferente: o que você poderia fazer manter em segurança em uma chamada de função, chamada de outro programa C ++? As mesmas coisas se aplicam aqui. Quando uma função é encerrado, qualquer coisa na pilha para que chamada de função é destruído; mas qualquer coisa na pilha é mantida a menos que você exclua explicitamente.

A resposta curta: enquanto você não desalocar o resultado que você está voltando para a função de chamada, ele permanecerá válida para re-entrada mais tarde. Apenas certifique-se de limpá-lo quando estiver pronto.

Enquanto a resposta aceita de @ denis-tulskiy faz sentido, eu personnally seguido sugestões de aqui .

Assim, em vez de usar um tipo de pseudo-ponteiro, como jlong (ou jint se você quiser economizar algum espaço em 32bits arco), use em vez de um ByteBuffer. Por exemplo:

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

que você pode mais tarde re-uso com:

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

Para casos muito simples, esta solução é muito fácil de usar. Suponha que você tem:

struct {
  int exampleInt;
  short exampleShort;
} MyNativeStruct;

No lado do Java, você simplesmente precisa fazer:

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

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

O que evita que você escrever muitos do código clichê! Deve-se no entanto prestar atenção a ordenação de bytes como explicado aqui .

O seu melhor para fazer isso exatamente como Unsafe.allocateMemory faz.

Crie o seu objeto, em seguida, digite-a (uintptr_t) que é um 32/64 bit inteiro sem sinal.

return (uintptr_t) malloc(50);

void * f = (uintptr_t) jlong;

Esta é a única maneira correta de fazê-lo.

Aqui está a verificação de sanidade Unsafe.allocateMemory faz.

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top