¿Desempeño de las bibliotecas matemáticas de Matriz Java?

Estamos calculando algo cuyo tiempo de ejecución está vinculado por las operaciones de la matriz. (A continuación, algunos detalles si están interesados.) Esta experiencia generó la siguiente pregunta:

¿La gente tiene experiencia con el rendimiento de las bibliotecas Java para las matemáticas matriciales (por ejemplo, multiplicar, invertir, etc.)? Por ejemplo:

  • JAMA
  • POTRO
  • Apache commons math

Busqué y no encontré nada.


Detalles de nuestra comparación de velocidad:

Estamos utilizando Intel FORTRAN (ifort (IFORT) 10.1 20070913). Lo hemos reimplementado en Java (1.6) usando las operaciones de matriz matemática Apache commons math 1.2, y está de acuerdo con todos sus dígitos de precisión. (Tenemos razones para quererlo en Java.) (Java dobles, Fortran real * 8). Fortran: 6 minutos, Java 33 minutos, misma máquina. jvisualm profiling muestra mucho tiempo invertido en RealMatrixImpl. {getEntry, isValidCoordinate} (que parece haber desaparecido en los juegos de Apache commons math 2.0, pero 2.0 no es más rápido). Fortran está utilizando rutinas Atlas BLAS (dpotrf, etc.).

Obviamente, esto podría depender de nuestro código en cada idioma, pero creemos que la mayoría del tiempo está en operaciones de matriz equivalentes.

En muchos otros cómputos que no involucran bibliotecas, Java no ha sido mucho más lento, y algunas veces mucho más rápido.

Solo para agregar mis 2 centavos. He comparado algunas de estas bibliotecas. Intenté multiplicar por matriz una matriz de dobles de 3000 por 3000 consigo mismo. Los resultados son los siguientes.

Usando ATLAS multiproceso con C / C ++, Octave, Python y R, el tiempo empleado fue de alrededor de 4 segundos.

Usando Jama con Java, el tiempo empleado fue de 50 segundos.

Usando Colt y Parallel Colt con Java, ¡el tiempo empleado fue de 150 segundos!

Al usar JBLAS con Java, el tiempo empleado fue de nuevo de alrededor de 4 segundos, ya que JBLAS usa ATLAS multiproceso.

Entonces, para mí, estaba claro que las bibliotecas de Java no funcionaban demasiado bien. Sin embargo, si alguien tiene que codificar en Java, entonces la mejor opción es JBLAS. Jama, Colt y Parallel Colt no son rápidos.

Soy el autor de Java Matrix Benchmark ( JMatBench ) y voy a dar mi opinión sobre esta discusión.

Hay una gran diferencia entre las bibliotecas de Java y aunque no hay un ganador claro en toda la gama de operaciones, hay algunos líderes claros como se puede ver en los últimos resultados de rendimiento (octubre de 2013).

Si está trabajando con matrices “grandes” y puede usar bibliotecas nativas, entonces el ganador claro (aproximadamente 3.5 veces más rápido) es MTJ con netlib optimizado para el sistema . Si necesita una solución pura de Java, MTJ , OjAlgo , EJML y Parallel Colt son buenas opciones. Para matrices pequeñas, EJML es el claro ganador.

Las bibliotecas que no mencioné mostraron importantes problemas de rendimiento o faltaban características clave.

Soy el autor principal de jblas y quería señalar que publiqué la versión 1.0 a finales de diciembre de 2009. Trabajé mucho en el paquete, lo que significa que ahora puede simplemente descargar un “contenedor de grasa” con las bibliotecas ATLAS y JNI. para Windows, Linux, Mac OS X, 32 y 64 bits (excepto para Windows). De esta forma obtendrá el rendimiento nativo simplemente agregando el archivo jar a su classpath. ¡Compruébalo en http://jblas.org !

Jeigen https://github.com/hughperkins/jeigen

  • envuelve la biblioteca de Eigen C ++ http://eigen.tuxfamily.org , que es una de las bibliotecas de C ++ gratuitas más rápidas disponibles
  • syntax relativamente escueta, por ejemplo, ‘mmul’, ‘sub’
  • maneja matrices densas y dispersas

Una prueba rápida, multiplicando dos matrices densas, es decir:

import static jeigen.MatrixUtil. *;

 int K = 100; int N = 100000; DenseMatrix A = rand(N, K); DenseMatrix B = rand(K, N); Timer timer = new Timer(); DenseMatrix C = B.mmul(A); timer.printTimeCheckMilliseconds(); 

Resultados:

 Jama: 4090 ms Jblas: 1594 ms Ojalgo: 2381 ms (using two threads) Jeigen: 2514 ms 
  • Comparado con jama, todo es más rápido 😛
  • En comparación con jblas, Jeigen no es tan rápido, pero maneja matrices dispersas.
  • En comparación con ojalgo, Jeigen tarda aproximadamente la misma cantidad de tiempo transcurrido, pero solo usa un núcleo, por lo que Jeigen usa la mitad de la CPU total. Jeigen tiene una syntax terser, es decir, ‘mmul’ frente a ‘multiplyRight’

Hay un punto de referencia de varios paquetes de matriz disponibles en java en http://code.google.com/p/java-matrix-benchmark/ para algunas configuraciones de hardware diferentes. Pero no es un sustituto para hacer su propio punto de referencia.

El rendimiento variará según el tipo de hardware que tenga (CPU, núcleos, memoria, caché L1-3, velocidad del bus), el tamaño de las matrices y los algoritmos que pretenda utilizar. Diferentes bibliotecas tienen diferentes aspectos de la concurrencia para diferentes algoritmos, por lo que no hay una sola respuesta. También puede encontrar que la sobrecarga de traducir a la forma esperada por una biblioteca nativa niega la ventaja de rendimiento para su caso de uso (algunas de las bibliotecas de Java tienen opciones más flexibles con respecto al almacenamiento matricial, que pueden usarse para optimizaciones de rendimiento adicionales).

Sin embargo, en general, JAMA, Jampack y COLT están envejeciendo y no representan el estado del rendimiento actual disponible en Java para el álgebra lineal. Las bibliotecas más modernas hacen un uso más efectivo de múltiples núcleos y cachés de CPU. JAMA fue una implementación de referencia, y prácticamente implementa algoritmos de libros de texto con poca atención al rendimiento. COLT e IBM Ninja fueron las primeras bibliotecas de Java en mostrar que el rendimiento era posible en Java, incluso si se retrasaban 50% de las bibliotecas nativas.

Acabo de comparar Apache Commons Math con jlapack.

Prueba: descomposición de valor singular de una matriz aleatoria de 1024×1024.

Máquina: Intel (R) Core (TM) 2 Duo CPU E6750 a 2.66 GHz, Linux x64

Código de octava: A = rand (1024); tic; [U, S, V] = svd (A); toc

 tiempo de ejecución de resultados
 -------------------------------------------------- -------
 Octava 36.34 sec

 JDK 1.7u2 64bit
     jlapack dgesvd 37.78 sec
     Apache commons math SVD 42.24 sec


 JDK 1.6u30 64bit
     jlapack dgesvd 48.68 sec
     apache commons math SVD 50.59 sec

 Rutinas nativas
 Lapack * invocado desde C: 37.64 sec
 Intel MKL 6.89 seg (!)

Mi conclusión es que jlapack llamado desde JDK 1.7 está muy cerca del rendimiento binario nativo de lapack. Usé la biblioteca binaria lapack que viene con la distribución de Linux e invoqué la rutina dgesvd para obtener las matrices U, S y VT también. Todas las pruebas se realizaron con doble precisión en exactamente la misma matriz en cada ejecución (excepto Octave).

Descargo de responsabilidad: no soy un experto en álgebra lineal, no estoy afiliado a ninguna de las bibliotecas anteriores y este no es un punto de referencia riguroso. Es una prueba “hecha en casa”, ya que me interesaba comparar el aumento del rendimiento de JDK 1.7 a 1.6, así como SVD de matemáticas comunes con jlapack.

Realmente no puedo comentar sobre bibliotecas específicas, pero en principio hay pocas razones para que tales operaciones sean más lentas en Java. Hotspot generalmente hace los tipos de cosas que uno espera que haga un comstackdor: comstack operaciones matemáticas básicas en variables Java con las instrucciones de la máquina correspondientes (usa instrucciones SSE, pero solo una por operación); los accesos a los elementos de una matriz se comstackn para usar instrucciones MOV “en bruto” como cabría esperar; toma decisiones sobre cómo asignar variables a los registros cuando puede; reordena instrucciones para aprovechar la architecture del procesador … Una posible excepción es que, como he mencionado, el Hotspot solo realizará una operación por instrucción SSE; en principio, podría tener una biblioteca de matriz fantásticamente optimizada que realiza múltiples operaciones por instrucción, aunque no sé si, por ejemplo, su biblioteca FORTRAN particular lo hace o si tal biblioteca existe. Si lo hace, actualmente no hay forma de que Java (o al menos, Hotspot) compita con eso (aunque por supuesto podría escribir su propia biblioteca nativa con esas optimizaciones para llamar desde Java).

Entonces, ¿qué significa todo esto? Bien:

  • en principio, vale la pena buscar una biblioteca de mejor rendimiento, aunque desafortunadamente no puedo recomendar uno
  • si el rendimiento es realmente crítico para usted, consideraría simplemente codificar sus propias operaciones matriciales, porque entonces puede realizar ciertas optimizaciones que una biblioteca generalmente no puede, o que una biblioteca particular que su biblioteca no usa (si tiene una máquina multiprocesador, averigüe si la biblioteca es en realidad multiprocesador)

Un obstáculo para las operaciones matriciales es a menudo problemas de ubicación de datos que surgen cuando se necesita recorrer fila por fila y columna por columna, por ejemplo, en la multiplicación de matrices, ya que debe almacenar los datos en un orden que optimice uno u otro. Pero si escribe manualmente el código, a veces puede combinar operaciones para optimizar la ubicación de los datos (por ejemplo, si está multiplicando una matriz por su transformación, puede convertir un cruce de columnas en un cruce de filas si escribe una función dedicada en lugar de combinar dos funciones de biblioteca). Como es habitual en la vida, una biblioteca le ofrecerá un rendimiento no óptimo a cambio de un desarrollo más rápido; debes decidir qué tan importante es para ti el rendimiento.

Hemos utilizado COLT para algunos cálculos financieros serios bastante grandes y hemos estado muy contentos con ello. En nuestro código altamente perfilado, casi nunca hemos tenido que reemplazar una implementación de COLT por una propia.

En sus propias pruebas (obviamente no independientes), creo que reclaman un factor de 2 de las rutinas ensambladoras Intel optimizadas a mano. El truco para usarlo bien es asegurarse de comprender su filosofía de diseño y evitar la asignación de objetos extraños.

El código de Linalg que depende en gran medida de Pentiums y las capacidades de computación vectorial de los procesadores posteriores (comenzando con las extensiones de MMX, como LAPACK y ahora Atlas BLAS) no está “fantásticamente optimizado”, sino simplemente estándar de la industria. Para replicar ese rendimiento en Java, necesitará bibliotecas nativas. He tenido el mismo problema de rendimiento que usted describe (principalmente, para poder calcular descomposiciones de Choleski) y no he encontrado nada realmente eficiente: Jama es pura Java, ya que se supone que es solo una plantilla y un kit de referencia para los implementadores. .. que nunca sucedió Conoces los recursos comunes matemáticos de Apache … En cuanto a COLT, todavía tengo que probarlo, pero parece depender en gran medida de las mejoras de Ninja, la mayoría de las cuales se lograron mediante la creación de un comstackdor de Java ad hoc, así que dudo que vaya a ayudar. En ese momento, creo que “solo” necesitamos un esfuerzo colectivo para construir una implementación nativa de Jama …

Basándose en la publicación de Varkhan, el código nativo específico de Pentium sería mejor:

Es posible que desee verificar el proyecto jblas . Es una biblioteca Java relativamente nueva que usa BLAS, LAPACK y ATLAS para operaciones de matriz de alto rendimiento.

El desarrollador ha publicado algunos puntos de referencia en los que jblas sale favorablemente en contra de MTJ y Colt.

Soy el autor de la biblioteca la4j (Linear Algebra for Java) y este es mi punto. He estado trabajando en la4j durante 3 años (la última versión es 0.4.0 [01 Jun 2013]) y solo ahora puedo comenzar a realizar análisis de rendimiento y optimizaciones ya que acabo de cubrir el funcional mínimo requerido. Entonces, la4j no es tan rápido como yo quería, pero estoy gastando mucho de mi tiempo para cambiarlo.

Actualmente estoy en el medio de portar la nueva versión de la4j a la plataforma JMatBench . Espero que la nueva versión muestre un mejor rendimiento que la anterior, ya que hay varias mejoras que hice en la4j, como un formato de matriz interna mucho más rápido, accesos inseguros y un algoritmo de locking rápido para las multiplicaciones de matrices.

¿Has echado un vistazo a la biblioteca Intel Math Kernel ? Afirma superar incluso a ATLAS . MKL se puede usar en Java a través de envoltorios JNI.

También hay UJMP

Para las aplicaciones de gráficos 3D, la implementación del vector lwjgl.util superó a las jblas antes mencionadas por un factor de aproximadamente 3.

He hecho 1 millón de multiplicaciones de matriz de un vec4 con una matriz de 4×4.

lwjgl terminó en aproximadamente 18ms, jblas requirió aproximadamente 60ms.

(Supongo que el enfoque JNI no es muy adecuado para la aplicación rápida y sucesiva de multiplicaciones relativamente pequeñas. Dado que la traducción / mapeo puede llevar más tiempo que la ejecución real de la multiplicación).

Hay muchas bibliotecas de álgebra lineal de Java disponibles libremente. http://www.ujmp.org/java-matrix/benchmark/ Lamentablemente, el punto de referencia solo le brinda información sobre la multiplicación de matrices (con la transposición de la prueba no se permite que las diferentes bibliotecas exploten sus respectivas características de diseño).

Lo que debe observar es cómo funcionan estas bibliotecas de álgebra lineal cuando se le pide que calcule varias descomposiciones de matriz. http://ojalgo.org/matrix_compare.html

He descubierto que si está creando muchas matrices de alta dimensión, puede hacer que Jama sea aproximadamente un 20% más rápido si lo cambia para usar una matriz dimensional única en lugar de una matriz bidimensional. Esto se debe a que Java no es compatible con matrices multidimensionales de manera eficiente. es decir. crea una matriz de matrices.

Colt ya lo hace, pero he descubierto que es más complicado y más poderoso que Jama, lo que puede explicar por qué las funciones simples son más lentas con Colt.

La respuesta realmente depende de lo que estés haciendo. Jama no admite una fracción de las cosas que Colt puede hacer que hacen una gran diferencia.

Matrix Tookits Java (MTJ) ya se mencionó anteriormente, pero tal vez valga la pena mencionarlo nuevamente para cualquier persona que se tropiece con este hilo. Para los interesados, parece que también se habla de que MTJ reemplace la biblioteca linalg en apache commons math 2.0 , aunque no estoy seguro de cómo está progresando últimamente.

Debe agregar Apache Mahout a su lista de compras.