Mu.
En serio ahora, no importa. No por ejemplos de este tamaño. Ambos tienen la misma complejidad. Si su código no es lo suficientemente rápido para usted, este es probablemente uno de los últimos lugares que miraría.
Ahora, si realmente quieres saber cuál es más rápido, medirlos. En SBCL, puede llamar a cada función en un bucle y medir la hora. Como tiene dos funciones simples, es suficiente con time
. Si su programa era más complicado, un profiler sería más útil. Sugerencia: si no necesita un generador de perfiles para sus mediciones, probablemente no tenga que preocuparse por el rendimiento.
En mi máquina (SBCL 64 bits), me encontré con sus funciones y se puso esto:
CL-USER> (time (loop repeat 1000 do (factorial_recursion 1000)))
Evaluation took:
0.540 seconds of real time
0.536034 seconds of total run time (0.496031 user, 0.040003 system)
[ Run times consist of 0.096 seconds GC time, and 0.441 seconds non-GC time. ]
99.26% CPU
1,006,632,438 processor cycles
511,315,904 bytes consed
NIL
CL-USER> (time (loop repeat 1000 do (factorial_loop 1000)))
Evaluation took:
0.485 seconds of real time
0.488030 seconds of total run time (0.488030 user, 0.000000 system)
[ Run times consist of 0.072 seconds GC time, and 0.417 seconds non-GC time. ]
100.62% CPU
902,043,247 processor cycles
511,322,400 bytes consed
NIL
Después de poner sus funciones en un archivo con (declaim (optimize speed))
en la parte superior, el tiempo de recurrencia se redujo a 504 milisegundos y la el tiempo de bucle cayó a 475 milisegundos.
Y si realmente quieres saber qué está pasando, prueba dissasemble
en tus funciones y mira lo que hay allí.
De nuevo, esto no parece un problema para mí. Personalmente, trato de usar Common Lisp como un lenguaje de scripting para prototipos, luego perfilo y optimizo las partes que son lentas. Pasar de 500 ms a 475 ms no es nada. Por ejemplo, en algún código personal, obtuve un par de órdenes de aceleración de mayor magnitud simplemente agregando un tipo de elemento a una matriz (lo que hace que el almacenamiento de la matriz sea 64 veces más pequeño en mi caso). Claro, en teoría hubiera sido más rápido reutilizar esa matriz (después de hacerla más pequeña) y no asignarla una y otra vez. Pero simplemente agregar :element-type bit
fue suficiente para mi situación: más cambios habrían requerido más tiempo con muy poco beneficio adicional. Tal vez soy descuidado, pero "rápido" y "lento" no significan mucho para mí. Prefiero "lo suficientemente rápido" y "demasiado lento". Ambas funciones son "lo suficientemente rápidas" en la mayoría de los casos (o ambas son "demasiado lentas" en algunos casos) por lo que no existe una diferencia real entre ellas.
Si su función es recursiva de cola, es fundamentalmente idéntica a un bucle. La recursividad de cola puede optimizarse en un bucle simple, haciéndolos idénticos. Sin embargo, tu función no es recursiva en la cola. – Gabe
reemplace decf por 1-. –
@Gabe, mientras que la recursividad de cola puede optimizarse en un ciclo, vale la pena señalar que las implementaciones de Common Lisp no son necesarias para optimizar las llamadas de cola, aunque sí lo hacen muchas implementaciones. –