Python: предотвращение замедления кэширования - PullRequest
3 голосов
/ 27 сентября 2011

Я работаю над веб-приложением с очень агрессивным кэшированием. Практически каждый компонент веб-приложения: представления, частичные представления, выходные данные контроллера, загрузка диска, вызовы REST-API, запросы к базе данных. Все, что может быть кэшировано на любом уровне, кэшируется с использованием декораторов.

Естественно, это невероятно быстро, так как подавляющее большинство генерации HTML состоит из чистых функций, с очень небольшой загрузкой из дискового / REST API. Кроме того, те немногие загрузки диска / запросы к базе данных / запросы REST API, которые я выполняю, также кэшируются до тех пор, пока не будут признаны недействительными, поэтому, если что-то просто не изменилось, они тоже очень быстрые.

Так что все работает быстро, но есть загвоздка: все это кэшируется в памяти, в одном огромном глобальном словаре в моем процессе WSGI, и, следовательно, может храниться напрямую без сериализации. Как только я начинаю помещать вещи в memcached, время, затрачиваемое на попадания в кеш, не сильно меняется, но помещение вещей в кеш начинает занимать гораздо больше времени. В целом это нормально, но начальная генерация «заполняющего кэша» каждой страницы идет от ~ 900 мс (что уже довольно быстро, учитывая, сколько плоских файлов она читает с диска) до ~ 9000 мс. Для справки, генерация произвольной страницы занимает около 10 мс после прогрева кеша.

Профилируя код, подавляющее большинство времени идет на cPickle. Итак, вопрос в том, как я могу сделать это быстрее? Существуют ли кеши в памяти, в которые я могу напрямую передавать свои объекты без сериализации? Или какой-нибудь способ ускорить кэширование моей огромной кучи объектов? Я мог бы просто обойтись без постоянного memcached, но тогда моя производительность (или ее отсутствие) будет зависеть от менеджера процессов Apache / WSGI.

1 Ответ

3 голосов
/ 27 сентября 2011

Если вы сериализуете объекты Python, а не простые типы данных, и имеет для использования pickle, попробуйте cPickle.HIGHEST_PROTOCOL:

my_serialized_object = cPickle.dumps(my_object, cPickle.HIGHEST_PROTOCOL)

Протокол по умолчанию совместим со старыми версиями Python, но вы, вероятно, не заботитесь об этом.

Я только что сделал простой тест с ключом в 1000 ключей, и он был почти на порядок быстрее.

ОБНОВЛЕНИЕ: Поскольку вы, похоже, уже используете самый высокий протокол, вам придется проделать дополнительную работу, чтобы повысить производительность.Вот что я хотел бы сделать на этом этапе:

  1. Определить, какие классы медленнее всего мариновать

  2. Создать пару методов в классечтобы реализовать более быстрый метод сериализации, скажем _to_string () и _from_string (s).Фактическая сериализация может быть адаптирована к тому, что включает в себя объект и как он будет использоваться.Например, некоторые объекты могут на самом деле содержать только простую строку, такую ​​как отрендеренный шаблон, а некоторые могут фактически отправляться в браузер как JSON, и в этом случае вы можете просто сериализовать в JSON и обслуживать его напрямую.Используйте модуль timeit , чтобы убедиться, что ваш метод действительно быстрее

  3. В вашем декораторе проверьте hasattr (object, '_to_string') и используйте его вместо этого, если онсуществует

Этот метод позволяет вам сначала заняться наихудшими классами и вносит минимальные нарушения в кодовую базу.

...