Солнце говорит: определи функцию-бутылочное горлышко с помощью Python cProfile!

9 min

language: ja bn en es hi pt ru zh-cn zh-tw

Мы!
У-у!
Приблизьтесь!
Мяу!

Здравствуйте, я бездарь.
В последнее время мне очень нравится слушать канал Масахиро Сакураи, бывшего сотрудника HAL Laboratory, в фоновом режиме, пока я вожусь с компьютером, как будто это радио.
На самом деле, вспомнились времена, когда я играл на 3DS, и Nintendo Direct от президента Ивата, которые я смотрел тогда, всегда были веселыми. Пересматривая это, я вижу, что такие люди, как Тосио Икеда из Fujitsu, обладают огромной энергией в документальных фильмах, и это был невероятный мир, когда они неоднократно ездили в США в то время. В то время шла Вьетнамская война, и, должно быть, оставались тени послевоенного периода после Второй мировой войны, но откуда взялась вся эта энергия?

Введение

Я не особо задумывался о скорости выполнения скриптовых языков, но когда я запускал свой cuckooget, это меня заинтриговало.
Часть кода, написанная на Rust, может быть выполнена внутри интерпретатора с помощью PyO3/maturin.
Проще говоря, функции, написанные на Rust, превращаются в библиотеки Python и выполняются через скомпилированный бинарный файл кода Rust. Простая причина в том, что для процессов, требующих вычислений CPU, выполнение через этот бинарный файл, вероятно, будет быстрее. В итоге это стало быстрее, но в этом есть большая ошибка.
Во-первых, измерения проводились без предварительного бенчмаркинга кода, написанного исключительно на Python.
Даже в «Заметках о программировании на C», написанных Робом Пайком, которого называют отцом Go, говорится:

Правило 1: Вы не можете знать, где программа будет тратить время. Узкие места возникают в неожиданных местах. Поэтому не делайте предположений и не занимайтесь «скоростными хаками», пока не выясните, где находится узкое место.

Если так, то измерения следует проводить. Если это принцип для низкоуровневого языка C, то не может быть, чтобы он не применялся к более высоким уровням.

Я часто забываю такие вещи, поэтому постоянно чувствую необходимость в чем-то вроде философии UNIX для самодисциплины, чтобы регулярно проверять себя.

Так я написал, но я человек, и хотя я это понимаю, я склонен к выбросам...

Измерение с помощью cProfile

Сначала я использовал примитивный метод: import time, start_time = time.time(), а после завершения выполнения функции end_time = time.time() и print(f"exec time {start_time - end_time}"). Но это так неэффективно, когда используешь компьютер! Поэтому я поискал что-то, что могло бы показывать время выполнения для каждой функции.
Оказывается, существует стандартная библиотека под названием cProfile. Следующий сайт очень понятен:
Анализ производительности кода Python с помощью cProfile

В моем случае я проверял следующим образом:

python3 -m cProfile -o profile.pstats ./main.py https://soulminingrig.com/ site

Таким образом, время выполнения функций, запускаемых с помощью ./main.py , сохраняется в бинарном файле profile.pstats.

Проверка результатов выполнения функций

На предыдущем сайте это делалось с использованием исполняемого файла Python, но гораздо проще войти в режим командной строки pstats и проверить.

python3 -m pstats profile.pstats

Теперь, войдя в режим командной строки pstats, проверьте результат вывода следующим образом:

profile.pstats% stats 5
Thu Nov  7 20:24:10 2024    profile.pstats

         48488696 function calls (47625490 primitive calls) in 51.151 seconds

   Random listing order was used
   List reduced from 3017 to 5 due to restriction <5>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1586    0.001    0.000    0.001    0.000 /home/haturatu/.local/lib/python3.12/site-packages/aiohttp/client_reqrep.py:909(__del__)
       80    0.000    0.000    0.001    0.000 /usr/lib/python3.12/copy.py:247(_reconstruct)
        4    0.000    0.000    0.005    0.001 /usr/lib/python3.12/encodings/__init__.py:71(search_function)
        3    0.000    0.000   98.745   32.915 /usr/lib/python3.12/asyncio/base_events.py:651(run_until_complete)
     1103    0.001    0.000    0.003    0.000 /usr/lib/python3.12/re/_parser.py:312(_class_escape)

Другие команды можно проверить с помощью help.

profile.pstats% help

Documented commands (type help <topic>):
========================================
EOF  add  callees  callers  help  quit  read  reverse  sort  stats  strip

В моем случае я вывел stats 100, открыл его в vim из другого терминала, скопировал и вставил, а затем вышел с помощью quit.
Затем я просматриваю этот текстовый файл, сохраненный после вставки в vim, используя awk и sort.

В случае tottime

$ awk '{ print $2","$6 }' bench | sort | column -t -s "," | tail 
0.009    /usr/lib/python3.12/re/_compiler.py:243(_optimize_charset)
0.010    /usr/lib/python3.12/concurrent/futures/_base.py:428(result)
0.012    /usr/lib/python3.12/asyncio/base_events.py:627(run_forever)
0.013    /usr/lib/python3.12/re/__init__.py:280(_compile)
0.018    /usr/lib/python3.12/re/_compiler.py:37(_compile)
0.022    /usr/lib/python3.12/concurrent/futures/_base.py:497(set_running_or_notify_cancel)
0.030    /home/haturatu/git/devgit/async_web_mirror.py:186(download_and_save_image)
0.031    /usr/lib/python3.12/re/_parser.py:512(_parse)
0.061    /usr/lib/python3.12/concurrent/futures/_base.py:537(set_result)
tottime  filename:lineno(function)

Кажется, что tottime само по себе достаточно для бенчмаркинга каждой функции.

Бутылочное горлышко Beautiful Soup 4 — это просто мое воображение?

В другой среде медленным оказался search_tag из element.py, вызываемый из bs4, который, казалось, замедлял работу.
Меня заинтересовало это место:

        if ((not self.name)
            or call_function_with_tag_data
            or (markup and self._matches(markup, self.name))
            or (not markup and self._matches(markup_name, self.name))):

Трудно поверить, что оператор or является узким местом...
Однако в этом element.py есть только 7 мест, где используется or для оценки, и три из них используются здесь.
Что-то здесь не так.


Итак, поскольку тема перешла к bs4, на этом пока все.
Снова жду вашего сотрудничества.

Related Posts