Domanda

Ho bisogno di calcolare Math.exp() da Java molto frequentemente, è possibile ottenere una versione nativa più veloce di Giava'S Math.exp()??

Ho provato solo jni + C, ma è più lento del semplice Giava.

È stato utile?

Soluzione

+1 per scrivere la tua implementazione exp().Cioè, se lo è Veramente un collo di bottiglia nella tua applicazione.Se riesci a gestire un po’ di imprecisione, esistono numerosi algoritmi di stima dell’esponente estremamente efficienti, alcuni dei quali risalgono a secoli fa.A quanto ho capito, l'implementazione exp() di Java è piuttosto lenta, anche per gli algoritmi che devono restituire risultati "esatti".

Oh, e non aver paura di scrivere l'implementazione di exp() in puro Java.JNI ha molto sovraccarico e la JVM è in grado di ottimizzare il bytecode in fase di esecuzione a volte anche oltre ciò che C/C++ è in grado di ottenere.

Altri suggerimenti

Ciò è già stato richiesto più volte (vedi ad es. Qui).Ecco un'approssimazione a Math.exp(), copiata da questo post sul blog:

public static double exp(double val) {
    final long tmp = (long) (1512775 * val + (1072693248 - 60801));
    return Double.longBitsToDouble(tmp << 32);
}

Fondamentalmente è uguale a una tabella di ricerca con 2048 voci e interpolazione lineare tra le voci, ma tutto questo con trucchi in virgola mobile IEEE.È 5 volte più veloce di Math.exp() sulla mia macchina, ma può variare drasticamente se compili con -server.

Usa Java.

Inoltre, memorizza nella cache i risultati dell'exp e quindi puoi cercare la risposta più velocemente che calcolarli nuovamente.

Vorresti concludere qualunque ciclo chiami Math.exp() anche in C.Altrimenti, il sovraccarico del marshalling tra Java e C supererà qualsiasi vantaggio in termini di prestazioni.

Potresti riuscire a farlo funzionare più velocemente se li esegui in batch.Effettuare una chiamata JNI aggiunge un sovraccarico, quindi non vuoi farlo per ogni exp() che devi calcolare.Proverei a passare un array di 100 valori e a ottenere i risultati per vedere se aiuta le prestazioni.

La vera domanda è: questo è diventato un collo di bottiglia per te?Hai profilato la tua applicazione e hai riscontrato che questa è una delle principali cause di rallentamento?

In caso contrario, consiglierei di utilizzare la versione di Java.Cerca di non pre-ottimizzare poiché ciò causerebbe solo un rallentamento dello sviluppo.Potresti dedicare molto tempo a un problema che potrebbe non essere un problema.

Detto questo, penso che il tuo test ti abbia dato la risposta.Se jni + C è più lento, usa la versione di Java.

Matematica comune3 viene fornito con una versione ottimizzata: FastMath.exp(double x).Ha accelerato significativamente il mio codice.

Fabien ho eseguito alcuni test e ho scoperto che era quasi il doppio più veloce di Math.exp():

 0.75s for Math.exp     sum=1.7182816693332244E7
 0.40s for FastMath.exp sum=1.7182816693332244E7

Ecco il Javadoc:

Calcola exp(x), il risultato della funzione è quasi arrotondato.Verrà arrotondato correttamente al valore teorico per il 99,9% dei valori di input, altrimenti avrà un errore di 1 UPL.

Metodo:

    Lookup intVal = exp(int(x))
    Lookup fracVal = exp(int(x-int(x) / 1024.0) * 1024.0 );
    Compute z as the exponential of the remaining bits by a polynomial minus one
    exp(x) = intVal * fracVal * (1 + z)

Precisione:Il calcolo viene eseguito con 63 bit di precisione, quindi il risultato dovrebbe essere arrotondato correttamente per il 99,9% dei valori di input, altrimenti con meno di 1 errore ULP.

Poiché il codice Java verrà compilato in codice nativo con il compilatore just-in-time (JIT), non c'è davvero alcun motivo per utilizzare JNI per chiamare il codice nativo.

Inoltre, non dovresti memorizzare nella cache i risultati di un metodo in cui i parametri di input sono numeri reali a virgola mobile.I guadagni ottenuti nel tempo andranno persi in gran parte in termini di spazio utilizzato.

Il problema con l'utilizzo di JNI è l'overhead implicato nell'effettuare la chiamata a JNI.La macchina virtuale Java è piuttosto ottimizzata al giorno d'oggi e le chiamate al Math.exp() integrato sono automaticamente ottimizzate per chiamare direttamente la funzione C exp() e potrebbero anche essere ottimizzate in un semplice assembly a virgola mobile x87 Istruzioni.

C'è semplicemente un sovraccarico associato all'utilizzo di JNI, vedere anche:http://java.sun.com/docs/books/performance/1st_edition/html/JPNativeCode.fm.html

Quindi, come altri hanno suggerito, prova a raccogliere le operazioni che implicherebbero l'uso della JNI.

Scrivi il tuo, su misura per le tue esigenze.

Ad esempio, se tutti i tuoi esponenti sono potenza di due, puoi utilizzare lo spostamento di bit.Se lavori con un intervallo o un insieme di valori limitato, puoi utilizzare le tabelle di ricerca.Se non hai bisogno di una precisione millimetrica, usi un algoritmo impreciso, ma più veloce.

È previsto un costo associato alle chiamate oltre il confine della JNI.

Se potessi spostare il ciclo che chiama exp() anche nel codice nativo, in modo che ci sia solo una chiamata nativa, potresti ottenere risultati migliori, ma dubito che sarà significativamente più veloce della soluzione Java pura.

Non conosco i dettagli della tua applicazione, ma se hai un insieme abbastanza limitato di possibili argomenti per la chiamata, potresti utilizzare una tabella di ricerca precalcolata per rendere più veloce il tuo codice Java.

Esistono algoritmi più veloci per l'esperienza a seconda di ciò che stai cercando di realizzare.Lo spazio del problema è limitato a un determinato intervallo, è necessaria solo una determinata risoluzione, precisione o accuratezza, ecc.

Se definisci il tuo problema molto bene, potresti scoprire che puoi usare una tabella con interpolazione, ad esempio, che farà saltare fuori dall'acqua quasi qualsiasi altro algoritmo.

Quali vincoli puoi applicare all'exp per ottenere quel compromesso in termini di prestazioni?

-Adamo

Eseguo un algoritmo di adattamento e l'errore minimo del risultato di adattamento è molto più grande della precisione di Math.exp ().

Le funzioni trascendentali sono sempre molto più lente dell'addizione o della moltiplicazione e costituiscono un noto collo di bottiglia.Se sai che i tuoi valori rientrano in un intervallo ristretto, puoi semplicemente creare una tabella di ricerca (Two sorted array ;un ingresso, una uscita).Utilizzare Arrays.binarySearch per trovare l'indice corretto e interpolare il valore con gli elementi in [indice] e [indice+1].

Un altro metodo è dividere il numero.Prendiamo ad es.3,81 e dividiamolo in 3+0,81.Ora moltiplichi e = 2,718 tre volte e ottieni 20,08.

Ora a 0,81.Tutti i valori compresi tra 0 e 1 convergono velocemente con la ben nota serie esponenziale

1+x+x^2/2+x^3/6+x^4/24....eccetera.

Prendi tutti i termini necessari per la precisione;sfortunatamente è più lento se x si avvicina a 1.Diciamo che vai a x ^ 4, quindi ottieni 2.2445 invece del corretto 2.2448

Quindi moltiplicare il risultato 2.781^3 = 20,08 con 2,781^0,81 = 2.2445 e hai il risultato 45.07 con un errore di una parte di duemila (corretto:45.15).

Potrebbe non essere più rilevante, ma giusto perché tu lo sappia, nelle versioni più recenti di OpenJDK (vedi Qui), Math.exp dovrebbe essere reso intrinseco (se non sai di cosa si tratta, controlla Qui).

Ciò renderà le prestazioni imbattibili sulla maggior parte delle architetture, perché significa che la VM Hotspot sostituirà la chiamata a Math.exp con un'implementazione di exp specifica del processore in fase di runtime.Non puoi mai battere queste chiamate, poiché sono ottimizzate per l'architettura...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top