python 读取二进制文件的速度

2015-10-14 15:23:47 +08:00
 raiz

我需要从 .wav 读取 pcm 数据,一开始使用 open() 直接打开文件,然后 read() 每次读一个样本,
测试一个 5.49M 文件, 时长 29s, 的文件,读到结尾,花了 1850 ms, 如果要处理数据。
能否通过先全部读到内存来加速, BytesIO 似乎只做这个事的, 但是我不知道它把文件读到内存是一次性的还是怎样,需要多少时间,测试花的时间差不多

from io import BytesIO
import time


if __name__ == '__main__':
    with open('1875.wav', 'rb') as raw_file:
        ①# file = raw_file            # 这句是直接从文件读取的方式 
        ②file = BytesIO(raw_file.read())   # 这句是用了 BytesIO  
        start_time = time.time()
        buff = []
        while True:
            byte = file.read(1)
            if byte:
                buff.append(byte)
            else:
                break

        end_time = time.time() 
        print(' time using: ', (end_time - start_time) * 1000, 'msec')

使用①的情况下 time using: 1862.1060848236084 msec
使用①的情况下 time using: 1743.0999279022217 msec
时间时有浮动的
所以我想知道, ByteIO 是怎样工作的, 它能达到我的目的吗?

5998 次点击
所在节点    Python
14 条回复
raiz
2015-10-14 15:34:57 +08:00
@raiz
使用①的情况下 time using: 1862.1060848236084 msec
使用②的情况下 time using: 1743.0999279022217 msec
waklin
2015-10-14 16:35:01 +08:00
你用的二进制方式打开的文件,直接调用 raw_file.read()就已经将文件读入到内存中了。

# -*- coding: utf-8 -*-

import time
if __name__ == '__main__':
with open('TenMiLines-small.csv', 'rb') as raw_file:
start_time = time.time()
buff = raw_file.read()
for b in buff:
# print repr(b)
# b 就是你所说的一个样本
pass
end_time = time.time()
print(' time using: ', (end_time - start_time) * 1000, 'msec')
waklin
2015-10-14 16:37:33 +08:00
把 open 的文件换成 1875.wav
raiz
2015-10-14 17:11:57 +08:00
@waklin hey 谢谢, 我知道 raw_file.read() 是读到内存,但是注意我的 start_time = time.time() 是放在它之后的,我是想测试从 BytesIo 读取的速度与 从 raw_file 的比较。
waklin
2015-10-14 17:46:40 +08:00
1. 你的第一个帖子里提到你处理 5.49M 的文件总共花了 29m ,读到结尾,花了 1850 ms ,如果我理解的没有错,现在你想提高的是 1850ms 的时间
2. 一个 10M 的文件我用我给出的代码的 file.read()到 buff 里,然后遍历 buff 仅花了 50ms 左右,不知道你的 1850ms 是怎么回事
3. BytesIO 和 raw_file 区别
BytesIO 维护的一段内存中的数据, read 的话,仅操作内存
raw_file 相当于一个文件游标, read 的话,是从硬盘上读取
理论上访问硬盘的速度会低于访问内存的速度,但是由于现在硬盘都会有单独的缓存,读取的数据不是很大时,差别不明显。
xylophone21
2015-10-14 19:16:28 +08:00
仅看代码,第一怀疑 buff.append(byte)这句,没验证。
xylophone21
2015-10-14 19:16:56 +08:00
看错了,请忽略我上面的帖子。。
mengzhuo
2015-10-14 22:15:49 +08:00
mark 目测需要流式处理
WKPlus
2015-10-14 23:34:53 +08:00
我的理解 BytesIO 的用法和 StringIO 类似,是用来提供类似文件操作一样的接口来操作内存中的 bytes 和 string 的,而不是用来全部读到内存来加速的。

你原来的程序比较慢,估计是 file.read(1)引起的,每次只读一个字节干那要循环多少次?
ryd994
2015-10-15 05:47:45 +08:00
差别不会太大的,操作系统有缓存
可以考虑 os.posix_fadvise ,可能会更好, POSIX_FADV_SEQUENTIAL
ryanking8215
2015-10-15 09:00:16 +08:00
class io.BytesIO([initial_bytes])
A stream implementation using an in-memory bytes buffer.

raw_file 是文件流,如果 open 时如果是 rb 的, buffering=-1 的,则使用内置的 chunked buffer ,这是针对你参数的结果。

从 file.read(1)上看,都是从 file 流里取 1 个字节。从效率上看, BytesIO 是内存访问, raw_file 是 buffer+Filesystem io 的结果,所以貌似慢一点。

但这都不是正确的打开方式,要效率高就要 chunk 读,减少 FS IO 和循环次数。 BytesIO 更大的意义在抽象层次上, fs 和 buffer 都能通过 file io 访问,是不是 test 的时候简单一点?

如果错误请指正,欢迎探讨。 -)
raiz
2015-10-15 09:35:45 +08:00
@ryanking8215 你说的几个点应该是导致两者时间接近的原因, 默认的 fs 是有 buffer 的,而我每次只读一个 字节 ,所以那个 buffer 是够用的,所以下限不再是文件系统 io 的速度,而是在内存总遍历所有字节的速度。 说到正确的打开方式, 比如说 photoshop , 打开图片,或 audition 打开一个音频文件,应该是整一个读到内存中的吧,策略是怎样的呢?
ryanking8215
2015-10-15 10:16:31 +08:00
@raiz 这要看具体应用啊,比如图片,要显示总是需要全部的数据。比如音频视频,文件很大,播放是流的方式,只需要分片取,不需要全部数据都进 memory
matthewgao
2015-10-15 13:57:15 +08:00
我和 @ryanking8215 的意见一样。

我觉得也没有必要这么做,在你的程序里通过文件流还是 ByteIO 基本上等于没有区别

如果文件很大不可能都读到内存中来,如果你内存只有 2G ,那么 4G 的电影肿么打开, swap 到死啊。。

还是根据需求吧,如果你真的文件很小,那么你索性就一次性都读进来,循环都不用

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/227971

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX