2's complement conversion. Is this right?

Bob Greschke bob at passcal.nmt.edu
Sat Apr 19 23:24:32 CEST 2008


On 2008-04-18 23:35:12 -0600, Ivan Illarionov <ivan.illarionov at gmail.com> said:

> On Sat, 19 Apr 2008 04:45:54 +0000, Ivan Illarionov wrote:
> 
>> On Fri, 18 Apr 2008 22:30:45 -0500, Grant Edwards wrote:
>> 
>>> On 2008-04-18, Bob Greschke <bob at passcal.nmt.edu> wrote:
>>> 
>>>> However, in playing around with your suggestion and Grant's code I've
>>>> found that the struct stuff is WAY slower than doing something like
>>>> this
>>>> 
>>>> Value = (ord(Buf[s])*65536)+(ord(Buf[s+1])*256)+ord(Buf[s+2]) if
>>>> Value
>>>>> = 0x800000:
>>>> Value -= 0x1000000
>>>> 
>>>> This is almost twice as fast just sitting here grinding through a few
>>>> hundred thousand conversions (like 3sec vs. ~5secs just counting on my
>>>> fingers - on an old Sun...it's a bit slow).  Replacing *65536 with
>>>> <<16 and *256 with <<8 might even be a little faster, but it's too
>>>> close to call without really profiling it.
>>> 
>>> I didn't know speed was important.  This might be a little faster
>>> (depending on hardware):
>>> 
>>> Value = (ord(Buf[s])<<16) | (ord(Buf[s+1])<<8) | ord(Buf[s+2])
>>> 
>>> It also makes the intention a bit more obvious (at least to me).
>>> 
>>> A decent C compiler will recognize that <<16 and <<8 are special and
>>> just move bytes around rather than actually doing shifts.  I doubt the
>>> Python compiler does optimizations like that, but shifts are still
>>> usually faster than multiplies (though, again, a good compiler will
>>> recognize that multiplying by 65536 is the same as shifting by 16 and
>>> just move bytes around).
>> 
>> So why not put it in C extension?
>> 
>> It's easier than most people think:
>> 
>> <code>
>> from3bytes.c
>> ============
>> #include <Python.h>
>> 
>> PyObject*
>> from3bytes(PyObject* self, PyObject* args) {
>> const char * s;
>> int len;
>> if (!PyArg_ParseTuple(args, "s#", &s, &len))
>> return NULL;
>> long n = (s[0]<<16) | (s[1]<<8) | s[2]; if (n >= 0x800000)
>> n -= 0x1000000;
>> return PyInt_FromLong(n);
>> }
>> 
>> static PyMethodDef functions[] = {
>> {"from3bytes",    (PyCFunction)from3bytes, METH_VARARGS}, {NULL,
>> NULL, 0, NULL},
>> };
>> 
>> 
>> DL_EXPORT(void)
>> init_from3bytes(void)
>> {
>> Py_InitModule("_from3bytes", functions);
>> }
>> 
>> buildme.py
>> ==========
>> import os
>> import sys
>> from distutils.core import Extension, setup
>> 
>> os.chdir(os.path.dirname(os.path.abspath(__file__))) sys.argv =
>> [sys.argv[0], 'build_ext', '-i'] setup(ext_modules =
>> [Extension('_from3bytes', ['from3bytes.c'])]) </code>
>> 
>> 'python buildme.py' will create '_from3bytes.so' file 'from _from3bytes
>> import from3bytes' will import C-optimized function
>> 
>> Hope this helps.
> 
> Sorry,
> the right code should be:
> 
> PyObject* from3bytes(PyObject* self, PyObject* args)
> {
>     const char * s;
>     int len;
>     if (!PyArg_ParseTuple(args, "s#", &s, &len))
>         return NULL;
>         long n = ((((unsigned char)s[0])<<16) | (((unsigned char)s[1])<<8) |
> ((unsigned char)s[2]));
>     if (n >= 0x800000)
>         n -= 0x1000000;
>     return PyInt_FromLong(n);
> }

No thanks.  Being able to alter and install these programs on whatever 
computer they are installed on is more important than speed.  I went 
down the C-extension path years ago and it turned into a big mess.  
Everything has to run on Sun's, Mac's, Linux and Windows without any 
major hassels if they have to be changed.  I can't reley on everything 
the program needs to be rebuilt being installed beyond Python and 
Tkinter (and pySerial and PIL for a couple of programs), which they 
need to run.  So no compiling and everything is in one file, in case a 
new version has to be upgraded by a user by emailing it to them while 
they're sitting on some mountain in Tibet.  Just unzip, stick the .py 
somewhere logical, (usually) double-click, and they are off and running.

Bob





More information about the Python-list mailing list