我正在努力改进添加两个固定长度数组所花费的时间。我必须将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 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
魔法我已经将函数计时:
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:
a = np.array( range(1024), dtype=np.int16).tostring()
layer(a,a,len(a)) == cython_layer(a,a,len(a))
# True
%timeit layer(a, a, len(a) )
# 100000 loops, best of 3: 6.06 µs per loop
%timeit cython_layer(a, a, len(a))
# 100000 loops, best of 3: 9.19 µs per loop
编辑:更改layer
以显示size=len(orig)//2
orig和new都是长度为2048的字节数组。将它们转换为short(np.int16
)会产生大小为1024的输出数组。 / p>
编辑2:我是个白痴。
edit3:行动中的例子
答案 0 :(得分:2)
一种解决方案是跳过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
基本上,我使用C API函数PyBytes_FromStringAndSize
为结果创建一个空字符串并对其进行修改。这样做的好处是,与您的版本不同,输入和输出都按原样使用而不是复制。请注意,允许您修改此类Python字符串的仅情况是您刚刚使用PyBytes_FromStringAndSize(NULL,length)
- this is in the C API documentation创建新字符串时。
然后我得到一个char*
(不复制数据,只指向现有数据)。
然后我将char*
转换为两个输入,输出为int16_t*
- 这只会改变内存的解释方式。
然后我循环遍历数组,进行添加并使用指针索引。
就速度而言,这比短字符串(length<100
)的Python实现快8倍。这主要是由于函数调用的固定Python开销正在创建我相信的numpy数组。对于较长的字符串(length>=100000
),我的版本实际上稍微慢一些。我怀疑numpy有一个更好的矢量化/并行化循环用于添加。
显示的代码是Python 3的表单 - 对于Python 2,您需要PyString_...
而不是PyBytes_...
使用np.frombuffer
代替np.fromstring
,您的纯Python版本可以略微提升(约10-20%)。这样可以避免复制输入。