round redondear decimales java performance profiling floor

redondear - math.random java



¿Significa esto que Java Math.floor es extremadamente lento? (5)

A continuación, le ofrecemos una comprobación de validez para su hipótesis de que el código realmente está gastando el 99% de su tiempo en el floor . Supongamos que tiene versiones de Java y C ++ del algoritmo que son correctas en términos de los resultados que producen. Por el bien del argumento, supongamos que las dos versiones llaman a las funciones de floor equivalentes el mismo número de veces. Así que una función de tiempo es

t(input) = nosFloorCalls(input) * floorTime + otherTime(input)

donde floorTime es el tiempo necesario para realizar una llamada al floor en la plataforma.

Ahora, si su hipótesis es correcta y floorTime es mucho más costoso en Java (en la medida en que toma aproximadamente el 99% del tiempo de ejecución), entonces esperaría que la versión Java de la aplicación se ejecute en un factor importante (50 veces o más). ) más lento que la versión C ++. Si no ve esto, lo más probable es que su hipótesis sea falsa.

Si la hipótesis es falsa, aquí hay dos explicaciones alternativas para los resultados del perfil.

  1. Esta es una anomalía de medida; es decir, el perfilador de alguna manera se ha equivocado. Intenta usar un perfilador diferente.

  2. Hay un error en la versión Java de su código que está causando que llame a floor muchas, muchas más veces que en la versión C ++ del código.

No tengo mucho Java.

Estoy escribiendo un código matemático optimizado y me sorprendieron los resultados de mi perfil. Mi código recopila valores, intercala los datos y luego elige los valores basados ​​en eso. Java se ejecuta más lento que mis implementaciones de C ++ y MATLAB.

Estoy usando javac 1.7.0_05 Estoy usando el JDK 1.7.05 de Sun / Oracle

Existe una función de piso que realiza una tarea relevante en el código.

  1. ¿Alguien sabe de la manera paradigmática de arreglar esto?
  2. Noté que mi función floor() está definida con algo llamado StrictMath . ¿Hay algo como -ffast-math para Java? Espero que haya una manera de cambiar la función de piso a algo más computacionalmente razonable sin escribir el mío.

    public static double floor(double a) { return StrictMath.floor(a); // default impl. delegates to StrictMath }

Editar

Así que algunas personas sugirieron que tratara de hacer un reparto. Intenté esto y no hubo absolutamente ningún cambio en el tiempo en la pared.

private static int flur(float dF) { return (int) dF; }

413742 función de piso fundido

394675 Math.floor

Estas pruebas se realizaron sin el perfilador. Se hizo un esfuerzo para usar un generador de perfiles pero el tiempo de ejecución se modificó drásticamente (más de 15 minutos, así que lo dejé).


En primer lugar: su generador de perfiles muestra que está gastando el 99% del tiempo de la CPU en la función de piso. Esto no indica que el piso sea lento. Si no haces nada más que piso () eso es totalmente sano. Sin embargo, dado que otros lenguajes parecen implementar un piso más eficiente, su suposición puede ser correcta.

Sé por la escuela que una implementación ingenua de floor (que funciona solo para números positivos y es única para los negativos) se puede realizar lanzando a un entero / largo. Eso es lenguaje agnóstico y algún tipo de conocimiento general de los cursos de CS.

Aquí hay algunos micro bancos. Funciona en mi máquina y respalda lo que aprendí en la escuela;)

rataman@RWW009 ~/Desktop $ javac Cast.java && java Cast 10000000 Rounds of Casts took 16 ms rataman@RWW009 ~/Desktop $ javac Floor.java && java Floor 10000000 Rounds of Floor took 140 ms #

public class Cast/Floor { private static final int ROUNDS = 10000000; public static void main(String[] args) { double[] vals = new double[ROUNDS]; double[] res = new double[ROUNDS]; // awesome testdata for(int i = 0; i < ROUNDS; i++) { vals[i] = Math.random() * 10.0; } // warmup for(int i = 0; i < ROUNDS; i++) { res[i] = floor(vals[i]); } long start = System.currentTimeMillis(); for(int i = 0; i < ROUNDS; i++) { res[i] = floor(vals[i]); } System.out.println(ROUNDS + " Rounds of Casts took " + (System.currentTimeMillis() - start) +" ms"); } private static double floor(double arg) { // Floor.java return Math.floor(arg); // or Cast.java return (int)arg; }

}


Es posible que desee dar un intento de FastMath .

Aquí hay una publicación sobre el desempeño de Math en Java vs. Javascript . Hay algunos buenos consejos sobre por qué la liberación de matemáticas predeterminada es lenta. Están discutiendo otras operaciones aparte del floor , pero creo que sus hallazgos pueden ser generalizados. Lo encontre interesante.

EDITAR

De acuerdo con esta entrada de error , floor se ha implementado un código Java puro en 7 (b79), 6u21 (b01) que resulta en un mejor rendimiento. El código de piso en el JDK 6 todavía es un poco más largo que el de FastMath , pero puede que no sea responsable de tal perf. degradación. ¿Qué JDK estás usando? ¿Podrías probar con una versión más reciente?


Vale la pena señalar que la supervisión de un método conlleva cierta sobrecarga y, en el caso de VisualVM, esto es bastante alto. Si tiene un método que se llama a menudo pero hace muy poco, puede parecer que usa mucha CPU. por ejemplo, he visto Integer.hashCode () como un gran bateador una vez. ;)

En mi máquina, un piso toma menos 5.6 ns, pero un yeso toma 2.3 ns. Es posible que desee probar esto en su máquina.

A menos que necesite manejar casos de esquina, un modelo simple es más rápido.

// Rounds to zero, instead of Negative infinity. public static double floor(double a) { return (long) a; }

public static void main(String... args) { int size = 100000; double[] a = new double[size]; double[] b = new double[size]; double[] c = new double[size]; for (int i = 0; i < a.length; i++) a[i] = Math.random() * 1e6; for (int i = 0; i < 5; i++) { timeCast(a, b); timeFloor(a, c); for (int j = 0; j < size; j++) if (b[i] != c[i]) System.err.println(a[i] + ": " + b[i] + " " + c[i]); } } public static double floor(double a) { return a < 0 ? -(long) -a : (long) a; } private static void timeCast(double[] from, double[] to) { long start = System.nanoTime(); for (int i = 0; i < from.length; i++) to[i] = floor(from[i]); long time = System.nanoTime() - start; System.out.printf("Cast took an average of %.1f ns%n", (double) time / from.length); } private static void timeFloor(double[] from, double[] to) { long start = System.nanoTime(); for (int i = 0; i < from.length; i++) to[i] = Math.floor(from[i]); long time = System.nanoTime() - start; System.out.printf("Math.floor took an average of %.1f ns%n", (double) time / from.length); }

huellas dactilares

Cast took an average of 62.1 ns Math.floor took an average of 123.6 ns Cast took an average of 61.9 ns Math.floor took an average of 6.3 ns Cast took an average of 47.2 ns Math.floor took an average of 6.5 ns Cast took an average of 2.3 ns Math.floor took an average of 5.6 ns Cast took an average of 2.3 ns Math.floor took an average of 5.6 ns


Math.floor() es increíblemente rápido en mi máquina en alrededor de 7 nanosegundos por llamada en un circuito cerrado. (Windows 7, Eclipse, Oracle JDK 7). Espero que sea muy rápido en casi todas las circunstancias y me sorprendería mucho si resultara ser el cuello de botella.

Algunas ideas:

  • Yo sugeriría volver a ejecutar algunos puntos de referencia sin un perfilador en ejecución . A veces sucede que los perfiladores crean una sobrecarga falsa cuando instrumentan el binario, especialmente para funciones pequeñas como Math.floor() que probablemente estén en línea.
  • Pruebe un par de JVM diferentes, es posible que haya golpeado un error oscuro
  • Pruebe la clase FastMath en la excelente biblioteca de matemáticas de Apache Commons , que incluye una nueva implementación de floor. Me sorprendería mucho si es más rápido, pero nunca se sabe.
  • Compruebe que no está ejecutando ninguna tecnología de virtualización o similar que pueda estar interfiriendo con la capacidad de Java para llamar a código nativo (que se utiliza en algunas de las funciones java.lang.Math , incluyendo Math.floor() )