Archive for 2008

Some more performance numbers for lxml

Montag, April 7th, 2008

Ian Bicking came up with some impressive numbers regarding lxml’s performance for HTML parsing.

Quotes:

  • “I knew lxml was fast before I started these benchmarks, but I didn’t expect it to be quite this fast.”
  • “no qualification, I would recommend lxml for just about any HTML task.”

lxml is Google’’s Top-5 for “elementtree”!

Donnerstag, Mai 8th, 2008

I just noticed that lxml reached rank 5 on Google when you look for “elementtree”, just after two links for ElementTree itself and another two for the Python standard library, so it’’s more of a rank 3!

Yahoo sees us within the top 10.

MSN at least underlines the ElementTree compatibility of lxml in its Top-5

although it doesn”t find our homepage is good enough for rank 1 when you ask it for “lxml”.

But then again, Microsoft isn”t the first place to ask for OpenSource software anyway…

Dummheit beerdigt

Donnerstag, März 27th, 2008

München bekommt keinen Transrapid. Damit ist diese Geschichte aus stoiberscher Geltungssucht und Steuerzahlerbetrug hoffentlich ad-acta gelegt.

Eine Frage bleibt: Wenn jetzt schon Ministrecken wie die zwischen München Hauptbahnhof und Flughafen ernsthaft in Erwägung gezogen werden, was spricht dann eigentlich gegen eine Strecke Lisboa-​Madrid-​Bordeaux-​Paris-​Bruxelles-​Köln-​Hannover-​Berlin-​Posnan-​Warszawa-​Kaunas-​Riga-​Talinn? Optional mit Verlängerung nach St. Petersburg. Wäre das nicht eine Strecke, die vernünftig von einem Hochgeschwindigkeitszug befahren werden könnte? In, sagen wir mal, zwölf Stunden für die runden 4400 Kilometer? Nur würde da wahrscheinlich der AGV als schnellstes Schienenfahrzeug in Sachen Geschwindigkeit (und wohl auch Kosten) immer noch am Transrapid vorbei ziehen. Aber gegen eine entsprechende AGV-Strecke hätte ich natürlich auch nichts einzuwenden…

A word about template engine writers

Freitag, März 7th, 2008

What I have to say about people writing template engines in Python.

Roman Herzogs Angst um die CSU

Donnerstag, März 6th, 2008

Roman Herzog trauert in der Sueddeutschen Zeitung der guten alten Zeit der Vier-Parteien-Republik nach. Der wunderbaren Zeit, als die Welt noch in Ordnung, die Regierungsmehrheiten noch stabil, und die CSU noch Weltmacht war. Und die bösen Linken noch nicht den großen Parteien die Stimmen wegschnappten.

Tja, Herr Herzog, wie wäre es denn, wenn Sie, anstatt gleich nach einem neuen Wahlrecht zu schreien, einfach mal Ihre eigene Partei bitten würden, die Grabenkriege zu beenden, und das Gut-Böse-Lagerdenken gegen eine themenorientierte Politik einzutauschen? Das würde dieses Land sicherlich weiterbringen, als durch eine Wahlrechtsreform die eine oder andere Mehrheit künstlich herbeizuführen.

Optimising Cython code

Mittwoch, März 5th, 2008

There was a request on the Cython mailing list on how to optimise Cython code. Here’s how I do it.

We have some Cython code that we want to benchmark:

x = 1
for i from 0 <= i < MAX:
    x = x + i

Ok, obviously this is stupid code, as this can be done much easier without a loop. But let’s say for the sake of argument that this is the best algorithm that we can come up with, and that we have a suspicion that it might not run as fast as we think it should.

First thing to do is to make that suspicion evidence by benchmarking. So I copy the code over to a Cython module and wrap it in a Python function:

# file: bench.pyx
def run(max):
    x = 1
    for i from 0 <= i < max:
        x = x + i
    return x

Now we compile the file:

# cython bench.pyx
# gcc -shared $CFLAGS -I/usr/include/python2.5 -o bench.so bench.c

And run it through Python’s great timeit module:

# python -m timeit -s 'from bench import run' 'run(100)'
1000000 loops, best of 3: 5.93 usec per loop

This looks exceedingly long-running to me ;)

Since I have no idea what to do better, I first look trough the generated C code. That’s not as hard as it sounds, as Cython copies the original Cython code into comments and marks the line that it generates code for. The loop code gets translated into this:

  /* ".../TEST/bench.pyx":3
 * def run(max):
 *     x = 1
 *     for i from 0 <= i < max:             # <<<<<<<<<<<<<<
 *         x = x + i
 *     return x
 */
  __pyx_2 = __pyx_PyInt_long(__pyx_v_max); if /* ... */
  for (__pyx_1 = 0; __pyx_1 < __pyx_2; __pyx_1++) {
    __pyx_3 = PyInt_FromLong(__pyx_1); if /* ... */
    Py_DECREF(__pyx_v_i);
    __pyx_v_i = __pyx_3;
    __pyx_3 = 0;

    /* ".../TEST/bench.pyx":4
 *     x = 1
 *     for i from 0 <= i < max:
 *         x = x + i             # <<<<<<<<<<<<<<
 *     return x
 */
    __pyx_3 = PyNumber_Add(__pyx_v_x, __pyx_v_i); if /* ... */
    Py_DECREF(__pyx_v_x);
    __pyx_v_x = __pyx_3;
    __pyx_3 = 0;
  }

The code I stripped (/* ... */) is error handling code. It’s emitted in one long line so that it’s easy to ignore - which is the best thing to do with it.

What you can see here is that Cython is smart enough to optimise the loop into a C loop with a C run variable (type long), but then the unsuspiciously looking operator ‘+’ uses Python API calls, so this is not what I had in mind when I wrote the code. I wanted it to be as fast and straight forward as it looks in Cython. However, Cython cannot know my intention here, as my code might as well depend on the semantics of Python’s ‘+’ operator (which is different from the ‘+’ operator in C).

Cython’s way of dealing with Python/C type ambiguity is explicit static type declarations through cdefs. By default, all variables are defined as if I had written cdef object variable, but in this case, I want them to be plain C integers. So here is the straight forward way to tell Cython that I want the variables x and i to have C semantics rather than Python semantics:

# file: bench.pyx
def run(max):
    cdef int i,x
    x = 1
    for i from 0 <= i < max:
        x = x + i
    return x

And the resulting C code shows me that Cython understood what I wanted:

  /* ".../TEST/bench.pyx":4
 *     cdef int i,x
 *     x = 1
 *     for i from 0 <= i < max:             # <<<<<<<<<<<<<<
 *         x = x + i
 *     return x
 */
  __pyx_1 = __pyx_PyInt_int(__pyx_v_max); if /* ... */
  for (__pyx_v_i = 0; __pyx_v_i < __pyx_1; __pyx_v_i++) {

    /* ".../TEST/bench.pyx":5
 *     x = 1
 *     for i from 0 <= i < max:
 *         x = x + i             # <<<<<<<<<<<<<<
 *     return x
 */
    __pyx_v_x = (__pyx_v_x + __pyx_v_i);
  }

Now timeit gives me something like this:

# python -m timeit -s 'from bench import run' 'run(100)'
1000000 loops, best of 3: 0.284 usec per loop

That’s about a factor of 20 compared to the original example. And that’s definitely enough for today.

Messages from happy users

Montag, Januar 28th, 2008

Zeit für ein bisschen Bauchpinselei.

Bug Reports

Sonntag, Januar 27th, 2008

Heute habe ich mal wieder einen guten Artikel zum Thema Bug-Reports gefunden. Der Artikel von ESR, How to ask questions the smart way, sollte ja hinlänglich bekannt und referenziert sein. Aber how to report bugs effectively liest sich gerade aus der Sicht eines Programmierers ganz gut, und ist vor allem um einiges kürzer. :) Insbesondere die Zusammenfassung ist kurz, prägnant und lesenswert für jeden Bug-Melder.

Es gibt auch eine deutsche Übersetzung.