5

我使用以下代码将 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')

我想提高速度效率是可能的,但是如何呢?

4

2 回答 2

4

看起来你在这里很迂回。这不会做同样的事情吗?

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

这将节省一些时间,因为它不会对临时数组进行零填充、跳过位移位并避免一些不必要的数据移动。不过,我实际上还没有对它进行基准测试,而且我预计节省的费用是适度的。

编辑:我现在已经执行了基准测试。对于len(data)1200 万,我的版本为 80 毫秒,我的版本为 39 毫秒,因此几乎是 2 倍的加速。正如预期的那样,没有很大的改进,但你的起点已经相当快了。

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

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

Edit3:为了更快的速度,我认为你将不得不去 python 之外。与我的版本相比,这个也使用 openMP 的 fortran 函数使我的速度提高了 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,但它默认使用所有可用的核心。

于 2014-03-02T15:30:36.197 回答
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 倍 :)

于 2014-03-02T18:39:21.207 回答