Python cython将字符指针投射到numpy短裤数组

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

我正在努力改进添加两个固定长度数组所花费的时间。我必须将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
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
(即不必要的复制)。对不起,had
to_string()
而不是
tostring()
。我还更新了python+numpy解决方案,以隐式使用字节数组的长度。您是否建议
np.fromstring(char)
可以工作?因为它只将前48个字节转换为短字节。不-我只是建议它被转换成
char*
->
str
->
np.array
,因此它被复制了两次。我不知道这是否容易避免。您是否有机会添加一个完整的工作示例,包括您正在使用的任何基准测试?那么您如何调用此函数?chunk\u size是多少?您发布的代码不起作用。。。我认为一个问题是,您的
char*
可能是从
str
自动转换而来的,您的函数被调用,然后自动转换为
str