如何提高读取和转换二进制文件的速度?

2024-06-07 11:32:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我知道以前有一些关于文件读取、二进制数据处理和使用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秒,双核内存为2 Mb,我的文件通常有20+Mb,这会带来一些非常烦人的延迟(特别是考虑到另一个基准共享软件程序,我正试图以更快的速度镜像加载文件)。

我想知道的是:

  1. 如果有一些违反“良好实践”的情况:排列不好的循环、重复操作所需时间超过必要时间、使用效率低下的容器类型(字典?)等等
  2. 如果这个读取速度是正常的,或者对于Python是正常的,并且如果读取速度
  3. 如果创建C++编译扩展可能会提高性能,如果它是推荐的方法。
  4. (当然)如果有人建议对此代码进行一些修改,最好是基于以前类似操作的经验。

谢谢你的阅读

(我已经发布了一些关于我这项工作的问题,我希望它们在概念上都不相关,我也希望不要太重复。)

Edit:channel_names是一个列表,因此我做了@eumiro建议的更正(删除键入的括号)

编辑:我现在同意塞巴斯蒂安的建议,将arrayfromfile()方法一起使用,并很快将最终代码放在这里。此外,每一个贡献对我都非常有用,我非常高兴地感谢每一个善意的回答。

使用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,速度的提高都令人印象深刻。


Tags: 文件代码inselffor时间channelposition
3条回答

如果文件只有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倍。

extend()acepts iterables,也就是说,您可以编写.extend(...),而不是.extend([...])。它可能会加快程序的速度,因为extend()将在生成器上处理,而不再在生成的列表上处理

代码中有一个不连贯的地方:首先定义channel_content = {},然后执行channel_content[channel]['recording'].extend(...),这需要一个键通道和一个子键“录制”的初始存在,并将一个列表作为一个值,以便能够扩展到某些内容

self.channel_content[channel]['nsamples']的性质是什么,以便它可以提交到int()函数?

间隔数从何而来?间隔的性质是什么?

rec in xrange(number_of_intervals)):循环中,我再也看不到rec。因此,在我看来,您重复的循环过程for channel in channel_names:是由间隔数表示的次数的两倍。是否存在要在f中读取的间隔*int(self.channel_content[channel]['nsamples'])*2个值的个数?

我在文件中读到:

class struct.Struct(format)

Return a new Struct object which writes and reads binary data according to the format string format. Creating a Struct object once and calling its methods is more efficient than calling the struct functions with the same format since the format string only needs to be compiled once.

这表达了与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.塞巴斯蒂安关于使用数组的建议

您可以使用^{}读取数据:

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倍。

相关问题 更多 >

    热门问题