Комментарии 58
Не привыкли питонисты память считать :-/
Да и потом, одно дело 1 раз потратить 8 часов, другое дело — каждому пользователю данного скрипта докупать дополнительно ОЗУ, потому что на оптимизации сэкономили.
Не знаю вариантов, при которых могло бы потребоваться докупать ОЗУ на пользовательских машинах ради исполнения python скрипта. У языка немного другая прикладная область и иные принципы использования. Python используется для быстрого решения сложных задач. Точнее, для быстрого кодинга. Он всегда занимает много памяти, жрёт ресурсы и медленно работает. Но реализация на нём сложного алгоритма обходится дешевле по времени, чем реализация на многих других языках. Сам принцип работы python — запустить его там, где требуется особый функционал, и не смотря на ресурсы. Поэтому, почти никто не пишет на нём клиентский софт.
Поэтому, почти никто не пишет на нём клиентский софт.
Ничего себе! Ну-ка, попробую-ка я прикинуть, каким несерверным софтом на питоне я пользуюсь или активно пользовался раньше: Zim, Gramps, Gajim, Deluge, Puddletag, Calibre, MComix… И это только на ПК, без учёта смартфона, на котором у меня тоже было что-то написанное на питоне.
И представьте себе, меньше месяца назад репортил баг об утечке памяти в менеджере заметок Zim. Да, мне существенно мешало, что он начинал отжирать гигабайты памяти вместо обычных 50-100 МБ.
Умножаем на количество установок, количество экземпляров на серверах — и внезапно может оказаться, что программисту доплатить дешевле.
Вообще говоря с фразы «Одна из главных проблем при написании крупных (относительно) программ на Python — минимизация потребления памяти» я неплохо посмеялся. Сколько себя помню, во всех проектах на питоне, которые я знаю — плевали на память от слова совсем. В крупных проектах, например, даже отключают сборщик мусора, на столько память не важна.
Хотя статья, безусловно, интересна, спасибо автору. Побольше бы статей о том, что происходит под капотом.
Перезапуском решают обычно не проблему зажористости по памяти, а проблему утечек. Очень часто под капотом питон приложений работают Си библиотеки, которые имеют неприятную привычку подтекать. В uwgi это решают ребутом приложения после определенного количества запросов, и обычно оно измеряется тысячами. Ребутать его после каждого запроса — это невероятно накладно.
Аналогично, в памяти uWSGI не хранится код импортированных модулей. Ещё стоит вспомнить реализацию apps в джанге. Обратите внимание на то, что INSTALLED_APPS — это массив строк.
Остальное по большей части все правильно за исключением самой концовки. Дочерний процесс не умрет на возвращении Response.
Ну и про uWSGI и INSTALLED_APPS это вообще отлично. Это вещи между собой вообще не связанные. uWSGI может запускать не django, а flask. Да, кстати, покурите еще такую тему, как постоянные подключения к БД в обоих фреймворках. Пруф docs.djangoproject.com/en/1.11/ref/databases
Интересно как вы это объясните в рамках своей гениальной теории одноразовых процессов.
Это уже противоречит некоторым тезисам моего оппонента.
он не хочет пруфы давать.
Что ж в оппонента критикуете, а бревно у себя в глазу не замечаете?
Документация противоречит всем вашим тезисам. Вы сначала пишете «учите матчасть», а потом оказывается что вы сами-то документацию не читали и матчасть толком не знаете? Стыдно должно быть.
А что касается SMTP, то вообще не понятно причём тут пайтон? Сервер SMTP на пайтоне? в любом случае, если ты откроешь исходник демона, то обнаружишь, что висит там только listener, а сам процесс обработки запроса стартует в отдельном потоке, либо вообще процессе. Т.е. фактически память вычищается после того, как скрипт отработает.
И да, SMTP сервер на питоне. И в том же приложении еще и HTTP сервер, и много чего еще. Вот, например доклад на PyCon Portland 2017 на тему SMTP сервера на питоне www.youtube.com/watch?v=1Uyo2c2GYKQ. Там упоминаюсь и я, и между строк мой проект.
Исходиники «демона» я читал подробно и очень внимательно. И трейсил, и дебажил. Обработка запроса действительно висит в отдельном процессе или потоке, но где вы наши «память фактически очищается», я не знаю.
Обработка запроса действительно висит в отдельном процессе или потоке
Вот отсюда идёт вывод о фактическом очищении памяти. Когда процесс умирает, связанные с ним данные должны удалиться. Да, есть риск, что этого не произойдёт. Но в контексте самого треда получается, что это, как-раз, происходит. Поскольку претензия идёт к управлению освобождаемой памятью внутри процесса. А когда процесс умирает, он перестаёт блокировать области памяти.
Что касается доклада, то обратите внимание на то, что вы и ваш проект упоминаетесь между строк. Да, возможно, у вас хороший проект, но это не делает ваше мнение единственно верным. И, как я помню, после смерти процесса ссылки на него в памяти убивает ось. Т.е. да, память должна вычищаться. На том и основаны применяемые методы. Методы, при которых основная работа Python скрипта производится в отдельном процессе, а в родительском лежат, преимущественно, неизменяемые данные.
wsgi(который, в частности реализует uwsgi, так же как и gunicorn и встроенный debug режим фласка например) просто передает построенный обьект запроса (Request) в хендлер приложения
Хендлер должен вернуть Response
Само приложение бежит, запущенное в процессе сервера реализующего wsgi
Сервер может запустить n-ое колва приложений, убивать каждое после n-ого кол-ва запросов (как костыль против утечек памяти)
Для того чтоб лично убедиться — достаточно выставить 1 процесс в uwsgi/gunicorn, убедиться что нет перезапуска после одного запроса(это никогда не дефолт) и записать какие либо данные в какой либо глобальный объект
При другом запросе — считать.
Данные будут там
Более того, подсказка, именно так держатся подключения к БД и именно так в принципе могут работать всякие функции вроде memoized, исключительно в памяти, без редисов и прочего
Я уже не говорю про инициализацию подключений и другие netI/O задержки, которые вполне могут быть при старте.
И если бы каждый запрос делал все это — лежали бы сервера со 100% оверхедом по cpu, очень тщательно занимаясь построением тяжеловесных объектов(о чем кстати эта статья), вместо того, чтоб обрабатывать предельно простые запросы юзверей, о том по какому же url-у котики лежат, занимая при этом фиксированное значение в легкодоступной памяти, без каких либо значимых овераллокаций.
Вот например (внезапно, оказывается автор с хабра): github.com/pyca/pyopenssl/issues/137
With latest released pyopenssl, cryptography and cffi «import OpenSSL» takes about 0.5s on a modern i7 CPU with SSD (OS X):
Для 2014 года пацан грамотно понтанулся
Построение различных объектов для быстрого доступа в работе — как по мне ничем не отличается от определения классов и функций. И там и там при импорте уже бежит много чего. И это неизбежное зло, как следствие хороших сторон языка… В том числе и monkey_patching при импорте. Абсолютно все сделать lazy нельзя… либо ценой неудобного кода
В этом контексте под побочными эффектами подразумеваю I/O, то есть — сеть и ненужные обращения к диску
В таком понимании — все чисто, импорты ~200ms на большом проекте. Но это только импорт
Запуск приложения уже подразумевает намного больше, а там уже вполне допустимо несколько уровней кеша(память-диск) с оригинальными данными из сети, различные библиотеки подгрузят свои конфигурационные файлы для автогенирации API…
И тут секунды для запуска уже реальны. Что явно расточительно, для обработки одиночного запроса :)
То что вы сейчас написали — бред. Ни один здравомыслящий разработчик не будет держать python бэкенд постоянно запущенным.
Лол, а в чем проблема держать его запущенным, если он никакой работы в фоне не выполняет? Чему там течь?
Вы правда где-то в 2000-х застряли, видимо свое хамство оттуда же принесли.
В статье не учтена работа GC. Принудительный запуск GC в ключевых местах (если, вдруг, при наблюдении за работой приложения, оказалось что стандартной его работы не хватило) — чистит память и нивелирует описанную проблему.
import gc
gc.collect()
Так что на практике, если не писать для встраиваемых систем с парой десятков мегабайт ОЗУ, — проблема, обычно, не является значимой.
А вот подборка размеров стандартных примитивов в статье, на мой взгляд, — действительно ценна.
Управление памятью в Python