Python 加速将24位二进制数据加载到16位numpy阵列

Python 加速将24位二进制数据加载到16位numpy阵列,python,arrays,numpy,cython,timeit,Python,Arrays,Numpy,Cython,Timeit,我使用以下代码将24位二进制数据加载到16位numpy数组中: temp = numpy.zeros((len(data) / 3, 4), dtype='b') temp[:, 1:] = numpy.frombuffer(data, dtype='b').reshape(-1, 3) temp2 = temp.view('<i4').flatten() >> 16 # >> 16 because I need to divide by 2**16 t

我使用以下代码将24位二进制
数据加载到16位
numpy
数组中:

temp = numpy.zeros((len(data) / 3, 4), dtype='b')
temp[:, 1:] = numpy.frombuffer(data, dtype='b').reshape(-1, 3)
temp2 = temp.view('<i4').flatten() >> 16       # >> 16 because I need to divide by 2**16 to load my data into 16-bit array, needed for my (audio) application
output = temp2.astype('int16')
temp=numpy.zero((len(data)/3,4),dtype='b')
temp[:,1:]=numpy.frombuffer(数据,dtype='b')。重塑(-1,3)

temp2=temp.view(“看起来你在这里很迂回。这不是也一样吗

output = np.frombuffer(data,'b').reshape(-1,3)[:,1:].flatten().view('i2')
这将节省一些时间,从非零填充临时数组、跳过位移位和避免一些不必要的数据移动。不过,我还没有实际对其进行基准测试,我预计节省的时间不会太多

编辑:我现在已经完成了基准测试。对于1200万的
len(data)
,我得到了你版本的80毫秒和我版本的39毫秒,所以几乎完全是一个2倍的加速。正如预期的那样,这不是一个很大的改进,但是你的起点已经相当快了

Edit2:我应该提到我在这里假设了little endian。但是,原始问题的代码也隐式地假设了little endian,所以这不是我的新假设

(对于big-endian(数据和体系结构),您可以将
1:
替换为
:-1
。如果数据的endian不同于CPU,则还需要颠倒字节顺序(
:-1

Edit3:为了获得更高的速度,我想你必须跳出python。这个fortran函数也使用openMP,与我的版本相比,它的加速比是2倍以上(比你的快4倍以上)

使用
FOPT=“-fopenmp”f2py-c-m basj{、.f90}-lgomp
编译。然后可以在python中导入并使用它:

import basj
def convert(data): return def mine2(data): return basj.f(np.frombuffer(data,'b')).view('i2')

您可以通过环境变量OMP_NUM_THREADS来控制要使用的内核数量,但它默认使用所有可用的内核。

受@amaurea答案的启发,这里有一个
cython
版本(我在原始代码中已经使用了cython,因此我将继续使用cython,而不是混合使用cython+fortran):

导入cython
将numpy作为np导入
cimport numpy作为np
def二进制24_到_int16(字符*数据):
cdef int i
res=np.zero(len(data)/3,np.int16)
b=((res.数据)
对于范围内的i(长度(数据)/3):
b[2*i]=数据[3*i+1]
b[2*i+1]=数据[3*i+2]
返回res

有一个系数4速度增益:)

注意:
数据
是从二进制文件中读取的,其中
打开
、模块
块。读取
谢谢。我目前正在尝试对其进行基准测试,我将给出结果。(备注:我需要结果作为连续数组,因此不可能使用视图)根据
.flags
,在我的测试中,结果是连续的。如果您想完全确保它始终是连续的(尽管我看不出它是如何变化的),可以将它包装在
np.ascontiguousarray
中,如果数组已经是连续的,这是一个自由操作,但在其他情况下会创建一个连续的副本。该数组被设置为连续的,它总是创建一个副本。这里的
视图
只需将每2个单字节数重新解释为一个2字节数。对于continuate(顺便问一下,是否有
numpy.iscontinuaous
返回
True
False
?),关于基准测试,我得到了相反的结果(这很奇怪!)当前位置你知道原因吗@amaurea@Basj每种方法的数据长度是多少?我使用
ipython
中的
timeit
来执行基准测试,我认为这应该是非常可靠的(它会重复测试几次以获得可靠的度量)。
import basj
def convert(data): return def mine2(data): return basj.f(np.frombuffer(data,'b')).view('i2')
import cython
import numpy as np
cimport numpy as np

def binary24_to_int16(char *data):
    cdef int i
    res = np.zeros(len(data)/3, np.int16)
    b = <char *>((<np.ndarray>res).data)
    for i in range(len(data)/3):
        b[2*i] = data[3*i+1]
        b[2*i+1] = data[3*i+2]
    return res