Domanda

Una o due decenni fa, valeva la pena di scrivere codice numerico di evitare l'uso moltiplica e divide e utilizzare addizione e sottrazione, invece. Un buon esempio utilizza differenze avanti per valutare una curva polinomiale invece di calcolare direttamente il polinomio.

È ancora il caso, o hanno moderne architetture di computer avanzato al punto in cui *, / non sono più molte volte più lento di +, -?

Per essere precisi, mi interessa codice compilato C / C ++ in esecuzione su moderni chip x86 tipica con ampia a bordo di hardware punto, non un piccolo micro cercando di fare FP nel software mobile. Mi rendo conto pipelining e altri miglioramenti architettonici ostino conteggi di ciclo specifici, ma mi piace ancora di ottenere un utile intuizione.

È stato utile?

Soluzione

Dipende anche sul mix di istruzioni. Il processore avrà più unità di calcolo in piedi da in qualsiasi momento, e si otterrà il massimo throughput, se tutti loro sono pieni per tutto il tempo. Quindi, l'esecuzione di un ciclo di mul di è altrettanto veloce come l'esecuzione di un loop o aggiunge -. Ma lo stesso non vale se l'espressione diventa più complesso

Per esempio, prendete questo ciclo:

for(int j=0;j<NUMITER;j++) {
  for(int i=1;i<NUMEL;i++) {
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ;
  }
}

per NUMITER = 10 ^ 7, Numel = 10 ^ 2, entrambi gli array inizializzati a piccoli numeri positivi (NaN è molto più lenta), questo richiede 6,0 secondi con doppie su 64 bit proc. Se sostituisco il ciclo con

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ;

Si richiede solo 1,7 secondi ... così da quando siamo "esagerato" le aggiunte, le Muls erano essenzialmente libero; e la riduzione aggiunte aiutato. Si ottiene di più confusa:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ;

- stesso mul / aggiungere distribuzione, ma ora la costante viene aggiunto in più che moltiplicato in - prende 3,7 secondi. Il processore è probabile ottimizzato per eseguire calcoli numerici tipici più efficiente; così dot-prodotto simile somme di Muls e somme in scala sono circa buono come si arriva; l'aggiunta di costanti non è così comune, in modo che sia più lento ...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/

prende di nuovo 1,7 secondi.

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/

(stesso ciclo iniziale, ma senza costosi aggiunta costante: 2,1 secondi)

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/

(per lo più Muls, ma un'aggiunta: 1,9 secondi)

Quindi, in sostanza; è difficile dire che è più veloce, ma se si desidera evitare i colli di bottiglia, più importante è quello di avere un mix sano, evitare di NaN o INF, evitare di aggiungere costanti. Qualunque cosa facciate, assicurarsi che si prova, e testare le varie impostazioni del compilatore, dal momento che spesso piccoli cambiamenti possono solo fare la differenza.

Alcuni altri casi:

bla *= someval; // someval very near 1.0; takes 2.1 seconds
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86

Altri suggerimenti

In teoria le informazioni sono qui:

Intel®64 e manuale IA-32 Architetture Optimization riferimento, APPENDICE C USO LATENCY e velocità

Per ogni processore elencano, la latenza FMUL è molto vicina a quella di FADD o FDIV. Su alcuni dei vecchi processori, FDIV è 2-3 tempo più lento di quello, mentre i processori più recenti, è la stessa di FMUL.

Avvertenze:

  1. Il documento ho linkato in realtà dice che non si può fare affidamento su questi numeri nella vita reale in quanto il processore farà ciò che vuole fare le cose più velocemente se è corretto.

  2. C'è una buona probabilità che il compilatore deciderà di utilizzare uno dei tanti set di istruzioni più recenti che hanno una virgola mobile moltiplicare / dividere a disposizione.

  3. Questo è un documento complicato solo scopo di essere letti da scrittori del compilatore e mi potrebbe essersi sbagliato. Come se non fossi chiaro il motivo per cui il numero di latenza FDIV manca completamente per alcune delle CPU.

Il modo migliore per rispondere a questa domanda è quello di scrivere in realtà un punto di riferimento / profilo del trattamento è necessario fare. Empirica deve essere utilizzato nel corso teorico quando mai possibile. Soprattutto quando si facile da raggiungere.

Se si conosce già diverse implementazioni della matematica che devi fare, si potrebbe scrivere un qualche transfermations codice differenti della matematica e vedere dove i vostri picchi di prestazioni. Questo permetterà al processore / compilatore di generare diversi flussi di esecuzione per riempire le tubazioni del processore e dare una risposta concreta alla tua risposta.

Se siete interesse in particolare l'esecuzione di istruzioni / MUL / ADD / Sub-tipo DIV si potrebbe anche gettare in qualche assembly inline per controllare in particolare che le varianti di queste istruzioni vengono eseguite. Tuttavia è necessario assicurarsi che si sta tenendo unità di esecuzione multilple occupato per ottenere una buona idea delle prestazioni del sistema è in grado di.

fare anche qualcosa di simile permetterebbe di confrontare le prestazioni su più varianti del processore, semplicemente eseguendo lo stesso programma su di loro, e potrebbe anche permettere di fattore di differenze scheda madre.

Modifica:

Architettura di base di un + - è identico. Così hanno logicamente assumono contemporaneamente per calcolare. * Dall'altro, richiedono strati multipli, tipicamente costruiti fuori "full adder" per completare una singola operazione. Questo garentees che, mentre un * può essere rilasciato alla pipeline ogni ciclo avrà una latenza superiore a quello di un circuito aggiuntivo / sottrarre. A fp / operazione viene tipicamente implementato utilizzando un metodo di approssimazione che converge verso iterativamente la risposta corretta nel tempo. Questi tipi di approssimazioni sono tipicamente implementati tramite moltiplicazione. Così, per virgola mobile si può generalmente assumere divisione ci vorrà più tempo perché è poco pratico per "srotolare" le moltiplicazioni (che è già un grande circuito e, di per sé) nella pipeline di una moltitudine di circuiti moltiplicatori. Ancora le prestazioni di un dato sistema è meglio misurata tramite test.

Non riesco a trovare un riferimento definitivo, ma vasta sperimentazione mi dice che la moltiplicazione galleggiante giorno d'oggi è quasi la stessa velocità di addizione e sottrazione, mentre la divisione non è (ma non "molte volte" più lento, o). È possibile ottenere l'intuizione che si desidera solo eseguendo i propri esperimenti - ricordate per generare i numeri casuali (milioni di loro) in anticipo, leggere prima di iniziare i tempi, e utilizzare i contatori delle prestazioni della CPU (con nessun altro processo in esecuzione, come per quanto li può fermare da) per la misurazione accurata!

La differenza di velocità di * / vs + - dipende dalla vostra architettura del processore. In generale con 86, in particolare, la differenza di velocità è diventato meno con processori moderni. * Dovrebbe essere vicino a +, in caso di dubbio: solo esperimento. Se hai un problema davvero difficile con un sacco di operazioni FP anche considerare l'utilizzo di vostra GPU (GeForce, ...), che funziona come un processore vettoriale.

Probabilmente c'è poca differenza di tempo tra la moltiplicazione e addizione. divisione d'altra parte è ancora significativamente più lento di moltiplicazione a causa della sua natura ricorsiva. sulle moderne istruzioni SSE architettura x86 devono essere considerati quando si fa un'operazione in virgola mobile piuttosto che utilizzando il fpu.Though un buon C / C ++ dovrebbe darvi la possibilità di utilizzare sse invece della FPU.

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