io.BytesIO非常慢。备择方案?优化?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了io.BytesIO非常慢。备择方案?优化?相关的知识,希望对你有一定的参考价值。

我正在使用相机在Raspberry Pi上运行Python v3.5脚本。该计划涉及记录来自picamera的视频,并从视频流中采集样本帧以执行操作。有时,处理字节缓冲区需要很长时间(20多秒)。代码的简化版本包含问题区域:

import io
import picamera

camera = picamera.PiCamera()
camera.start_recording("/path/to/file.h264")
cnt = 0
while True:
    if cnt > 30:
        stream = io.BytesIO()
        camera.capture(stream, use_video_port=True, resize=(1920, 1080), format='rgba')
        cnt = 0
    else:
        cnt += 1

过了一会儿,打开字节流所花费的时间就变得疯狂了。在我的最新一次运行中,一个实例花了48秒! This figure shows a plot of the times to open the byte stream for each cycle.我对代码中有问题的区域中的每一行进行了时序测试,我可以确认是导致延迟的stream = io.BytesIO()线。

当我使用psutils在此任务期间监视Raspberry Pi的CPU和内存时,我发现没有明显的问题。 CPU使用率为10-15%,虚拟内存使用率为~24.2%,正在使用0交换。

除了Python程序之外,Pi上没有运行其他用户执行的进程。硬件正在运行带有GUI的默认Raspbian安装。

由于Python程序是1000多行,因此我不会在此问题文本中包含除最小示例之外的任何内容。如果你想看看它的上下文信息,please take a look at this Gist with the code

初步搜索表明,这是BytesIO的一个已知问题。 Python的一些旧的错误跟踪(大约2014年)表明,对于3.5版本中的某些情况,这已得到改进。

问题是:

  • 为什么BytesIO在这里慢?
  • 是否有另一种流式传输字节的方法更快?
  • 有没有更好的方法来使用BytesIO来获得我需要的东西?

编辑:我在循环中添加了一行,强制流在每个进程结束时使用stream.close()关闭,但这似乎无效。我仍然有20秒以上的流开放时间。

EDIT_2:我从编辑的信息中误读了测试中的值,并错过了这些值具有科学记数法。

答案

When BytesIO is called in a loop, it must be manually closed.

在该示例中,由于Python处理关闭字节流的方式,BytesIO似乎很慢。来自the documentation for BytesIO

使用内存中字节缓冲区的流实现。它继承了BufferedIOBase。调用close()方法时,将丢弃缓冲区。

为什么大多数用户永远不会看到这个

在退出时发出命令之前,通常不会销毁字节缓冲区。当Python脚本完成并且环境被解构时,iobase_exitsee line 467)会发出自动close()。可以假设大多数用户只是在缓冲区中打开一个字节流并使其保持打开状态直到脚本完成。也许这不是“最好”的方式,但这就是我见过的实现io的大多数脚本都使用它。

当重新调用新流而不关闭时,缓冲区似乎不断堆积,偶尔需要系统协商在内存限制时关闭它们。 Raspberry Pi的有限资源似乎加剧了这种情况。这可以通过做一些奇特的事情来测量内存使用,因为缓冲区填满了,但我在这里并不关心它,这超出了我的经验水平。

顺序使用!=重新进入

如果稍后重新输入SAME缓冲区,则不应该出现这种情况。通过发出运行时错误来保护IO类免受此边缘情况的影响。见here。这是我在原始问题中报告的单独案例,因为每次调用BytesIO时都会生成一个新缓冲区。讨论这一点是相关的,因为对文档这一部分的误解促成了问题中描述的事件。

OP中MWE的校正

import io
import picamera

camera = picamera.PiCamera()
camera.start_recording("/path/to/file.h264")
cnt = 0
while True:
    if cnt > 30:
        stream = io.BytesIO()
        camera.capture(stream, use_video_port=True, resize=(1920, 1080), format='rgba')
        stream.close()
        cnt = 0
    else:
        cnt += 1

以上是关于io.BytesIO非常慢。备择方案?优化?的主要内容,如果未能解决你的问题,请参考以下文章

在 python3 中写入 csv 中的 io.BytesIO 失败

使用 io.BytesIO 从天蓝色存储读取图像?

将 io.StringIO 转换为 io.BytesIO

将 io.BytesIO 转换为 io.StringIO 以解析 HTML 页面

pythonio.BytesIO简要介绍及示例

如下VBA代码 在EXCEL运行时非常慢,有哪位大侠能帮忙解决优化一下。