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

时间:2019-03-22 07:55:52

标签: python camera raspberry-pi bytesio bytestream

我正在使用相机在Raspberry Pi上运行Python v3.5脚本。该程序包括从picamera录制视频,并从视频流中获取样本帧以对其进行操作。有时,处理字节缓冲区需要很长时间(20+ s)。包含问题区域的代码的简化版本是:

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

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

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

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

问题是:

  • 为什么BytesIO在这里慢?
  • 有没有其他更快的流字节方法?
  • 是否有更好的方法来使用BytesIO来获取我需要的东西?

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

EDIT_2:我从修改后的信息中误读了测试中的值,却错过了这些值具有科学计数法的想法。

1 个答案:

答案 0 :(得分:0)

在循环中调用BytesIO时,必须手动将其关闭。

在该示例中,由于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
相关问题