Rails Summit Latin America

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