使用JNI,或者不使用JNI(Android性能)



我刚刚在开发的Android游戏中添加了一些计算成本高昂的代码。有问题的代码是碰撞检测例程的集合,这些例程经常被调用(游戏循环的每次迭代),并进行大量计算。我觉得我的碰撞检测实现开发得相当好,而且速度相当快。

我一直在使用Traceview来评测代码,这段新的碰撞检测代码使我的游戏逻辑持续时间增加了一倍,这一点不足为奇。这显然是一个令人担忧的问题,因为对于某些设备来说,这种性能打击可能会使我的游戏从可玩状态变成不可玩状态。

我一直在考虑用不同的方法来优化这段代码,我想知道通过将代码移到C++中并使用JNI访问它,我是否能显著节省性能?

以上问题是我主要关心的问题,也是我提出这个问题的原因。我已经确定以下两个原因将是使用JNI的其他积极结果。然而,这还不足以说服我将代码移植到C++。

  • 这将使代码更干净。由于大多数冲突检测都是某种向量数学,因此能够使用重载运算符比在Java中使用一些更详细的向量类要干净得多。

  • 内存管理会更简单。你说的更简单?好吧,这是一个游戏,所以运行垃圾收集器是不受欢迎的,因为如果GC不断地中断清理,它可能会破坏游戏的性能。在C中,我不必担心垃圾收集器,所以我可以避免在Java中用临时静态变量做的所有丑陋的事情,只需要依赖C++的旧堆栈内存

虽然这个问题可能很冗长,但我想我已经涵盖了所有的要点。考虑到这些信息,是否值得将我的代码从Java移植到C++并使用JNI访问它(出于提高性能的原因)?此外,有没有一种方法可以衡量或估计潜在的性能增益?

编辑:

所以我做到了。结果如何?从TraceView的角度来看,我的碰撞检测程序的速度提高了6倍。

不过到达那里并不容易。除了必须跳JNI舞,我还必须进行一些我没有想到的优化。主要是使用直接分配的浮点缓冲区将数据从Java传递到本机。我最初的尝试只是使用一个浮点数组来保存有问题的数据,因为从Java到C++的转换更自然,但速度非常慢。在java和本机之间复制数组时,直接缓冲区完全回避了性能问题,给我留下了6倍的缓冲。

此外,我没有滚动自己的向量类,而是使用了Eigen数学库。我不确定这对性能有多大影响,但至少,它为我节省了开发自己的(效率较低)向量类的时间。

另一个教训是,过多的日志记录对性能不利(jic并不明显)。

这并不是你问题的直接答案,但以下链接可能对你有用:

  • 安卓开发者,JNI提示
  • 安卓开发者,为性能而设计

在第二个链接中,写有以下内容:

本机代码并不一定比Java更高效。一方面与Java本机转换和JIT相关的成本无法跨越这些边界进行优化。如果您正在分配本机资源(本地堆上的内存、文件描述符或其他),安排及时收集这些资源。您还需要为每个您希望运行的体系结构(而不是依赖于它具有JIT)。您甚至可能需要编译多个版本来满足您的需求相同的体系结构:中为ARM处理器编译的本机代码G1无法充分利用Nexus One中的ARM为Nexus One中的ARM编译的程序不会在G1中的ARM上运行。

当您有一个现有的本机代码时,本机代码主要是有用的你想移植到Android的代码库,而不是用于"加速"部件Java应用程序。

如果你还处于游戏开发的早期阶段,你可以考虑使用一个提供良好碰撞检测机制的游戏引擎,比如Libgdx,它在box2d碰撞检测方面做得相当好。

最新更新