V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
raiz
V2EX  ›  Python

python 读取二进制文件的速度

  •  
  •   raiz · 2015-10-14 15:23:47 +08:00 · 5991 次点击
    这是一个创建于 3327 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我需要从 .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 是怎样工作的, 它能达到我的目的吗?

    14 条回复    2015-10-15 13:57:15 +08:00
    raiz
        1
    raiz  
    OP
       2015-10-14 15:34:57 +08:00
    @raiz
    使用①的情况下 time using: 1862.1060848236084 msec
    使用②的情况下 time using: 1743.0999279022217 msec
    waklin
        2
    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
        3
    waklin  
       2015-10-14 16:37:33 +08:00
    把 open 的文件换成 1875.wav
    raiz
        4
    raiz  
    OP
       2015-10-14 17:11:57 +08:00
    @waklin hey 谢谢, 我知道 raw_file.read() 是读到内存,但是注意我的 start_time = time.time() 是放在它之后的,我是想测试从 BytesIo 读取的速度与 从 raw_file 的比较。
    waklin
        5
    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
        6
    xylophone21  
       2015-10-14 19:16:28 +08:00
    仅看代码,第一怀疑 buff.append(byte)这句,没验证。
    xylophone21
        7
    xylophone21  
       2015-10-14 19:16:56 +08:00
    看错了,请忽略我上面的帖子。。
    mengzhuo
        8
    mengzhuo  
       2015-10-14 22:15:49 +08:00 via iPhone
    mark 目测需要流式处理
    WKPlus
        9
    WKPlus  
       2015-10-14 23:34:53 +08:00
    我的理解 BytesIO 的用法和 StringIO 类似,是用来提供类似文件操作一样的接口来操作内存中的 bytes 和 string 的,而不是用来全部读到内存来加速的。

    你原来的程序比较慢,估计是 file.read(1)引起的,每次只读一个字节干那要循环多少次?
    ryd994
        10
    ryd994  
       2015-10-15 05:47:45 +08:00
    差别不会太大的,操作系统有缓存
    可以考虑 os.posix_fadvise ,可能会更好, POSIX_FADV_SEQUENTIAL
    ryanking8215
        11
    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
        12
    raiz  
    OP
       2015-10-15 09:35:45 +08:00
    @ryanking8215 你说的几个点应该是导致两者时间接近的原因, 默认的 fs 是有 buffer 的,而我每次只读一个 字节 ,所以那个 buffer 是够用的,所以下限不再是文件系统 io 的速度,而是在内存总遍历所有字节的速度。 说到正确的打开方式, 比如说 photoshop , 打开图片,或 audition 打开一个音频文件,应该是整一个读到内存中的吧,策略是怎样的呢?
    ryanking8215
        13
    ryanking8215  
       2015-10-15 10:16:31 +08:00
    @raiz 这要看具体应用啊,比如图片,要显示总是需要全部的数据。比如音频视频,文件很大,播放是流的方式,只需要分片取,不需要全部数据都进 memory
    matthewgao
        14
    matthewgao  
       2015-10-15 13:57:15 +08:00
    我和 @ryanking8215 的意见一样。

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

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

    还是根据需求吧,如果你真的文件很小,那么你索性就一次性都读进来,循环都不用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1172 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 23:53 · PVG 07:53 · LAX 15:53 · JFK 18:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.