Efficiente 10 alla potenza doppia

Oct 22 2020

Devo aumentare 10 volte la potenza di un doppio molte volte.

Esiste un modo più efficiente per farlo rispetto alla libreria matematica pow(10,double)? Se conta, i miei doppi sono sempre negativi tra -5 e -11.

Presumo che pow (double, double) utilizzi un algoritmo più generale di quello richiesto per pow (10, double) e potrebbe quindi non essere il metodo più veloce. Date alcune delle risposte di seguito, potrebbe essere stata un'ipotesi errata.

Quanto al perché, è per l'interpolazione logaritmica. Ho una tabella di valori x e y. Il mio oggetto ha un valore x noto (che è quasi sempre un doppio).

double Dbeta(struct Data *diffusion, double per){
  double frac;
  while(per>diffusion->x[i]){
      i++;
  }
  frac = (per-diffusion->x[i-1])/(diffusion->x[i]-diffusion->x[i-1]);
  return pow(10,log10DB[i-1] + frac * (log10DB[i]-log10DB[i-1]));
}

Questa funzione viene chiamata molte volte. Mi è stato detto di esaminare il profilo, quindi è quello che farò prima.

Mi è stato appena detto che avrei potuto usare logaritmi naturali invece di base 10, il che è ovviamente giusto. (la mia stupidità a volte stupisce anche me stesso.)

Dopo aver sostituito tutto con logaritmi naturali, tutto funziona un po 'più velocemente. Con la profilazione (che è una nuova parola che ho imparato oggi) ho scoperto che il 39% del mio codice viene speso nella funzione exp, quindi per coloro che si chiedevano se fosse in realtà questa parte a strozzare il mio codice, lo era.

Risposte

3 PascalGetreuer Oct 22 2020 at 14:58

Sì, la funzione pow è lenta (circa 50 volte il costo di un moltiplicatore, per chi richiede benchmark).

  • Con qualche trucco di log / esponenti, possiamo esprimere 10 ^ x come

    10^x = exp(log(10^x)) = exp(x * log(10)).
    

    Quindi puoi implementare 10 ^ x con exp(x * M_LN10), che dovrebbe essere più efficiente di pow.

  • Se la doppia precisione non è critica, usa la versione float della funzione expf(o powf), che dovrebbe essere più efficiente della doppia versione.

  • Se l'accuratezza approssimativa è OK, calcola anticipatamente una tabella nell'intervallo [-5, -11] ed esegui una rapida ricerca con l'interpolazione lineare.

Alcuni benchmark (utilizzando glibc 2.31):

Benchmark                Time
---------------------------------
pow(10, x)               15.54 ns
powf(10, x)               7.18 ns
expf(x * (float)M_LN10)   3.45 ns
5 TomKarzes Oct 22 2020 at 14:53

Perché pow(10.0, n)dovrebbe essere più veloce da impostare c = log(10.0), che puoi calcolare una volta, quindi usare exp(c*n), che dovrebbe essere significativamente più veloce di pow(10.0, n)(che fondamentalmente sta facendo la stessa cosa internamente, tranne che sarebbe il calcolo log(10.0)più e più volte invece che solo una volta). Oltre a ciò, probabilmente non c'è molto altro che puoi fare.