java - Rendimiento JNI vs. JNA
c concurrency (4)
Tenemos una aplicación c/asm
nativa que utiliza GPU (OpenCL) para un gran encrypt/decrypt
datos con un método específico, y simplemente funciona perfecto, no hay problema. Una parte del proyecto (web y distribución) ha sido desarrollada por JEE
, y solo necesitamos llamar a la aplicación / biblioteca nativa.
Hemos intentado llamarlo como un proceso externo separado utilizando la clase de Process
. El problema es que no podemos controlar la aplicación (evento, manejadores, hilos, etc.). También tratamos de simplemente cambiar el código C en código Java, pero el rendimiento murió. Excepto que ejecuta el código nativo como proceso, estoy pensando en JNA y JNI, pero hay algunas preguntas.
Preguntas:
- Para una mejor (más rápida) solución de lectura / escritura, ¿es posible intercambiar datos por memoria directa (no administrada) [Java (
ByteBuffer#allocateDirect()
)] en JNI y JNA? - ¿Es posible gestionar y gestionar el proceso por código nativo y acceder a la memoria de la GPU (compartida) a través del código Java (OpenCL lib)?
- ¿Qué hay del rendimiento? ¿JNA es más rápido que JNI?
Tenemos dos dispositivos agrupados AMD W7000 en Redhat Linux6 x64.
El gran número de crujidos se realiza en C / GPU, toda su interfaz Java <-> C es mezclar datos de entrada / salida. Me sorprendería si esto es un cuello de botella.
En cualquier caso, escriba el código más simple y claro que hace el trabajo. Si resulta que el rendimiento no es suficiente, mida dónde están los cuellos de botella, y hágalo uno por uno hasta que el rendimiento sea correcto. El tiempo del programador es mucho más valioso que el tiempo de la computadora, excepto en circunstancias muy especiales.
JNA es mucho más lento que JNI, pero mucho más fácil. Si el rendimiento no es un problema, use JNA.
El uso de búferes directos tiene la ventaja de que las operaciones más críticas no utilizan JNI o JNA y, por lo tanto, son más rápidas. Usan intrínseco cuando significa que se convierten en instrucciones de código de máquina única.
Si el código de Java es significativamente más lento que C, es probable que el código no se haya optimizado lo suficiente. En general, la GPU debería hacer todo el trabajo, por lo que si Java es un poco más lenta, esto no debería hacer mucha diferencia.
por ejemplo, si pasas el 99% del tiempo en la GPU y Java tarda el doble, el total será 99 + 2% o 1% más lento.
Desarrollé un dll simple y puse una función vacía que no hace nada. Luego llamé a esa función desde dll con JNA y también JNI, así que traté de calcular el costo de llamarlos. Al observar el rendimiento después de muchas llamadas, JNI era 30-40 veces más rápido que JNA.
De las preguntas frecuentes oficiales de JNA :
¿Cómo se compara el rendimiento de JNA con el JNI personalizado?
El mapeo directo de JNA puede proporcionar un rendimiento cercano al de JNI personalizado. Casi todas las características de mapeo de tipos de mapeo de interfaz están disponibles, aunque la conversión de tipo automático probablemente incurrirá en gastos indirectos.
La sobrecarga de llamadas para una sola llamada nativa utilizando la asignación de interfaz JNA puede ser un orden de magnitud (~ 10X) mayor que el JNI personalizado equivalente (si es realmente diferente en el contexto de su aplicación). En términos brutos, la sobrecarga de llamadas es del orden de cientos de microsegundos en lugar de decenas de microsegundos. Tenga en cuenta que esa es la sobrecarga de la llamada, no el tiempo total de la llamada. Esta magnitud es típica de la diferencia entre sistemas que usan información de tipo mantenida dinámicamente y sistemas donde la información de tipo se compila estáticamente. La información de tipo de códigos duros JNI en la invocación del método, donde la asignación de interfaz JNA determina dinámicamente la información de tipo en el tiempo de ejecución.
Puede esperar una aceleración de aproximadamente un orden de magnitud moviéndose al mapeo directo de JNA, y un factor de dos o tres moviéndose desde allí a JNI personalizado. La diferencia real variará según el uso y las firmas de función. Al igual que con cualquier proceso de optimización, primero debe determinar dónde necesita un aumento de velocidad y luego ver cuánta diferencia hay al realizar optimizaciones específicas. La facilidad de programar todo en Java normalmente supera las pequeñas ganancias de rendimiento cuando se usa JNI personalizado.