2009-05-03 16 views
9

Tengo una aplicación C++ en la que necesito comparar dos valores y decidir cuál es mayor. La única complicación es que un número está representado en el espacio de registro, el otro no. Por ejemplo:C++ Exp vs. Log: ¿Cuál es más rápido?

double log_num_1 = log(1.23); 
double num_2 = 1.24; 

Si quiero comparar num_1 y num_2, tengo que usar ya sea log() o exp(), y me pregunto si uno es más fácil de calcular que el otro (es decir, se ejecuta en menos tiempo, en general). Puede suponer que estoy usando la biblioteca estándar cmath.

En otras palabras, los siguientes son semánticamente equivalentes, por lo que es más rápido:

if(exp(log_num_1) > num_2)) cout << "num_1 is greater"; 

o

if(log_num_1 > log(num_2)) cout << "num_1 is greater"; 
+1

¿Por qué no escribes algunas pruebas y publicas tus resultados? :) – xian

Respuesta

21

AFAIK los algoritmos, la complejidad es la misma, la diferencia debe ser solo una constante (con suerte insignificante). Debido a esto, usaría el exp(a) > b, simplemente porque no se rompe en la entrada no válida.

+3

Muy buen punto acerca de que log() es más frágil. Especialmente dado que la diferencia de rendimiento probablemente no sea un problema en la realidad. –

+1

Ahora esta es una buena razón para elegir exp() sobre log(). – dmckee

+0

+1, de acuerdo. A menos que esté haciendo algo de verdad, realmente con alto rendimiento, no debería preocuparse por las micro-optimizaciones como esta ... –

3

algunas pruebas rápidas en pitón (que utiliza c para las matemáticas):

$ time python -c "from math import log, exp;[exp(100) for i in xrange(1000000)]" 

real 0m0.590s 
user 0m0.520s 
sys  0m0.042s 

$ time python -c "from math import log, exp;[log(100) for i in xrange(1000000)]" 

real 0m0.685s 
user 0m0.558s 
sys  0m0.044s 

indicaría que el registro es un poco más lento

Editar: las funciones de C se están optimizando a cabo por el compilador parece, por lo que el bucle es lo que está pasando hasta el momento

Curiosamente, en C que parecen ser la misma velocidad (posiblemente por razones Marcos mencionados en el comentario)

#include <math.h> 

void runExp(int n) { 
    int i; 
    for (i=0; i<n; i++) { 
     exp(100); 
    } 
} 

void runLog(int n) { 
    int i; 
    for (i=0; i<n; i++) { 
     log(100); 
    } 
} 

int main(int argc, char **argv) { 
    if (argc <= 1) { 
     return 0; 
    } 
    if (argv[1][0] == 'e') { 
     runExp(1000000000); 
    } else if (argv[1][0] == 'l') { 
     runLog(1000000000); 
    } 
    return 0; 
} 

veces dando:

$ time ./exp l 

real  0m2.987s 
user  0m2.940s 
sys  0m0.015s 

$ time ./exp e 

real  0m2.988s 
user  0m2.942s 
sys  0m0.012s 
+0

Buena idea, pero Python puede realizar un control de rango adicional en el registro, y bien puede ser implementado de manera diferente a las libretas de OP. Además, ¿la diferencia siempre es alrededor del 7%? Porque eso podría ser solo un error experimental. – Mark

6

¿te realmente necesita saber? ¿Esto va a ocupar una gran parte de tu tiempo de ejecución? ¿Cómo lo sabes?

Peor aún, puede depender de la plataforma. ¿Y que?

Por lo tanto, pruébelo si le importa, pero pasar mucho tiempo agonizando por la micro-optimización generalmente es una mala idea.

+2

Peor aún, puede depender del procesador en el que se esté ejecutando (es casi seguro que su aplicación sobrevivirá a la generación actual de CPU). –

+0

Aún no he realizado ningún perfil, pero es probable que sea un punto caliente en mi código. Sin embargo, probablemente tengas razón. Simplemente tenía curiosidad, y pensé que podría ser una pregunta útil estar aquí para la posteridad. –

+0

La curiosidad nunca es mala. Demasiados ciclos de procesador son desperdiciados hoy por los desarrolladores, no es lo suficientemente curioso. "Solo tira más hardware" es la raíz de todo mal. – Svante

1

Puede depender de su libma, plataforma y procesador. Lo mejor es escribir un código que llame al exp/log una gran cantidad de veces, y usar time para llamarlo varias veces para ver si hay una diferencia notable.

Ambos tardan básicamente el mismo tiempo en mi computadora (Windows), así que usaría exp, ya que está definida para todos los valores (suponiendo que compruebe ERANGE). Pero si es más natural usar log, debe usar eso en lugar de intentar optimizar sin una buena razón.

7

Editar: modificó el código para evitar el desbordamiento de exp(). Esto causó que el margen entre las dos funciones se redujera considerablemente. Gracias, fredrikj.

Código:

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 

int main(int argc, char **argv) 
{ 
    if (argc != 3) { 
     return 0; 
    } 

    int type = atoi(argv[1]); 
    int count = atoi(argv[2]); 

    double (*func)(double) = type == 1 ? exp : log; 

    int i; 
    for (i = 0; i < count; i++) { 
     func(i%100); 
    } 

    return 0; 
} 

(Compilar utilizando :)

[email protected] /home/emil/dev $ gcc -o test_log test_log.c -lm 

Los resultados parece bastante concluyentes:

[email protected] /home/emil/dev $ time ./test_log 0 10000000 

real 0m2.307s 
user 0m2.040s 
sys  0m0.000s 

[email protected] /home/emil/dev $ time ./test_log 1 10000000 

real 0m2.639s 
user 0m2.632s 
sys  0m0.004s 

Un poco sorprendentemente ingrese parece ser el más rápido.

la especulación pura:

Tal vez la matemática subyacente taylor series converge más rápido para acceder o algo? En realidad, me parece como el natural logarithm es más fácil de calcular que el exponential function:

ln(1+x) = x - x^2/2 + x^3/3 ... 
e^x = 1 + x + x^2/2! + x^3/3! + x^4/4! ... 

No estoy seguro de que las funciones de la biblioteca C, incluso hace como este, sin embargo. Pero no parece totalmente improbable.

+1

+1: el tuyo es diferente al mío porque estás evaluando un valor cambiante, mientras que el mío estaba siendo optimizado por el compilador en nada. Entonces el error fue mío, no tuyo, creo que – cobbal

+0

Ah, interesante. ¿Obtienes resultados similares cuando haces lo mismo? –

+4

No, la serie de Taylor para la función exponencial converge más rápido que para el logaritmo natural. Pero eso probablemente sea irrelevante porque la biblioteca matemática probablemente usa un polinomio minimax y no un polinomio de Taylor. La razón exp es mucho más lenta que el inicio de sesión en su prueba es probable que se desborde la mayor parte del tiempo, y esto debe ser manejado. Tenga en cuenta que exp (i) se desborda ya en torno a i = 700. el registro no se desborda. –

0

si está seguro de que este es un punto de acceso - el compilador instrinsics es sus amigos. Aunque depende de la plataforma (si busca rendimiento en lugares como este, no puede ser agnóstico de plataforma) So. La pregunta realmente es: cuál es la instrucción asm en su arquitectura de destino y ciclos de latencia. Sin esto, es pura especulación.

4

Dado que se trabaja con valores < < 1, tenga en cuenta que x-1> log (x) para x < 1, lo que significa que x-1 < log (y) implica log (x) < registro (y), que ya se ocupa de 1/e ~ 37% de los casos sin tener que usar log o exp.

+0

Qué prueba agregada puede o no ser más rápida, pero sin duda es un buen enfoque para probar y crear un perfil. –

+0

Eso es simplemente genial. –

1

Tendría sentido que log sea más rápido ... Exp tiene que realizar varias multiplicaciones para llegar a su respuesta, mientras que log solo tiene que convertir la mantisa y el exponente en base-e de base-2.

Simplemente asegúrese de verificar los límites (como muchos otros han dicho) si está utilizando el registro.

Cuestiones relacionadas