Email

Optimizing a Python Program

These days, I've been optimizing a Python program I wrote. Optimizing is a fun task, but very difficult. Most of the time, the first solution I think is even worse than the previous situation. I need more experience.

Some processes were too slow in my program and I realized it was because I was performing too much disk I/O operations. I thought a solution could be read more data in memory and operate there. Now I have excessive memory consumption.

Here is a very simplified description of my memory consumption problem:

I have a text file. Each line in the file represents an item of a large list. Each line has two string values separated by a character. Something like a CSV file. I have to read the file content and put it in a list.

A line in the file looks like this:

Content of the first value|Content of the second value

The separator is '|'

Here is a simple Python program that read the file:

class Field:
    def __init__(self, line):
        self.value1, self.value2 = line.split('|')

fields = []

with open('test_data') as file_:
    for line in file_:
        fields.append(Field(line))

Running this program with a test file of about 42 MB gives this results:

Execution time (time): 0m4.108s
Memory consumed (pmap): 166652K

I was surprised by the high memory usage of the program. If the file is 42MiB, I thought the program should use a similar amount of memory, obviously a higher amount, but not almost four times the size of the file.

An equivalent program in C (error checking is omitted):

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

#define VALUE1_MAX 30
#define VALUE2_MAX 80
#define LINE_SIZE VALUE1_MAX + VALUE2_MAX + 3
#define BUFFER 10000

typedef struct
{
    char value1[VALUE1_MAX+1];
    char value2[VALUE2_MAX+1];
} field;

int main()
{
    FILE *file = fopen("test_data", "r");

    field *fields = (field*) malloc(BUFFER*sizeof(field));
    char line[LINE_SIZE];
    char *part;
    long i=0;
    long size = BUFFER;
    while(fgets(line, LINE_SIZE, file) != NULL) {
        part = strtok(line, "|");
        strcpy(fields[i].value1, part);
        part = strtok(NULL, "|");
        strcpy(fields[i].value2, part);

        i++;
        if (i == size) {
            size += BUFFER;
            fields = (field*) realloc(fields, size*sizeof(field));
        }
    }
    fclose(file);
    free(fields);
    return 0;
}

Results for the C program:

Execution time (time): 0m0.537s
Memory consumed (pmap): 57548K

This is much better.

The problem with the Python program seems to be the Field objects using more memory than they need. Testing the program without the Field creations, changing fields.append(Field(line)) with fields.append(line) seems to perform better:

Execution time (time): 0m0.575s
Memory consumed (pmap): 66808K

Clearly, the Field object is the bottleneck both in memory consumption and execution time. This is probably because of some default memory allocations that Python makes for the object and its fields. Python is a really cool language, but it doesn't let you control the way the memory is used. This is a positive thing in most of the cases, but in some of them, like this one, is negative.

Most of the times, there are only very small parts of a program that really need to be optimized. And a programmer is much more productive with Python than with C. It doesn't make sense to rewrite the program in C. Instead, a C module could be written for the bottlenecks.

I was too lazy to learn how to use the Python C API, so I looked a this project called Cython. Cython is a language designed for writing Python extensions. It's very similar to Python, but is translated to C and compiled to an easy to use Python module. Cython also lets you mix C code and Python code easily. It lets you use high level python objects or low level C data types as you need and mix them properly.

I decided to rewrite the Field class in Cython:

#field.pyx
DEF VALUE1_MAX = 30
DEF VALUE2_MAX = 80

cdef extern from "string.h":
    char *strcpy(char *dest, char *src)

cdef class Field:
    cdef readonly char value1[VALUE1_MAX+1]
    cdef readonly char value2[VALUE2_MAX+1]

    def __init__(self, line):
        v1, v2 = line.split('|')
        strcpy(self.value1, v1)
        strcpy(self.value2, v2)

This extension type can be used almost in the same way than a real Python object:

>>> f = Field('Hello|World')
>>> f.value1
'Hello'
>>> f.value2
'World'
>>> 

I had to modify the original Python script to use the new module:

from field import Field

fields = []

with open('test_data') as file_:
    for line in file_:
        fields.append(Field(line))

Results of the new program:

Execution time (time): 0m1.257s
Memory consumed (pmap): 69800K

This is a huge improvement. With a very small change, the program now consumes almost 100MB less memory and it runs three times faster. I could write more parts in Cython, using strtok() instead of str.split(), or even rewriting the entire list and reading process. I would probable get a performance very similar to the C program. But I'm comfortable with the results now. I'm still surprised with the small effort compared to the awesome results.

If you want to do your own tests. Here is a simple script to generate a test file with 500k values:

import string
import random

with open('test_data', 'w') as f:
    for i in range(500000):
        value1 = ''.join(random.choice(string.letters)
                         for s in range(random.randint(15, 30)))
        value2 = ''.join(random.choice(string.letters)
                         for s in range(random.randint(50, 80)))
        f.write(value1 + '|' + value2 + '\n')

Loading... Vote up! Vote down! Discussion (4)

La Magia de las Piramides

En estos días estaba pensando en que las famosas pirámides o "empresas captadoras de dinero", como eufemísticamente las llaman algunos medios, dejaron de ser algo que se veía sólo en la televisión, en los noticieros, para convertirse algo de casi todo el mundo. Me puse a pensar y me dí cuenta que de las personas que conozco aquí en Popayán, son más los que han metido plata en las pirámides que aquellos que no. En toda parte se oye hablar de eso: en el bus, en la calle, en la universidad, donde el odontólogo, en el banco. Familiares, amigos y desconocidos hablan de todo el dinero que están ganando con estos sistemas. No se trata de gente poco educada que por ignorancia y ganas de dinero fácil se mete, ahora es normal ver profesores universitarios, con todo y sus títulos de doctorado, "invirtiendo" en estas "empresas". Lo mismo para los estratos económicos, ahora se ve desde el celador hasta el juez.

Las pirámides siempre argumentan su toque de midas diciendo que lo que pasa es que estamos acostumbrados a que los bancos nos roben y se vuelvan ricos a costa de nuestro dinero. Aunque no soy defensor de los bancos (Como decía mi profesor de derecho laboral, el sistema financiero es la causa de todo mal), estoy convencido que esta justificación es un grave error.

Para tener perspectiva, hagamos un ejemplo: DRFE (Dinero Rápido Fácil y Efectivo o "El Hueco" como le dicen aquí), una de estas "empresas", pagaba hasta hace unos días 100% del valor invertido en un mes. Esto significa que si yo meto un millón de pesos, en un año puedo reclamar 4.096 millones. ¿Les parece una exageración? No lo es. Lo que pasa es que no estamos acostumbrados a hacer proyecciones exponenciales, y nos pasa lo mismo que le pasó a Rai Bhalit. Expliquémolo paso por paso:

Si me dicen que en un mes me dan el 100% de lo que invertí, significa, en otras palabras, que mi dinero se duplica en un mes. Si meto un millón, al mes recibo dos. Ahora:

Monto inicial: 1 millón.

1° mes: 2 Millones.

2° mes: 4 Millones.

3° mes: 8 Millones.

4° mes: 16 Millones.

5° mes: 32 Millones.

6° mes: 64 Millones.

7° mes: 128 Millones.

8° mes: 256 Millones.

9° mes: 512 Millones.

10° mes: 1.024 Millones.

11° mes: 2.048 Millones.

12° mes: 4.096 Millones.

En resumen: total = monto_inicial * 2 ^ numero_meses

Increíble ¿Verdad?

Ahora, digo que hasta hace unos días esto es lo que estaba ofreciendo DRFE, porque últimamente he escuchado que ahora está dando el 150%. Lo que significa que metiendo un millón, al año puedo reclamar casi 60.000 Millones (total = monto_inicial * 2.5 ^ numero_meses).

Por otro lado, la esperanza de muchos está en otras "empresas captadoras" que son más "moderadas" como DMG. DMG entrega un 10% mensual o un 100% semestral. Sin embargo, si hacemos cuentas, igual obtenemos resultados mágicos. Con DMG si meto 1 millón de pesos en cuatro años tengo 256 millones. Puedo comprar casa, ¡adiós hipotecas a 30 años!

Este sistema exponencial de reproducir el dinero no puede ser igualado por ningún modelo de negocios. Ni siquiera el narcotráfico puede generar tanto. Ni siquiera Google, una de las empresas que más rápido ha generado dinero en todo el mundo. ¿Cuanto tendría Google en sus diez años si hubiera invertido el monto con el que comenzó en algo como DRFE?

Entonces ¿Cómo diablos es que funcionan estas empresas? Pues es simple, son pirámides. Las pirámides son simplemente mecanismos de flujo de dinero diseñados para quitarle el dinero a muchas personas y dárselo a unas pocas. En elblogboyacense.com tienen una explicación muy sencilla que me voy a permitir copiar:

Imagínense que yo envío un correo a 5 personas, que llamaré “mis amigos”; les indico que sólo han de depositar 10.000 pesos en mi cuenta.

Yo gano 50.000 pesos, y no me costó nada. Mis 5 “amigos” han gastado 10.000 pesos cada uno.

Si aquí se rompe la cadena, yo gané 50.000 pesos sin hacer nada, y 5 “amigos” perdieron 10.000 pesos cada uno.

Si yo les digo a esos 5 “amigos” que cada uno escriba un correo a 5 personas y que repitan la operación, tendremos ya 25 personas involucradas.

Mis 5 “amigos” habrán recibido 50.000 pesos cada uno. Y como se gastaron 10.000 en mi, habrán ganado 40.000 pesos, es decir, el 400% de la inversión que hicieron conmigo.

Si aquí se rompe la cadena, resulta que:

- yo gané 50.000 pesos, sin hacer ningún trabajo productivo - mis 5 amigos ganaron 40.000 pesos cada uno, sin hacer ningún trabajo productivo - los 25 amigos de mis amigos han perdido cada uno 10.000 pesos

Total de ganancias de la cadena: 50.000 + 5*40.000 = 250.000 pesos

Total de pérdidas de la cadena: 25*10.000 = 250.000 pesos

Por tanto, 6 personas (mis 5 amigos y yo) han engañado a 25 personas.

Ese es el esquema tradicional de las pirámides. Es obvio que es algo insostenible y también que al final los perdedores resultan ser un número exponencialmente mayor a los ganadores. Pero entonces ¿Cómo diablos se mantienen estas empresas como DRFE? Según dicen, esta empresa ya lleva 3 años funcionando. DMG también lleva un buen tiempo en el escenario. Tengo una hipótesis al respecto. Creo que estas empresas han perfeccionado el sistema clásico de la pirámide hasta un modelo que dura mucho más y por tanto hace caer a mucha más gente.

En primer lugar, a diferencia del ejemplo de elblogboyacense, en estas empresas no es cada persona la que se encarga de conseguir las otras que servirán de su base en la pirámide, en cambio, de esto se encarga la empresa, lo cual automáticamente genera confianza entre los que entran al sistema.

En segundo lugar, la pirámide se hace mucho más angosta, reduciendo el coeficiente sobre el que se paga. En lugar de pagar 400% como en el ejemplo, se paga 100%, esto hace que para pagar a una persona se necesitan dos en lugar de cinco. Por tanto la pirámide dura mucho más con el mismo número de gente. En el caso de DMG con el 10% la pirámide es mucho más angosta todavía.

En tercer lugar, está lo que he denominado "ganancia virtual vs ganancia real". Para explicarlo voy a dar un ejemplo: Hace un mes, un amigo mio decidió meter un millón de pesos en DRFE. Por estos días fue a recoger sus ahora dos millones. En vista de lo bueno del negocio, decidió sacar un millón y dejar otro millón para que vaya engordando. Sus cuentas son que en Febrero tendrá 17 Millones. Si lo vemos detenidamente, mi amigo invirtió un millón y al cabo de un mes tiene el mismo millón en sus bolsillos, más otro millón "invertido". Ese otro millón es "ganancia virtual". En este caso él no tuvo ganancia real, porque metió un millón y salio con un millón. Por supuesto que uno puede decir que él hubiera podido sacar los dos millones y haber tenido una ganancia real, y es cierto, pero lo relevante aquí es que este patrón, según lo que he observado, es muy común en la mayoría de los inversores. Ellos meten un monto de dinero, al primer ciclo sacan lo invertido o una cantidad menor y dejan las ganancias engordando. Al hacer esto comprueban que la empresa si cumple y además les da confianza pensar que no van a perder mucho si se cae. Mi teoría es que la mayor parte de las ganancias de las que habla la gente son virtuales, es decir, que no han llegado a sus bolsillos realmente y probablemente nunca llegarán. Todo esto en otras palabras, quiere decir que la gente está usando sus propias ganancias para generar nuevos eslabones inferiores de la pirámide, otro factor que influye en que esta dure más.

Lo grave es que no todo el mundo juega con su propio dinero. Hay muchas personas haciendo prestamos al banco y vendiendo todo lo que tienen para meterlo a la pirámide. Los clasificados del periódico están cada vez más llenos de anuncios de gente vendiendo negocios, bienes raíces y carros.

De todos modos, con todos los trucos que han usado estas empresas para alargar la vida de las pirámides, todavía tienen que cumplir con una regla esencial: El crecimiento de la pirámide debe ser exponencial. Esto quiere decir que para que la pirámide se sostenga, cada mes deben tener un numero exponencialmente mayor de nuevos clientes. En el momento que esto se deje de cumplir, el sistema colapsa. Estas empresas han sabido muy bien como cumplir con esta meta. Es por eso que en sólo tres años, DRFE que comenzó sólo en Pasto, ahora ya está en un montón de ciudades. Sin embargo, eventualmente sera imposible seguir con el mismo ritmo. El hecho de necesitar cada vez un numero mayor de personas nuevas concuerda sospechosamente con el patrón de pagos. DRFE comenzó pagando el 20%, luego fue subiendo poco a poco, llegó a 50%, 80%, luego 100% y ahora anuncia 150%. Peligrosamente anchan la pirámide con el objetivo de atraer más gente. Pero en este tipo de pirámides, entre más ancha, más rápido cae. Podría estar cerca la caída de DRFE.

Esto va a terminar mal, muy mal. Los perdedores van a ser demasiados. Probablemente será una de las más grandes estafas que se hayan conocido. Probablemente haya mucha violencia también. Aquí en el Cauca ya se han presentado casos de pequeñas pirámides de barrio, en donde los enfurecidos perdedores han ajusticiado a cuanta persona relacionan con los estafadores. No quiero imaginarme como terminará todo esto con estas súper pirámides.

Hoy cuando iba caminando por la calle, alguien estaba vendiendo un CD con "Un documental sobre El Hueco" el cual compré y resultó ser una entrevista a los dueños de DRFE hecha por un canal de Pasto. Decidí subirlo a YouTube:

Parte 1

Parte 2

Parte 3

Parte 4

Se los dejo a los curiosos.

Update

Datos curiosos:

En vista de que Federico me hablaba por Twitter sobre invertir la plata que nos dio Google por el Summer of Code (US $4.500). Manuel y yo hablabamos sobre algunas de las maravillas que se podrían hacer:

Si la dejo a dos años al 100% tendría: US $75.497'472.000:

Si la dejo a dos años al 150% tendría: US $15'987.211'554.602:

¿Más ideas?

Update 2

   

Loading... Vote up! Vote down! Discussion (12)

Summer of Code: Lunar Eclipse Final Report

I had a lot of fun participating in the Google Summer of Code. As I said before, I worked improving Lunar Eclipse, an open source visual designer for Silverlight/Moonlight. This is what I achieved during the summer:

Tools & Handle Support:

I worked on editing support for the following Silverlight Elements:

Circle, Ellipse, Rectangle and Square
These are the basic boxed shapes. Previous version of Lunar Eclipse already had support for this elements. Anyway I almost refactored the entire Handle & Tool subsystem. This helped me to adapt it to more complex shapes like bezier paths.
Line and PolyLine
Simple two point line and multiple point line.
Bezier Path and Pen Tool
I worked on two tools for creating Path figures. One let you create a path using Bezier Segments. This is working fine but there are a couple of bugs regarding translations of points. I also implemented a Pen tool for creating paths based on manual movement, it's working, but it needs to be optimized to produce less nodes.
TextBox
I have partial TextBox support. I couldn't implement graphical text editing because the Entry control is not yet implemented in Moonlight.

I also tried to add Image support. But it was impossible to use the Downloader and set media to objects using the GtkSilver widget (used for creating Desktop Moonlight applications).

Screenshot de Lunar Eclipse

Screenshot de Lunar Eclipse

Screenshot de Lunar Eclipse

Other features

Selection Rect - Select All - Clear Selection - Delete Selection
I improved the selection rectangle and all the selection subsystem. This was important in order to implement other operations such as ordering and alignment. The use of keys for selection (like control to add to selection) is not working because the Keyboard class was not implemented in Moonlight at the time.
Property Panel
The properties panel was fixed. I believe that in the future, Lunar Eclipse will use the toolbox system of MonoDevelop, but some of the internal parts of this work, such as property introspection, can be reused in the future.
Infinite Undo Redo
Undo and Redo was implemented in previous versions of Lunar Eclipse, anyway, I fixed a lot of bugs related with this and implemented Undo - Redo support for all tools and operations.
File Open / Save
As simple as that. Files can now be saved and loaded :)
Zooming and Scrolling
Zooming is now possible. There are some ugly effects caused by the GtkSilver implementation. I guess this will be fixed in the future. Scrolling is working good but it needs improvements to be more usable.
Ordering and Alignment
Send to Front, Send Backwards, Align Right, etc.
Serialization and Back
Serialization has been fixed. Loading from XAML is working too so you can move back and forth between Xaml and Design. Serialization still need a lot of love. Output is too verbose and the text indentation structure is not 'remembered' by the serlializator. I want something similar to the MS Expression Blend's system, which is awesome.
Copy - Paste - Clone
Clipboard operations. Copy was a bit difficult because Silverlight elements don't have a clone method. I Implemented my own Clone method based on the serialization work.
New interface
This wasn't a critical feature, but I rewrote the GTK# based user interface. This was an easy task thanks to the awesome MonoDevelop.

Video Demo:

Here is a demo video showing some of the features of Lunar Eclipse:

High resolution version

Future

It was really difficult to work on Lunar Eclipse the last weeks of the Summer of Code. Moonlight hackers started to work heavily on the 2.0 version. Moonlight became very unstable and its API changed a lot, (besides Silverlight 2 Beta 2 API changed too). All these changes broke Lunar Eclipse. The last objective, animation support, was not completed. Anyway, I definitely want to keep working on this project. I decided to freeze Lunar Eclipse while Moonlight gets more stable, but I started to work on adding support for Silverlight projects to MonoDevelop. After that, I want to fix bugs in LE and start isolating the design surface to be easily plugged in applications such as MonoDevelop or a Web Based Lunar Eclipse.

Loading... Vote up! Vote down! Discussion (6)

Python vs C#: Queries

One of the most beloved C# 3.0 features is Linq. Linq brings great power to C#, it allows you to easily write structured queries over collections or remote data sources. Now with C# is possible to make queries as easy as with other languages like Python. I decided to compare the way you make queries with C# and with Python. I found a great page showing 101 Linq examples, I decided to write Python versions of this examples. Which version do you like more?

Where - Simple 1

C# version:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var lowNums = from n in numbers where n < 5 select n;
Python version:
numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0]
low_nums = (n for n in numbers if n < 5)

Where - Indexed

C# version:

string[] digits = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
var shortDigits = digits.Where((digit, index) => digit.Length < index);
Python version:
digits = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
short_digits = (digit for index, digit in enumerate(digits) if len(digit) < index)

Select - Simple 1

C# version:

var numsPlusOne = from n in numbers select n + 1;
Python version:
nums_plus_one = (n + 1 for n in numbers)

Select - Anonymous Types 1

C# version:

string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };

var upperLowerWords =
    from w in words
    select new {Upper = w.ToUpper(), Lower = w.ToLower()};

Python version:

The exact Python version would be something like:

words = ['aPPLE', 'BlUeBeRrY', 'cHeRry']

upper_lower_words = ( type('', (), {'upper': w.upper(), 'lower': w.upper() })
                      for w in words)

But I feel more Pythonic this:

upper_lower_words = ( (w.lower(), w.upper()) for w in words)

Or even this:

upper_lower_words = ( {'upper': w.upper(), 'lower': w.upper() }
                      for w in words)

SelectMany - Compound from 1

C# version:

int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };

var pairs =
    from a in numbersA,
         b in numbersB
    where a < b
    select new {a, b};

Python version:

numbersA = [0, 2, 4, 5, 6, 8, 9]
numbersB = [1, 3, 5, 7, 8 ]

pairs = ( (a, b) for a in numbersA 
                 for b in numbersB 
                 if a < b)
   

SelectMany - from Assignment

C# version:

var orders = from c in customers,
                  o in c.Orders,
                  total = o.Total
             where total >= 2000.0M
             select new {c.CustomerID, o.OrderID, total};

Python version:

I couldn't find how to make the assignment in Python, so the version is:

orders = ( {'customer_id': c.customer_id,
            'order_id': o.order_id,
            'total': o.total }
           for c in customers
           for o in c.orders
           if o.total > 2000)

SelectMany - Multiple from

C# version:

var orders = from c in customers
             where c.Region == "WA"
             from o in c.Orders
             where o.OrderDate >= cutoffDate
             select new {c.CustomerID, o.OrderID};
         

Python version:

orders = ( (c.customer_id, o.order_id)
           for c in customers if c.region == 'WA'
           for o in c.orders if o.date >= cutoff_date)

Take Simple

C# version:

var first3Numbers = numbers.Take(3);

Python version:

if we are working with something like a list, we could do:

first_3_numbers = numbers[:3]

but, if we are working with iterators, we must do:

first_3_numbers = itertools.islice(numbers, None, 3)

Skip - Simple

C# version:

var allButFirst4Numbers = numbers.Skip(4);

Python version:

all_but_fist_4_numbers = numbers[4:] # list version
all_but_fist_4_numbers = itertools.islice(numbers, 4, None) # iterator version

TakeWhile - Simple

C# version:

var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);

Python version:

fist_numbers_less_that_6 = itertools.takewhile(lambda x: x < 6, numbers)

SkipWhile - Simple

C# version:

var allButFirst3Numbers = numbers.SkipWhile(n => n % 3 != 0);

Python version:

all_but_first_3_numbers = itertools.dropwhile(lambda x: x % 3 != 0, numbers)

First & Last

C# version:

numbers.First()
numbers.Last()

Python version:

numbers[0]  # first for a list
numbers[-1] # last for a list

numbers.next()   # first for iterator
list(numbers)[0] # first for iterator

list(numbers)[-1] # last for iterator

First - Indexed

C# version:

int evenNum = numbers.First((num, index) => (num % 2 == 0) && (index % 2 == 0));

Python version:

even_num = [n for i, n in enumerate(numbers) if n%2 == 0 and i%2 == 0][0]

or:

even_num = (n for i, n in enumerate(numbers) if n%2 == 0 and i%2 == 0).next()

to be continued...

Loading... Vote up! Vote down! Discussion (5)

Segunda GUADEC

A menos que algo extraordinario ocurra, este año asistiré por segunda vez a GUADEC. La primera vez, en Birmingham, la pasé muy bien. Esta vez será en Estambul, lugar que, según he escuchado, es uno de los más bellos en el mundo. Estoy muy emocionado por este viaje. Muchas gracias a la Fundación GNOME por patrocinar gran parte de mis gastos.

Mi tiquete

Loading... Vote up! Vote down! Discussion

-soc

[unable to open file: weblogs/ceronman/-soc]

Loading... Vote up! Vote down! Discussion

I am a SOCker !

I'm participating in the Google's Summer of Code of this year! I was selected for the Mono Project. I'm going to work on a Visual XAML Editor for Moonlight called Lunar Eclipse. LE was started months ago by Alan McGovern (from MonoTorrent). My work is to improve it and make it shine!. My mentor is the famous Miguel de Icaza

This is going to be the funniest summer ever!!

Loading... Vote up! Vote down! Discussion (8)

Distributed Version Control

Actualmente se está dando una avalancha de migraciones desde sistemas de control de versiones centralizados (Subversion) hacia sistemas distribuidos. Prácticamente todos los grandes proyectos de software libre o ya migraron o están en discusiones sobre la migración o están usando sistemas paralelos.

A diferencia de lo que sucedió con Subversion, donde también hubo una avalancha de migraciones desde CVS, en esta ocasión hay varias buenas alternativas, lo que hace la cosa mucho más interesante. La guerra entre Git, Mercurial y Bazaar, está generando un montón de innovaciones en el campo del control de versiones. Nos está beneficiando mucho a nosotros los usuarios.

La carrera por popularidad la va ganando Git con proyectos como Linux, X.org, freedesktop.org, Wine, OLPC, entre otros. Mercurial va muy cerca con proyectos como Mozilla, OpenSolaris, OpenJDK, Xen, entre otros. Bazaar va en último lugar siendo usado por Ubuntu y otros más.

Git está escrito en C y muchos de sus comandos son scripts de Shell y Perl. En consecuencia, el soporte de Git en Windows es muy pobre. Mercurial está escrito principalmente en Python. Algunas rutinas cuello de botella están escritas en C. Mercurial funciona muy bien en Windows. Bazaar es 100% Python. Funciona muy bien en Windows. Mercurial y Bazaar son extensibles via Python.

Git va en primer lugar en cuanto a velocidad, Mercurial de segundo, Bazaar de tercero.

Git tiene una interfaz de usuario difícil de comprender. Mercurial y Bazaar son muy fáciles de usar.

En cuestión de documentación, Mercurial se lleva el primer puesto, Bazaar el segundo y Git, de lejos, el tercero.

Elegir uno de los tres es una tarea complicada. Hay un millón de páginas y entradas de blog que hablan a favor de uno u otro. Como van las cosas es muy posible que haya que aprender a usar los tres, al menos a un nivel básico. Mi elección personal ha sido Mercurial, por su documentación, facilidad de uso y velocidad.

Mi experiencia en las últimas semanas con Mercurial ha sido muy placentera. Los sistemas distribuidos no solo dan una gran ventaja para proyectos grandes y con muchos contribuyentes, sino también hace las cosas mucho más fáciles en proyectos pequeños de uno o pocos desarrolladores. Empezar a versionar un proyecto nunca fue más fácil.

Tengo muchos pequeños proyectos que no están versionados, por ejemplo los de PyWeek. No lo hago porque me da pereza arrancar un nuevo repositorio de Subversion, importar, hacer checkout... Con Mercurial es tan fácil comenzar un nuevo repositorio que nunca más tendré un proyecto no versionado.

La velocidad es un factor muy importante a la hora de que a uno no le de pereza usar un sistema de control de versiones. Con Mercurial todas las operaciones se sienten instantáneas. En cambio Subversion se siente taaan lento, incluso usado localmente.

El poder clonar repositorios fácilmente me da más seguridad. Sé que es menos probable que se pierdan datos. Sitios como freehg.org y github.com son lo máximo.

Trabajar al estilo Branchy Development se siente muy bien. Ahora tengo la costumbre de crear un branch por cada nueva característica, por pequeña que sea. Trabajar paralelamente cada cosa en un branch aparte es muy cómodo, ya que una cosa no rompe la otra. Además todo el historial termina mucho más organizado. Luego cuando todo está listo, un merge sin traumas.

A la hora de colaborar a un proyecto de software libre, me parece fundamental el hecho de que uno siempre tiene acceso al sistema de control de versiones. A diferencia de Subversión donde sólo el que tiene acceso de commit puede usarlo. El sistema de red de confianza del que hablaba Linus Torvals en el famoso vídeo de Git, también me parece una forma más natural de hacer las cosas.

Hay dos cosas que no me gustan de Mercurial. Una es que el soporte para Subversion es bastante pobre. No hay algo tan bueno como Git-svn. La otra es que la forma de hacer branches haciendo clones todavía se siente muy Subversion, me gusta más el estilo Git (Ivan habla de ello más detalladamente). No uso Git porque en el trabajo eventualmente me toca hacer ciertas operaciones en Windows. Sin embargo, como dije antes parece que dominar Git va ser esencial pronto. De todos modos aprender Mercurial me ha servido para entender muchos conceptos que no entendí bien cuando comencé a leer sobre Git.

Loading... Vote up! Vote down! Discussion (5)

PyBotLearn

PyWeek is finished. Unfortunately, I didn't have enough time for it this year, so I'm almost a DNF. The theme for this year was Robot. My idea ended up being more like an application than a game. I wanted to create an educational environment for teaching programing to children. Something similar to Logo. The idea was to build a game where your could program a small robot. It should be interactive so children could experiment on the fly.

In other words, It should be something like this: A window with a robot and a world, bellow a Python console. The users should be able to write commands in the console and the robot should execute them. The robot should be able to interact with it's environment and with the user too. Error, warnings, etc, should be given to the programmer in a friendly way. Additionally, there should be various challenges that the programmers should solve. This should be the fun factor for the game.

But, unfortunately, as I said before, I didn't have more that a couple of hours every day since Thursday, so I've failed on most of my objectives, and the only thing I have is a barely working demo. No challenges, no fun, no objects, no cool environment. Any way, I still like the idea, and I will try to continue with this project. I would love to hear more opinions about it.

My final submission was called "PyBotLearn". You can download it from my PyWeek 6 Entry Page.

Here is a small video of the demo:

A higher resolution video can be downloaded from here.

Loading... Vote up! Vote down! Discussion (1)

Effective Text Editing

I saw the video 7 Habits For Effective Text Editing 2.0 that arhuaco recommended months ago. I was tempted to start learning Vim, but after thinking for a while, I came to the conclusion that there is no good reason for learning Vim. I still don't get why people likes Vim that much. Most of the features that Bram Mooleenar showed in the video have been present in other tools for years and, in my humble opinion, they work much better than in Vim. Other things Bram talked about are just too crazy for me (He suggested that word processing would be more productive if we edit every paragraph in Vim and then copy-paste it on Microsoft Word... WTF!!)

Here are my habits for effective text editing:

Golden Rule: Use the best tool for the Job. I have learned that using a generic text editor for everything leads to be very unproductive. I like to use JEdit as my generic Text Editor. I love it for things like XML, C/C++, this weblog, etc. It has all the advanced features I need in a text editor. I used to use it for C# and Python but I discovered that using an specialized IDE for these languages is thousands of times more productive. Now I use MonoDevelop for C# and PyDev for Python.

The most important features I need when editing source code that can be hardly founded in a generic text editor are:

There are other cool features that I like and most of the time are present in a good generic text editor:

Loading... Vote up! Vote down! Discussion (1)

Loading... Vote up! Vote down!

Last update: 2007-06-28 (Rev 11825)

svnwiki $Rev: 15576 $