2013-03-12 20 views
13

Sulla base delle discussioni relative a una risposta alla domanda this, ho scoperto un comportamento davvero strano dell'ottimizzatore di Java Hotspot. Il comportamento osservato può essere visto almeno in Oracle VM 1.7.0_17, ma sembra che si verifichino anche nelle versioni precedenti di Java 6.Strano comportamento dell'ottimizzatore del ciclo dell'hotspot

Prima di tutto, ero già a conoscenza dell'ottimizzatore, ovviamente consapevole del fatto che alcuni metodi nell'API standard sono invarianti e non hanno effetti collaterali. Quando si esegue un ciclo come double x=0.5; for(double d = 0; d < Math.sin(x); d += 0.001);, l'espressione Math.sin(x) non viene valutata per ogni iterazione, ma l'ottimizzatore è consapevole del fatto che il metodo non ha effetti collaterali rilevanti e che il risultato è invariato, purché x non venga modificato nel ciclo.

Ora ho notato che cambiare semplicemente x da 0,5 a 1,0 disabilitato questa ottimizzazione. Ulteriori test indicano che l'ottimizzazione è abilitata solo se abs (x) < asin (1/sqrt (2)). C'è una buona ragione per quello, che non vedo, o è una limitazione inutile alle condizioni ottimizzanti?

Edit: L'ottimizzazione sembrano essere implementato in hotspot/src/share/vm/opto/subnode.cpp

+6

Come fai a sapere che "l'espressione Math.sin (x) non viene valutata per ogni iterazione"?Hai guardato il codice assembly? O tempo misurato? Si noti inoltre che 'Math.sin' è un metodo intrinseco in Java 1.7 (probabilmente prima) quindi il codice eseguito non è il codice Java mostrato nel sorgente JDK ... – assylias

+0

@assylias: Misurando il tempo, ma si ha un buon punto . Mi chiedo se sia l'effettiva implementazione di Math.sin, che è ottimizzata per gli argomenti jarnbjo

+0

@jarbjo L'implementazione per cpus x86_64 è qui: http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/6e9aa487055f/src/cpu/x86/vm/stubGenerator_x86_64.cpp intorno alla riga 2878. – assylias

risposta

2

penso tue domande su specificamente Oracle JVM, perché l'attuazione della matematica è l'attuazione-dipendente. Ecco buona risposta in merito all'esecuzione Dalvik per esempio: native code for Java Math class

Generalmente

  1. sin (a) * sin (a) + cos (a) * cos (a) = 1
  2. sin (pi/2 - a) = cos (a)
  3. sin (-a) = -sin (a)
  4. cos (-a) = cos (a)

quindi non abbiamo bisogno di peccato/cos funzioni imple mentazione per x < 0 o x> pi/4.

Suppongo che questa è la risposta (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5005861):

Siamo a conoscenza dei risultati almabench e e articolo OSNews su prestazioni trigonometrica. Tuttavia, l'implementazione HotSpot di sin/cos su x86 per anni ha utilizzato e continua a utilizzare le istruzioni fsin/fcos x87 in un intervallo in cui tali istruzioni soddisfano la qualità dei requisiti di implementazione , in pratica [-pi/4, pi/4 ]. Al di fuori dell'intervallo , i risultati di fsin/fcos possono essere ovunque nell'intervallo [-1, 1] con poca relazione al vero seno/coseno dell'argomento. Per esempio , fsin (Math.PI) ottiene solo la metà delle cifre del risultato corretto. Il motivo è che le implementazioni dell'istruzione fsin/fcos utilizzano un algoritmo non ideale per l'argomento argomento riduzione; il processo di riduzione argomento viene spiegato nel bug 4857011.

Conclusione: avete visto i risultati di algoritmo di riduzione argomento in azione, non la limitazione di ottimizzazione.

+0

Per in che misura il fatto che 'Math.Sin (Math.Pi)' non produca esattamente zero una "caratteristica"? Direi che la maggior parte del codice del mondo reale che chiama 'Math.Sin' lo fa con argomenti che hanno un errore di arrotondamento da 1/4LSB a 1/2LSB; la riduzione dell'argomento usando 'Math.Pi' contrasta questo errore di arrotondamento nella maggior parte delle applicazioni del mondo reale, mentre la riduzione dell'argomento usando π la preserva. – supercat