#cpp #highload
1. Ещё один доклад Daisy Holman про всякие необычные C++ tricks. Можно полистать презу.
Ранее упоминал её тут и тут.
2. Статья про устройство real-time messaging в Slack.
3. Статья про Tinder API Gateway.
4. Migrating Critical Traffic At Scale with No Downtime в Netflix.
5. Monoliths are not dinosaurs.
1. Ещё один доклад Daisy Holman про всякие необычные C++ tricks. Можно полистать презу.
Ранее упоминал её тут и тут.
2. Статья про устройство real-time messaging в Slack.
3. Статья про Tinder API Gateway.
4. Migrating Critical Traffic At Scale with No Downtime в Netflix.
5. Monoliths are not dinosaurs.
👍7❤🔥4
#common
Про то, что в поисковых движках (для полнотекстового поиска) обычно есть.
Интересно, что на самом деле ничего очень специфичного именно для поиска вроде как и нет. Т.е. конечно алгоритмов и подходов внутри подобных решений хватает, но чтобы прям только тут, такого ничего не видно.
Базой почти всегда служит инвертированный индекс — множество id документов для каждого ключевого слова, в которых это самое слово встречается. Выглядит это примерно вот так:
Правда вектор интов это всё-таки упрощение, т.к. чаще всего хочется хранить что-то более сложное. Например структурку
Собственно если у вас именно вектор интов в значении мапки, вы можете его очень сильно пожать. Ловите профит, правда инфы из такого индекса вы получаете мало. Хотя если научиться каким-то образом ужимать весь ваш
Тут правда миллион вопросов возникает: как должен быть устроен
Т.к. индекс обычно поддерживает очень много разной инфы, приходится очень много сжимать, потому что огромные индексы — неэффективно и больно. Из простого и понятного для последовательностей чисел (в нашем случае например для id документов) можно применять дельта-кодирование, когда вы возрастающую положительную последовательность меняете на разницу с предыдущим:
11, 15, 16, 21, 37, …
11, 4, 1, 5, 16, …
Если же у нас и отрицательные, применяют что-то около zig-zag кодирования.
Но вообще как числа жать, уже давно придумали: elias gamma, golomb, rice, huffman и другое. С ними правда тоже вопросики есть, но можно крутиться.
Можно ещё посмотреть в сторону varint (или group varint/pfor/simple9/simple16).
Но в любом случае надо что-то тут делать, потому что сжимать оч важно.
Важной частью ещё является ранжирование. Учитывая развесистые модели, которые для решения этой задачи используются, эта часть может занимать огромное количество времени. И сверху ещё (т.к. обычно там какой-никакой мль), результаты могут быть с вопросом. Тут рядом считают какие-нибудь статистики вроде TF/BM25, которые в поисковых движках уже стали классикой. Иногда ещё движки, если они разрабываются как SaaS решение, могут предоставлять возможность создавать свои факторы для ранжирования и как-то их крутить. Очень полезно в рамках различных предметных областей, т.к. непонятно, где ваша разработка будет использоваться. Хотя конечно почти наверняка делают более специализированные штуки (например в силу специфики работы, видел движки для екома, но скорее внутри, чем снаружи).
Обычно в движке у вас есть ещё какой-то матчинг (чтобы какие-то результаты по запросу получать), опечаточник, саджест, иногда какие-то атрибутивные real-time обновления, чтобы индекс каждый раз заново не варить (иногда умеют делать фулл индексы real-time, но не совсем, а иногда умеют варить только дельту). Короч очень много всего.
И есть конечно же огромная куча подходов и решений, которые часто встречаются в других областях. Например как в бд (потому что движки это по факту специфические базы данных), как в nlp (кодировки, токенизация, морфология, стемминг/лемминг, языковые модели), ну и интеграции для более гибкой настройки (иногда свой диалект SQL, различные возможности подменять этапы работы с текстом, интеграции с моделями для ранжирования, с библиотеками для векторного поиска (если вдруг надо) и чем угодно ещё).
Постик основан на докладе, но ссылку на него я вам пока не дам, потому что он в закрытом доступе. Как появится запись, обязательно поделюсь.
Про то, что в поисковых движках (для полнотекстового поиска) обычно есть.
Интересно, что на самом деле ничего очень специфичного именно для поиска вроде как и нет. Т.е. конечно алгоритмов и подходов внутри подобных решений хватает, но чтобы прям только тут, такого ничего не видно.
Базой почти всегда служит инвертированный индекс — множество id документов для каждого ключевого слова, в которых это самое слово встречается. Выглядит это примерно вот так:
using Index = Map<String, Vector<int>>;Правда вектор интов это всё-таки упрощение, т.к. чаще всего хочется хранить что-то более сложное. Например структурку
Posting (вхождение слова в документ), которая хранит всякие разные дополнительные штуки: id поля в документе (можно потом с разными весами их крутить), позиция слова в документе, различные флаги и что угодно ещё. Собственно если у вас именно вектор интов в значении мапки, вы можете его очень сильно пожать. Ловите профит, правда инфы из такого индекса вы получаете мало. Хотя если научиться каким-то образом ужимать весь ваш
Posting в одно чиселко, можно тут и пооптимизировать. Тут правда миллион вопросов возникает: как должен быть устроен
Map? а Vector<Posting>? Эти структуры могут быть примерно чем угодно в зависимости от вашей прикладной области и требований. Т.к. индекс обычно поддерживает очень много разной инфы, приходится очень много сжимать, потому что огромные индексы — неэффективно и больно. Из простого и понятного для последовательностей чисел (в нашем случае например для id документов) можно применять дельта-кодирование, когда вы возрастающую положительную последовательность меняете на разницу с предыдущим:
11, 15, 16, 21, 37, …
11, 4, 1, 5, 16, …
Если же у нас и отрицательные, применяют что-то около zig-zag кодирования.
Но вообще как числа жать, уже давно придумали: elias gamma, golomb, rice, huffman и другое. С ними правда тоже вопросики есть, но можно крутиться.
Можно ещё посмотреть в сторону varint (или group varint/pfor/simple9/simple16).
Но в любом случае надо что-то тут делать, потому что сжимать оч важно.
Важной частью ещё является ранжирование. Учитывая развесистые модели, которые для решения этой задачи используются, эта часть может занимать огромное количество времени. И сверху ещё (т.к. обычно там какой-никакой мль), результаты могут быть с вопросом. Тут рядом считают какие-нибудь статистики вроде TF/BM25, которые в поисковых движках уже стали классикой. Иногда ещё движки, если они разрабываются как SaaS решение, могут предоставлять возможность создавать свои факторы для ранжирования и как-то их крутить. Очень полезно в рамках различных предметных областей, т.к. непонятно, где ваша разработка будет использоваться. Хотя конечно почти наверняка делают более специализированные штуки (например в силу специфики работы, видел движки для екома, но скорее внутри, чем снаружи).
Обычно в движке у вас есть ещё какой-то матчинг (чтобы какие-то результаты по запросу получать), опечаточник, саджест, иногда какие-то атрибутивные real-time обновления, чтобы индекс каждый раз заново не варить (иногда умеют делать фулл индексы real-time, но не совсем, а иногда умеют варить только дельту). Короч очень много всего.
И есть конечно же огромная куча подходов и решений, которые часто встречаются в других областях. Например как в бд (потому что движки это по факту специфические базы данных), как в nlp (кодировки, токенизация, морфология, стемминг/лемминг, языковые модели), ну и интеграции для более гибкой настройки (иногда свой диалект SQL, различные возможности подменять этапы работы с текстом, интеграции с моделями для ранжирования, с библиотеками для векторного поиска (если вдруг надо) и чем угодно ещё).
Постик основан на докладе, но ссылку на него я вам пока не дам, потому что он в закрытом доступе. Как появится запись, обязательно поделюсь.
👍7❤2
#common
Узнал про такую штуку как hashcash. Он используется в различных криптоштуках в качестве proof-of-work. Но сейчас мы не будем останавливаться на битке и подобных вещах. Хочется рассказать о том, как такое можно использовать в житейских простых задачах вроде пагинации (писал немного про неё тут).
С реализацией пагинации могут быть некоторые проблемы, если выдача у вас нестабильная. То есть офсет и курсор это круто, если множество данных у вас плюс-минус фиксировано и их порядок не меняется (плюс-минус, потому что можно добрасывать новое в конец и ничего в моменте не сломать). Но такая ситуация не всегда. Например вы хотите сделать пагинацию для рекомендашек. При каждом запросе у вас происходит полный цикл процесса рекомендаций, в конце которого (в упрощённом случае) находится ранжирование. Т.к. это обычно машинки и фичей бывает очень много (а они ещё могут очень быстро меняться) сущность, которую вы отранжировали на 1ю позицию в первом запросе, при втором запросе может вполне себе отранжироваться на 21ю. И с курсором 20 во втором запросе вы получите в выдаче то, что уже показывали. Почти наверняка это плохое решение, потому что пользователь уже видел этот контент и деняк больше вы не заработаете. Да и выглядит стьюпидно.
В зависимости от реализации поискового движка может возникнуть та же проблема.
Получается, надо где-то сохранить инфу о том, что мы пользователю уже показывали. Учитывая, что эта инфа валидна только для конкретного юзера, почти наверняка придётся каким-то образом получать данные с клиента (читай фронта). Давайте тогда запихнём эту инфу в курсор, который теперь будет иметь немного более сложный вид: это будет конкатенированная строка хешей объектов, которые мы отдали. И собственно из рексистемы отдадим эту строку как курсор клиенту, после чего он может прислать этот курсор обратно, а мы уже поймём по нему, что мы показывали, а что нет.
Хешировать тут можно всё что хотите. Хорошим вариантом будет какой-нибудь уникальный для каждой сущности idшник, множество которых вы просто заджойните в одну длинную строку. Потом можно засплитить эту строку по разделителю в множество хешей и проверить, какие айтемы вы уже пользователю отдавали, а какие нет. Это конечно может быть долговато, но можно покрутить хеш-функцию.
Собственно примерно поэтому и называется hashcash: вы кешируете множество объектов с помощью кеширования каждого из них.
Ещё из проблем тут есть:
- т.к. хеширование двух различных объектов может давать один хеш, вы можете отсеять что-то, что на самом деле не показывали, но, насколько я могу понимать, это обычно не крит, потому что с таким же успехом товар мог отфильтроваться на каком-нибудь другом этапе рекомендаций (отбор кандидатов, после ранжирования оказаться слишком низко, реранкинг). Если такое возникает часто, опять же можно посмотреть в сторону других хеш-функций, которые дают более разнообразные результаты;
- т.к. с каждым запросом кол-во показанных сущностей растёт, сам курсор тоже будет расти. Тут можно выбрать какое-то ограничение для длины курсора с трейдоффом кол-во показанного юзеру контента <-> технические возможности вашей системы. Если вы понимаете, что из всех пользователей долистывает до 300ого айтема только полпроцента, может не так страшно не показывать больше 300 объектов? Ну и оч длинную строку парсить на бекенде не хочется, т.к. это потенциально таймауты (а ещё та же проблема на клиенте, а ещё нагрузка на сеть вырастает). Можно опять же выбрать хеш-функцию так, чтобы она давала более короткие хеши, но тут важно не переборщить, чтобы не получить прошлую проблему в большом объёме.
Концептуально про рекомендательные системы расскажу попозже.
Узнал про такую штуку как hashcash. Он используется в различных криптоштуках в качестве proof-of-work. Но сейчас мы не будем останавливаться на битке и подобных вещах. Хочется рассказать о том, как такое можно использовать в житейских простых задачах вроде пагинации (писал немного про неё тут).
С реализацией пагинации могут быть некоторые проблемы, если выдача у вас нестабильная. То есть офсет и курсор это круто, если множество данных у вас плюс-минус фиксировано и их порядок не меняется (плюс-минус, потому что можно добрасывать новое в конец и ничего в моменте не сломать). Но такая ситуация не всегда. Например вы хотите сделать пагинацию для рекомендашек. При каждом запросе у вас происходит полный цикл процесса рекомендаций, в конце которого (в упрощённом случае) находится ранжирование. Т.к. это обычно машинки и фичей бывает очень много (а они ещё могут очень быстро меняться) сущность, которую вы отранжировали на 1ю позицию в первом запросе, при втором запросе может вполне себе отранжироваться на 21ю. И с курсором 20 во втором запросе вы получите в выдаче то, что уже показывали. Почти наверняка это плохое решение, потому что пользователь уже видел этот контент и деняк больше вы не заработаете. Да и выглядит стьюпидно.
В зависимости от реализации поискового движка может возникнуть та же проблема.
Получается, надо где-то сохранить инфу о том, что мы пользователю уже показывали. Учитывая, что эта инфа валидна только для конкретного юзера, почти наверняка придётся каким-то образом получать данные с клиента (читай фронта). Давайте тогда запихнём эту инфу в курсор, который теперь будет иметь немного более сложный вид: это будет конкатенированная строка хешей объектов, которые мы отдали. И собственно из рексистемы отдадим эту строку как курсор клиенту, после чего он может прислать этот курсор обратно, а мы уже поймём по нему, что мы показывали, а что нет.
Хешировать тут можно всё что хотите. Хорошим вариантом будет какой-нибудь уникальный для каждой сущности idшник, множество которых вы просто заджойните в одну длинную строку. Потом можно засплитить эту строку по разделителю в множество хешей и проверить, какие айтемы вы уже пользователю отдавали, а какие нет. Это конечно может быть долговато, но можно покрутить хеш-функцию.
Собственно примерно поэтому и называется hashcash: вы кешируете множество объектов с помощью кеширования каждого из них.
Ещё из проблем тут есть:
- т.к. хеширование двух различных объектов может давать один хеш, вы можете отсеять что-то, что на самом деле не показывали, но, насколько я могу понимать, это обычно не крит, потому что с таким же успехом товар мог отфильтроваться на каком-нибудь другом этапе рекомендаций (отбор кандидатов, после ранжирования оказаться слишком низко, реранкинг). Если такое возникает часто, опять же можно посмотреть в сторону других хеш-функций, которые дают более разнообразные результаты;
- т.к. с каждым запросом кол-во показанных сущностей растёт, сам курсор тоже будет расти. Тут можно выбрать какое-то ограничение для длины курсора с трейдоффом кол-во показанного юзеру контента <-> технические возможности вашей системы. Если вы понимаете, что из всех пользователей долистывает до 300ого айтема только полпроцента, может не так страшно не показывать больше 300 объектов? Ну и оч длинную строку парсить на бекенде не хочется, т.к. это потенциально таймауты (а ещё та же проблема на клиенте, а ещё нагрузка на сеть вырастает). Можно опять же выбрать хеш-функцию так, чтобы она давала более короткие хеши, но тут важно не переборщить, чтобы не получить прошлую проблему в большом объёме.
Концептуально про рекомендательные системы расскажу попозже.
👍14❤1🤔1🤯1
#common
Рекомендательные системы 1/2.
Опустим часть с мотивацией нужности подобных штук.
Пусть у нас есть множество пользователей и айтемов, которые мы рекомендуем. И пусть у нас есть история оценок по каждому пользователю, как он оценивал те или иные айтемы. Задача — научиться предсказывать оценку пользователя по какому-то ранее неоценённому айтему.
У вас может не быть явной системы оценивания, но могут быть другие сигналы — клики на айтемы, покупки айтемов, их просмотр/прослушивание, если вы какой-то видео-/аудиохостинг. Потому фидбек от пользователя делится на explicit (когда он явно сообщает, что ему что-то (не)нравится) и implicit (когда непонятно, нравится ли пользователю что-то, но можно выдвигать гипотезы, что действие пользователя означает).
Часто в таких штуках применяется коллаборативная фильтрация — методы в рексистемах, основанные на похожести айтемов и взаимодействии пользователя с айтемами.
Из терминов рядом ещё есть User2User (user based) и Item2Item рекомендации. Думаю, тут смысл понятен.
Есть ещё контентный подход — когда рекомендуют айтемы по схожести контента в них.
Ну и гибридные есть, которые сразу обе концепции совмещают, потому что у каждого подхода есть свои pros/cons, которые при совмещении могут закрываться.
На этапе ранжирования важной проблемой является кол-во данных. Фактически не представляется возможным отранжировать все существующие айтемы и взять из них топ, просто потому что это очень трудозатратно. Потому есть отбор кандидатов — какой-то относительно дешёвый способ сузить общее кол-во айтемов к небольшому множеству, чтобы все последующие действия производить только с ними. Тут конечно важно не испортить полноту айтемов, чтобы было вообще из чего в итоге выбирать. Как это можно делать:
- эвристически: выбирать самое популярное (в целом, или по геопозиции, или среди такого же возраста, или что угодно ещё);
- коллаборативно (item2item): офлайн посчитать 100 похожих айтемов для каждого, сложить это в какое-нибудь key-value и в онлайне взять для ста интересующих нас айтемов по 100 кандидатов. Отскорить итоговые 10k;
- юзать контентные похожести (взять какое-нибудь hnsw и для каждого айтема брать 100 самых близких в пространстве объектов);
- что-то продуктовое: докинуть чего-нибудь нового, например.
После отбора кандидатов и самого ранжирования наступает этап реранкинга (берём топ айтемов после ранжирования). Тут важно не только уметь показать пользователю то, что ему понравится, но и сделать это красиво. Например, повысить разнообразие выдачи, чтобы он не смотрел всё время на одно и то же (тут не только про тематику, но и про разнообразие форматов, если такая возможность есть). Или показывать только что-то старое, а не свежий контент.
В коллаборативной фильтрации есть проблема холодного старта: если у айтема/юзера нет никакого рейтинга/оценок, непонятно, кому и стоит ли вообще это рекомендовать.
Для юзеров можно попытаться собрать как можно больше инфы (хотя бы при регистрации задать пару лишних вопросов или как-то поонбордить его и предложить сразу что-то оценить).
Для айтемов тут можно использовать контентный подход.
Рекомендательные системы 1/2.
Опустим часть с мотивацией нужности подобных штук.
Пусть у нас есть множество пользователей и айтемов, которые мы рекомендуем. И пусть у нас есть история оценок по каждому пользователю, как он оценивал те или иные айтемы. Задача — научиться предсказывать оценку пользователя по какому-то ранее неоценённому айтему.
У вас может не быть явной системы оценивания, но могут быть другие сигналы — клики на айтемы, покупки айтемов, их просмотр/прослушивание, если вы какой-то видео-/аудиохостинг. Потому фидбек от пользователя делится на explicit (когда он явно сообщает, что ему что-то (не)нравится) и implicit (когда непонятно, нравится ли пользователю что-то, но можно выдвигать гипотезы, что действие пользователя означает).
Часто в таких штуках применяется коллаборативная фильтрация — методы в рексистемах, основанные на похожести айтемов и взаимодействии пользователя с айтемами.
Из терминов рядом ещё есть User2User (user based) и Item2Item рекомендации. Думаю, тут смысл понятен.
Есть ещё контентный подход — когда рекомендуют айтемы по схожести контента в них.
Ну и гибридные есть, которые сразу обе концепции совмещают, потому что у каждого подхода есть свои pros/cons, которые при совмещении могут закрываться.
На этапе ранжирования важной проблемой является кол-во данных. Фактически не представляется возможным отранжировать все существующие айтемы и взять из них топ, просто потому что это очень трудозатратно. Потому есть отбор кандидатов — какой-то относительно дешёвый способ сузить общее кол-во айтемов к небольшому множеству, чтобы все последующие действия производить только с ними. Тут конечно важно не испортить полноту айтемов, чтобы было вообще из чего в итоге выбирать. Как это можно делать:
- эвристически: выбирать самое популярное (в целом, или по геопозиции, или среди такого же возраста, или что угодно ещё);
- коллаборативно (item2item): офлайн посчитать 100 похожих айтемов для каждого, сложить это в какое-нибудь key-value и в онлайне взять для ста интересующих нас айтемов по 100 кандидатов. Отскорить итоговые 10k;
- юзать контентные похожести (взять какое-нибудь hnsw и для каждого айтема брать 100 самых близких в пространстве объектов);
- что-то продуктовое: докинуть чего-нибудь нового, например.
После отбора кандидатов и самого ранжирования наступает этап реранкинга (берём топ айтемов после ранжирования). Тут важно не только уметь показать пользователю то, что ему понравится, но и сделать это красиво. Например, повысить разнообразие выдачи, чтобы он не смотрел всё время на одно и то же (тут не только про тематику, но и про разнообразие форматов, если такая возможность есть). Или показывать только что-то старое, а не свежий контент.
В коллаборативной фильтрации есть проблема холодного старта: если у айтема/юзера нет никакого рейтинга/оценок, непонятно, кому и стоит ли вообще это рекомендовать.
Для юзеров можно попытаться собрать как можно больше инфы (хотя бы при регистрации задать пару лишних вопросов или как-то поонбордить его и предложить сразу что-то оценить).
Для айтемов тут можно использовать контентный подход.
👍2🤔2🆒1
#common
Рекомендательные системы 2/2.
Есть ещё такой трабл как feedback loop. Это когда рексистема из-за того, что рекомендует какие-то конкретные классы айтемов, будет всё чаще рекомендовать именно эти классы айтемов, т.к. по ним всё больше и больше фидбека. Ещё система может подстроиться под большинство популярных айтемов/пользователей и каким-то конкретным, выбивыющимся из общей картины пользователям, рекомендовать не самый подходящий контент.
Простые решения тут:
- можно подмешивать случайные айтемы в выдачу;
- делить рекомендации по тегам/темам/чему-то ещё.
Из понятного тут ещё можно сказать про шардирование. Например у вас огромное количество данных. Вы раскладываете данные батчами на несколько серверов. При запросе делаете запрос на каждый шард и получаете топ-100 с каждого шарда. Потом собираете все топы в какой-то один и переранжируете все айтемы ещё раз. Получаете честный топ-100 и отдаёте клиенту. Это классический подход. Надеюсь, ничего нового я вам тут не сказал.
Можно ещё сказать, что поиск и рекомендашки похожи по задаче, но в поиске у вас обычно есть какой-то конкретный запрос, когда в рекомендациях запрос — история поведения пользователя, что немного задачу усложняет.
Но я не настоящий сварщик в мльных штучках всяких. Просто интересно стало.
Рекомендательные системы 2/2.
Есть ещё такой трабл как feedback loop. Это когда рексистема из-за того, что рекомендует какие-то конкретные классы айтемов, будет всё чаще рекомендовать именно эти классы айтемов, т.к. по ним всё больше и больше фидбека. Ещё система может подстроиться под большинство популярных айтемов/пользователей и каким-то конкретным, выбивыющимся из общей картины пользователям, рекомендовать не самый подходящий контент.
Простые решения тут:
- можно подмешивать случайные айтемы в выдачу;
- делить рекомендации по тегам/темам/чему-то ещё.
Из понятного тут ещё можно сказать про шардирование. Например у вас огромное количество данных. Вы раскладываете данные батчами на несколько серверов. При запросе делаете запрос на каждый шард и получаете топ-100 с каждого шарда. Потом собираете все топы в какой-то один и переранжируете все айтемы ещё раз. Получаете честный топ-100 и отдаёте клиенту. Это классический подход. Надеюсь, ничего нового я вам тут не сказал.
Можно ещё сказать, что поиск и рекомендашки похожи по задаче, но в поиске у вас обычно есть какой-то конкретный запрос, когда в рекомендациях запрос — история поведения пользователя, что немного задачу усложняет.
Но я не настоящий сварщик в мльных штучках всяких. Просто интересно стало.
👍6🆒1
#cpp
Немножко отдельных фактов из одного доклада с C++ Russia.
1. Посмотрим на такой код:
Скомпилируется ли этот код? Ответ:нет . А почему?
Семантически этот код можно считать корректным. Мы вызываем лямбду, которая возвращает нам указатель на выделенную память, которую тут же удаляем.
Давайте посмотрим на грамматику для [expr.delete]:
Т.е. тут либо
Т.е. при наличии квадратных скобок после
2. Из интересного ещё такой пример:
Почему первое работает, а второе нет?
В языке есть специальные слова, которые не являются ключевыми. Они являются идентификаторами со специальным значением (
Эти два примера из очень приятного доклада Константина Владимирова про семантические процессы в C++.
У автора, кроме потрясающих курсов по плюсам на youtube, есть ещё канал в тг: @cpp_lects_rus.
Конфа в целом отсмотрена. Жду, когда выложат доклады в открытый доступ, и обязательно поделюсь тем, что понравилось.
=========================
У меня тут на днях google benchmark не хотел в cmake подтягиваться, потому что чуваки переименовали master в main. Вот она западная толлерантность. Из-за неё проекты не собираются.
Немножко отдельных фактов из одного доклада с C++ Russia.
1. Посмотрим на такой код:
delete [] { return new int; }();Скомпилируется ли этот код? Ответ:
Семантически этот код можно считать корректным. Мы вызываем лямбду, которая возвращает нам указатель на выделенную память, которую тут же удаляем.
Давайте посмотрим на грамматику для [expr.delete]:
delete-expression:
:: opt delete cast-expression
:: opt delete [] cast-expressionТ.е. тут либо
delete с [expr.cast], либо delete []. Вроде всё корректно. У нас может быть выбрана первая альтернатива, потому выражение является корректным. Однако если почитать стандарт повнимательнее, мы увидимWhenever the delete keyword is immediately followed by empty square brackets, it shall be interpreted as the second alternative.Т.е. при наличии квадратных скобок после
delete, всегда должна выбираться вторая альтернатива. Впрочем, если посмотреть на ошибку от clang, с этой проблемой можно разобраться довольно быстро. 2. Из интересного ещё такой пример:
int override = 0; // ok
int private = 0; // errorПочему первое работает, а второе нет?
В языке есть специальные слова, которые не являются ключевыми. Они являются идентификаторами со специальным значением (
final, import, module, override). И если у компилятора есть сомнения, является ли это специальным значением, он разрешает это в пользу ответа нет. Т.е. подстраивается под существующий контекст. Эти два примера из очень приятного доклада Константина Владимирова про семантические процессы в C++.
У автора, кроме потрясающих курсов по плюсам на youtube, есть ещё канал в тг: @cpp_lects_rus.
Конфа в целом отсмотрена. Жду, когда выложат доклады в открытый доступ, и обязательно поделюсь тем, что понравилось.
=========================
У меня тут на днях google benchmark не хотел в cmake подтягиваться, потому что чуваки переименовали master в main. Вот она западная толлерантность. Из-за неё проекты не собираются.
🔥14👍6💩2💔2
#highload #cpp
1. Старые песни о главном: How to design software architecture: Top tips and best practices.
2. eBay’s Blazingly Fast Billion-Scale Vector Similarity Engine.
3. API Design Reviews Are Dead. Long Live API Design Reviews!
4. Solving Undefined Behavior in Factories with constinit from C++20.
1. Старые песни о главном: How to design software architecture: Top tips and best practices.
2. eBay’s Blazingly Fast Billion-Scale Vector Similarity Engine.
3. API Design Reviews Are Dead. Long Live API Design Reviews!
4. Solving Undefined Behavior in Factories with constinit from C++20.
👍8🔥2❤1
#common #cpp
1. Newsletter Вадима Кравченко про документацию. Довольно годный.
2. Какая-то приконая статья про организационные структуры. Понравилась из-за конкретной истории в качестве примера.
3. Статья про различные способы организации транзакций.
4. Про статус C++26 и несколько интерсных пропозалов: link.
5. API Evolution Without Versioning.
Тут конечно немножко обман, потому что я почему-то ожидал какую-то хорошую альтернативу, но увидел знакомые вещи, которые мы используем регулярно рядом с обычным подходом с версиями. Версии кстати очень больно. Даже в нашем случае, когда клиентов не очень много. Потому что и их хватает, чтобы поддерживать легаси год+, пока все не перейдут.
===========================
6. Тут на днях пытались понять, почему такой код (https://godbolt.org/z/v55f5zW6o) падает на ассерте. Потому что если зайти на cppreference/rehash, можно увидеть текст
Который я читаю следующим образом:
- кол-во бакетов становится count;
- если max_load_factor превышается, кол-во бакетов увеличивается, чтобы не превышался.
Стандарт же говорит немного по-другому:
Т.е. кол-во бакетов нестрого больше минимального необходимого количества. Что вообще-то означает, что может быть вполне себе + один/сто/миллион бакетов сверху.
Эх. И не поправить сппреф🥳 ❌
1. Newsletter Вадима Кравченко про документацию. Довольно годный.
2. Какая-то приконая статья про организационные структуры. Понравилась из-за конкретной истории в качестве примера.
3. Статья про различные способы организации транзакций.
4. Про статус C++26 и несколько интерсных пропозалов: link.
5. API Evolution Without Versioning.
Тут конечно немножко обман, потому что я почему-то ожидал какую-то хорошую альтернативу, но увидел знакомые вещи, которые мы используем регулярно рядом с обычным подходом с версиями. Версии кстати очень больно. Даже в нашем случае, когда клиентов не очень много. Потому что и их хватает, чтобы поддерживать легаси год+, пока все не перейдут.
===========================
6. Тут на днях пытались понять, почему такой код (https://godbolt.org/z/v55f5zW6o) падает на ассерте. Потому что если зайти на cppreference/rehash, можно увидеть текст
Sets the number of buckets to count and rehashes the container, i.e. puts the elements into appropriate buckets considering that total number of buckets has changed. If the new number of buckets makes load factor more than maximum load factor (count < size() / max_load_factor()), then the new number of buckets is at least size() / max_load_factor().Который я читаю следующим образом:
- кол-во бакетов становится count;
- если max_load_factor превышается, кол-во бакетов увеличивается, чтобы не превышался.
Стандарт же говорит немного по-другому:
a.rehash(n)
Postconditions: a.bucket_count() >= a.size() / a.max_load_factor() and a.bucket_count() >= n.Т.е. кол-во бакетов нестрого больше минимального необходимого количества. Что вообще-то означает, что может быть вполне себе + один/сто/миллион бакетов сверху.
Эх. И не поправить сппреф
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2❤1🤔1
#cpp
Немножко про оптимизации и C++ 1/2 [это база].
0. SSO -- small/short string optimization.
Если мы посмотрим на
Что позволяет хранить значения маленьких строк сразу на стеке -> не тратить время на аллокацию/деаллокацию. Но, конечно же, есть и минусы. Например, кроме того, что ваш
У bfilipek есть небольшая статья про то, как узнать размер этого буфера с помощью
sso -- частный случай small object/buffer optimization.
Такой подход также применяется в
Если данные хранятся на куче, то при муве мы просто переложим указатель на область памяти из
Ещё sbo используют в различных реализациях
1. Empty base [class] optimization (ebo/ebco).
Имеем такой код:
Как известно, в C++
В случае нескольких родителей каждый компилятор делает что хочет. В msvc, например, оптимизация применяется только к последнему родителю (остальные занимают по байту). Clang и GCC же применяют ко всем.
Предположим, мы хотим использовать кастомный делитер с
Рядом с ebco обычно говорят ещё про атрибут
Компилятор сам посмотрит, правда ли типы пустые, и, если да, не будет выделять для них лишнюю память. Подобное можно использовать вместе с stateless аллокаторами, предикатами, хеш-функциями и любыми другими подобными объектами-функторами.
Про другие атрибуты писал тут: https://news.1rj.ru/str/thisnotes/218.
Немножко про оптимизации и C++ 1/2 [это база].
0. SSO -- small/short string optimization.
Если мы посмотрим на
sizeof(std::string), почти наверняка (без упоминания специфических архитектур и старых компиляторов) мы увидим 32 (что больше, чем ожидаемые 24 при стандартной схеме с указателем на данные и двумя чиселками size и capacity). Примерно потому что на самом деле строка выглядит так:template <typename T>
struct basic_string {
char* begin_;
size_t size_;
union {
size_t capacity_;
char sso_buffer[16];
};
};Что позволяет хранить значения маленьких строк сразу на стеке -> не тратить время на аллокацию/деаллокацию. Но, конечно же, есть и минусы. Например, кроме того, что ваш
sizeof может стать больше (может и не стать, всё зависит от размера вашего буфера), вы ещё делаете move-операции более дорогими. У bfilipek есть небольшая статья про то, как узнать размер этого буфера с помощью
constexpr/constinit. А тут на so можно посмотреть какой-то домашний бенчмарк с пруфом, что это действительно что-то ускоряет. sso -- частный случай small object/buffer optimization.
Такой подход также применяется в
std::function. Затрекать можно следующим кодом:auto f = [i = 0]() { std::cout << &i << ' '; };
std::function<void()> alpha = f;
alpha();
std::function<void()> beta = std::move(alpha);
beta();Если данные хранятся на куче, то при муве мы просто переложим указатель на область памяти из
alpha в beta. Если же данные будут хранится на стеке, то придётся перекладывать сам объект и адреса в выводе будут разные (gb). Ещё sbo используют в различных реализациях
small_vector. Причём если в случае std::string/std::function вы храните либо на стеке, либо на куче, то тут иногда делают вперемешку: могут часть оставить в буффере, а часть вынести на кучу; ну или на кучу всё, чтобы данные лежали в непрерывном куске памяти. 1. Empty base [class] optimization (ebo/ebco).
Имеем такой код:
struct A {};
struct B : A {};Как известно, в C++
sizeof пустого объекта -- 1 байт. Т.е. у B размер должен быть хотя бы 2 байта. Но конечно же это не так. Он всё ещё 1 байт, потому что в дело вступает ebco. В случае нескольких родителей каждый компилятор делает что хочет. В msvc, например, оптимизация применяется только к последнему родителю (остальные занимают по байту). Clang и GCC же применяют ко всем.
Предположим, мы хотим использовать кастомный делитер с
std::unique_ptr. Как сделать так, чтобы хранение делитера не занимало памяти? В случае, если делитер это какой-то stateless класс-функтор, от него можно отнаследоваться. Тогда в дело вступает ebco, что позволяет не занимать лишний байт, но при этом использовать operator(). Обычно это делают с помощью compressed_pair (реализовано это примерно разбором случаев на компиляции, когда можно наследоваться от типа, а когда нет; если нельзя, то тип просто хранится как член класса). Так что, если вам нужен кастомный делитер, лучше делать его без состояния. Рядом с ebco обычно говорят ещё про атрибут
[[no_unique_address]] (C++20). Когда вы помечаете какой-то из членов класса подобным атрибутом, вы говорите, что этот объект может не иметь уникального адреса в памяти и потенциально перекрываться с другими членами класса. Так с C++20 вы можете реализовать compressed_pair гораздо проще:template <typename T, typename U>
struct compressed_pair {
[[no_unique_address]] T _val1;
[[no_unique_address]] U _val2;
};Компилятор сам посмотрит, правда ли типы пустые, и, если да, не будет выделять для них лишнюю память. Подобное можно использовать вместе с stateless аллокаторами, предикатами, хеш-функциями и любыми другими подобными объектами-функторами.
Про другие атрибуты писал тут: https://news.1rj.ru/str/thisnotes/218.
❤2👍2🔥1
#cpp
Немножко про оптимизации и C++ 2/2.
2. Про RVO/NRVO писал ранее: t.me/thisnotes/198.
Рядом с этой темой можно посмотреть доклад про move-only C++ design.
3. Про девиртуализацию писал Женя: t.me/cxx95/88.
==============================
4. strlen elision.
Как известно, функция strlen работает за длину строки. Соответственно, если вы используете её в циклах:
Вы автоматом ловите квадрат в асимптотике. Потому хорошей практикой является выносить вот эту границу справа в отдельную переменную:
По-хорошему так делать и для итераторов, потому что операция получения end() у вашего контейнера может быть довольно тяжёлой (но ещё лучше понимать ограничения вашего контейнера и выбирать из конкретной ситуации). Но компиляторы не глупые: сами умеют иногда подобные вещи оптимизировать. Самый простой пример, это когда код вроде:
компилятор сделает вам:
Ну это база.
Недавно на собесе меня попросили написать
Как писали на каком-то сабреддите:
Optimize strlen by not using it.
Немножко про оптимизации и C++ 2/2.
2. Про RVO/NRVO писал ранее: t.me/thisnotes/198.
Рядом с этой темой можно посмотреть доклад про move-only C++ design.
3. Про девиртуализацию писал Женя: t.me/cxx95/88.
==============================
4. strlen elision.
Как известно, функция strlen работает за длину строки. Соответственно, если вы используете её в циклах:
for (int i = 0; i < strlen(str); ++i) {…}Вы автоматом ловите квадрат в асимптотике. Потому хорошей практикой является выносить вот эту границу справа в отдельную переменную:
for (int i = 0, end = strlen(str); i < end; ++i) {}По-хорошему так делать и для итераторов, потому что операция получения end() у вашего контейнера может быть довольно тяжёлой (но ещё лучше понимать ограничения вашего контейнера и выбирать из конкретной ситуации). Но компиляторы не глупые: сами умеют иногда подобные вещи оптимизировать. Самый простой пример, это когда код вроде:
int main() {
return std::strlen(“hi”);
}компилятор сделает вам:
main: # @main
mov eax, 2
retНу это база.
Недавно на собесе меня попросили написать
strlen. Потом ещё каким-то образом попросили это дело поускорять. Кек, что таким ещё занимаются. Хотя конечно, если погуглить, можно найти что-нибудь вроде такого или такого. Так что может и не без оснований, но лучше я вкину вот такую статью про полезность собеседований подобного рода. Как писали на каком-то сабреддите:
Optimize strlen by not using it.
👍11❤2🔥2
this->notes.
#cpp Немножко отдельных фактов из одного доклада с C++ Russia. 1. Посмотрим на такой код: delete [] { return new int; }(); Скомпилируется ли этот код? Ответ: нет . А почему? Семантически этот код можно считать корректным. Мы вызываем лямбду, которая возвращает…
Доклад Константина теперь доступен по ссылке: https://www.youtube.com/watch?v=lc3UkIZ4zOY&t=110s
Категорически рекомендую.
Категорически рекомендую.
❤2
#highload
Пару ссылочек.
1. What I Wish I Had Known Before Scaling Uber to 1000 Services.
У uber есть вот такая статья про domain-oriented архитектуру. Ключевая идея — объединять сервисы в домены по бизнесовому назначению и ходить в домены снаружи исключительно через gateway (там конечно ещё много всяких тонкостей и сложностей), что позволяет навести некоторый порядок в вашей архитектуре и том, как ваши бекенды взаимодействуют.
2. What is a Vector Database?
Хорошая и понятная статья про хранение векторов.
==========================
Оч устал за последнее время. Отваливаюсь отдыхать на пару недель.
Пару ссылочек.
1. What I Wish I Had Known Before Scaling Uber to 1000 Services.
У uber есть вот такая статья про domain-oriented архитектуру. Ключевая идея — объединять сервисы в домены по бизнесовому назначению и ходить в домены снаружи исключительно через gateway (там конечно ещё много всяких тонкостей и сложностей), что позволяет навести некоторый порядок в вашей архитектуре и том, как ваши бекенды взаимодействуют.
2. What is a Vector Database?
Хорошая и понятная статья про хранение векторов.
==========================
Оч устал за последнее время. Отваливаюсь отдыхать на пару недель.
🔥8❤1
#common
A/B-тестирование 1/2.
A/B-тесты это когда вы сравниваете поведение пользователей при различных условиях. Например вы запускаете новую фичу и хотите понять, как она влияет на пользователей и влияет ли вообще. В данном случае вы можете поделить пользователей на две группы: тестовую с фичой и контрольную, у которой новой фичи нет. После какого-то времени смотрим на метрики и понимаем, отличаются ли значения у этих двух групп (прокрасились ли метрики и как они это сделали). Если результаты положительные, фичу можно принимать (в общем случае). Можно конечно принимать решения на ощущениях, но это опасный путь, который в долгосроке засадит ваш продукт в глубокую яму (если им злоупотреблять конечно).
Ну это ладно. Концептуально, я думаю, вы всё это знаете. Хочется осветить несколько связанных моментов.
1. Про разделение пользователей на группы😎
Это вообще довольно широкая тема, но концептуально тут существует всего пару подходов:
- берём всех пользователей и делим на k групп. При запуске нового эксперимента (экспа) берём две свободные группы из общего пула. Как только группы заканчиваются, приходится ждать, пока какой-нибудь эксп завершится, чтобы можно было начать новый;
- второй способ это перекрывать пользователей. Например пользователь может находиться сразу в нескольких тестовых группах и таким образом попадать под несколько экспериментов сразу (какие-то могут быть вкл, какие-то выкл). Реализовать это можно так: брать user_id и хешировать его с некоторой уникальной для эксперимента солью (seed для нашего хеша). Если сделать всё аккуратно, получится равномерное распределение пользователей на группы.
Однако в таком подходе стоит следить за тем, являются ли экспы независимыми, чтобы не похерить себе результаты.
Можно подходы в разных видах комбинировать.
Иногда у продуктов бывает своя специфика, из-за которой проводить аб-тесты над отдельными пользователями некорректно. Например в социальных сетях иногда проводят эксперименты над кластерами пользователей (link).
2. Важно правильно задизайнить эксперимент🤔
В моём опыте был один довольно понятный пример. Мы проводили тест с фичой на одной специфичной страничке нашего аппа, на которую заходят не все пользователи. При аб-тесте 50/50 (пользователь попадал в конкретную группу равновероятно при авторизации) мы словили очень маленький профит и решили фичу раскатить на всех. После чего метрики выросли очень сильно (порядка х10), хотя ожидалось всего в два раза. При выяснении, что же произошло, раскопали, что пользователи в тестовой группе в целом меньше пользовались этой частью приложения, когда в контрольной группе было больше таких пользователей. Тут налицо некорректный подход к дизайну экспа. Правильнее было бы делить пополам всех пользователей, которые пользуются конкретной частью приложения. В нашем случае вот рандом получился смещённым, что немного завело нас в тупик.
A/B-тестирование 1/2.
A/B-тесты это когда вы сравниваете поведение пользователей при различных условиях. Например вы запускаете новую фичу и хотите понять, как она влияет на пользователей и влияет ли вообще. В данном случае вы можете поделить пользователей на две группы: тестовую с фичой и контрольную, у которой новой фичи нет. После какого-то времени смотрим на метрики и понимаем, отличаются ли значения у этих двух групп (прокрасились ли метрики и как они это сделали). Если результаты положительные, фичу можно принимать (в общем случае). Можно конечно принимать решения на ощущениях, но это опасный путь, который в долгосроке засадит ваш продукт в глубокую яму (если им злоупотреблять конечно).
Ну это ладно. Концептуально, я думаю, вы всё это знаете. Хочется осветить несколько связанных моментов.
1. Про разделение пользователей на группы
Это вообще довольно широкая тема, но концептуально тут существует всего пару подходов:
- берём всех пользователей и делим на k групп. При запуске нового эксперимента (экспа) берём две свободные группы из общего пула. Как только группы заканчиваются, приходится ждать, пока какой-нибудь эксп завершится, чтобы можно было начать новый;
- второй способ это перекрывать пользователей. Например пользователь может находиться сразу в нескольких тестовых группах и таким образом попадать под несколько экспериментов сразу (какие-то могут быть вкл, какие-то выкл). Реализовать это можно так: брать user_id и хешировать его с некоторой уникальной для эксперимента солью (seed для нашего хеша). Если сделать всё аккуратно, получится равномерное распределение пользователей на группы.
Однако в таком подходе стоит следить за тем, являются ли экспы независимыми, чтобы не похерить себе результаты.
Можно подходы в разных видах комбинировать.
Иногда у продуктов бывает своя специфика, из-за которой проводить аб-тесты над отдельными пользователями некорректно. Например в социальных сетях иногда проводят эксперименты над кластерами пользователей (link).
2. Важно правильно задизайнить эксперимент
В моём опыте был один довольно понятный пример. Мы проводили тест с фичой на одной специфичной страничке нашего аппа, на которую заходят не все пользователи. При аб-тесте 50/50 (пользователь попадал в конкретную группу равновероятно при авторизации) мы словили очень маленький профит и решили фичу раскатить на всех. После чего метрики выросли очень сильно (порядка х10), хотя ожидалось всего в два раза. При выяснении, что же произошло, раскопали, что пользователи в тестовой группе в целом меньше пользовались этой частью приложения, когда в контрольной группе было больше таких пользователей. Тут налицо некорректный подход к дизайну экспа. Правильнее было бы делить пополам всех пользователей, которые пользуются конкретной частью приложения. В нашем случае вот рандом получился смещённым, что немного завело нас в тупик.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤3
#common
A/B-тестирование 2/2.
3. AA(B)-тесты🥳
Иногда вы хотите быть уверенным, что поведение пользователей и соответственно результаты эксперимента будут корректными, потому делите пользователей не на две группы, а на три (A, A и B). Таким образом вы смотрите, что ваши A-группы ведут себя одинаково и что B-группа приносит. Если же обе A показывают разные результаты, то у вас что-то не так с дизайном эксперимента (ну или чем-то другим). Соответстственно иногда даже проводят AA-тесты, чтобы понять, всё ли ок с точки зрения разбиения/поведения пользователей.
4. Ухудшающие тесты😍
Иногда вы хотите понять, стоит ли ещё развивать некоторую функциональность. Самый простой пример — замедление чего-либо. Можно замедлить фичу/страницу на 100мс и понять, влияет ли это на пользователей. Если в тестовой группе с замедлением мы получаем негативную прокраску метрик, значит имеет смысл оптимизировать проверяемую систему, чтобы получить профит.
Аналогично можно делать с качеством рекомендаций. И вообще с чем угодно.
Вообще все компании проводят неявные ухудшающие эксперименты. Например во время инцидентов (когда что-то падает). Тут может быть полезно не только починить проблему, но и проанализировать, как это повлияло на пользователей, чтобы получить какие-то новые знания.
5. Просадка метрик не всегда плохо😁
Иногда фича может повергнуть пользователя в шок своей непривычностью и новизной. Если вы делаете какие-то изменения, которые могут в будущем принести больше пользы (технически и продуктово), нормально принимать изменения с прокрашенными в красный метриками. Тут конечно надо знать меру и понимать, как возвращать результаты обратно. Если всё совсем плохо, но очень хочется запустить, садиться и думать, почему всё так плохо и что поменять в фиче, чтобы пользователям она заходила больше.
Ну а может ваша фича крутая, просто снаружи в мире произошло что-то, что массово повлияло на пользователей? Причём это может влиять как в хорошую, так и в плохую сторону. Такие вещи важно отслеживать.
6. Жгущие эксперименты☺️
Иногда новая функциональность начинает приносить огромный профит сразу с запуска. Чтобы не терять профит во время проведения теста, можно после включения 50/50 зафиксировать результаты, включить фичу на 80% пользователей и продолжить эксперимент на 10/10 (все доли конечно индивидуальны).
Подобный подход даже иногда автоматизируют, когда, например, раз в сутки самый успешный вариант раскатывается автоматически на бОльшую долю пользователей. В итоге можно получить автоматически раскатанный лучший вариант к концу аб-тестирования.
UPD 7. Обратные эксперименты🥴
Обратный эксп это когда вы отключаете фичу вместо её включения.
Мы как-то приняли фичу, а через какое-то время начали на неё получать репорты время от времени. Решили провести обратный эксп, чтобы понять, что с ней происходит, и получили без неё улучшение метрик. В итоге выключили её.
Причина была в ряде других фич, которые были пользователям более удобны, и вот эта подозрительная выбивалась из общей картины, из-за чего портила опыт юзеров.
Получилось, что мы включили функционал и получили рост метрик, а потом отключили функционал и получили ещё больший рост : )
Тут ссылочки на два доклада:
- Какие архитектурные решения в Яндекс Go позволяют запускать десятки продукт.экспериментов;
- Без A/B — результат XЗ, или Как мы построили платформу A/B-тестов в Ozon / Евгений Пак (Ozon).
A/B-тестирование 2/2.
3. AA(B)-тесты
Иногда вы хотите быть уверенным, что поведение пользователей и соответственно результаты эксперимента будут корректными, потому делите пользователей не на две группы, а на три (A, A и B). Таким образом вы смотрите, что ваши A-группы ведут себя одинаково и что B-группа приносит. Если же обе A показывают разные результаты, то у вас что-то не так с дизайном эксперимента (ну или чем-то другим). Соответстственно иногда даже проводят AA-тесты, чтобы понять, всё ли ок с точки зрения разбиения/поведения пользователей.
4. Ухудшающие тесты
Иногда вы хотите понять, стоит ли ещё развивать некоторую функциональность. Самый простой пример — замедление чего-либо. Можно замедлить фичу/страницу на 100мс и понять, влияет ли это на пользователей. Если в тестовой группе с замедлением мы получаем негативную прокраску метрик, значит имеет смысл оптимизировать проверяемую систему, чтобы получить профит.
Аналогично можно делать с качеством рекомендаций. И вообще с чем угодно.
Вообще все компании проводят неявные ухудшающие эксперименты. Например во время инцидентов (когда что-то падает). Тут может быть полезно не только починить проблему, но и проанализировать, как это повлияло на пользователей, чтобы получить какие-то новые знания.
5. Просадка метрик не всегда плохо
Иногда фича может повергнуть пользователя в шок своей непривычностью и новизной. Если вы делаете какие-то изменения, которые могут в будущем принести больше пользы (технически и продуктово), нормально принимать изменения с прокрашенными в красный метриками. Тут конечно надо знать меру и понимать, как возвращать результаты обратно. Если всё совсем плохо, но очень хочется запустить, садиться и думать, почему всё так плохо и что поменять в фиче, чтобы пользователям она заходила больше.
Ну а может ваша фича крутая, просто снаружи в мире произошло что-то, что массово повлияло на пользователей? Причём это может влиять как в хорошую, так и в плохую сторону. Такие вещи важно отслеживать.
6. Жгущие эксперименты
Иногда новая функциональность начинает приносить огромный профит сразу с запуска. Чтобы не терять профит во время проведения теста, можно после включения 50/50 зафиксировать результаты, включить фичу на 80% пользователей и продолжить эксперимент на 10/10 (все доли конечно индивидуальны).
Подобный подход даже иногда автоматизируют, когда, например, раз в сутки самый успешный вариант раскатывается автоматически на бОльшую долю пользователей. В итоге можно получить автоматически раскатанный лучший вариант к концу аб-тестирования.
UPD 7. Обратные эксперименты
Обратный эксп это когда вы отключаете фичу вместо её включения.
Мы как-то приняли фичу, а через какое-то время начали на неё получать репорты время от времени. Решили провести обратный эксп, чтобы понять, что с ней происходит, и получили без неё улучшение метрик. В итоге выключили её.
Причина была в ряде других фич, которые были пользователям более удобны, и вот эта подозрительная выбивалась из общей картины, из-за чего портила опыт юзеров.
Получилось, что мы включили функционал и получили рост метрик, а потом отключили функционал и получили ещё больший рост : )
Тут ссылочки на два доклада:
- Какие архитектурные решения в Яндекс Go позволяют запускать десятки продукт.экспериментов;
- Без A/B — результат XЗ, или Как мы построили платформу A/B-тестов в Ozon / Евгений Пак (Ozon).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤2
#common
0. У Вадима Кравченко вышел большой пост про то, как быть хорошим ментором. Очень годный. Там ещё внутри миллион ссылок на другие более узкие статьи. Может я со временем их вычитаю и донесу частями самое интересное.
Рядом ещё могу вкинуть статью из вастрик клуба: Ты ментор. Не обосрись.
1. What’s Wrong With OpenAPI? Чуваки рассказывают, что не так с OpenAPI и что они сделали, чтобы жить лучше. Выглядит даже прикона.
2. Приконая статья про различные способы организовать ваши команды. Мы вообще работаем кросс-функциональным подходом. Словил себя на мысли, что часть недостатков такого подхода сказалась и на мне.
3. Интересная статья (правда по верхам) про эволюцию подходов к организации архитектуры вашего бекенда.
0. У Вадима Кравченко вышел большой пост про то, как быть хорошим ментором. Очень годный. Там ещё внутри миллион ссылок на другие более узкие статьи. Может я со временем их вычитаю и донесу частями самое интересное.
Рядом ещё могу вкинуть статью из вастрик клуба: Ты ментор. Не обосрись.
1. What’s Wrong With OpenAPI? Чуваки рассказывают, что не так с OpenAPI и что они сделали, чтобы жить лучше. Выглядит даже прикона.
2. Приконая статья про различные способы организовать ваши команды. Мы вообще работаем кросс-функциональным подходом. Словил себя на мысли, что часть недостатков такого подхода сказалась и на мне.
3. Интересная статья (правда по верхам) про эволюцию подходов к организации архитектуры вашего бекенда.
👍5❤4🤔1
#cpp
Наткнулся случайно на доку gcc, где описаны всякие штуки, которые поддерживает компилятор, и при просмотре увидел разные интересные (и не очень) штуки (и стандартные, и специфические для конкретного компилятора). Расскажу про несколько из них.
1. Тут в п.6 я писал про new_handler. Аналогичные штуки есть для std::terminate и std::unexpected.
2. Есть целый пак контейнеров, которые расширяют то, что есть в стандартной библиотеке. Некоторые концептуально повторяют уже существующие, некоторые нет:
- какая-то реализация для красно-чёрного дерева;
- реализация rope;
- реализация slist, что, кажется, просто forward list;
- какая-то ещё одна реализация строк под названием __versa_string;
- dynamic_bitset. Хотя конечно лучше брать такое в boost;
- думаю, многим известный valarray;
- gslice, являющийся подмножеством valarray;
- indirect_array, которые тоже подмножество valarray, но зададётся множеством индексов элементов;
- mask_array, задающий подмножество битовой маской.
- mini_vector, являющийся урезанной версией
- gp_hash_table — общий класс для hash-based ассоциативных контейнеров. Из интересного тут можно задавать свою resize policy (думал про такое для вектора когда-то).
3. Разные алгоритмы:
- subtractive_rng — генератор случайных чисел, основанный на каком-то там алгоритме;
- функция для нахождения медианы. Интересно, как это нормально делать сейчас стандартной библиотекой, чтобы не складывать числа в контейнер и не юзать
4. Всякие utils:
- реализация decimal;
- bitmap_allocator;
- freelist (про него и чувака выше можно почитать тут);
- пак утилит для дебага.
5. Type traits:
- реализации is_detected и detected_or;
- is_fast_hash, который реализован как угар имхо: для всех типов он fast, а для long double нет😏
Но вообще тут поинт в том, что вы как юзер можете написать специализацию, которая говорит, что для вашего типа хеш считается долго. Контейнер при этом будет кешировать значения хешей.
Короч накодили и живут себе.
Наткнулся случайно на доку gcc, где описаны всякие штуки, которые поддерживает компилятор, и при просмотре увидел разные интересные (и не очень) штуки (и стандартные, и специфические для конкретного компилятора). Расскажу про несколько из них.
1. Тут в п.6 я писал про new_handler. Аналогичные штуки есть для std::terminate и std::unexpected.
2. Есть целый пак контейнеров, которые расширяют то, что есть в стандартной библиотеке. Некоторые концептуально повторяют уже существующие, некоторые нет:
- какая-то реализация для красно-чёрного дерева;
- реализация rope;
- реализация slist, что, кажется, просто forward list;
- какая-то ещё одна реализация строк под названием __versa_string;
- dynamic_bitset. Хотя конечно лучше брать такое в boost;
- думаю, многим известный valarray;
- gslice, являющийся подмножеством valarray;
- indirect_array, которые тоже подмножество valarray, но зададётся множеством индексов элементов;
- mask_array, задающий подмножество битовой маской.
- mini_vector, являющийся урезанной версией
std::vector и работающий только для built-in и POD типов. А ещё он память не чистит🤪- gp_hash_table — общий класс для hash-based ассоциативных контейнеров. Из интересного тут можно задавать свою resize policy (думал про такое для вектора когда-то).
3. Разные алгоритмы:
- subtractive_rng — генератор случайных чисел, основанный на каком-то там алгоритме;
- функция для нахождения медианы. Интересно, как это нормально делать сейчас стандартной библиотекой, чтобы не складывать числа в контейнер и не юзать
nth_element. Пропозалов на это не видел. 4. Всякие utils:
- реализация decimal;
- bitmap_allocator;
- freelist (про него и чувака выше можно почитать тут);
- пак утилит для дебага.
5. Type traits:
- реализации is_detected и detected_or;
- is_fast_hash, который реализован как угар имхо: для всех типов он fast, а для long double нет
Но вообще тут поинт в том, что вы как юзер можете написать специализацию, которая говорит, что для вашего типа хеш считается долго. Контейнер при этом будет кешировать значения хешей.
Короч накодили и живут себе.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10👌2❤1🔥1
#highload #pub
Мы с коллегой немножко напряглись и написали статью про то, как у нас работает поиск. Прошу к столу.
https://habr.com/ru/companies/yandex/articles/748134/
Мы с коллегой немножко напряглись и написали статью про то, как у нас работает поиск. Прошу к столу.
https://habr.com/ru/companies/yandex/articles/748134/
Хабр
Три движка для одной Лавки: как эволюционировала система поиска в сервисе
Лавка — сервис быстрой доставки продуктов. Один из важнейших сценариев использования сервиса для покупателя — это поиск. Примерно 30% товаров добавляются в корзину именно из его результатов. А ещё,...
❤24🔥13
#cpp
C++ Zero Cost Conf 2023.
Трек в Москве (link на трансляцию).
Был доклад про устройство компиляторов (я базово писал про это) и зачем в VK свой.
> Тут докладчик сказал, что они написали свой compiler и ускорили бекенд в 10 раз. Подумал, что очень хороший план писать на чём-то вроде php, чтобы потом переписать или накрутить трансляцию в плюсы и говорить, что всё очень сильно ускорили😂
> Оказалось, что доклад реально про KPHP, про который они уже рассказывали мильён раз в других местах🤯
Константин Владимиров рассказывал про масштабируемую векторизацию RISCV. Я не поклонник подобных низкоуровневых штук, потому скипнул. Но являюсь фанатом Константина, так что, если тема вам интересна, можно посмотреть. Уверен, доклад крутой и понятный.
У Ильи Шишкова был доклад под названием “Не с первого раза: упрощаем С++ код с помощью DSL”. Доклад примерно про то, как ребята решали какую-то бизнесовую задачу (я такое очень люблю). Я не прям понял всю специфику задачи, но солв интересный, не прям тривиальный C++ (всё понятно, но не циклы с ифами), процесс движения по задаче знаком (я думаю) каждому, рассказывает он хорошо. Можно смотреть.
От Романа Русяева был доклад про концепции неопределённого поведения (зачем оно всё же нужно? кроме как бесить программистов конечно же). Рассказ не под пиво (мне пришлось иногда напрягаться), но это даже и хорошо. Примеры интересные. Можно смотреть.
Доклад про корутины скипнул.
Антон Полухин рассказал новости от рабочей группы 21. Я делал выжимку в феврале. Докину про C++26:
- можно будет использовать плейсхолдер🤯 ;
- нормальный
- hazard pointer и rcu;
-
-
- ещё больше
-
- хеширование для типов из
- рефлексия пока не готова. Мб повезёт и будет в 26м стандарте (мб нет);
- контракты могут к 26ому стандарту доехать;
и всякое разное ещё.
Ощущается, что на некоторые вопросы Антон не пытался отвечать и просто съезжал👀
Что-то московский трек не очень порадовал. Есть ощущение, что конфа делается чтобы делать конфу. Завтра расскажу про белградскую часть.
C++ Zero Cost Conf 2023.
Трек в Москве (link на трансляцию).
Был доклад про устройство компиляторов (я базово писал про это) и зачем в VK свой.
> Тут докладчик сказал, что они написали свой compiler и ускорили бекенд в 10 раз. Подумал, что очень хороший план писать на чём-то вроде php, чтобы потом переписать или накрутить трансляцию в плюсы и говорить, что всё очень сильно ускорили
> Оказалось, что доклад реально про KPHP, про который они уже рассказывали мильён раз в других местах🤯
Константин Владимиров рассказывал про масштабируемую векторизацию RISCV. Я не поклонник подобных низкоуровневых штук, потому скипнул. Но являюсь фанатом Константина, так что, если тема вам интересна, можно посмотреть. Уверен, доклад крутой и понятный.
У Ильи Шишкова был доклад под названием “Не с первого раза: упрощаем С++ код с помощью DSL”. Доклад примерно про то, как ребята решали какую-то бизнесовую задачу (я такое очень люблю). Я не прям понял всю специфику задачи, но солв интересный, не прям тривиальный C++ (всё понятно, но не циклы с ифами), процесс движения по задаче знаком (я думаю) каждому, рассказывает он хорошо. Можно смотреть.
От Романа Русяева был доклад про концепции неопределённого поведения (зачем оно всё же нужно? кроме как бесить программистов конечно же). Рассказ не под пиво (мне пришлось иногда напрягаться), но это даже и хорошо. Примеры интересные. Можно смотреть.
Доклад про корутины скипнул.
Антон Полухин рассказал новости от рабочей группы 21. Я делал выжимку в феврале. Докину про C++26:
- можно будет использовать плейсхолдер
_ для неиспользуемых переменных (много раз в рамках области видимости). Теперь читы с макросами для получения анонимных имён не нужны (3й пункт). Тут Антон несколько раз говорил, что комитет смотрел на несколько крупных кодовых баз, чтобы реальные юзкейсы для каких-то фич и делал из этого какие-то выводы. С одной стороны круто. С другой стороны имхо параша какая-то. Словил вайб подгонки некоторых фичей под существующий у кого-то говнокод- нормальный
std::to_string для floating_point;- hazard pointer и rcu;
-
native_handle какой-то для получения дескрипторов файлов;-
std::function_ref;- ещё больше
constexpr (для комплексных чисел например);-
std::submdspan — подмножество std::mdspan из C++23 (код какой-то оч неприятный конечно с этими штуками);- хеширование для типов из
std::chrono;- рефлексия пока не готова. Мб повезёт и будет в 26м стандарте (мб нет);
- контракты могут к 26ому стандарту доехать;
и всякое разное ещё.
Ощущается, что на некоторые вопросы Антон не пытался отвечать и просто съезжал
Что-то московский трек не очень порадовал. Есть ощущение, что конфа делается чтобы делать конфу. Завтра расскажу про белградскую часть.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2💔2
#cpp
C++ Zero Cost Conf 2023.
Трек в Белграде (link на трансляцию).
Первый доклад был про layout классов в C++. Как-то базово по ощущениям получилось. Имхо не для конференций такого левела (ну или я ту мач от неё ожидаю просто).
Второй — Акторная система YDB: история оптимизаций, позволивших обогнать конкурентов. Ещё не было ни одного доклада от челов из YDB, который мне зашёл, так что я скипнул. Мб потому что предметная область в целом не оч интересна.
Андрей Аксёнов рассказывал доклад под названием “Очередная крафтовая хеш-таблица”. Я в целом к его манере рассказывать привык, но какой-то конкретики в докладе не увидел. Можно послушать, но какие-то абстрактные рассуждения получились.
Хотя в целом я доклады Андрея люблю, потому что после них потом можно сидеть и гуглить всякие штуки довольно долго, просто потому что он упоминает много всего.
Был доклад от Александра Малкова про grpc и userver. Слушать немножко тяжеловато.
Я полгода назад смотрел, что там и как поддерживается и в тот момент нужны были довольно большие приседания, чтобы получить то, что есть для http. Если ребята и правда там поддержали много всего нужного, круто.
Правда словил вайб, что чересчур реклама какая-то фреймворка, но может докладчику реально очень нравится. В целом мы на нём живём. Проблем не ощущается.
Доклад Александра Боргардрта не слушал, т.к. он рассказывал то же самое на C++ Russia в мае (мб конечно что-то поменялось, но я не завлёкся послушать фулл доклад ради каких-то точечных отличий).
Я правда послушал его ответы на вопросы и что-то как-то токсично..
Последний доклад — Парсим числа через SIMD от Сергея Слотина (у него в прошлом году был неплохой доклад про ускорение бинпоиска). Я походу не оч осознал задачу, но там ускоряется парсинг чисел в json. Что это за джсоны такие интересно, в которых чисел так много, что подобные приседания становятся полезны.
А ещё осознал, что мне не понравилось в плане съёмок. Я как-то привык к CppCon и считаю, что там оч хорошо эта часть сделана. В записях там всегда есть преза и отдельным окошком докладывающий. Тут же иногда меняются планы (я вот слайд читал -> план поменялся -> не могу читать слайд -> жду, пока вернётся стандартный план), зачем-то показываются слушающие. Не прям удобно.
Потраченного времени не жаль, но верю, что ребята могут и сделают в будущем лучше.
C++ Zero Cost Conf 2023.
Трек в Белграде (link на трансляцию).
Первый доклад был про layout классов в C++. Как-то базово по ощущениям получилось. Имхо не для конференций такого левела (ну или я ту мач от неё ожидаю просто).
Второй — Акторная система YDB: история оптимизаций, позволивших обогнать конкурентов. Ещё не было ни одного доклада от челов из YDB, который мне зашёл, так что я скипнул. Мб потому что предметная область в целом не оч интересна.
Андрей Аксёнов рассказывал доклад под названием “Очередная крафтовая хеш-таблица”. Я в целом к его манере рассказывать привык, но какой-то конкретики в докладе не увидел. Можно послушать, но какие-то абстрактные рассуждения получились.
Хотя в целом я доклады Андрея люблю, потому что после них потом можно сидеть и гуглить всякие штуки довольно долго, просто потому что он упоминает много всего.
Был доклад от Александра Малкова про grpc и userver. Слушать немножко тяжеловато.
Я полгода назад смотрел, что там и как поддерживается и в тот момент нужны были довольно большие приседания, чтобы получить то, что есть для http. Если ребята и правда там поддержали много всего нужного, круто.
Правда словил вайб, что чересчур реклама какая-то фреймворка, но может докладчику реально очень нравится. В целом мы на нём живём. Проблем не ощущается.
Доклад Александра Боргардрта не слушал, т.к. он рассказывал то же самое на C++ Russia в мае (мб конечно что-то поменялось, но я не завлёкся послушать фулл доклад ради каких-то точечных отличий).
Я правда послушал его ответы на вопросы и что-то как-то токсично..
Последний доклад — Парсим числа через SIMD от Сергея Слотина (у него в прошлом году был неплохой доклад про ускорение бинпоиска). Я походу не оч осознал задачу, но там ускоряется парсинг чисел в json. Что это за джсоны такие интересно, в которых чисел так много, что подобные приседания становятся полезны.
А ещё осознал, что мне не понравилось в плане съёмок. Я как-то привык к CppCon и считаю, что там оч хорошо эта часть сделана. В записях там всегда есть преза и отдельным окошком докладывающий. Тут же иногда меняются планы (я вот слайд читал -> план поменялся -> не могу читать слайд -> жду, пока вернётся стандартный план), зачем-то показываются слушающие. Не прям удобно.
Потраченного времени не жаль, но верю, что ребята могут и сделают в будущем лучше.
👍16❤3👎1
#common #cpp
0. Это вообще довольно известная штука, но я о ней не писал, так что поделюсь.
Когда вы учите C++, вам говорят, что возвращаемое значение на перегрузку не влияет. Но это не значит, что нельзя использовать силу рук, чтобы намутить такое. Мы же всё-таки на C++ пишем☺️
Пусть есть две функции, которые отличаются только возвращаемым значением:
Мы хотим, чтобы следующий код заработал:
Нам поможет вот такая замечательная структура:
у которой операторы приведения типа как раз совершают необходимую логику, соответствующую функциям
Ну или можно сделать немножко аккуратнее и написать общую функцию, чтобы детали реализации не висели наружу:
И ссылочки.
1. Unexpected downsides of UUID keys in PostgreSQL.
2. Big Data is Dead. Статья прикольная, потому что идёт вразрез с моим мироощущением, где данных очень много и рано или поздно это начнёт приносить проблемы.
3. Goodbye, Clean Code.
0. Это вообще довольно известная штука, но я о ней не писал, так что поделюсь.
Когда вы учите C++, вам говорят, что возвращаемое значение на перегрузку не влияет. Но это не значит, что нельзя использовать силу рук, чтобы намутить такое. Мы же всё-таки на C++ пишем
Пусть есть две функции, которые отличаются только возвращаемым значением:
int process(std::string s);
bool process(std::string s);Мы хотим, чтобы следующий код заработал:
int i = process(some_string);
bool b = process(some_string);Нам поможет вот такая замечательная структура:
struct converter {
std::string s;
operator int() const;
operator bool() const;
};у которой операторы приведения типа как раз совершают необходимую логику, соответствующую функциям
process. Тогда наш код превращается в int i = converter{some_string};
bool b = converter{some_string};Ну или можно сделать немножко аккуратнее и написать общую функцию, чтобы детали реализации не висели наружу:
converter process(std::string s) {
return converter{s};
}
int i = process(some_string);
bool b = process(some_string);И ссылочки.
1. Unexpected downsides of UUID keys in PostgreSQL.
2. Big Data is Dead. Статья прикольная, потому что идёт вразрез с моим мироощущением, где данных очень много и рано или поздно это начнёт приносить проблемы.
3. Goodbye, Clean Code.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27❤4😈3😁2🤯1
#common
Мои ребята делают стартап со стримингом музыки.
Суть кратко:
- есть треки, на которые вы можете покупать nft;
- когда этот трек слушается на платформе, вы (пользователь) получаете доход в токенах;
- исполнитель получает доход как обычно + часть токенов с продаж nft;
- вы можете поддерживать артистов и зарабатывать на угадывании трендов.
На днях они выложили статью про то, как у них работает так называемая tokenomics, как они её моделировали, какие есть проблемные места. Учитывая, что я не прям в этой всей теме, получилось интересно.
Мои ребята делают стартап со стримингом музыки.
Суть кратко:
- есть треки, на которые вы можете покупать nft;
- когда этот трек слушается на платформе, вы (пользователь) получаете доход в токенах;
- исполнитель получает доход как обычно + часть токенов с продаж nft;
- вы можете поддерживать артистов и зарабатывать на угадывании трендов.
На днях они выложили статью про то, как у них работает так называемая tokenomics, как они её моделировали, какие есть проблемные места. Учитывая, что я не прям в этой всей теме, получилось интересно.
🤯15🤡6🔥4❤1👍1