Ho un codice numerico che risolve un'equazione f(x) = 0
, in cui devo sollevare x
a una potenza p
. Lo risolvo usando un sacco di cose, ma alla fine ho il metodo di Newton. La soluzione sembra essere uguale a x = 1
, e quindi è la causa dei miei problemi. Quando la soluzione iterata si avvicina a 1
, ad esempio x = 1 + 1e-13
, il tempo necessario per calcolare lo std::pow(x, p)
cresce enormemente, facilmente di un fattore pari a 100, rendendo il mio codice inutilizzabile.molto lento std :: pow() per le basi molto vicino a 1
La macchina che esegue questa operazione è AMD64 (Opteron 6172) su CentOS e il comando è semplice . Un comportamento simile si presenta su tutte le mie macchine, tutte x64. Come documentato here, questo non è solo il mio problema (ad esempio, qualcun altro è incazzato troppo), appare solo su x64 e solo per vicino a 1.0
. La cosa simile accade a exp
.
Risolvere questo problema è vitale per me. Qualcuno sa se c'è qualche modo per aggirare questa lentezza?
EDIT: John ha sottolineato che ciò è dovuto ai denormali. La domanda è quindi, come risolvere questo problema? Il codice è C++, compilato con g++
per l'uso all'interno di GNU Octave
. Sembra che, sebbene io abbia impostato CXXFLAGS
per includere -mtune=native
e -ffast-math
, ciò non sta aiutando e il codice viene eseguito altrettanto lentamente.
SOLUZIONE PSEUDO PER ORA: a tutti coloro che si preoccupano di questo problema, le soluzioni suggerite di seguito non hanno funzionato per me personalmente. Ho davvero bisogno della solita velocità di std::pow()
, ma senza la lentezza intorno a x = 1
. La soluzione per me è personalmente ricorrendo a un trucco:
inline double mpow(double x, double p) __attribute__ ((const));
inline double mpow(double x, double p)
{
double y(x - 1.0);
return (std::abs(y) > 1e-4) ? (std::pow(x, p)) : (1.0 + p * y * (1.0 + (p - 1.0) * y * (0.5 + (1.0/6.0) * (p - 2.0) * y)));
}
il limite potrebbe essere cambiato, ma per -40 < p < 40 l'errore è più piccolo di circa 1e-11, che è abbastanza buono. L'overhead è minimo da ciò che ho trovato, quindi questo risolve il problema per me.
Questo potrebbe essere correlato a problemi di prestazioni generali con [numeri subnormali] (http://en.wikipedia.org/wiki/Denormal_number). I calcoli con valori in virgola mobile molto vicini a 0 possono essere 100 volte più lenti del normale. Vedi http://stackoverflow.com/questions/9314534/why-does-changing-0-1f-to-0-slow-down-performance-by-10x. –
Buon punto. Qualche suggerimento su come risolvere questo? Correggere i numeri esattamente a 1 se sono abbastanza vicini? –
@JohnKugelman: Se si legge il collegamento, questo è dovuto al fatto che glibc utilizza una funzione molto più lenta (chiamata '__slowpow') quando vengono forniti determinati valori di input. – interjay