用Python从二进制文件中提取特定字节
我有非常大的二进制文件,其中包含y传感器的x个int16数据点,以及包含一些基本信息的头文件。二进制文件被写为每个采样时间的y值,最多x个采样,然后是另一组读数,依此类推。如果我想要所有的数据,我使用的是用Python从二进制文件中提取特定字节,python,numpy,mmap,seek,fromfile,Python,Numpy,Mmap,Seek,Fromfile,我有非常大的二进制文件,其中包含y传感器的x个int16数据点,以及包含一些基本信息的头文件。二进制文件被写为每个采样时间的y值,最多x个采样,然后是另一组读数,依此类推。如果我想要所有的数据,我使用的是numpy.fromfile(),它的工作速度非常快。但是,如果我只想要传感器数据的子集或特定传感器,我目前有一个可怕的双for循环,使用file.seek()、file.read()、和struct.unpack(),这会花费很长时间。在python中有没有其他更快的方法?也许是我不太理解的m
numpy.fromfile()
,它的工作速度非常快。但是,如果我只想要传感器数据的子集或特定传感器,我目前有一个可怕的双for
循环,使用file.seek()
、file.read()
、和struct.unpack()
,这会花费很长时间。在python中有没有其他更快的方法?也许是我不太理解的mmap()
?或者只是使用整个fromfile()
然后再进行二次采样
data = numpy.empty(num_pts, sensor_indices)
for i in range(num_pts):
for j in range(sensor_indices):
curr_file.seek(bin_offsets[j])
data_binary = curr_file.read(2)
data[j][i] = struct.unpack('h', data_binary)[0]
在遵循@rrauenza关于mmap
的建议后,我将代码编辑为
mm = mmap.mmap(curr_file.fileno(), 0, access=mmap.ACCESS_READ)
data = numpy.empty(num_pts,sensor_indices)
for i in range(num_pts):
for j in range(len(sensor_indices)):
offset += bin_offsets[j] * 2
data[j][i] = struct.unpack('h', mm[offset:offset+2])[0]
虽然这比以前快了,但仍然比以前慢了几个数量级
shape = (x, y)
data = np.fromfile(file=self.curr_file, dtype=np.int16).reshape(shape)
data = data.transpose()
data = data[sensor_indices, :]
data = data[:, range(num_pts)]
我用一个较小的30MB文件进行了测试,该文件只有16个传感器,数据量为30秒。原始编码为160秒,mmap
为105秒,np.fromfile
和子采样为0.33秒
剩下的问题是-显然,使用
numpy.fromfile()
处理小文件会更好,但对于大得多的文件来说,会有问题吗?这些文件可能长达20 Gb,包含数小时或数天的数据,最多500个传感器?我肯定会尝试mmap()
:
您正在读取大量的小数据位,如果您正在为要提取的每个int16
调用seek()
和read()
,这些数据位会产生大量的错误
我编写了一个小测试来演示:
#!/usr/bin/python
import mmap
import os
import struct
import sys
FILE = "/opt/tmp/random" # dd if=/dev/random of=/tmp/random bs=1024k count=1024
SIZE = os.stat(FILE).st_size
BYTES = 2
SKIP = 10
def byfile():
sum = 0
with open(FILE, "r") as fd:
for offset in range(0, SIZE/BYTES, SKIP*BYTES):
fd.seek(offset)
data = fd.read(BYTES)
sum += struct.unpack('h', data)[0]
return sum
def bymmap():
sum = 0
with open(FILE, "r") as fd:
mm = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
for offset in range(0, SIZE/BYTES, SKIP*BYTES):
data = mm[offset:offset+BYTES]
sum += struct.unpack('h', data)[0]
return sum
if sys.argv[1] == 'mmap':
print bymmap()
if sys.argv[1] == 'file':
print byfile()
我运行了两次每个方法来补偿缓存。我使用time
是因为我想测量user
和sys
时间
结果如下:
[centos7:/tmp]$ time ./test.py file
-211990391
real 0m44.656s
user 0m35.978s
sys 0m8.697s
[centos7:/tmp]$ time ./test.py file
-211990391
real 0m43.091s
user 0m37.571s
sys 0m5.539s
[centos7:/tmp]$ time ./test.py mmap
-211990391
real 0m16.712s
user 0m15.495s
sys 0m1.227s
[centos7:/tmp]$ time ./test.py mmap
-211990391
real 0m16.942s
user 0m15.846s
sys 0m1.104s
[centos7:/tmp]$
(总和-211990391只是验证两个版本是否执行相同的操作。)
查看每个版本的第二个结果,mmap()
约为实时结果的1/3。用户时间约为1/2,系统时间约为1/5
您的其他加速选择包括:
(1) 如前所述,加载整个文件。大I/O而不是小I/O可以加快速度。但是,如果超出系统内存,您将返回分页,这将比mmap()
(因为您必须分页)更糟糕。我在这里不是特别有希望,因为mmap
已经在使用更大的I/O了
(2) 并发性。也许通过多个线程并行读取文件可以加快速度,但是您需要处理Python。避免GIL将更好地工作,并且您可以轻松地将数据传递回顶级处理程序。然而,这将对下一项——位置——起作用:您可能会使I/O更随机
(3) 地点。以某种方式组织您的数据(或排列您的读取顺序),使您的数据更加紧密mmap()
根据系统页面大小将文件分块分页:
>>> import mmap
>>> mmap.PAGESIZE
4096
>>> mmap.ALLOCATIONGRANULARITY
4096
>>>
如果您的数据距离较近(在4k区块内),它将已经加载到缓冲区缓存中
(4) 更好的硬件。就像SSD一样
我在SSD上运行了这个,速度快得多。我运行了python的概要文件,想知道解包是否昂贵。这不是:
$ python -m cProfile test.py mmap
121679286
26843553 function calls in 8.369 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 6.204 6.204 8.357 8.357 test.py:24(bymmap)
1 0.012 0.012 8.369 8.369 test.py:3(<module>)
26843546 1.700 0.000 1.700 0.000 {_struct.unpack}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {method 'fileno' of 'file' objects}
1 0.000 0.000 0.000 0.000 {open}
1 0.000 0.000 0.000 0.000 {posix.stat}
1 0.453 0.453 0.453 0.453 {range}
代码:
你调查过熊猫吗?这是伟大的排序大数据集在几乎任何你想要的方式。嗨,欢迎来到StackOverflow!你的标题有点混乱——strip通常意味着删除。一个更准确的词可能是extract。您的超大文件有多大?我认为这实际上取决于您拥有的RAM数量。这是个问题
mmap()
应该随着数据的增长而线性扩展,numpy.fromfile()
不会随着数据大小的增长而扩展,因为在某个时候您需要翻页。您可以使用numpy.memmap
并充分利用这两个世界。在建立索引之前进行切片(0:num\u pts
),以最小化副本。
$ time ./test2.py 4
[(4415068.0, 13421773), (-145566705.0, 13421773), (14296671.0, 13421773), (109804332.0, 13421773)]
(-17050634.0, 53687092)
real 0m5.629s
user 0m17.756s
sys 0m0.066s
$ time ./test2.py 1
[(264140374.0, 53687092)]
(264140374.0, 53687092)
real 0m13.246s
user 0m13.175s
sys 0m0.060s
#!/usr/bin/python
import functools
import multiprocessing
import mmap
import os
import struct
import sys
FILE = "/tmp/random" # dd if=/dev/random of=/tmp/random bs=1024k count=1024
SIZE = os.stat(FILE).st_size
BYTES = 2
SKIP = 10
def bymmap(poolsize, n):
partition = SIZE/poolsize
initial = n * partition
end = initial + partition
sum = 0.0
unpacks = 0
with open(FILE, "r") as fd:
mm = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
for offset in xrange(initial, end, SKIP*BYTES):
data = mm[offset:offset+BYTES]
sum += struct.unpack('h', data)[0]
unpacks += 1
return (sum, unpacks)
poolsize = int(sys.argv[1])
pool = multiprocessing.Pool(poolsize)
results = pool.map(functools.partial(bymmap, poolsize), range(0, poolsize))
print results
print reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), results)