Medida doble vs BigDecimal

Escribí un punto de referencia simple que prueba el rendimiento de multiplicar dobles frente a BigDecimal. ¿Es correcto mi método? Utilizo valores aleatorios porque el compilador optimizó las constantes de multiplicación muchas veces (por ejemplo, Math.PI * Math.E).
Pero:
- No sé si generar números aleatorios dentro de una prueba corrompe el resultado.
- Lo mismo para crear nuevos BigDecimal objetos dentro de una prueba.

Solo quiero probar el rendimiento de la multiplicación (no el tiempo utilizado por el constructor).

¿Cómo puede hacerse esto?

import java.math.*;
import java.util.*;

public class DoubleVsBigDecimal
{
    public static void main(String[] args)
    {
        Random rnd = new Random();
        long t1, t2, t3;
        double t;

        t1 = System.nanoTime();

        for(int i=0; i<1000000; i++)
        {
            double d1 = rnd.nextDouble();
            double d2 = rnd.nextDouble();
            t = d1 * d2;
        }

        t2 = System.nanoTime();

        for(int i=0; i<1000000; i++)
        {
            BigDecimal bd1 = BigDecimal.valueOf(rnd.nextDouble());
            BigDecimal bd2 = BigDecimal.valueOf(rnd.nextDouble());
            bd1.multiply(bd2);
        }

        t3 = System.nanoTime();

        System.out.println(String.format("%f",(t2-t1)/1e9));
        System.out.println(String.format("%f",(t3-t2)/1e9));
        System.out.println(String.format("%f",(double)(t3-t2)/(double)(t2-t1)));
    }
}

preguntado el 22 de mayo de 12 a las 20:05

4 Respuestas

No solo estás cronometrando la operación de multiplicación, también estás cronometrando otras cosas.

Necesitas hacer algo como:

    long time = 0; 
    for(int i=0; i<1000000; i++) {
        double d1 = rnd.nextDouble();
        double d2 = rnd.nextDouble();
        long start = System.nanoTime();
        t = d1 * d2;
        long end = System.nanoTime();
        time += (end-start)
    }
    long meantime = time / 1000000;

entonces probablemente también calcule el error estándar. Además, probablemente necesitará calentar el jvm con algunos cálculos primero antes de comenzar; de lo contrario, obtendrá algunos valores altos al comienzo.

contestado el 22 de mayo de 12 a las 20:05

Is long start = System.nanoTime(); t = d1 * d2; long end = System.nanoTime(); suficientemente preciso? Si es así, no hay necesidad de un bucle. En realidad, si no es el bucle, no lo hornees con más precisión. - enero ajan

no, necesita el promedio, para asegurarse de que no fue solo por casualidad que obtuvo esa lectura, Robert

La evaluación comparativa en Java es Muy raro. Por ejemplo, la JVM en realidad no optimizará por completo una pieza de código hasta que ya se haya ejecutado muchas veces, pero es más justo realizar la medición después de esa optimización, porque cualquier sistema de producción llamará a este método muchas veces.

Hay un montón de otras trampas con la evaluación comparativa de Java. Probablemente la forma más sencilla de evitarlos es usar una herramienta de evaluación comparativa de Java creada por expertos, por ejemplo Calibrar.

contestado el 22 de mayo de 12 a las 20:05

Puede generar dos colecciones de 1000 dobles/BigDecimals por adelantado y multiplicar cada uno con cada uno en 2 bucles anidados:

public static void main(String[] args)
{
    Random rnd = new Random();
    List <Double> dl = new ArrayList <Double> ();
    List <BigDecimal> bdl = new ArrayList <BigDecimal> ();

    for(int i=0; i<1000; i++)
    {
        double d = rnd.nextDouble();
        dl.add (d);
        bdl.add (new BigDecimal (d));
    }

    long t1 = System.nanoTime();
    double t;
    for (double d1 : dl)
            for (double d2 : dl)
                t = d1 * d2;

    long t2 = System.nanoTime();

    for (BigDecimal b1 : bdl)
        for (BigDecimal b2 : bdl)
                b1.multiply (b2);

    long t3 = System.nanoTime();

    System.out.println (String.format ("%f", (t2 - t1) / 1e9));
    System.out.println (String.format ("%f", (t3 - t2) / 1e9));
    System.out.println (String.format ("%f", (double) (t3 - t2) / (double) (t2 - t1)));
} 

El primer código produjo valores bastante estables como este, cuando se repitió:

0,186755
10,970243
58,741445

Y mi código tiene valores diferentes, pero también estables:

0,077177
1,112490
14,414710

La diferencia en la relación es 1:4, que es bastante. No tanto, como la relación BigDecimal:doble, pero bueno ---

(i386-32, modo cliente, JRE-1.6, linux, oracle, 2Ghz Centrino single core).

contestado el 22 de mayo de 12 a las 20:05

Recomiendo encarecidamente este artículo como una introducción al benchmarking:

http://www.ibm.com/developerworks/java/library/j-benchmark1/index.html

contestado el 22 de mayo de 12 a las 21:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.