2010-07-28 19 views
8

Sto scrivendo un'applicazione che utilizza l'algoritmo Dijkstra per trovare percorsi minimi nel grafico. I pesi dei nodi e degli spigoli del grafico sono numeri float, quindi l'algoritmo esegue molti aritmetici su numeri mobili. Potrei ottenere un tempo di esecuzione migliore se converto tutto il peso in int s? Le operazioni aritmetiche int sono più veloci in Java e quelle mobili?int vs float efficienza aritmetica in Java

Ho provato a scrivere un semplice benchmark per verificarlo, ma non sono soddisfatto dei risultati ottenuti. Forse il compilatore ha ottimizzato alcune parti del programma, quindi i risultati non mi sembrano buoni.


EDIT:

Il problema che sto cercando di risolvere è nel campo Information Retrieval. L'applicazione dovrebbe mostrare le risposte a una query posta come un insieme di parole chiave.

La mia struttura dati è un grafico orientato ponderato. Dato un insieme di nodi foglia, devo trovare un albero più piccolo che colleghi questi nodi e mostri la risposta all'utente. I pesi sono assegnati da una funzione di ponderazione basata parzialmente sulla tecnica tf/idf. L'utente non sa quali pesi assegnare ai nodi e ai bordi vuole solo vedere le risposte rilevanti per la query che ha posto. Non sono richiesti risultati esatti, solo una possibilità per enumerare le risposte in base al loro peso. Solo l'uso nativo della funzione di ponderazione (come ho accennato è basato su tf/idf) dà pesi flottanti quindi ho usato i float finora.

Spero che questo aggiunga un po 'di background alla domanda.

+0

Qual è stato il risultato comunque? – Amarghosh

+0

Ho capito che il moltiplicarsi di un integer è un po 'più veloce del 13%, ma il confronto tra due integer è più lento del 22%. – jutky

+0

Non ne sono completamente sicuro, ma per Dijkstra basterebbero solo le operazioni di addizione e confronto. E per quelle operazioni, non dovrebbe variare così tanto per float o int. Sono davvero sorpreso che il confronto tra i numeri interi sarebbe del 22% più lento. Posso sapere che tipo di benchmarking hai effettuato? – tafa

risposta

1

Come sempre con questo tipo di cose, è necessario impostare alcuni obiettivi di rendimento, quindi configurare l'app per vedere se li soddisfa.

Spesso si possono trovare risultati sorprendenti; che il tempo impiegato non è affatto influenzato dal tipo numerico di base o che il tuo algoritmo non è ottimale.

E per quanto riguarda le ottimizzazioni del compilatore, rappresentano una parte reale e valida dell'ottimizzazione delle prestazioni.

Se l'utilizzo di tipo A è teoricamente più veloce rispetto all'utilizzo di tipo B, ma il compilatore può ottimizzare il tipo B per essere più veloce in uno scenario reale, è una prova preziosa, non una fonte di dissapointment.

+2

Volevo solo sapere se le prestazioni migliorano, posso guadagnare il tempo necessario per cambiare buona parte dell'applicazione. Ma sembra che non possa saperlo con certezza in anticipo, e il modo migliore per verificarlo è implementare due versioni dell'algoritmo e misurare i tempi di esecuzione. – jutky

2

per operazioni semplici int è più veloce, tuttavia con int è possibile che si debba fare più lavoro per ottenere lo stesso risultato. per esempio.

come float

float f = 15 * 0.987; 

come int

int i = 15 * 987/1000; 

La divisione supplementare significa il funzionamento int può richiedere più tempo.

+0

nell'algoritmo di Dijkstra Riassumo e paragono il peso dei percorsi, quindi l'operazione di devision è piuttosto posteriore per me. Come fai a sapere che per operazioni semplici gli intro sono più veloci, è una scena comune o puoi indicarmi qualche argomento sull'argomento. – jutky

+1

è necessario esaminare il codice nativo generato dalla JVM e confrontare i cicli di clock. Tuttavia, entrambe le operazioni sono abbastanza veloci rispetto al costo delle mancate cache e delle chiamate di sistema. È molto probabile che la scelta del tipo di dati non faccia molta differenza. –

-6

Non credo.

Float è 4 byte. E l'Int in java è anch'esso a 4 byte.

Perché non utilizzare la data (java.util.Date) per ottenere il tempo di esecuzione?

È possibile definire un grafico che è proprio 100000 nodi. Quindi calcola.

+0

Si potrebbe voler usare la parola "byte" invece di "bit". Un intero 4 * bit * può contenere solo sedici valori distinti ... –

+0

Mi dispiace ... Il mio inglese è scadente. Quindi ho usato la parola sbagliata. (La mia lingua madre non è inglese) In realtà, penso che se la velocità è più veloce di float, è perché l'hardware. In fisica, l'int è forse facile da ottenere che fluttuare. – rainisic

0

Se si desidera confrontare i pesi, è preferibile che fluisca a int.

+0

Lo stesso commento di un'altra risposta: è una scena comune o puoi indicarmi un po 'di letteratura sull'argomento. – jutky

0

Generalmente non si deve preoccupare di una scelta tra int e float per motivi di prestazioni.

Ecco un estratto dalla Appendice di Java Puzzlers:

-virgola mobile aritmetica è inesatta. Non utilizzare virgola mobile dove sono richiesti risultati esatti; invece, utilizzare un tipo integrale o BigDecimal. Preferire double a float.

Se non avete una buona ragione, si dovrebbe in genere preferiscono double a float se è necessario utilizzare un'operazione in virgola mobile. Se si desidera ottenere il risultato esatto, andare avanti e utilizzare BigDecimal; sarà più lento dal momento che non è un primitivo, ma se la profilazione non dimostra che non è accettabile, questa è spesso l'opzione migliore.

Se è necessario utilizzare l'operazione in virgola mobile, tentare di ottimizzare questo utilizzando int è sconsigliato. È probabile che si tratti di un'ottica prematura e complicherà inutilmente il codice. Scrivilo nel modo più naturale e più leggibile. Non complicare inutilmente il tuo codice per ottenere un leggero miglioramento delle prestazioni.

Se in realtà non è necessario il funzionamento in virgola mobile, utilizzare in ogni caso int o long.

+0

Vedere anche http://stackoverflow.com/questions/2550281/floating-point-vs-integer-calculations-on-modern-hardware e http://stackoverflow.com/questions/2010252/float-versus-integer-arithmetic -performance-on-modern-chips – polygenelubricants

+0

Ho aggiunto qualche sfondo alla domanda. spero che chiarisca alcune cose. Grazie per i link. – jutky

0

Penso che le prestazioni dipendano molto dall'algoritmo e dalla piattaforma su cui è in esecuzione il software.

Se si eseguono calcoli matrix/array su una piattaforma X86, il runtime potrebbe ottimizzarlo per utilizzare SSE, che è un set di istruzioni esteso solo float/double.

Su altre piattaforme il runtime potrebbe ottimizzare su OpenCL (non credo che qualcuno lo faccia al momento, ma potrebbe succedere :). Non ho idea di cosa corre più veloce su tale piattaforma e in quali condizioni. Potrebbe essere semplicemente che OpenCL sia ottimizzato per un carico di lavoro intero.

In queste circostanze concluderei che non è utile ottimizzare il tipo di dati (float o int) a questo punto e solo ottimizzare la leggibilità del codice.

Se il codice è altamente critico per le prestazioni e si sa esattamente su quale hardware il sistema sarà in esecuzione ora e in futuro, è possibile testare carichi di lavoro tipici con vari algoritmi e selezionare quello che meglio soddisfa le proprie esigenze.

Ma in generale, è sufficiente utilizzare un algoritmo che è possibile comprendere, mantenere il codice leggibile e, quindi, il conteggio degli errori basso. Il codice veloce non vale più di tanto se i risultati non sono corretti :)

1

Le sottrazioni di numeri interi sono ~ 2,5 volte più veloci delle doppie sottrazioni, sulla mia macchina. Le moltiplicazioni intere tuttavia sono solo ~ 1,5 volte più veloci delle doppie moltiplicazioni.

Il seguente test funziona su dati casuali, che potrebbero impedire l'ottimizzazione del compilatore.

// test whether int subs are faster than double subs 
public void compareIntAndFloatSubtraction(){ 

    int N = 100000; // input array size 
    int k = 100000; // number of mathematical operations performed on each element 

    // generate random data 
    int[] ints = new int[N]; 
    double[] doubles = new double[N]; 
    Random r = new Random(1l); 
    for (int i = 0; i < N; i++) { 
     ints[i] = r.nextInt(); 
     doubles[i] = r.nextDouble(); 
    } 

    // measure integer subtractions 
    long before = System.currentTimeMillis(); 
    for (int i = 1; i < N; i++) { 
     for (int j = 0; j < k; j++) { 
      ints[i] -= ints[i-1]; // referring to another element might prevent from optimization also 
     } 
    } 
    System.out.println(String.format("time needed for int subs [ms]: %s", System.currentTimeMillis()-before)); 

    // measure double subtractions 
    before = System.currentTimeMillis(); 
    for (int i = 1; i < N; i++) { 
     for (int j = 0; j < k; j++) { 
      doubles[i] -= doubles[i-1]; 
     } 
    } 
    System.out.println(String.format("time needed for double subs [ms]: %s", System.currentTimeMillis()-before)); 

} 
+1

Grazie per il test. Eseguendolo per i test di sottrazione sulla mia macchina (Xeon, 64-bit, Windows, Java 1.8) ha prodotto: tempo necessario per sottosistemi [ms]: 7704, tempo necessario per i sottositoli [ms]: 10869. Mi interessava di più di e meno di operazioni, perché stavo testandolo per l'utilizzo nella costruzione di una TreeMap. Il risultato delle operazioni di confronto/* if (raddoppia [i] raddoppia [i-1]) tmp ++; */were: tempo necessario per int cmp [ms]: 8479, tempo necessario per double cmp [ms]: 15925. – Henry

+1

FYI, ho eseguito nuovamente il test per il tipo di dati "lungo". Il risultato per la sottrazione: tempo necessario per i sottotitoli lunghi [ms]: 7513, tempo necessario per i doppi sottotitoli [ms]: 10898. E il risultato per i confronti: tempo necessario per un lungo cmp [ms]: 15768, tempo necessario per il doppio cmp [ ms]: 16012. Per "long", sembra che le prestazioni siano simili per i controlli di uguaglianza. – Henry

+0

Non puoi eseguire benchmark nella stessa esecuzione per diversi tipi di valore, la JVM sarà calda –