Dear Zufar and Kirill,
let me summarise the options for calling native code from Java on MOE.
You essentially have 2 options: JNI and Nat/J.
Unlike RoboVM’s Bro, Nat/J is a layer on top of JNI, not a parallel solution. The reason for this design decision is that we wanted Nat/J to work with any JNI compatible VM, not just the one used by MOE. For example we use Nat/J with the Oracle VM in the device launcher and in the Nat/J Generator (to bind libclang to Java).
This means, that in general there is no shortcut in Nat/J around JNI. If you already have JNI code (e.g. for use on Android), that should work with the same performance characteristics on iOS with MOE, since the very same code (ART VM) will be executed on both platforms.
We also need to keep in mind, that ART includes a lot of advanced features, e.g. compacting and concurrent GC, that makes such things like “just give me the address of the object” impossible (or just very dangerous).
One thing you can try to do is to enable “Fast JNI” mode for specific, performance critical JNI functions. This is an extension of ART, so it should work on both Android and iOS with MOE. It is kind of a hidden feature of ART, here is the comment from the ART source code (jni_internal.cc):
> // Notes about fast JNI calls:
> //
> // On a normal JNI call, the calling thread usually transitions
> // from the kRunnable state to the kNative state. But if the
> // called native function needs to access any Java object, it
> // will have to transition back to the kRunnable state.
> //
> // There is a cost to this double transition. For a JNI call
> // that should be quick, this cost may dominate the call cost.
> //
> // On a fast JNI call, the calling thread avoids this double
> // transition by not transitioning from kRunnable to kNative and
> // stays in the kRunnable state.
> //
> // There are risks to using a fast JNI call because it can delay
> // a response to a thread suspension request which is typically
> // used for a GC root scanning, etc. If a fast JNI call takes a
> // long time, it could cause longer thread suspension latency
> // and GC pauses.
> //
> // Thus, fast JNI should be used with care. It should be used
> // for a JNI call that takes a short amount of time (eg. no
> // long-running loop) and does not block (eg. no locks, I/O,
> // etc.)
> //
> // A ‘!’ prefix in the signature in the JNINativeMethod
> // indicates that it’s a fast JNI call and the runtime omits the
> // thread state transition from kRunnable to kNative at the
> // entry.
When we tested the fast JNI mode, we measured up to 10x performance increase with short JNI functions, so it might be worth a try for you. But keep in mind that this is very platform specific and should be used with care to avoid any unwanted side effects.
Another option can be to use direct nio Buffers (possibly allocated from native code) instead of Java arrays. This can avoid GC overhead completely.
Finally, we are not opposed to adding platform specific optimizations to Nat/J or the MOE runtime. We just always want to make sure that there is an alternative code path that will work on other Java runtimes.
For example: we might introduce a @FastJNI annotation in the future, that will use the fast JNI mode on ART VM and do nothing on other VMs.
Best Regards,
Gergely