我知道之前有一些关于文件读取,二进制数据处理和使用struct
的整数转换的问题,所以我来这里询问一段我认为花费太多时间的代码跑。正在读取的文件是多通道数据采样记录(短整数),具有插入的数据间隔(因此嵌套的for
语句)。代码如下:
# channel_content is a dictionary, channel_content[channel]['nsamples'] is a string
for rec in xrange(number_of_intervals)):
for channel in channel_names:
channel_content[channel]['recording'].extend(
[struct.unpack( "h", f.read(2))[0]
for iteration in xrange(int(channel_content[channel]['nsamples']))])
使用此代码,我获得每兆字节读取2.2秒的双核和2Mb RAM,而我的文件通常有20+ Mb,这会产生一些非常烦人的延迟(特别考虑到我试图镜像的另一个基准共享软件程序)加载文件WAY更快)。
我想知道的是:
感谢您阅读
(我已经发布了一些关于我的这个工作的问题,我希望它们在概念上都是无关的,我也希望不要过于重复。)
编辑: channel_names
是一个列表,所以我做了@eumiro建议的修正(删除错误的括号)
修改:我目前正在讨论Sebastian建议使用array
fromfile()
方法,并很快将最终代码放在此处。此外,每一个结果对我都非常有用,我非常高兴地感谢所有善意回答的人。
最终形式与array.fromfile()
一起进行一次,然后通过切割大数组为每个通道交替扩展一个数组:
fullsamples = array('h')
fullsamples.fromfile(f, os.path.getsize(f.filename)/fullsamples.itemsize - f.tell())
position = 0
for rec in xrange(int(self.header['nrecs'])):
for channel in self.channel_labels:
samples = int(self.channel_content[channel]['nsamples'])
self.channel_content[channel]['recording'].extend(
fullsamples[position:position+samples])
position += samples
一次读取文件或以任何形式使用struct
,速度提升非常令人印象深刻。
答案 0 :(得分:15)
您可以使用array
来阅读您的数据:
import array
import os
fn = 'data.bin'
a = array.array('h')
a.fromfile(open(fn, 'rb'), os.path.getsize(fn) // a.itemsize)
比@samplebias's answer的struct.unpack
快40倍。
答案 1 :(得分:7)
如果文件只有20-30M,为什么不读取整个文件,只需一次调用unpack
解码nums,然后通过遍历数组在你的通道中分配它们:
data = open('data.bin', 'rb').read()
values = struct.unpack('%dh' % len(data)/2, data)
del data
# iterate over channels, and assign from values using indices/slices
快速测试表明,在20M文件上,这比struct.unpack('h', f.read(2))
加速了10倍。
答案 2 :(得分:1)
不确定它是否会更快,但我会尝试一次解码大块的单词而不是一个单词。例如,您可以一次读取100个字节的数据,如:
s = f.read(100)
struct.unpack(str(len(s)/2)+"h", s)
答案 3 :(得分:1)
extend() acepts iterables,即代替.extend([...])
,您可以编写.extend(...)
。它可能会加速程序,因为 extend()将在生成器上处理,不再在构建列表上处理
您的代码存在不一致:您先定义channel_content = {}
,然后执行channel_content[channel]['recording'].extend(...)
,需要初步存在密钥频道和子密钥< strong>'recording',列表作为值,可以扩展为某种内容
self.channel_content[channel]['nsamples']
的性质是什么,以便可以提交给 int()函数?
number_of_intervals 来自哪里?间隔的性质是什么?
在rec in xrange(number_of_intervals)):
循环中,我再也看不到 rec 了。所以在我看来,你重复相同的循环过程for channel in channel_names:
的次数与 number_of_intervals 表示的次数相同。是否有number_of_intervals * int(self.channel_content [channel] ['nsamples'])* 2要读取的值?
我在文档中读到:
class struct.Struct(format)
返回a 写入和的新Struct对象 根据数据读取二进制数据 格式字符串格式。创建一个 结构对象一次并调用它 方法比调用更有效 结构函数具有相同的功能 格式,因为只有格式字符串 需要编译一次。
这表达了与samplebias相同的想法。
如果您的目标是创建字典,还可以将 dict()与生成器一起用作参数
我建议
channel_content = {}
for rec in xrange(number_of_intervals)):
for channel in channel_names:
N = int(self.channel_content[channel]['nsamples'])
upk = str(N)+"h", f.read(2*N)
channel_content[channel]['recording'].extend(struct.unpack(x) for i,x in enumerate(upk) if not i%2)
我不知道如何考虑J.F. Sebastian建议使用数组
答案 4 :(得分:0)
来自文件调用的单个数组肯定是最快的,但如果数据与其他值类型交错,则无法工作。
在这种情况下,可以与之前的struct回答相结合的另一个大速度增加是,不是多次调用unpack函数,而是使用每个chunk的格式预编译struct.Struct对象。来自docs:
创建一个Struct对象并调用其方法更多 比以后用相同的格式调用struct函数更有效 格式字符串只需要编译一次。
例如,如果你想一次解包1000个交错短裤和浮点数,你可以写:
chunksize = 1000
structobj = struct.Struct("hf" * chunksize)
while True:
chunkdata = structobj.unpack(fileobj.read(structobj.size))
(请注意,该示例仅是部分示例,需要考虑更改文件末尾的chunksize并打破while循环。)