RubyInline, hacer que se hagan las cosas mas rapido cuando es necesario de forma simple
September 12th, 2006
Este articulo fue anteriormente publicado por Pat Eyler sobre herramientas Test-Primero (Test-first). Fue republicado por Rubylation Network y esta disponible en otros lenguajes
Hoy en ruby-talk, encontre un hilo de conversacion llamado “Para obtener performance, escribe en C” (“For Performance, Write it in C”). Me gustaria presentar una pequeña enmienda a dicha tesis. Permitanme explicarles.
Todo comenzo con un email en mi trabajo. Alguien estuvo distribuyendo un generador de numeros primos en distintos lenguajes (C, Java, C#, Perl, and Python). Todos ellos usando el mismo (feo) algoritmo, supuestamente para mostrar cuan performante era cada lenguaje. Desde que soy el evangelista local de Ruby, fui solicitado para escribir la version Ruby. Aqui esta con lo que comence (cuidado, hay un feo algoritmo delante):
How fast is it? Well, time says: Cuan rapido es? Bien, los tiempos dicen:
$ time ./primes.rb > /dev/null real 0m2.905s user 0m2.716s sys 0m0.004s $
Ciertamente, nada para como para tomar nota, pero no tan lejos de Perl o Phyton tampoco.
Deseando mejorarlo, y no pudiendo modificar el algoritmo (deseamos comparar manzanas con mermelada a lo sumo, pero no manzanas con naranjas). Se que mi unica esperanzae es encontrar el/los cuello/s de botella y reescribirlo/s en C?. Mi primer paso es usar el Ruby profiler y ver que es lo que dice (oh, ya que estamos, reduje el valor de num a 100 asi esto puede completarse dentro de el ciclo de mi vida…. el profiler es lento)
$ ruby -rprofile primes.rb > /dev/null % cumulative self self total time seconds seconds calls ms/call ms/call name 63.64 0.14 0.14 101 1.39 3.56 Range#each 13.64 0.17 0.03 1133 0.03 0.03 Fixnum#% 9.09 0.19 0.02 1233 0.02 0.02 Fixnum#== 4.55 0.20 0.01 100 0.10 0.20 Kernel.puts 4.55 0.21 0.01 200 0.05 0.05 IO#write 4.55 0.22 0.01 248 0.04 0.04 Fixnum#to_s 0.00 0.22 0.00 74 0.00 0.00 Fixnum#/ 0.00 0.22 0.00 100 0.00 0.00 Fixnum#- 0.00 0.22 0.00 1 0.00 220.00 #toplevel $
Lo cual me dice que la mayoria de mi tiempo se esta consumiendo en each (bien, actualmente consumido en el block que envie a each. Se esta tomando unos alegres 1.39 msec por llamada, comparados a .0X msec por el resto. Que deberia pasar si reescribo ese block?
Entra a RubyInline (Una gran herramiente escrita por zenspider and Eric Hodel). Yo no soy un guro en C y de ningun modo me podria calificar de experto, pero este es realmente facil de utilizar. Mi nuevo codigo queda asi:
No demasiado feo. Al menos aun se puede ver que esta pasando. El loop principal en Ruby ayuda mucho tambien (especialmente si este fuese un programa mucho mas grande). Cuanta diferencia hace esto? Veamos que dicen los tiempos:
$ time ./cprimes.rb > /dev/null real 0m0.328s user 0m0.288s sys 0m0.020s
De acuerdo a la magnitud de la mejora. Nada despreciable.
Cual es la leccion aqui? Optimizar lo que necesites (y solo lo que necesitas), con profile encontrara que necesitas optimizar (puede ser lento, pero profiling es tu amigo), usa las herramientas correctas (reescribir una parte del codigo con RubyInline es mejor que reescribir toda la aplicacion en C).
Este articulo fue publicado el miercoles 6 de septiembre a las 23:00 hs. Disponible en otros lenguajes en The Rubylation Network





Leave a Reply