6

我希望在纯python的内存中有效地将一堆小pdf连接(附加)在一起。具体来说,通常情况是 500 个单页 pdf,每个大小约为 400 kB,合并为一个。假设 pdf 可作为内存中的迭代器使用,例如列表:

my_pdfs = [pdf1_fileobj, pdf2_fileobj, ..., pdfn_fileobj]  # type is BytesIO

其中每个 pdf_fileobj 的类型为 BytesIO。然后,基本内存使用量约为 200 MB(500 个 pdf,每个 400kB)。

理想情况下,我希望使用不超过 400-500 MB 的总内存(包括my_pdfs)连接以下代码。但是,情况似乎并非如此,最后一行的调试语句表明使用的最大内存几乎是 700 MB。此外,使用 Mac os x 资源监视器,当到达最后一行时,分配的内存被指示为 600 MB。

运行会将gc.collect()其减少到 350 MB(几乎太好了?)。在这种情况下,为什么我必须手动运行垃圾收集来摆脱合并垃圾?我已经看到这(可能)导致内存在稍微不同的情况下积累,我现在将跳过。

import PyPDF2
import io
import resources  # For debugging

def merge_pdfs(iterable):
    ''' Merge pdfs in memory '''
    merger = PyPDF2.PdfFileMerger()
    for pdf_fileobj in iterable:
        merger.append(pdf_fileobj)

    myio = io.BytesIO()
    merger.write(myio)
    merger.close()

    myio.seek(0)
    return myio

my_concatenated_pdf = merge_pdfs(my_pdfs)

# Print the maximum memory usage
print('Memory usage: %s (kB)' % resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)

问题摘要

  • 为什么上面的代码需要将近 700 MB 的内存来合并 200 MB 的 pdf?不应该 400 MB + 开销就足够了吗?我该如何优化它?
  • 当有问题的变量应该已经超出范围时,为什么我需要手动运行垃圾收集来摆脱 PyPDF2 合并垃圾?
  • 这种通用方法怎么样?BytesIO适合使用这种情况吗?merger.write(myio)考虑到一切都发生在 ram 中,它的运行似乎有点慢。

谢谢!

4

2 回答 2

1

问:为什么上面的代码需要将近 700 MB 的内存来合并 200 MB 的 pdf?不应该 400 MB + 开销就足够了吗?我该如何优化它?

A: 因为.append创建了一个新的流对象然后你使用merger.write(myio),它创建了另一个流对象并且你已经有 200 MB 的 pdf 文件在内存中,所以 3*200 MB。


问:当有问题的变量应该已经超出范围时,为什么我需要手动运行垃圾收集来摆脱 PyPDF2 合并垃圾?

答:这是 PyPDF2 中的一个已知问题


问:这个通用方法怎么样?BytesIO适合使用这种情况吗?

答:考虑到内存问题,您可能想尝试不同的方法。也许一个一个合并,暂时将文件保存到磁盘,然后从内存中清除已经合并的文件。

于 2018-01-02T10:54:20.917 回答
0

PyMuPdf 库也可能是解决PDFMergerfrom性能问题的一个不错的选择PyPDF2

于 2019-04-23T11:59:01.957 回答