list2str and performance

spr spr299 at ifrance.com
Fri Oct 20 06:10:35 EDT 2006


Hi,

I'm trying to learn Python and I'd appreciate any comments about my
small snippets of code. I read an old anecdote about performance here:

http://www.python.org/doc/essays/list2str/

First, I tried to figure out what would be the most pythonic approach by
today's standards:


def ListToStr(l):
    """Convert a list of integers into a string.
    """
    return "".join([chr(n) for n in l])

def StrToList(s):
    """Convert a string into a list of integers.
    """
    return [ord(c) for c in s]


By the way, using a generator expression in this case seem a bit slower
than a list comprehension and I'm not sure why.

I tried to improve the performance with Psyco and it became about 6
times as fast just for free. Then, I quickly tried Pyrex but I didn't
feel comfortable with using a dialect. So I trying Boost.Python, and as
I optimized the code it became more and more C-ish. The result is this:


// Convert a list of integers into a string.
PyObject* ListToStr(const boost::python::list& l)
{
    PyObject* s;
    Py_BEGIN_ALLOW_THREADS

    const size_t length = PyList_GET_SIZE(l.ptr());

    // Couldn't find a function for allocating a PyString from scratch.
    s = (PyObject*)_PyObject_NewVar(&PyString_Type, length);
    ((PyStringObject*)s)->ob_shash = -1;
    ((PyStringObject*)s)->ob_sstate = SSTATE_NOT_INTERNED;
    ((PyStringObject*)s)->ob_sval[((PyStringObject*)s)->ob_size] = '\0';

    char* s_items = PyString_AS_STRING(s);
    char* ps = s_items, *ps_end = ps + length;
    PyIntObject** pl = (PyIntObject**)((PyListObject*)l.ptr())->ob_item;

    while (ps < ps_end)
    {
        *ps++ = (char)(*pl++)->ob_ival;
    }

    Py_END_ALLOW_THREADS
    return s;
}

// Convert a string into a list of integers.
PyObject* StrToList(const boost::python::str& s)
{
    PyObject* l;
    Py_BEGIN_ALLOW_THREADS

    const size_t length = PyString_GET_SIZE(s.ptr());
    l = PyList_New(length);

    PyObject** pl = ((PyListObject*)l)->ob_item, **pl_end = pl + length;
    unsigned char* ps = (unsigned char*)PyString_AS_STRING(s.ptr());

    while (pl < pl_end)
    {
        *pl++ = PyInt_FromLong(*ps++);
    }

    Py_END_ALLOW_THREADS
    return l;
}

Is it safe here to use Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS?
On my machine this is about 50 times as fast as plain Python, but is
this as fast as it can get?

When performance matters and you have to develop a CPU-bound
application, do you think it is possible to eventually achieve nearly
the best performance by extending Python with C or C++ modules, or is it
better to take the embedding approach, that is, use a C or C++ core that
calls Python scripts?



More information about the Python-list mailing list