Python cython将字符指针投射到numpy短裤数组
我正在努力改进添加两个固定长度数组所花费的时间。我必须将2个字节字符串转换成2个固定长度的短数组,然后将这两个数组相加,最后将结果数组作为字节字符串输出 目前我有:Python cython将字符指针投射到numpy短裤数组,python,arrays,performance,numpy,cython,Python,Arrays,Performance,Numpy,Cython,我正在努力改进添加两个固定长度数组所花费的时间。我必须将2个字节字符串转换成2个固定长度的短数组,然后将这两个数组相加,最后将结果数组作为字节字符串输出 目前我有: import cython cimport numpy as np import numpy as np @cython.boundscheck(False) @cython.wraparound(False) def cython_layer( char* c_string1, char* c_string2, int leng
import cython
cimport numpy as np
import numpy as np
@cython.boundscheck(False)
@cython.wraparound(False)
def cython_layer( char* c_string1, char* c_string2, int length ):
cdef np.ndarray[ np.int16_t, ndim=1 ] np_orig = np.fromstring( c_string1[:length], np.int16, count=length//2 )
cdef np.ndarray[ np.int16_t, ndim=1 ] np_new = np.fromstring( c_string2[:length], np.int16, count=length//2 )
res = np_orig + np_new
return res.tostring()
但是,更简单的仅numpy方法会产生非常相似(更好)的性能:
def layer(self, orig, new, length):
np_orig = fromstring(orig, np.int16, count=length // 2)
np_new = fromstring(new, np.int16, count=length // 2)
res = np_orig + np_new
return res.tostring()
对于这个简单的例子,有可能提高numpy速度吗?我的直觉是肯定的,但我对Cython没有足够的把握来改进。使用Ipython%timeit
magic,我已将函数计时到:
100000 loops, best of 3: 5.79 µs per loop # python + numpy
100000 loops, best of 3: 8.77 µs per loop # cython + numpy
e、 g:
编辑:更改层以显示长度为2048的字节数组。将它们转换为shorts(np.int16
)将产生大小为1024的输出数组
我是个白痴
edit3:一种解决方案是跳过numpy数组,只使用C指针:
from cpython.bytes cimport PyBytes_FromStringAndSize
from libc.stdint cimport int16_t
def layer2(char* orig, char* new, length):
cdef:
bytes res = PyBytes_FromStringAndSize(NULL,2*(length//2))
char* res_as_charp = res
int16_t* orig_as_int16p = <int16_t*>orig
int16_t* new_as_int16p = <int16_t*>new
int16_t* res_as_int16p = <int16_t*>res_as_charp
Py_ssize_t i
for i in range(length//2):
res_as_int16p[i] = orig_as_int16p[i] + new_as_int16p[i]
return res
来自cpython.bytes cimport PyBytes\u来自stringandsize
从libc.stdint cimport int16\t
def layer2(字符*原始,字符*新,长度):
cdef:
bytes res=PyBytes\u FromStringAndSize(NULL,2*(长度//2))
char*res\u as\u charp=res
int16_t*orig_as_int16p=orig
int16\u t*新的\u as\u int16p=新的
int16*res\u as\u int16p=res\u as\u charp
Py_ssize_t i
对于范围内的i(长度//2):
res_as_int16p[i]=原始as_int16p[i]+新as_int16p[i]
返回res
本质上,我使用C API函数PyBytes\u FromStringAndSize
为结果创建一个空字符串,并对其进行修改。这样做的好处是,与您的版本不同,输入和输出都按原样使用,而不是复制。请注意,只有在您刚刚使用StringAndSize(NULL,length)-创建了一个新的Python字符串时,才允许您这样修改Python字符串
然后我得到一个char*
(不复制数据,只指向现有数据)
然后,我将输入和输出的char*
转换为int16\u t*
——这只会改变内存的解释方式
然后我在数组上循环进行加法并使用指针索引
就速度而言,对于短字符串(length=100000
),这大约是Python实现的8倍。实际上,我的版本稍微慢一点。我怀疑numpy有一个更好的矢量化/并行化的加法循环
附加说明 显示的代码是形式为Python 3的-对于Python 2,您需要
PyString\uu…
而不是PyBytes\u…
通过使用
np.frombuffer
而不是np.fromstring
,您可以在纯Python版本上获得轻微的改进(~10-20%)。这样可以避免复制输入。一种解决方案是跳过numpy数组,只使用C指针:
from cpython.bytes cimport PyBytes_FromStringAndSize
from libc.stdint cimport int16_t
def layer2(char* orig, char* new, length):
cdef:
bytes res = PyBytes_FromStringAndSize(NULL,2*(length//2))
char* res_as_charp = res
int16_t* orig_as_int16p = <int16_t*>orig
int16_t* new_as_int16p = <int16_t*>new
int16_t* res_as_int16p = <int16_t*>res_as_charp
Py_ssize_t i
for i in range(length//2):
res_as_int16p[i] = orig_as_int16p[i] + new_as_int16p[i]
return res
来自cpython.bytes cimport PyBytes\u来自stringandsize
从libc.stdint cimport int16\t
def layer2(字符*原始,字符*新,长度):
cdef:
bytes res=PyBytes\u FromStringAndSize(NULL,2*(长度//2))
char*res\u as\u charp=res
int16_t*orig_as_int16p=orig
int16\u t*新的\u as\u int16p=新的
int16*res\u as\u int16p=res\u as\u charp
Py_ssize_t i
对于范围内的i(长度//2):
res_as_int16p[i]=原始as_int16p[i]+新as_int16p[i]
返回res
本质上,我使用C API函数PyBytes\u FromStringAndSize
为结果创建一个空字符串,并对其进行修改。这样做的好处是,与您的版本不同,输入和输出都按原样使用,而不是复制。请注意,只有在您刚刚使用StringAndSize(NULL,length)-创建了一个新的Python字符串时,才允许您这样修改Python字符串
然后我得到一个char*
(不复制数据,只指向现有数据)
然后,我将输入和输出的char*
转换为int16\u t*
——这只会改变内存的解释方式
然后我在数组上循环进行加法并使用指针索引
就速度而言,对于短字符串(length=100000
),这大约是Python实现的8倍。实际上,我的版本稍微慢一点。我怀疑numpy有一个更好的矢量化/并行化的加法循环
附加说明 显示的代码是形式为Python 3的-对于Python 2,您需要
PyString\uu…
而不是PyBytes\u…
通过使用
np.frombuffer
而不是np.fromstring
,您可以在纯Python版本上获得轻微的改进(~10-20%)。这样可以避免复制输入。那么您如何调用此函数?块大小是多少?您发布的代码不起作用。。。我认为一个问题是,您的char*
可能是从调用的str
函数自动转换而来,然后在传递到np.fromstring
@DavidW之前自动转换为str
(即不必要的复制)。对不起,hadto_string()
而不是tostring()
。我还更新了python+numpy解决方案,以隐式使用字节数组的长度。您是否建议np.fromstring(char)
可以工作?因为它只将前48个字节转换为短字节。不-我只是建议它被转换成char*
->str
->np.array
,因此它被复制了两次。我不知道这是否容易避免。您是否有机会添加一个完整的工作示例,包括您正在使用的任何基准测试?那么您如何调用此函数?chunk\u size是多少?您发布的代码不起作用。。。我认为一个问题是,您的char*
可能是从str
自动转换而来的,您的函数被调用,然后自动转换为str