2015-06-11 11 views
6

Il metodo standard Math.sqrt() sembra già abbastanza veloce in Java, ma ha lo svantaggio intrinseco che implica sempre operazioni a 64 bit che non fanno altro che ridurre la velocità quando si tratta di valori a 32 bit float. È possibile fare meglio con un metodo personalizzato che utilizza un parametro float, esegue solo operazioni a 32 bit e restituisce un risultato come float?Java: implementazione fp a 32 bit di Math.sqrt()

vidi:

Fast sqrt in Java at the expense of accuracy

e ha fatto poco più che rafforzare l'idea che Math.sqrt() è generalmente difficile da battere. Ho visto anche:

http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi

che mi ha mostrato un sacco di interessanti C++ hack/ASM che io sono semplicemente troppo ignorante per porta direttamente a Java. Benchè sqrt14 potrebbe essere interessante come parte di una chiamata JNI. . .

Ho anche guardato ad Apache Commons FastMath, ma sembra che la libreria abbia come predefinito lo standard Math.sqrt(), quindi non ci aiuto. E poi c'è Yeppp !:

http://www.yeppp.info/

ma non ho disturbato con ancora.

+1

Io non sono molto sicuro che avrai il vantaggio di velocità si spera si otterrebbe –

+1

"operazioni a 64 bit. .. non fa altro che ridurre la velocità quando si ha a che fare con valori float a 32 bit "è un errore. In generale, le operazioni in virgola mobile vengono sempre eseguite nella precisione della FPU, e il sovraccarico arriva ad allargare e restringere gli operandi 'float' a' double' per adattarsi alla FPU. – EJP

+1

@EJP è vero per 'sqrtsd' vs' sqrtss', ma ovviamente da una prospettiva Java non è possibile controllarlo. Per quanto riguarda la FPU vecchio stile che funziona come descriveresti, è essenzialmente obsoleto (e gravemente azzoppato in alcuni Intel Atom) – harold

risposta

5

Non serve nulla per accelerare sqrt per valori a 32 bit. HotSpot JVM lo fa automaticamente per te.

Il compilatore JIT è abbastanza intelligente da riconoscere lo schema f2d -> Math.sqrt() -> d2f e lo sostituisce con l'istruzione della CPU sqrtss più veloce invece di sqrtsd. The source.

Il benchmark:

@State(Scope.Benchmark) 
public class Sqrt { 
    double d = Math.random(); 
    float f = (float) d; 

    @Benchmark 
    public double sqrtD() { 
     return Math.sqrt(d); 
    } 

    @Benchmark 
    public float sqrtF() { 
     return (float) Math.sqrt(f); 
    } 
} 

E i risultati:

Benchmark Mode Cnt  Score  Error Units 
Sqrt.sqrtD thrpt 5 145501,072 ± 2211,666 ops/ms 
Sqrt.sqrtF thrpt 5 223657,110 ± 2268,735 ops/ms 
+0

Interessante! Impara qualcosa di nuovo ogni giorno. – user3765373

0

Come ti sembra di conoscere JNI:

Basta scrivere un wrapper minimo per double sqrt(double) e float sqrt(float) da math.h e confrontare le prestazioni della libreria standard di C.

Suggerimento: non si avverte alcuna differenza a meno che non si esegua molto il rooting quadrato, e quindi il vantaggio in termini di prestazioni dell'utilizzo delle istruzioni SIMD per eseguire più sqrts in una volta molto probabilmente domina gli effetti. Avrai bisogno di ottenere un array allineato in memoria dei valori in virgola mobile da Java, che può essere piuttosto difficile, se stai usando le librerie standard Java.

Problemi correlati