Низкая производительность отчета

Я использую reportlab, чтобы преобразовать большую библиотеку (простой текст на русском языке) в формат PDF. Когда исходный файл достаточно мал (скажем, около 10-50 кБ), он работает нормально. Но если я пытаюсь конвертировать большие тексты (более 500 КБ), потребуется много времени, чтобы продолжить работу отчета. Кто-нибудь знает в чем может быть проблема?

BYTES_TO_READ = 10000 

def go(text):
    doc = SimpleDocTemplate("output.pdf")
    Story = [Spacer(1, 2*inch)]
    style = styles["Normal"]
    p = Paragraph(text, style)
    Story.append(p)
    doc.build(Story)

def get_text_from_file():
    source_file = open("book.txt", "r")
    text = source_file.read(BYTES_TO_READ)
    source_file.close()
    return text

go(get_text_from_file())

Итак, когда я пытаюсь установить BYTES_TO_READ от 200 до 300 тысяч (т. е. просто чтобы увидеть, что происходит, не читая всю книгу, а только ее часть) - это занимает ОГРОМНОЕ количество времени

1 ответ

Решение

Позвольте мне в предисловии сказать, что у меня совсем нет опыта работы с reportlab. Это всего лишь общее предложение. Это также не касается того, как именно вы должны анализировать и форматировать текст, который вы читаете, в правильные структуры. Я просто продолжаю использовать Paragraph класс для написания текста.

С точки зрения производительности, я думаю, что ваша проблема связана с попыткой прочитать огромную строку один раз и передать эту огромную строку в виде одного абзаца в reportlab. Если подумать, какой абзац на самом деле составляет 500 Кбайт?

То, что вы, вероятно, захотите сделать, это прочитать небольшими порциями и собрать свой документ:

def go_chunked(limit=500000, chunk=4096):

    BYTES_TO_READ = chunk

    doc = SimpleDocTemplate("output.pdf")
    Story = [Spacer(1, 2*inch)]
    style = styles["Normal"]

    written = 0

    with open("book.txt", "r") as source_file:
        while written < limit:
            text = source_file.read(BYTES_TO_READ)
            if not text:
                break
            p = Paragraph(text, style)
            Story.append(p)
            written += BYTES_TO_READ

    doc.build(Story)

При обработке всего 500 Кбайт:

%timeit go_chunked(limit=500000, chunk=4096)
1 loops, best of 3: 1.88 s per loop

%timeit go(get_text_from_file())
1 loops, best of 3: 64.1 s per loop

Опять же, очевидно, что это просто разделение вашего текста на произвольные абзацы размером с BYTES_TO_READ значение, но не сильно отличается от одного огромного абзаца. В конечном счете, вы можете захотеть проанализировать текст, который вы читаете, в буфер и определить свои собственные абзацы, или просто разбить на строки, если это формат вашего исходного источника:

def go_lines(limit=500000):

    doc = SimpleDocTemplate("output.pdf")
    Story = [Spacer(1, 2*inch)]
    style = styles["Normal"]

    written = 0

    with open("book.txt", "r") as source_file:
        while written < limit:
            text = source_file.readline()
            if not text:
                break
            text = text.strip()
            p = Paragraph(text, style)
            Story.append(p)
            written += len(text)

    doc.build(Story)

Спектакль:

%timeit go_lines()
1 loops, best of 3: 1.46 s per loop
Другие вопросы по тегам