Cython将二进制字符串快速转换为int数组

时间:2014-11-06 16:51:45

标签: python arrays cython distutils

我有一个大型二进制数据文件,我想将其加载到C数组中以便快速访问。数据文件只包含一个4字节的序列。

我通过pkgutil.get_data函数获取数据,该函数返回二进制字符串。 以下代码有效:

import pkgutil
import struct

cdef int data[32487834]

def load_data():
    global data
    py_data = pkgutil.get_data('my_module', 'my_data')
    for i in range(32487834):
        data[i] = <int>struct.unpack('i', py_data[4*i:4*(i+1)])[0]
    return 0

load_data()

问题是这段代码很慢。读取整个数据文件可能需要7或8秒。将文件直接读入C中的数组只需1-2秒,但我想使用pkgutil.get_data,以便我的模块能够可靠地找到安装后的数据。

所以,我的问题是:最好的方法是什么?有没有办法直接将数据转换为int数组而不需要调用struct.unpack?而且,作为第二个问题,有没有办法简单地获取指向数据的指针,以避免不必要地复制120MB的数据?

或者,有没有办法让pkgutil返回数据的文件路径而不是数据本身(在这种情况下,我可以使用C文件IO来快速读取文件。

编辑:

仅供记录,这是最终使用的代码(基于Veedrac的答案):

import pkgutil

from cpython cimport array
import array

cdef int[:] data

cdef void load_data():
    global data
    py_data = pkgutil.get_data('my_module', 'my_data')
    data = array.array('i', py_data)

load_data()

一切都很快。

1 个答案:

答案 0 :(得分:3)

你真的应该使用Numpy:

import numpy
import random
import struct

data = struct.pack('i'*100, *[random.randint(0, 1000000) for _ in range(100)])

numpy.fromstring(data, dtype="int32")
#>>> array([642029, 967046, 599565, ...etc], dtype=int32)

然后只使用standard methods to get a pointer from that中的任何一个。

如果你想避免使用Numpy,一个更快但更少平台无关的方法是通过一个char指针:

cdef int *data_view = <int *><char *>data

这有很多&#34; undefined&#34; -ness,所以要小心。另外注意不要修改数据!

两者之间的妥协是使用cpython.array

from cpython cimport array
import array

def main(data):
    cdef array.array[int] data_arr = array.array('i', data)
    cdef int *data_ptr = data_arr.data.as_ints

为您提供定义良好的语义,并且内置库速度快。