2010-07-28 24 views
8

Estoy escribiendo una aplicación que utiliza el algoritmo Dijkstra para encontrar rutas mínimas en el gráfico. Los pesos de los nodos y los bordes en el gráfico son float números, por lo que el algoritmo realiza muchas aritméticas en números float. ¿Puedo ganar un tiempo de ejecución? ¿Mejorar si convierto todo el peso en int s? ¿Las operaciones aritméticas int son más rápidas en Java que las flotantes?int vs float eficiencia aritmética en Java

Intenté escribir un punto de referencia simple para verificarlo, pero no estoy satisfecho con los resultados que obtuve. Posiblemente el compilador ha optimizado algunas partes del programa para que los resultados no se vean bien para mí.


EDIT:

El problema que estoy tratando de resolver es en el campo de la recuperación de información. La aplicación debe mostrar las respuestas a una consulta planteada como un conjunto de palabras clave.

Mi estructura de datos es un gráfico dirigido ponderado. Dado un conjunto de nodos hoja, tengo que encontrar un árbol más pequeño que conecte estos nodos y muestre la respuesta al usuario. Los pesos son asignados por una función de ponderación basada parcialmente en la técnica tf/idf. El usuario no sabe qué pesos le asigno a los nodos y los bordes, solo quiere que las respuestas sean relevantes para la consulta que planteó. Por lo tanto, no se requieren resultados exactos, solo la posibilidad de enumerar las respuestas de acuerdo con su ponderación. Solo el uso nativo de la función de ponderación (como lo mencioné se basa en tf/idf) da pesos de flotación, así que utilicé flotadores hasta el momento.

Espero que esto agregue algunos antecedentes a la pregunta.

+0

¿Cuál fue el resultado de todos modos? – Amarghosh

+0

Me di cuenta de que la multiplicación de las cifras es un poco más rápido, alrededor del 13%, pero comparar dos entradas es más lento, aproximadamente un 22%. – jutky

+0

No estoy del todo seguro, pero para Dijkstra, solo las operaciones de suma y comparación serían suficientes. Y para esas operaciones, no debería variar tanto para float o int. Estoy realmente sorprendido de que la comparación entera sea un 22% más lenta. ¿Puedo aprender qué tipo de evaluación comparativa llevó a cabo? – tafa

Respuesta

1

Como siempre con este tipo de cosas, debe establecerse unos objetivos de rendimiento, y luego perfilar la aplicación para ver si los cumple.

Muchas veces puede encontrar resultados sorprendentes; que el tiempo tomado casi no se ve afectado por el tipo numérico básico, o que su algoritmo es subóptimo.

Y en cuanto a las optimizaciones del compilador, son una parte real y válida de la optimización del rendimiento.

Si usar el tipo A es teóricamente más rápido que usar el tipo B, pero su compilador puede optimizar el tipo B para ser más rápido en un escenario real, entonces esa es una evidencia valiosa, no fuente de decepción.

+2

Solo quería saber si el rendimiento mejora, puedo ganar el tiempo que me llevaría cambiar bastante buena parte de la aplicación. Pero parece que no puedo saberlo con seguridad de antemano, y la mejor manera de verificar esto es implementar dos versiones del algoritmo y medir los tiempos de ejecución. – jutky

2

para operaciones simples int es más rápido, sin embargo, con int puede tener que hacer más trabajo para obtener el mismo resultado. p.ej.

como flotador

float f = 15 * 0.987; 

como int

int i = 15 * 987/1000; 

La división adicional significa la operación int puede tomar más tiempo.

+0

en algoritmo de Dijkstra Acabo de resumir y comparar el peso de las rutas, por lo que la operación de devision es bastante posterior para mí. ¿Cómo se puede saber que para las operaciones simples las operaciones son más rápidas, es una escena común o puede indicarme algo de literatura sobre el tema. – jutky

+1

necesita ver el código nativo generado por la JVM y comparar los ciclos del reloj. Sin embargo, ambas operaciones son bastante rápidas en comparación con el costo de fallas de caché y llamadas al sistema. Es muy probable que la elección del tipo de datos no haga mucha diferencia. –

-6

No lo creo.

Float is 4 byte. Y el Int en Java también es de 4 bytes.

¿Por qué no utilizar la fecha (java.util.Date) para obtener el tiempo de ejecución?

Puede definir un gráfico que tenga 100000 nodos propios. Luego, calcúlalo.

+0

Es posible que desee utilizar la palabra "byte" en lugar de "bit". Un entero de 4 * bits solo puede contener dieciséis valores distintos ... –

+0

Lo siento ... Mi inglés es pobre. Así que usé la palabra incorrecta. (Mi lengua materna no es el inglés) En realidad, creo que si la velocidad es más rápido que flotar, es porque el hardware. En la física, la int puede ser más fácil de lograr que flotar. – rainisic

0

Si solo desea comparar pesos, debe preferir que int flote.

+0

El mismo comentario que para otra respuesta: es una escena común o puede indicarme algo de literatura sobre el tema. – jutky

0

Por lo general, no debe preocuparse por una elección entre int y float por motivos de rendimiento.

Aquí es un extracto del Apéndice de Java Puzzlers:

aritmética de punto flotante es inexacta. No use punto flotante donde se requieran resultados exactos; en su lugar, use un tipo integral o BigDecimal. Prefiera double a float.

A menos que tenga una muy buena razón, generalmente se debe preferir a doublefloat si debe utilizar la operación de punto flotante. Si se desea obtener el resultado exacto, siga adelante y use BigDecimal; será más lento ya que no es primitivo, pero a menos que el perfil demuestre que no es aceptable, esta es a menudo la mejor opción.

Si debe usar la operación de punto flotante, entonces tratar de optimizar esto usando int es desacertado. Es probable que esto sea una optimización prematura y solo complicará el código innecesario. Escríbelo de la forma más natural y legible. No complique innecesariamente su código por el simple aumento de rendimiento.

Si no necesita el funcionamiento en coma flotante, utilice int o long en su lugar.

+0

Vea también http://stackoverflow.com/questions/2550281/floating-point-vs-integer-calculations-on-modern-hardware y http://stackoverflow.com/questions/2010252/float-versus-integer-arithmetic -performance-on-modern-chips – polygenelubricants

+0

He añadido algunos antecedentes a la pregunta. espero que aclare algunas cosas. Gracias por los enlaces. – jutky

0

Creo que el rendimiento depende en gran medida del algoritmo y la plataforma en la que se ejecuta el software.

Si está haciendo cálculos de matrices/matrices en una plataforma X86, el tiempo de ejecución podría optimizarlo para usar SSE, que es un conjunto de instrucciones ampliado flotante/doble solamente.

En otras plataformas, el tiempo de ejecución podría optimizarse para OpenCL (no creo que nadie haga eso ahora, pero podría suceder :). No tengo idea de qué es lo que funciona más rápido en una plataforma así, y bajo qué condiciones. Puede ser que OpenCL esté optimizado para una carga de trabajo entera.

Bajo estas circunstancias, concluiría que no es útil optimizar el tipo de datos (flotante o int) en este punto, y simplemente optimizar la legibilidad del código.

Si su código es de alto rendimiento crítico, y usted sabe exactamente en qué hardware se ejecutará el sistema ahora y en el futuro, puede probar cargas de trabajo típicas con varios algoritmos y seleccionar el que mejor se adapte a sus necesidades.

Pero, en general, solo use un algoritmo que pueda entender, mantenga el código legible y, por lo tanto, el número de errores sea bajo. El código rápido no vale tanto si los resultados no son correctos :)

1

Las restas enteras son ~ 2,5 veces más rápidas que las restas dobles, en mi máquina. Las multiplicaciones enteras, sin embargo, son solo ~ 1.5 veces más rápidas que las multiplicaciones dobles.

La siguiente prueba funciona con datos aleatorios, lo que puede evitar que el compilador optimice.

// test whether int subs are faster than double subs 
public void compareIntAndFloatSubtraction(){ 

    int N = 100000; // input array size 
    int k = 100000; // number of mathematical operations performed on each element 

    // generate random data 
    int[] ints = new int[N]; 
    double[] doubles = new double[N]; 
    Random r = new Random(1l); 
    for (int i = 0; i < N; i++) { 
     ints[i] = r.nextInt(); 
     doubles[i] = r.nextDouble(); 
    } 

    // measure integer subtractions 
    long before = System.currentTimeMillis(); 
    for (int i = 1; i < N; i++) { 
     for (int j = 0; j < k; j++) { 
      ints[i] -= ints[i-1]; // referring to another element might prevent from optimization also 
     } 
    } 
    System.out.println(String.format("time needed for int subs [ms]: %s", System.currentTimeMillis()-before)); 

    // measure double subtractions 
    before = System.currentTimeMillis(); 
    for (int i = 1; i < N; i++) { 
     for (int j = 0; j < k; j++) { 
      doubles[i] -= doubles[i-1]; 
     } 
    } 
    System.out.println(String.format("time needed for double subs [ms]: %s", System.currentTimeMillis()-before)); 

} 
+1

Gracias por la prueba. Ejecutarlo para pruebas de resta en mi máquina (Xeon, 64 bits, Windows, Java 1.8) rindió: tiempo necesario para int subs [ms]: 7704, tiempo necesario para subs dobles [ms]: 10869. Estaba más interesado en mayor que y menos que las operaciones, porque lo estaba probando para su uso en la construcción de un TreeMap. El resultado de las operaciones de comparación/* if (dobles [i] dobla [i-1]) tmp ++; */were: tiempo necesario para int cmp [ms]: 8479, tiempo necesario para el doble cmp [ms]: 15925. – Henry

+1

FYI, ejecuté la prueba nuevamente para el tipo de datos "largo". El resultado para la resta: tiempo necesario para subs largos [ms]: 7513, tiempo necesario para subs dobles [ms]: 10898. Y el resultado para comparaciones: tiempo necesario para cmp largo [ms]: 15768, tiempo necesario para doble cmp [ ms]: 16012. Para "longs", parece que el rendimiento es similar para los controles de igualdad. – Henry

+0

No puede realizar el benchmark en la misma ejecución para diferentes tipos de valores, la JVM estará caliente –