Página 1 de 1

Comparativa de lenguajes

NotaPublicado: 2008-01-15 08:17 @387
por roldan
He realizado una pequeña y chapucera comparativa entre varios lenguajes: C, Perl, Ruby y Python (lo tenéis más bonito en mi blog: http://roldarin.blogspot.com/2008/01/co ... jes-i.html ).
Lo que quería preguntaros es el asunto de Perl...

Comparativa entre lenguajes I

Vamos a ir realizando una comparativa entre varios lenguajes de programación.
Estos serán C, Perl, Python y Ruby.

Elegimos C porque es un lenguaje de nivel medio que nos dará un buen tope de eficiencia. Utilizaremos programas sencillos para que la habilidad como programador influya lo menos posible.

Tampoco nos importa la eficiencia. Se trata de una comparativa en tiempos de ejecución, no de realmente comparar lenguajes.

El primer programa consistirá en mostrar los primeros 33 números de Fibonacci. El último número calculado es 3524578.

Primero utilizaremos una implementación recursiva. Mostraremos además el número de llamadas a la función, que debería ser independiente del lenguaje.

También es de esperar que las funciones recursivas tarden más, ya que estamos haciendo simples sumas y al hacerlo recursivo añadimos complejidad.

RECURSIVO

Códigos

Sintáxis: [ Descargar ] [ Ocultar ]
Using c Syntax Highlighting
#include
long int llamadas=0;
int fibonacci(int n) {
llamadas++;
if (n==0 | n==1)
return 1;
else
return fibonacci(n-1) + fibonacci(n-2);
}

main() {
int i;

for (i=0; i<33;i++)>
printf("%d \n", llamadas);
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using ruby Syntax Highlighting
#!/usr/bin/ruby
$llamadas=0
def fibonacci(n)
$llamadas+=1
return 1 if n==1 or n==0
fibonacci(n-1)+fibonacci(n-2)
end

33.times { |num| puts fibonacci(num).to_s }
puts $llamadas.to_s
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
$llamadas=0;
sub fibonacci {
$llamadas++;
my $n = shift;
return $n if ( $n == 0 or $n == 1 );
return fibonacci( $n-1 ) + fibonacci( $n-2 );
}
for ($num=0; $num < n="=" n="=" llamadas="0" n="=" n="=" fibo="fibonacci">
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using python Syntax Highlighting
#!/usr/bin/python
llamadas=0
def fib(n):
global llamadas
llamadas+=1
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2)

for i in range(34):
print fib(i)
print llamadas
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4


Tiempos
Código: Seleccionar todo
C: 18454895 llamadas

real 0m0.636s
user 0m0.628s
sys 0m0.004s

Ruby: 18454895 llamadas

real 1m12.161s
user 1m4.044s
sys 0m8.093s

Perl: 29860668 llamadas !!!

real 1m5.042s
user 1m5.000s
sys 0m0.012s


Python: 18454895 llamadas

real 0m29.577s
user 0m29.562s
sys 0m0.004s



ITERATIVO

Códigos

Sintáxis: [ Descargar ] [ Ocultar ]
Using c Syntax Highlighting
#include
int fib (int n)
{
if (n==0 | n==1) return 1;
int a = 1, b = 1;
int i;
for (i = 3; i <= n; ++i) { int a_prev = a; a = b; b += a_prev; } return b; } main() { int i; for (i=0; i<34;i++) printf("%d \n", fib(i)); }
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using ruby Syntax Highlighting
#!/usr/bin/ruby
def fibonacci(n)
curr = 1 succ = 1
n.times do |i|
curr, succ = succ, curr + succ
end
return curr
end
33.times { |num| puts fibonacci(num).to_s }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using python Syntax Highlighting
#!/usr/bin/python
def fib(n): a, b = 1, 1 for i in range(n): a, b = b, a + b return a for i in range(33): print fib(i)
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -w
sub fibonacci
{
my ($n, $a, $b) = (shift, 1, 1);
($a, $b) = ($b, $a + $b) while $n-- > 0;
$a;
}
for ($num=0; $num < 33; $num++) {
print fibonacci($num) ."\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Tiempos
Código: Seleccionar todo
C:

real 0m0.002s
user 0m0.000s
sys 0m0.000s

Ruby:

real 0m0.012s
user 0m0.008s
sys 0m0.000s

Python:

real 0m0.028s
user 0m0.012s
sys 0m0.008s

Perl:

real 0m0.010s
user 0m0.004s
sys 0m0.004s


Vemos la potencia de C y que la recursividad se paga caro.

Extraña el numero de llamadas que hacer Perl, y que en modo iterativo Python sea más lento. Aun así parece el más rápido de los lenguajes interpretados.

Lo que más me extraña es lo de Perl. Lo comprobé varias veces y siempre salian una cantidad de llamadas
superior al resto.

¿A alguien se le ocurre el por qué?

NotaPublicado: 2008-01-15 10:33 @481
por explorer
No cabe duda de que la función debería dar el mismo número de llamadas que el resto de lenguajes:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -l
$llamadas = 0;

sub fibonacci {
    my $n = shift;
    $llamadas++;
    return $n if $n < 2;
    return fibonacci( $n-1 ) + fibonacci( $n-2 );
}

for ( 0 .. 32 ) {
    print fibonacci( $_ );
}
print "$llamadas\n";
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Esto es lo que sale:
Código: Seleccionar todo
18454895

real    0m29.155s
user    0m28.938s
sys     0m0.048s
Bueno, los tiempos son distintos a los tuyos porque mi máquina es distinta a la tuya.

Pero... este asunto de los tiempos es interesante...

Puede ser interesante cuando se comparan cosas que pertenecen al mismo universo de aplicación, pero estás comparando un programa en C, compilado, con programas escritos en lenguajes de script.

Hay muchos estudios que intentan comparar estas soluciones, para justificar en contra o a favor el uso de estas soluciones, pero casi todos están sesgados: solo tienen en cuenta un factor, como por ejemplo, en tu caso, la velocidad de ejecución.

Hoy en día, el hardware es barato, comparado con lo más caro: el programador. Por lo tanto, en un desarrollo real, lo importante es que el programador genere la solución lo más rápidamente posible. Si lo hace en un determinado lenguaje, se supone que ese lenguaje lo ha elegido alguien que presupone que es el mejor para esa tarea (a veces no: lo elige el jefe por razones desconocidas incluso para él).

El año pasado salieron comentarios que mostraban que un programador de Perl era 2.4 veces más productivo que un programador de Java, y generaba una solución 2,75 veces más corta.

El comparar velocidad de ejecución puede ser interesante si el problema a resolver requiere un esfuerzo computacional muy fuerte, según los requerimientos, frente a otros más livianos, como los de gestión.

Y una vez elegido el lenguaje, también la experiencia del programador será determinante. Cuanto más especializado esté, más posibilidades habrá de que encuentre una solución más óptima, que aproveche el hardware, dentro del poco tiempo que él ha prometido que tardaría en dar la solución.

Por ejemplo, el anterior ejemplo de Fibonacci es ideal para demostrar el uso de un módulo incluido en las distribuciones Perl llamado Memoize. Modificamos, muy ligeramente, el programa anterior, añadiendo estas líneas al principio:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
use Memoize;
memoize('fibonacci');
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Con este cambio, le decimos a Perl que "recuerde" todas las llamadas a la función fibonacci, ya que una solución recursiva hace muchas llamadas iguales con los mismos parámetros. No tiene sentido volver a calcular lo mismo una y otra vez si nos podemos acordar de lo que hicimos antes.

Si lo ejecutamos sale:
Código: Seleccionar todo
65

real    0m0.023s
user    0m0.016s
sys     0m0.004s
Sí... solo son necesarias 65 llamadas para resolver el problema del cálculo de Fibonacci de los primeros 33 números. Y en poco más de dos centésimas de segundo.

Mil veces más rápido, con solo añadir dos líneas.

¿Cómo medimos esa mejora con un test como los que muestras?

Otra cosa, en la solución iterativa, la implementación correcta es esta:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -l
sub fibonacci {
    my ($n, $a, $b) = (shift, 0, 1);
    ($a, $b) = ($b, $a + $b) while $n-- > 0;
    $a;
}

for ( 0 .. 32 ) {
    print fibonacci($_);
}
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
para que salga el mismo resultado que en la recursiva.

NotaPublicado: 2008-01-15 14:28 @644
por roldan
Joder, ya veo dónde metí la pata. Resulta que hay códigos que empiezan 0,1,2,3,5.....3524578
mientra que otros son 1,1,2,3,5.....3524578.

Supuse equivocadamente que esto no sería importante a efectos generales pero sí lo es debido a que también implica cambiar la cota para parar de calcular.

He corregido los scripts para que den todos 1,1,2,3,5.....3524578. Ahora si que dan el mismo número de llamadas como cabría esperar. :D

A modo de curiosidad, ahora estoy en otro ordenador, lanzo el script de Python, y funciona, si lo vuelvo a lanzar peta, y sigue petando:
Código: Seleccionar todo
....
 return fib(n-1) + fib(n-2)
 RuntimeError: maximum recursion depth exceeded


Mañana los vuelvo a probar y pongo tiempos.

La idea de mis comparaciones era ver cómo se comportan Perl, Python y Ruby respecto a realizar tareas SENCILLAS.

Más que nada por curiosidad. Puse C para ver cuánta eficiencia se perdía al hacerlo en un lenguaje de más alto nivel. Estuve por poner ensamblador pero lo tengo muyyy olvidado.

Además, Ruby, Python y Perl me parecen que están escritos en C, así se puede ver la complejidad que hay detrás al menos en lo que a estos cálculos se refiere.

Como ya dije, a pesar del título, no es una comparativa de lenguajes sino más bien de tiempos de ejecución. Tengo pensado hacer alguno más, para comparar cómo manejan estructuras de datos, IO, cálculos "complejos". Curiosidad... cada lenguaje es diferente, y sobre todo depende del proyecto a realizar y del programador.

Respecto a la productividad yo creo que el mejor es Ruby, cuya filosofía es justamente esa, desarrollar código lo más rápido y fácilmente posible. Aunque a costa de eficiencia.

La idea de Perl de que cada cosa se puede hacer de varias maneras se lleva al límite en Ruby, cada cosa se puede escribir de varias maneras, aunque hagan lo mismo, y esa comodidad se paga.

También me imaginaba que Python los superaría en tiempos, es un lenguaje muy ordenado y eso para un interpretado es muy importante. La filosofía de Python tira más hacia solo hay una manera clara de hacerlo.

También conocía el memoize, y por supuesto que se pueden hacer mejoras, como por ejemplo reemplazar if n==1 or n==0 por if n<2 te ahorras una comparación y un or. Pero justamente no me interesaba la eficiencia del código, y mucho menos tirar de bibliotecas sino que los códigos dieran trabajo y que fueran lo más análogos posibles en todos los lenguajes.

NotaPublicado: 2008-01-15 17:39 @777
por explorer
Pues nada, es mejor hacer eso que no hacer nada, desde luego.

Te animo a que sigas investigando.

No puedo decirte si Ruby es más productivo, ya que no lo conozco. He intentado aprender Python pero solo he visto problemas comparado con Perl. Por eso sigo con él. Te animo a que los aprendas todos y así luego decir cuál es el mejor. Yo ya decidí, pero en parte, por mi edad :-)

Por favor, edita el mensaje con los ejemplos y arréglalos...

NotaPublicado: 2008-01-15 19:10 @840
por creating021
Yo, curioso por C y C++, comparé los tiempos de ejecución de un Hola Mundo y como era de esperarse, C es más rápido que C++ ya que C usa menos librerías, esto lo digo ya que pasa lo mismo con los scripts y sería interesante comparar estos lenguajes usando librerías/módulos ya que el tiempo de ejecución aumenta.

Por otro lado, cada lenguaje tendrá su truco... como memoize o SelfLoader, pero lo que cuenta es lo que haces... nada te da hacer un programa ( mal hecho ) en ASM, aunque un ejecutable de estos tome menos tiempo en ejecutar, el código mal hecho usará ese tiempo o más.

NotaPublicado: 2008-01-16 04:53 @245
por roldan
Ya lo he corregido, espero. Los códigos ahora van por separado.
El editor de blogger es un poco penoso. Si editas un post a veces te lo corta por las buenas, confunde partes del código con html... hay que hacer apaños.

He hecho otra comparativa
http://roldarin.blogspot.com/2008/01/co ... es-ii.html

Como podéis ver el tiempo de calculo de Perl es muy bueno.

El siguiente será comparar IO.

Yo de Python apenas sé. De C y Perl tengo poca practica. Me llevo bastante mejor con Ruby.
Se aprende enseguida ya que está enfocado a ponerle las cosas fáciles al programador.