Производительность и оптимизация
В этом документе представлен обзор техник и инструментов, которые могут помочь сделать ваш код Django более эффективным — быстрее и с меньшим использованием системных ресурсов.
Введение
Обычно первоочередной задачей является создание работающего кода, логика которого функционирует должным образом для получения ожидаемого результата. Однако иногда этого недостаточно для обеспечения эффективной работы кода.
В этом случае требуется нечто — и на практике, часто множество вещей — для повышения производительности кода без или с минимальным влиянием на его поведение.
Общие подходы
Для чего вы оптимизируете?
Важно четко понимать, что вы подразумеваете под «производительностью». Нет единого показателя производительности.
Повышение скорости может быть наиболее очевидной целью программы, но иногда могут быть затребованы другие улучшения производительности, такие как снижение потребления памяти или уменьшение нагрузки на базу данных или сеть.
Улучшения в одной области часто приводят к улучшению производительности в другой, но не всегда; иногда одно может даже произойти за счет другого. Например, повышение скорости программы может привести к увеличению использования памяти. Хуже того, это может быть контрпродуктивно — если повышение скорости требует слишком много памяти, что приводит к недостатку памяти в системе, вы навредите больше, чем поможете.
Есть и другие компромиссы, которые следует учитывать. Ваше собственное время является ценным ресурсом, более важным, чем время ЦП. Некоторые улучшения могут быть слишком сложными, чтобы их стоило внедрять, или могут повлиять на переносимость или поддерживаемость кода. Не все улучшения производительности стоят усилий.
Таким образом, вам нужно знать, какие улучшения производительности вы хотите получить, а также знать, что у вас есть веская причина для этого — и для этого вам нужно:
Тестирование производительности
Нельзя просто угадывать или предполагать, где в вашем коде лежат неэффективные места.
Инструменты Django
django-debug-toolbar — очень полезный инструмент, который предоставляет информацию о том, что делает ваш код и сколько времени он тратит на это. В частности, он может отобразить все SQL-запросы, генерируемые вашей страницей, и время выполнения каждого из них.
Для расширения функциональности инструментария также доступны сторонние панели, которые (например) могут предоставлять отчеты о производительности кэша и времени рендеринга шаблонов.
Сервисы третьих сторон
Существует ряд бесплатных сервисов, которые анализируют и предоставляют отчеты о производительности страниц вашего сайта с точки зрения удаленного HTTP-клиента, фактически моделируя опыт реального пользователя.
Они не могут предоставлять отчеты о внутреннем устройстве вашего кода, но могут предоставить полезную информацию о общей производительности вашего сайта, включая аспекты, которые не могут быть адекватно измерены изнутри среды Django. Примеры включают:
Существуют также несколько платных сервисов, которые выполняют аналогичный анализ, включая некоторые, которые учитывают Django и могут интегрироваться с вашим кодом для более всестороннего профилирования его производительности.
Делайте все правильно с самого начала
Некоторая работа по оптимизации включает в себя устранение недостатков производительности, но часть работы можно просто интегрировать в то, что вы делаете всегда, как часть хороших практик, которые вы должны применять еще до того, как начнете думать об улучшении производительности.
В этом отношении Python — отличный язык для работы, так как решения, которые выглядят элегантно и правильны, обычно являются лучшими по производительности. Как и большинство навыков, изучение того, что «выглядит правильно», требует практики, но одним из самых полезных руководств является:
Работайте на соответствующем уровне
Django предлагает множество способов решения задач, но то, что что-то можно сделать определенным способом, не означает, что это самый подходящий способ. Например, вы можете обнаружить, что можете вычислить одно и то же значение — количество элементов в коллекции, возможно — в QuerySet, на Python или в шаблоне.
Однако почти всегда быстрее выполнять эту работу на более низких уровнях, а не на более высоких. На более высоких уровнях система должна обрабатывать объекты через несколько уровней абстракции и слоев механизмов.
То есть база данных обычно может делать вещи быстрее, чем Python, который может делать их быстрее, чем язык шаблонов:
# QuerySet operation on the database
# fast, because that's what databases are good at
my_bicycles.count()
# counting Python objects
# slower, because it requires a database query anyway, and processing
# of the Python objects
len(my_bicycles)
# Django template filter
# slower still, because it will have to count them in Python anyway,
# and because of template language overheads
{{ my_bicycles|length }}
Как правило, наиболее подходящий уровень для задачи — это самый низкий уровень, для которого комфортно кодировать.
Примечание
Приведенный выше пример носит исключительно иллюстративный характер.
Во-первых, в реальной ситуации вам необходимо учитывать, что происходит до и после вашего подсчета, чтобы определить оптимальный способ выполнения этой задачи в конкретном контексте. В документации по оптимизации базы данных описан случай, когда подсчет в шаблоне будет лучше.
Во-вторых, следует рассмотреть и другие варианты: в реальной ситуации {{
my_bicycles.count }}, которая вызывает QuerySet count() метод непосредственно из шаблона, может быть наиболее подходящим выбором.
Кэширование
Часто вычисление значения является дорогостоящим (то есть, ресурсоемким и медленным), поэтому может быть огромная польза от сохранения значения в быстродоступном кэше, готовом к последующему использованию.
Это достаточно важная и мощная техника, поэтому Django включает в себя всеобъемлющий фреймворк кэширования, а также другие небольшие фрагменты функциональности кэширования.
Фреймворк кэширования
Фреймворк кэширования Django предоставляет значительные возможности для повышения производительности, сохраняя динамическое содержимое, чтобы его не нужно было вычислять для каждого запроса.
Для удобства Django предлагает разные уровни зернистости кэширования: вы можете кэшировать вывод определенных представлений или только части, которые сложно генерировать, или даже весь сайт.
Внедрение кэширования не должно рассматриваться как альтернатива улучшению производительности плохо написанного кода. Это один из заключительных этапов создания высокопроизводительного кода, а не обходной путь.
cached_property
Часто бывает необходимо вызывать метод экземпляра класса несколько раз. Если эта функция дорогостоящая, то это может быть неэффективно.
Использование декоратора cached_property сохраняет значение, возвращаемое свойством; при следующем вызове функции для данного экземпляра она вернет сохраненное значение, а не пересчитает его. Обратите внимание, что это работает только для методов, которые принимают self в качестве единственного аргумента, и что это изменяет метод на свойство.
Некоторые компоненты Django также имеют собственную функциональность кэширования; эти компоненты обсуждаются в разделах, относящихся к этим компонентам.
Понимание лени
Ленивость — это стратегия, дополняющая кэширование. Кэширование предотвращает перевычисление, сохраняя результаты; ленивость откладывает вычисление до момента фактической необходимости.
Ленивость позволяет нам ссылаться на вещи до их создания или даже до момента, когда их создание возможно. Это имеет множество применений.
Например, ленивое переопределение может быть использовано до того, как будет известен целевой язык, потому что оно не выполняется до тех пор, пока переведенная строка фактически не понадобится, например, в рендеренном шаблоне.
Ленивость также является способом сэкономить усилия, пытаясь избежать работы с самого начала. То есть, один из аспектов лени — не делать ничего, пока это не потребуется, потому что это может оказаться не нужным впоследствии. Поэтому ленивость может иметь последствия для производительности, и чем сложнее работа, тем больше можно выиграть от лени.
Python предоставляет ряд инструментов для ленивой оценки, особенно через конструкции генератора и выражение генератора. Стоит изучить ленивость в Python, чтобы обнаружить возможности использования ленивых паттернов в вашем коде.
Ленивость в Django
Django сам по себе достаточно ленивый. Хороший пример этого можно найти в оценке QuerySets. Наборы запросов являются ленивыми. Таким образом, можно создать QuerySet, передать его и объединить с другими QuerySets, не выполняя фактических вызовов к базе данных для извлечения описываемых им элементов. Передаваемым объектом является объект QuerySet, а не коллекция элементов, которые — в конечном итоге — будут потребованы из базы данных.
С другой стороны, некоторые операции приведут к принудительной оценке набора запросов. Избегание преждевременной оценки QuerySet позволит сэкономить время и ресурсы на дорогостоящем и ненужном запросе к базе данных.
Django также предлагает декоратор keep_lazy(). Это позволяет функции, которая была вызвана с ленивым аргументом, вести себя лениво, оцениваясь только при необходимости. Таким образом, ленивый аргумент — который мог быть дорогостоящим — не будет подвергнут оценке, пока это строго не потребуется.
Базы данных
Оптимизация базы данных
Уровень баз данных Django предоставляет различные способы помочь разработчикам получить наилучшую производительность от своих баз данных. Документация по оптимизации базы данных собирает ссылки на соответствующую документацию и добавляет различные советы, которые описывают шаги, которые нужно предпринять при попытке оптимизировать использование вашей базы данных.
Другие рекомендации по работе с базой данных
Это особенно полезно на виртуальных хостах с ограниченной производительностью сети, например.
Производительность HTTP
Средства промежуточного слоя
Django поставляется с несколькими полезными средствами промежуточного слоя middleware, которые могут помочь оптимизировать производительность вашего сайта. Они включают:
ConditionalGetMiddleware
Добавляет поддержку современным браузерам для условного получения ответов на основе заголовков ETag и Last-Modified. Также рассчитывает и устанавливает метку ETag при необходимости.
GZipMiddleware
Сжимает ответы для всех современных браузеров, экономя пропускную способность и время передачи. Обратите внимание, что GZipMiddleware в настоящее время считается рискованным с точки зрения безопасности и уязвим для атак, которые сводят на нет защиту, предоставляемую TLS/SSL. Подробнее см. предупреждение в GZipMiddleware.
Сессии
Использование кэшированных сессий
Использование кэшированных сессий может повысить производительность, устранив необходимость загрузки данных сессии из медленного источника хранения, например, базы данных, и вместо этого сохранив часто используемые данные сессии в оперативной памяти.
Статические файлы
Статические файлы, которые по определению не являются динамическими, являются отличной целью для оптимизации.
CachedStaticFilesStorage
Используя возможности кэширования веб-браузеров, вы можете полностью исключить запросы к сети для данного файла после первоначальной загрузки.
CachedStaticFilesStorage добавляет тег, зависящий от содержимого, к именам файлов статических файлов, чтобы сделать их безопасными для долгосрочного кэширования браузерами без пропуска будущих изменений. При изменении файла также изменяется тег, поэтому браузеры автоматически перезагрузят ресурс.
“Минификация”
Несколько сторонних инструментов и пакетов Django предоставляют возможность «минифицировать» HTML, CSS и JavaScript. Они удаляют ненужные пробелы, новые строки и комментарии, а также сокращают имена переменных, тем самым уменьшая размер документов, публикуемых вашим сайтом.
Производительность шаблонов
Обратите внимание, что:
- использование
{% block %}быстрее, чем использование{% include %} - сильно фрагментированные шаблоны, собранные из многих мелких элементов, могут влиять на производительность
Кэшированный загрузчик шаблонов
Включение cached template loader часто значительно улучшает производительность, так как это предотвращает компиляцию каждого шаблона каждый раз, когда он должен быть отображен.
Использование разных версий доступного программного обеспечения
Иногда стоит проверить, существуют ли разные и более производительные версии используемого вами программного обеспечения.
Эти методы нацелены на более опытных пользователей, которые хотят расширить границы производительности уже хорошо оптимизированного сайта Django.
Однако, это не магическое решение проблем производительности, и вряд ли они принесут значительные улучшения сайтам, которые не используют базовые методы правильным образом.
Примечание
Стоит повторить: поиск альтернатив уже используемому программному обеспечению — никогда не является первоочередным решением проблем производительности. Когда вы доходите до этого уровня оптимизации, вам необходимо формальное решение для бенчмаркинга.
Новое — часто, но не всегда, лучше
Вполне редко случается, чтобы новая версия хорошо поддерживаемого программного обеспечения была менее эффективной, но разработчики не могут предвидеть все возможные варианты использования. Поэтому, хотя следует понимать, что новые версии, скорее всего, будут работать лучше, не следует просто предполагать, что это всегда так.
Это верно и для Django. Последующие версии предлагают ряд улучшений во всей системе, но вам все равно следует проверить реальную производительность вашего приложения, потому что в некоторых случаях вы можете обнаружить, что изменения приводят к худшим, а не к лучшим результатам.
Новые версии Python, а также пакетов Python, часто тоже работают лучше. Но всё же измеряйте, а не предполагайте.
Примечание
Если вы не столкнулись с необычной проблемой производительности в какой-либо конкретной версии, вы, как правило, обнаружите лучшие возможности, надёжность и безопасность в новой версии, и эти преимущества намного значительнее, чем любые возможные выигрыши или потери в производительности.
Альтернативы языку шаблонов Django
Практически во всех случаях встроенный язык шаблонов Django вполне адекватен. Однако, если узкие места в вашем проекте Django, похоже, лежат в системе шаблонов, и вы исчерпали все другие возможности для исправления этого, сторонняя альтернатива может быть ответом.
Jinja2 может предложить улучшение производительности, особенно когда дело доходит до скорости.
Альтернативные системы шаблонов различаются по степени, в которой они используют язык шаблонов Django.
Примечание
Если у вас возникают проблемы с производительностью в шаблонах, в первую очередь необходимо понять, почему. Использование альтернативной системы шаблонов может оказаться быстрее, но те же преимущества также могут быть достигнуты, не прибегая к этому – например, дорогостоящие операции и логика в шаблонах могут быть реализованы более эффективно в ваших представлениях.
Альтернативные реализации программного обеспечения
Стоит проверить, есть ли в программном обеспечении Python, которое вы используете, другая реализация, которая может выполнять тот же код быстрее.
Однако: большинство проблем производительности на хорошо написанных сайтах Django возникают не на уровне выполнения Python, а в неэффективных запросах к базе данных, кэшировании и шаблонах. Если вы полагаетесь на плохо написанный код Python, ваши проблемы с производительностью вряд ли будут решены за счёт его более быстрого выполнения.
Использование альтернативной реализации может привести к проблемам совместимости, развертывания, портируемости или поддержки. Само собой разумеется, что перед внедрением нестандартной реализации следует убедиться, что она обеспечивает достаточное улучшение производительности вашего приложения, чтобы перевесить потенциальные риски.
С учётом этих оговорок, вы должны быть осведомлены о:
PyPy
PyPy — это реализация Python на Python (стандартная реализация Python написана на C). PyPy может обеспечить значительное повышение производительности, обычно для ресурсоёмких приложений.
Ключевой целью проекта PyPy является совместимость с существующими API и библиотеками Python. Django совместим, но вам необходимо проверить совместимость других библиотек, на которые вы полагаетесь.
Реализации Python-библиотек на C
Некоторые библиотеки Python также реализованы на C и могут быть намного быстрее. Они стремятся предложить те же API. Обратите внимание, что проблемы совместимости и различия в поведении не исключены (и не всегда сразу очевидны).
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/topics/performance/