从Python传输字节数组到C并返回
我需要快速处理XOR字节数组,
在Python的一个变体中for i in range(len(str1)): str1[i]=str1[i] ^ 55
运行得非常慢
我用C语言写了这个模块。
我对C语言了解得很少,以前从来没有写过。
在一个变体中
PyArg_ParseTuple (args, "s", &str))
一切都按预期工作,但我需要用s*代替s,因为元素可能包含嵌入的空值,但如果我在调用时把s改成s*,Python就会崩溃。
PyArg_ParseTuple (args, "s*", &str)) // crash
也许像我这样的初学者想用我的例子作为开始,写一些自己的东西,所以把所有信息都带到这个例子中,适用于Windows。
解析参数和构建值的页面 http://docs.python.org/dev/c-api/arg.html
test_xor.c
#include <Python.h>
static PyObject* fast_xor(PyObject* self, PyObject* args)
{
const char* str ;
int i;
if (!PyArg_ParseTuple(args, "s", &str))
return NULL;
for(i=0;i<sizeof(str);i++) {str[i]^=55;};
return Py_BuildValue("s", str);
}
static PyMethodDef fastxorMethods[] =
{
{"fast_xor", fast_xor, METH_VARARGS, "fast_xor desc"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initfastxor(void)
{
(void) Py_InitModule("fastxor", fastxorMethods);
}
test_xor.py
import fastxor
a=fastxor.fast_xor("World") # it works with s instead s*
print a
a=fastxor.fast_xor("Wo\0rld") # It does not work with s instead s*
compile.bat
rem use http://bellard.org/tcc/
tiny_impdef.exe C:\Python26\python26.dll
tcc -shared test_xor.c python26.def -IC:\Python26\include -LC:\Python26\libs -ofastxor.pyd
test_xor.py
5 个回答
5
这可以通过numpy很快完成。自己用C语言写一个异或(xor)函数,速度可能不会比这个快多少:
In [1]: import numpy
In [2]: data = numpy.uint8(numpy.random.randint(0, 256, 10000))
In [3]: timeit xor_data = numpy.bitwise_xor(data, 55)
100000 loops, best of 3: 17.4 us per loop
如果你在处理一个大数据集(比如说有1亿个数据点),那么这个方法的速度和你提到的代码速度差不多:
In [12]: data = numpy.uint8(numpy.random.randint(0, 256, 100000000))
In [13]: timeit xor_data = numpy.bitwise_xor(data, 55)
1 loops, best of 3: 198 ms per loop
6
另一种方法是使用 PyObject_GetBuffer
。下面的模块定义了 fast_xor
,可以用于任何支持缓冲区协议的对象,同时定义了 fast_xor_inplace
,适用于可以写入的缓冲区对象,比如 bytearray
。这个版本返回 None
。我还添加了一个第二个 unsigned char
参数,默认值是 55。
示例:
>>> s = 'abc'
>>> b = bytearray(s)
>>> fast_xor(s), fast_xor(s, 0x20)
('VUT', 'ABC')
>>> fast_xor_inplace(b, 0x20)
>>> b
bytearray(b'ABC')
>>> fast_xor_inplace(s)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
BufferError: Object is not writable.
>>> fast_xor(b, 256)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: unsigned byte integer is greater than maximum
源代码:
#include <Python.h>
static PyObject *fast_xor_inplace(PyObject *self, PyObject *args)
{
PyObject *arg1;
unsigned char arg2 = 55;
Py_buffer buffer;
char *buf;
int i;
if (!PyArg_ParseTuple(args, "O|b:fast_xor_inplace", &arg1, &arg2))
return NULL;
if (PyObject_GetBuffer(arg1, &buffer, PyBUF_WRITABLE) < 0)
return NULL;
buf = buffer.buf;
for(i=0; i < buffer.len; i++)
buf[i] ^= arg2;
PyBuffer_Release(&buffer);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *fast_xor(PyObject *self, PyObject *args)
{
PyObject *arg1;
unsigned char arg2 = 55;
PyObject *result;
Py_buffer buffer;
char *buf, *str;
int i;
if (!PyArg_ParseTuple(args, "O|b:fast_xor", &arg1, &arg2))
return NULL;
if (PyObject_GetBuffer(arg1, &buffer, PyBUF_SIMPLE) < 0)
return NULL;
result = PyString_FromStringAndSize(NULL, buffer.len);
if (result == NULL)
return NULL;
buf = buffer.buf;
str = PyString_AS_STRING(result);
for(i=0; i < buffer.len; i++)
str[i] = buf[i] ^ arg2;
PyBuffer_Release(&buffer);
return result;
}
static PyMethodDef fastxorMethods[] =
{
{"fast_xor", fast_xor, METH_VARARGS, "fast xor"},
{"fast_xor_inplace", fast_xor_inplace, METH_VARARGS, "fast inplace xor"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initfastxor(void)
{
Py_InitModule3("fastxor", fastxorMethods, "fast xor functions");
}
9
你不需要构建一个扩展模块来快速完成这个任务,你可以直接使用NumPy。不过针对你的问题,你需要一些像这样的C代码:
#include <Python.h>
#include <stdlib.h>
static PyObject * fast_xor(PyObject* self, PyObject* args)
{
const char* str;
char * buf;
Py_ssize_t count;
PyObject * result;
int i;
if (!PyArg_ParseTuple(args, "s#", &str, &count))
{
return NULL;
}
buf = (char *)malloc(count);
for(i=0;i<count;i++)
{
buf[i]=str[i] ^ 55;
}
result = Py_BuildValue("s#", buf, count);
free(buf);
return result;
}
你不能改变字符串对象的内容,因为在Python中,字符串是不可变的。你可以使用 "s#" 来获取 char *
指针和缓冲区的长度。
如果你可以使用NumPy:
In [1]: import fastxor
In [2]: a = "abcdsafasf12q423\0sdfasdf"
In [3]: fastxor.fast_xor(a)
Out[3]: 'VUTSDVQVDQ\x06\x05F\x03\x05\x047DSQVDSQ'
In [5]: import numpy as np
In [6]: (np.frombuffer(a, np.int8)^55).tostring()
Out[6]: 'VUTSDVQVDQ\x06\x05F\x03\x05\x047DSQVDSQ'
In [7]: a = a*10000
In [8]: %timeit fastxor.fast_xor(a)
1000 loops, best of 3: 877 us per loop
In [15]: %timeit (np.frombuffer(a, np.int8)^55).tostring()
1000 loops, best of 3: 1.15 ms per loop