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

Jul*_*ian 0 python arrays distutils cython

我有一个大型二进制数据文件,我想将其加载到 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()
Run Code Online (Sandbox Code Playgroud)

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

所以,我的问题是:最好的方法是什么?有没有办法直接将数据转换为整数数组,而不需要对 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()
Run Code Online (Sandbox Code Playgroud)

一切都很快。

Vee*_*rac 5

您可能真的应该只使用 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)
Run Code Online (Sandbox Code Playgroud)

然后只需使用任何标准方法即可从中获取指针

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

cdef int *data_view = <int *><char *>data
Run Code Online (Sandbox Code Playgroud)

这有很多“未定义”的地方,所以要小心。另外注意不要修改数据!

两者之间的一个很好的折衷方案是使用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
Run Code Online (Sandbox Code Playgroud)

它为您提供了明确定义的语义,并且内置库的速度很快。