2012-08-01 32 views
5

Tengo una situación donde el rendimiento es extremadamente importante. En el núcleo de mi algoritmo hay un método que hace algunos cálculos básicos con dos primitivas double. Este método se llama más de diez millones de veces por ejecución del algoritmo.Java IEEE 64-bit 754 double, valores para evitar

El código se ve más o menos así;

public int compare(double xA, double xB, double yA, double yB); 

    double x = xA * xB; 
    double y = yA * yB; 

    double diff = x - y; 

    return (diff < 0.0 ? -1 : (diff > 0.0 ? 1 : 0)); 

} 

Los parámetros xA y yA toman sus valores de un conjunto. Este conjunto se puede ajustar en el código. Veo diferencias de rendimiento enormes (aproximadamente el doble) según los valores que coloqué en el conjunto. Parece que si el conjunto contiene un 0.1 o un 0.3, el rendimiento tiene un gran éxito. Mantener el conjunto a solo múltiplos de 0.5 le da el mejor rendimiento.

¿El compilador optimiza x * 0.5 como x >> 1, etc.? ¿O es esto porque 0.1 no se puede definir en binario?

Me gustaría entender esta situación un poco mejor para que pueda optimizar esto. Supongo que podría ser un problema bastante difícil a menos que alguien sepa exactamente cómo javac y el jvm (en nuestro caso, el punto de acceso) maneja la multiplicación doble.

+0

¿Seguro la diferencia de rendimiento es directamente en esta rutina y no una consecuencia del cambio de xA y Ya causar resultados diferentes para ser devueltos, cambiando así lo que se ejecuta después de esta rutina vuelve? –

+0

Puede eliminar la resta borrando esa línea y reemplazando la declaración con 'return x y? 1: 0; '. –

+0

Si el ajuste disponible se extiende hasta el punto en que los valores p y q para xA y yA pueden reemplazarse por p/q y 1, entonces se puede eliminar una multiplicación, dejando 'doble x = xA * xB; double y = yB; '(sujeto a los problemas habituales de redondeo de coma flotante). –

Respuesta

1

Sólo un par de algunas ideas:

  • Si los valores son múltiplos de 0,5, entonces habrá pocos bits significativos en la mantissa, por lo que parece factible que la multiplicación tiene un menor número de Ciclos. En realidad, los bits significativos para la multiplicación no parecen ser un problema, ya que los procesadores modernos tomarán solo dos ciclos por el doble (como se explica en Floating point division vs floating point multiplication).

    E.g. Me imagino que con 0.5, 1, 2, 4, etc. la mantisa será todo ceros (el primer "1" se omite por ser implícito). Para .75, 1.5, 3 etc., será un "1" en el m.s.b. seguido por todos los ceros. Mientras que 0.1 usaría toda la importancia y precisión para ser representada, e incluso entonces tendría un pequeño error.

  • Sobre el resultado devuelto: ¿Hay algún problema con Math.signum()?. Es decir, tal vez esto haría lo mismo:

    return Math.signum(x-y); 
    
  • Si precission no es lo más importante, es posible considerar el uso de un solo precission (float). (aunque si eso significa que va a convertir el doble desde y hacia adelante, entonces puede que no valga la pena).

+1

Yep signum funcionaría, pero significaría un elenco para int para la devolución, por esa razón lo evité. – lynks

Cuestiones relacionadas