我有一个处理脚本,旨在提取“uint16”类型的二进制数据文件,并一次以 6400 块为单位进行各种处理。代码最初是用 Matlab 编写的,但由于分析代码是用 Python 编写的,我们希望通过在 Python 中完成所有工作来简化流程。问题是我注意到我的 Python 代码比 Matlab 的 fread 函数慢得多。
简单地说,Matlab 代码是这样的:
fid = fopen(filename);
frame = reshape(fread(fid,80*80,'uint16'),80,80);
虽然我的 Python 代码很简单:
with open(filename, 'rb') as f:
frame = np.array(unpack("H"*6400, f.read(12800))).reshape(80, 80).astype('float64')
文件大小在 500 MB -> 400 GB 之间变化很大,所以我相信找到一种在 Python 中解析数据的更快方法可以为更大的文件带来好处。 500 MB 通常有大约 50000 个块,这个数字随着文件大小线性增加。我看到的速度差异大致是:
Python = 4 x 10^-4 seconds / chunk
Matlab = 6.5 x 10^-5 seconds / chunk
随着时间的推移,处理表明 Matlab 比我实现的 Python 方法快约 5 倍。我已经探索了诸如 numpy.fromfile 和 numpy.memmap 之类的方法,但是因为这些方法需要在某个时候将整个文件打开到内存中,所以它限制了用例,因为我的二进制文件非常大。是否有一些我缺少的pythonic方法来执行此操作?我原以为 Python 在打开 + 读取二进制文件方面会非常快。任何意见是极大的赞赏。
最佳答案
将块写入文件:
In [117]: dat = np.random.randint(0,1028,80*80).astype(np.uint16)
In [118]: dat.tofile('test.dat')
In [119]: dat
Out[119]: array([266, 776, 458, ..., 519, 38, 840], dtype=uint16)
以您的方式导入:
In [120]: import struct
In [121]: with open('test.dat','rb') as f:
...: frame = np.array(struct.unpack("H"*6400,f.read(12800)))
...:
In [122]: frame
Out[122]: array([266, 776, 458, ..., 519, 38, 840])
使用
fromfile
导入In [124]: np.fromfile('test.dat',count=6400,dtype=np.uint16)
Out[124]: array([266, 776, 458, ..., 519, 38, 840], dtype=uint16)
比较时间:
In [125]: %%timeit
...: with open('test.dat','rb') as f:
...: ...: frame = np.array(struct.unpack("H"*6400,f.read(12800)))
...:
1000 loops, best of 3: 898 µs per loop
In [126]: timeit np.fromfile('test.dat',count=6400,dtype=np.uint16)
The slowest run took 5.41 times longe....
10000 loops, best of 3: 36.6 µs per loop
fromfile
快得多。struct.unpack
的时间,没有 np.array
是 266 µs;对于 f.read
,23。所以它是 unpack
加上更通用和更健壮的 np.array
需要更长的时间。文件读取本身不是问题。 ( np.array
可以处理多种输入,列表列表,对象列表等,因此必须花更多时间解析和评估输入。)fromfile
的一个稍微快一点的变体是你的 read 加上 frombuffer
:In [133]: with open('test.dat','rb') as f:
...: frame3 = np.frombuffer(f.read(12800),dtype=np.uint16)