加速将24位二进制数据加载到16位numpy数组中

时间:2014-03-02 14:39:06

标签: python arrays numpy cython timeit

我使用以下代码将24位二进制data加载到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')

我认为可以提高速度效率,但是如何?

2 个答案:

答案 0 :(得分:4)

好像你在这里非常迂回。这不会做同样的事情吗?

output = np.frombuffer(data,'b').reshape(-1,3)[:,1:].flatten().view('i2')

这样可以节省一些时间,从不填充临时数组,跳过bitshift并避免一些不需要的数据移动。不过,我还没有真正对它进行基准测试,我希望节省的费用不高。

编辑:我现在已经执行了基准测试。对于1200万的len(data),我的版本为80毫秒,而我的版本为39毫秒,所以几乎可以说是速度提高了2倍。正如预期的那样,并没有一个很大的改进,但是你的出发点已经非常快了。

Edit2:我应该提一下,我在这里假设了小端。然而,原始问题的代码也隐含地假设小端,所以这不是我的新假设。

(对于大端(数据和体系结构),您将1:替换为:-1。如果数据的字节序数不同于CPU,那么您还需要反转bytes(::-1)。)

Edit3:为了更快的速度,我想你将不得不走出python。这个fortran函数也使用openMP,与我的版本相比,速度提高了2倍(比你的版本快4倍)。

subroutine f(a,b)
        implicit none
        integer*1, intent(in)  :: a(:)
        integer*1, intent(out) :: b(size(a)*2/3)
        integer :: i
        !$omp parallel do
        do i = 1, size(a)/3
                b(2*(i-1)+1) = a(3*(i-1)+2)
                b(2*(i-1)+2) = a(3*(i-1)+3)
        end do
        !$omp end parallel do
end subroutine

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控制要使用的核心数,但默认使用所有可用核心。

答案 1 :(得分:1)

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

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            

速度增加4倍:)