Как стать автором
Обновить

Как и почему перешли с Python на Go в основном сервисе рекомендаций Авито

Время на прочтение10 мин
Количество просмотров24K
Всего голосов 47: ↑43 и ↓4+42
Комментарии41

Комментарии 41

Не совсем понятно, что же в конце концов изменилось в "подготовке фич". Создается впечатление, что после всех разговоров об IO и workers - задача свелась к оптимизации каких-то однопоточных преобразований массивов/списков...

Видимо, речь, действительно, о создании трех тысяч неких пользовательских объектов.

После всех разговоров об IO и workers в Python выяснилось, что только подготовка фич на Go из Python кода в лоб, последовательно в 1 процессе без ProcessPoolExecutor - в 20-30 раз быстрее чем чистый Python. Как правильно заметили ниже в комментариях:

> в 20-30 раз быстрее потому, что код на Go компилируется, а птичий пайтоновский >скрипт интерпретируется виртуальной машиной.

Если все перевести на Go, то будет еще и более эффективная модель шедулинга рутин, что еще сильнее отражается на latency и CPU, что и показано в статье в числах на примере нашего сервиса.


более эффективная модель шедулинга рутин

Я как раз и пытался понять, каким образом вышесказанное связано с "последовательно в 1 процессе без ProcessPoolExecutor". Да - работа с нативными массивами/списками в Питоне сравнительно медленная - именно поэтому всякие numpy, PyTorch, OpenCV это обертки к заоптимизированным C/C++ библиотекам. Но это уже вопрос выбора инструмента...

Вы правы, что есть различные инструменты повышения производительно Python кода. Работу с массивами/списками оптимизировали и все равно уперлись в возможности Python, к тому же это все не решило проблемы с накладными расходами питона на CPU-bound задачи.

в 20-30 раз быстрее потому, что код на Go компилируется, а птичий пайтоновский скрипт интерпретируется виртуальной машиной.

Зашёл на главную Авито – размер страницы ~2 МБ, огромная такая кучка картинок нерелевантных объявлений. Делайте сайты нормально, хотя бы главную, и не нужно будет «выгадывать по 70 мс на сериализации».

так ведь любая компания заинтересована в оптимизации бекенда, тк затраты на железо составляют одну из основных статей расходов + время такое, что железо найти это дорого и сложно, а фронт он на машинках юзеров работает и платят за комфортное использование браузера и сайтов тоже юзеры.

А кто будет эту кучу мусора для каждого пользователя генерировать, если не бэкенд?

Скорее, в «разумной» оптимизации. Все-таки основная статья расходов - разработка (зарплаты). Переписать код на другой язык может оказаться дороже, чем утроить мощность сервера. Многие компании отдают предпочтение быстрой разработке, а не оптимизации: бекенд на питоне (быстрая разработка, но не очень хорошее использование ресурсов сервера) - очень популярный и быстроразвивающийся стек, как косвенное подтверждение.

За комфортное использование браузера платит не только юзер. Если у вас криво реализован фронт - будет страдать пользовательский опыт и вы будете терять пользователей.

Вы правы. Оптимизацией фронт-части, уменьшение TTFB и прочие метрики - это важная составляющая оптимизации.

И этим нужно заниматься (мы сейчас именно этому способу оптимизации уделяем меньше времени чем хотелось бы)

Но статья вовсе не про это. Картинки, подготовка рекомендаций - это отдельная задача, важная для того, чтобы скрол главной был гладеньким.

Статья о том, что даже решение уровня "закидаем железом" перестало быть достаточно эффективным.

Почему го, а не с++, java(kotlin), .net ?

  1. java(kotlin), .net - менее эффективны, чем Go.

  2. с++ - сложнее писать и поддерживать код, меньше специалистов на рынке.

  3. Go - основной язык в компании.

Зря не смотрите .net. Microsoft последние несколько лет прикладывает серьёзные усилия для повышения производительности кроссплатформенных asp.net core и entity framework core. Их фреймворки уже не уступают golang в производительности хоть и потребляют больше памяти. Также Microsoft развивает открытый ML.net - фреймворк для ml на c#. Очень советую ознакомится с данными новинками.

java(kotlin), .net - менее эффективны, чем Go.

Citation needed. Я не знаю про .NET, но Java и JVM не "менее эффективны" (чтобы это не значило).

Go - основной язык в компании.

В общем, других причин и не надо.

  • Go - основной язык в компании.

Теперь понятно. С этого и стоило бы начать, что "Go - основной язык в компании". Следовательно его поддержка для вас относительна дешева.

Ну и Go из коробки идеально подходит для такого рода задач)

А что, пользователи вот прям реально смотрят все эти тысячи подобранных объявлений? Для человека со стороны величина в 3000 объявлений кажется очень большой. Особенно если учесть, что это рекомендации (то, что пользователю навязывают), а не результаты поиска или что-то подобное (то, что он хочет). И непонятно, насколько часто это всё генерируется - на каждый рендер страницы или по какому-то интервалу? Кешируются ли где-то сгенерированные результаты? Если да, то на какой срок? Они отдаются на фронт всей пачкой, или же есть какая-то пагинация?

Быть может, проблему надо вообще с другого конца решать - понять, нужен ли на деле такой монструозный поток данных, и уменьшить его, если не нужен. Лучший способ оптимизации производительности - это не делать ненужную работу.

Полностью поддерживаю, главный вопрос "зачем это вообще существует?" так и повис в воздухе. Есть объективные доказательства того что вся эта махина приносит значимую пользу?

более половины всех просмотров объявлений идут с ленты рекомендаций на главной, польза есть)

и треть контактов

Открыл авиту. Все(!) предложения с главной страницы абсолютно нерелеватны.

Красивый мерс, ржвая газель, модель машинки, щенок, женская сумочка, драные джинсы, несколько частных домов, домик из лего и прочая чушь.

Может, в глубине что-то полезное есть? Вы уж не поленитесь, пожалуйста, промотайте хотя бы на пару сотен объявлений вниз! /s

Скорее всего это coldStart популярные объявления в локации. Покликай на то, что тебе интересно, мы будем о тебе знать немного больше, после чего тут же появятся более релевантные рекомендации.

У меня очень полезные рекомендации, примерно то, что нужно. Видимо, зависит от истории использования

Юзеры смотрят не все, но чтобы выбрать самый подходящий топ айтемов для пользователя нужно все равно обработать все эти 3000 айтемов, а чтобы получить 3000 - нужно обработать перед этим 100 миллионов. При уменьшении количества кандидатов для ранжирования просаживаются наши рек метрики - проверяли через АБ. Работа тут осталась только нужная.

На клиент приходит только одна страница - 30 айтемов, есть пагинация.

Некоторые пользователи залипают в ленту и листают ее до конца

Честно говоря - у меня статья оставила впечатление джуна на интервью, рассказывающего о проекте в котором ему дали постоять в сторонке и посмотреть как другие работают. "У нас есть какая-то лютая хрень, перемножающая здоровенные массивы. Мы ее уже пихали и туда, и сюда - но CPU она жрет столько же. Потом мы ее переписали на компилируемом языке, включили оптимизации и чудо, все стало работать быстрее. А Гуру, который как раз мимокрокодил - сказал что-то про шедулинг короутин. Что он имел в виду - я не понял, но решил вставить в статью.

И в целом, цель рекомендаций не навязать что-то пользователю, а помочь быстрее найти, то чем он интересуется на основе истории его действий.

А где можно почитать как вы используете вон тот 1Тб редис?

Не могу не спросить: Почему не Rust?

язык не поддержан в компании на уровне платформы)

Профилируйте ваш сервис. Используйте py-spy, как мы, или другой профайлер Python. 

Мы у себя пошли дальше и включили профилирование всех процессов 24/7. Получился крутой и простой selfhosted opensource аналог blackfire/newrelic:

  • Собираем трейсы с помощью с помощью phpspy в неблокирующем режиме с поиском процессов по регулярке.

  • Конвертируем и агрегируем трейсы, навешиваем теги (проект, название машины/контейнера и тд...) с помощью своего адаптера https://github.com/zoonru/pyrospy.

  • Засылаем трейсы в https://github.com/pyroscope-io/pyroscope

  • Анализируем трейсы в интерактивном интерфейсе pyroscope. Там куча возможностей: фильтрация по тегам, поиск по методам, сравнение производительности с течением времени или на разных машинах.

Подробнее в посте: https://habr.com/ru/post/662349/

Можно пойти еще дальше: экспортировать метрики из pyroscope в grafana. Потом, например, навешать алертов, что бы мониторить время работы критичных мест :)

В таких статьях не хватает оценки времени на саму разработку. Ну, например, написали микросервис А за 10 часов, было нас 1 программист. На оптимизацию через пул процессов потратили 15 минут. Купили один сервак дополнительно, ибо могли. А потом пришли к нам и сказали что пора бюджет экономить.

Пошли переписывать на го. Потратили 15 дней, кодили в 7-ром. Цель достигнута. Получили премии с продажи лишнего сервака. И прирост fps по всем направлениям.

Это точно добавит аргументов в копилку "а надо ли переписывать".

Кодю на обоих языках. И почему-то создание одного и тогоже на го, занимает на порядок времени больше, чем на python. Об этом все фанаты оптимизации в го, почему то всегда умалчивают.

А про то что после переписи на го. Начинается уже оптимизация в го, тоже забывают рассказать. Где половина кода со всей динамикой и дженериками откидывается и все кодиться автогенератором кода с максимумом статики. Что тоже требует временных затрат.

А к чему это всё. Да потому что завтра к нам в отдел придёт менеджер и скажет, ну весь питон на го переписывают - вперед. Сколько времени надо - 2 дня хватит?

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

В нашем случае уже не получалось остаться на питоне, так как:

  • железа докинуть сложно, уже начались проблемы при деплое, в кластере часто не хатало ресурсов при выкатке новой версии сервиса;

  • исчерпали почти все возможности питона, заоптимизировали все до чего дотянулись руки, местами в ущерб читаемости кода;

Разработка же активно продолжалась, сервис центральный в него постоянно внедряются новые фичи, которые растят наши метрики, но сказываются на CPU и latancy, что в скором времени заблокировало бы дальнейшую разработку.

Процесс переписывания такого сервиса действительно дорогостоящий, если говорить о цифрах, то стоит закладывать 40% времени на саму разработку и 60% на тестирование/отладку. Мы сверяли через АБ, часть трафика на старый сервис, часть на новый, АБ на основную ручку сервиса стал зеленый с 7го раза, при том что все было покрыто тестами и была пройдена оффлайн сверка.


Вообще цифры по трудозатратам и то как это лучше сделать - отдельная статья. В данной статье только отражены, причины того почему мы решились на это и что получили. К этому решению шли около года, до этого пытались остаться на Python всеми силами. На железе кстати сэконимили прилично)

Опыт на питоне 8 лет, на go 4 года. Сказать честно, когда начал писать на go, ощущение как будто 2 пальца отрезали с каждой руки, но сейчас такой проблемы нет) Если у команды нет экспертизы на go, то в такое ввязываться не стоит.

Feature engineering -- наверное логично было ускорять через go, тем более если в компании много компетенций в языке.

Но почему сильно распараллеленый ml реализован на python, а не на spark/scala?

Спасибо за статью, возникла пара вопросов:

  1. Пробовали ли другие json библиотеки, помимо orjson ?
  2. Пробовали ли uvloop?

Спасибо, что прочитали)
1. нет
2. uvloop - уже очень давно его и используем, все цифры питона с uvloop

Спасибо за статью. Простите за оффтоп, честно говоря сильно удивлен комментам к этой статье. Ребята делятся опытом, рассказывают интересный нетривиальный кейс оптимизации своего сервиса. Нет чтобы поблагодарить автора за статью, я вижу комменты в стиле:

  • они выбрали го, ну с ними все понятно

  • джун постоял, чет написал

  • я вот пользуюсь авито, вы показываете мне нерелевантную хрень

Очень расстраивает наше комьюнити

Если нужен wrapper на CatBoost для Golang - https://github.com/mirecl/catboost-cgo

Зарегистрируйтесь на Хабре, чтобы оставить комментарий