2012-08-01 16 views
5

Ho una situazione in cui le prestazioni sono estremamente importanti. Al centro del mio algoritmo c'è un metodo che fa alcuni calcoli di base con due primitive double. Questo metodo è chiamato oltre dieci milioni di volte per esecuzione dell'algoritmo.Java IEEE 64-bit 754 double, valori da evitare

Il codice è simile a questo;

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)); 

} 

I parametri xA e yA assumono valori da un set. Questo set può essere ottimizzato nel codice. Sto vedendo differenze di prestazioni enormi (circa il doppio) a seconda dei valori che ho inserito nel set. Sembra che se il set contiene uno 0.1 o uno 0.3, le prestazioni hanno un grande successo. Mantenere il set a soli multipli di 0.5 offre le migliori prestazioni.

Il compilatore sta ottimizzando x * 0.5 come x >> 1 ecc.? Oppure perché 0.1 non può essere definito in binario?

Mi piacerebbe capire questa situazione un po 'meglio in modo da poter ottimizzare questo. Immagino che potrebbe essere un problema piuttosto difficile a meno che qualcuno non sappia esattamente come javac e jvm (nel nostro caso hotspot) gestiscono la doppia moltiplicazione.

+0

Sei sicuro la differenza di prestazioni è direttamente di questa routine e non una conseguenza del cambiamento xA e yA causando risultati diversi da restituire, modificando in tal modo ciò che viene eseguito dopo questa routine ritorna? –

+0

È possibile eliminare la sottrazione eliminando quella riga e sostituendo la restituzione con 'return x y? 1: 0; '. –

+0

Se il tweaking disponibile si estende al punto in cui i valori p e q per xA e yA possono essere sostituiti da p/q e 1, allora una moltiplicazione può essere eliminata, lasciando 'double x = xA * xB; double y = yB; '(soggetto ai soliti problemi di arrotondamento a virgola mobile). –

risposta

1

Solo un paio di alcune idee:

  • Se i valori sono multiplo di 0,5, allora ci saranno pochi bit significativi nel mantissa, così sembra possibile che la moltiplicazione prende un minor numero di cicli. I bit effettivamente significativi per la moltiplicazione non sembrano essere un problema, poiché i processori moderni impiegheranno solo due cicli per il doppio (come spiegato in Floating point division vs floating point multiplication).

    E.g. Immagino che con 0.5, 1, 2, 4, ecc. La mantissa sarà tutti zero (prima "1" è ommited per essere implicito). Per .75, 1.5, 3 ecc., Sarà un "1" nel m.s.b. seguito da tutti gli zeri. Considerando che 0.1 userebbe tutti i significati e le precisioni per essere rappresentati, e anche allora hanno un piccolo errore.

  • Informazioni sul risultato restituito: C'è qualche problema con Math.signum()?. Voglio dire, forse questo avrebbe fatto lo stesso:

    return Math.signum(x-y); 
    
  • Se precission non è di primaria importanza, si potrebbe considerare l'utilizzo singolo precission (float). (anche se questo significa che stai convertendo avanti e indietro dal doppio, allora potrebbe non valerne la pena).

+1

Signum Yep funzionerebbe, ma significherebbe un cast per int per il ritorno, per questo motivo l'ho evitato. – lynks

Problemi correlati