Para usar o JNI ou não usar o JNI (desempenho do Android)

10

Acabei de adicionar alguns códigos computacionalmente caros a um jogo Android que estou desenvolvendo. O código em questão é uma coleção de rotinas de detecção de colisão que são chamadas com muita freqüência (a cada iteração do loop de jogo) e estão fazendo uma grande quantidade de computação. Eu sinto que minha implementação de detecção de colisão é razoavelmente bem desenvolvida e tão razoavelmente rápida quanto eu posso fazer em Java.

Eu tenho usado o Traceview para criar o perfil do código e essa nova peça O código de detecção de colisão de certa forma surpreendentemente dobrou a duração da minha lógica de jogo. Isso é obviamente uma preocupação, pois, para determinados dispositivos, esse desempenho pode levar meu jogo de um estado jogável a um estado não jogável.

Eu estive considerando maneiras diferentes de otimizar esse código, e estou querendo saber se, ao mover o código para C ++ e acessá-lo com o JNI, eu conseguirei algumas economias de desempenho perceptíveis?

A pergunta acima é minha principal preocupação e minha razão para perguntar. Eu determinei que as duas razões a seguir seriam outros resultados positivos do uso do JNI. No entanto, não é suficiente para me convencer a portar meu código para C ++.

  • Isso tornaria o código mais limpo. Como a maioria das detecções de colisão é um tipo de matemática vetorial, é muito mais fácil usar operadores sobrecarregados do que usar algumas classes vetoriais mais detalhadas em Java.

  • O gerenciamento de memória seria mais simples. Mais simples você diz? Bem, este é um jogo para que o coletor de lixo em execução não seja bem-vindo porque o CG pode acabar arruinando o desempenho do seu jogo se ele tiver que interromper constantemente para limpar. Em C eu não tenho que me preocupar com o coletor de lixo, então eu posso evitar todas as coisas feias que eu faço em Java com variáveis estáticas temporárias e apenas confiar na memória de pilha velha e boa de C ++

Por mais cansativa que seja esta pergunta, acho que cobri todos os meus pontos. Dada essa informação, valeria a pena portar meu código de Java para C ++ e acessá-lo com o JNI (por razões de melhorar o desempenho)? Além disso, existe uma maneira de medir ou estimar um potencial ganho de desempenho?

EDITAR:

Então eu fiz isso. Resultados? Bem, do ponto de vista do TraceView, foi um aumento de 6x na velocidade da minha rotina de detecção de colisão.

Não foi fácil chegar lá. Além de ter que fazer a dança JNI, eu também tive que fazer algumas otimizações que eu não esperava. Principalmente, usando um buffer de flutuação diretamente alocado para passar dados do Java para o nativo. Minha tentativa inicial apenas usou um array float para armazenar os dados em questão, porque a conversão de Java para C ++ era mais natural, mas isso era realmente muito lento. O buffer direto variou completamente os problemas de desempenho com a cópia de matriz entre o java e o nativo, e me deixou com um salto de 6x.

Além disso, em vez de rolar minha própria classe de vetores, usei apenas a biblioteca de matemática Eigen. Eu não tenho certeza do quanto isso afetou o desempenho, mas, pelo menos, isso me salvou o tempo de desenvolver minha própria classe vetorial (menos eficiente).

Outra lição aprendida é que o logging excessivo é ruim para o desempenho (jic que não é óbvio).

    
por user8709 27.01.2012 в 11:00
fonte

2 respostas

4

Não é realmente uma resposta direta à sua pergunta, mas os links a seguir podem ser úteis para você:

No segundo link, o seguinte está escrito:

  

O código nativo não é necessariamente mais eficiente que o Java. Por uma coisa,   há um custo associado à transição nativa de Java e ao JIT   não pode otimizar através desses limites. Se você está alocando nativos   recursos (memória no heap nativo, descritores de arquivo ou qualquer outro),   pode ser significativamente mais difícil organizar a coleta oportuna de   esses recursos. Você também precisa compilar seu código para cada   arquitetura que você deseja rodar (ao invés de confiar em ter um JIT).   Você pode até ter que compilar várias versões para o que você considera   a mesma arquitetura: código nativo compilado para o processador ARM em   o G1 não pode aproveitar ao máximo o ARM no Nexus One e codificar   compilado para o ARM no Nexus One não será executado no ARM no G1.

     

O código nativo é útil principalmente quando você tem um nativo existente   codebase que você deseja portar para o Android, não para "acelerar" as partes   de um aplicativo Java.

    
por Jave 27.01.2012 / 11:11
fonte
1

Se você ainda está num estágio bastante inicial do desenvolvimento do jogo, pode considerar o uso de um mecanismo de jogo que fornece um bom mecanismo de detecção de colisão, como o Libgdx, que faz um bom trabalho na detecção de colisões box2d.

    
por Arun Singh 01.02.2012 / 12:27
fonte