用 BytesIO 节省内存的令人惊讶的方法

主要观点:在 Python 中,若需内存中存储字节的类文件对象,可使用io.BytesIO()。读取数据时若数据量较大,应节省内存,文中涵盖BytesIO的介绍、BytesIO.read()的内存使用影响、高效访问BytesIO数据的两种替代方法及权衡。
关键信息

  • io.BytesIO可创建内存中存储字节的类文件对象,如f = BytesIO(),可写入字节并通过f.read()读取。
  • BytesIO.read()在一次性读取所有数据时会使内存使用量翻倍,如通过测量函数可看出。
  • BytesIO.getbuffer()返回底层数据的memoryview,不分配新内存,如data : memoryview = f.getbuffer(),但memoryview缺少bytes的某些方法,且从编译扩展访问memoryview对象有问题,从memoryview创建新bytes对象会复制数据。
  • BytesIO.getvalue()返回BytesIO的内容作为bytes对象,神奇的是不分配内存,利用了写时复制,如data : bytes = f.getvalue(),写入时才会分配新内存。
    重要细节
  • 测量内存使用的函数report_allocated通过gc.collect()收集垃圾,tracemalloc.get_traced_memory()获取内存使用情况。
  • memoryview在 CPython 3.11 及之后版本的稳定 C ABI 中可正常访问,当前多数开源项目支持 3.10 和 3.9 版本,在这些版本中从编译扩展访问memoryview有问题。
  • 实际应用中,从read()切换到getvalue()可节省内存,如在Polars 的 pull request中有所体现。总结:提取BytesIO数据时,避免BytesIO.read()(除非多次小读),需要所有内容为bytesBytesIO.getvalue(),可用memoryview时用BytesIO.getbuffer()
阅读 8
0 条评论