Для запуска интерпретатора Python требуется ~12 секунд, и все это происходит в `import pyexpat`
Я использую Python, установленный на Homebrew, на моем Mac (под управлением OS X 10.13.1), и в последнее время я заметил, что переводчику требуется очень много времени для запуска.
В попытке решить эту проблему, я сделал простую проверку time
:
PIPER-ALPHA:~$ time bpython -c 'pass'
real 0m12.141s
user 0m1.662s
sys 0m10.073s
… Который показал вопиющую проблему: 12 секунд!
Я тогда использовал gnomon
- очень удобный npm
Первый модуль используется для определения сроков работы инструментов CLI - для выявления проблемы до неисправного модуля Python. Я использовал эту команду:
PIPER-ALPHA:~$ PYTHONVERBOSE=1 bpython -c 'pass' 2>&1 | tee -a /tmp/bpython-startup-messages | gnomon
... то gnomon
output показывает время, затрачиваемое каждой строкой, выданной подробным выводом интерпретатора Python. Это выглядит так:
… Я выделил строку вывода, выполнение которой заняло почти двенадцать секунд - безусловно, самую длинную, поскольку каждая другая строка обычно занимала несколько наносекунд или, возможно, максимум несколько микросекунд.
Обычно, если я сталкиваюсь с вонючим расширением Python, я перекомпилирую его сам или каким-либо другим образом настрою его из исходного кода, чтобы должным образом сделать его беспроблемным по мере необходимости. Но в этом случае я имею дело с модулем c-extension, который является частью более крупного модуля стандартной библиотеки Python, каждый из которых поставляется с двоичным пакетом Homebrew (который называется "бутылкой" в аромате Homebrew), который содержит эта версия Python.
Кто-нибудь еще может засвидетельствовать это? В частности, это проблема, которую кто-то еще видит при запуске Python в аналогичных обстоятельствах? И, самое главное, как я могу это исправить? Нужно ли перестраивать всю установку Python, используя Homebrew или без него?
1 ответ
Я понял это - ответ оказывается одновременно осветляющим и смущающим - и мое решение может помочь другим, столкнувшись с подобными обстоятельствами.
В двух словах: мучительно длинные паузы ~12 с, которые я испытывал во время загрузки интерпретатора Python, были вызваны слишком большим количеством установленных модулей расширения Python. Это не было проблемой с Python 2.7 в комплекте xml.parsers.expat
модуль, ни с его C-API pyexpat
расширение.
Для остроумия: мое использование gnomon
инструмент, который предоставил то, что казалось прямым и прямым доказательством, указывающим на эти модули, в конечном итоге ввел меня в заблуждение относительно моих выводов о том, где должен быть найден проблемный код.
После публикации моего вопроса, я сделал немного дополнительной судебной экспертизы. Изменяя код Python, который я передавал интерпретатору, вызывая проверки скорости командной строки, я обнаружил, что gnomon
отчет будет показывать ту же остановку в двенадцать с лишним секунд, но с частотой различных import
заявления. Кроме того, я обнаружил, что некоторые варианты команд (например, те, которые выполняются с использованием pythonpy
CLT) вообще не страдает от поведения остановки.
Мне удалось точно определить строки кода, ответственные за проявление проблем, когда я наткнулся на него случайно - во время выполнения моих тестов бесконечно длинные остановки были не менее раздражающими, и я закончил контрольным тестированием ряда тестов. в середине остановки. Прерванные тестовые прогоны прекращаются с KeyboardInterrupt
исключения, и сопровождающий вывод трассировки стека показал функцию, в которой вещи перетаскивали:
... то pkg_resources
модуль, когда импортируется, обходит каждый из каталогов расширений, названных в sys.path
перечисление каждого пакета в каждом расширении и последующее чтение и последующий анализ всех связанных метаданных для всех этих расширений. Используя любую часть pkg_resources
(что само по себе является частью существенного setuptools
module) запускает это трудоемкое действие (которое затем кэшируется, по крайней мере, на время существования этого конкретного вызова интерпретатора). В зависимости от того, как установлена ваша установка Python и как вы вызываете своего интерпретатора, вы можете или не можете в конечном итоге сделать что-то, чтобы вызвать использование pkg_resources
, но он довольно широко используется в пакетах расширений Python, поэтому велика вероятность, что он будет вызван чем-то.
Фактическая функция, ответственная за фактический цикл, который фактически перечисляет пакеты, _initialize_master_working_set()
- это тот, который я выделил на скриншоте выше. Это то, что все мои KeyboardInterrupt
выявлены следы стека. Оттуда сразу стало ясно, что разочаровывающие остановки были круто линейной функцией от количества присутствующих пакетов "Сырного магазина" (с чем я был опрометчив после обновления моего ноутбука).
Я немедленно приступил к деинсталляции pip примерно 50% расширений, которые я установил безвозмездно, а затем сократил еще 40% или около того, подняв большую часть моих активно разрабатываемых Python-компонентов в изолированные virtualenv
каталоги проектов.
После этого я почувствовал себя довольно глупо, поскольку мне удалось хитро ввести в заблуждение себя с помощью использования причудливых инструментов анализа, а затем случайно найти реальное решение - одно из тех, что возникли в результате моей неосторожной невнимательности, не меньше. Несмотря на это, это все еще что-то, что может укусить других разработчиков Pythonic, и, следовательно, стоит рецензии. Настоящим вам предлагается учиться на моих крутых приключениях в вопросе сортировки и диагностики!