this->notes. – Telegram
this->notes.
4.54K subscribers
25 photos
1 file
330 links
О разработке, архитектуре и C++.

Tags: #common, #cpp, #highload и другие можно найти поиском.
Задачки: #poll.
Мои публикации: #pub.
Автор и предложка: @vanyakhodor.
GitHub: dasfex.
Download Telegram
#common

Узнал про такую штуку как hashcash. Он используется в различных криптоштуках в качестве proof-of-work. Но сейчас мы не будем останавливаться на битке и подобных вещах. Хочется рассказать о том, как такое можно использовать в житейских простых задачах вроде пагинации (писал немного про неё тут).

С реализацией пагинации могут быть некоторые проблемы, если выдача у вас нестабильная. То есть офсет и курсор это круто, если множество данных у вас плюс-минус фиксировано и их порядок не меняется (плюс-минус, потому что можно добрасывать новое в конец и ничего в моменте не сломать). Но такая ситуация не всегда. Например вы хотите сделать пагинацию для рекомендашек. При каждом запросе у вас происходит полный цикл процесса рекомендаций, в конце которого (в упрощённом случае) находится ранжирование. Т.к. это обычно машинки и фичей бывает очень много (а они ещё могут очень быстро меняться) сущность, которую вы отранжировали на 1ю позицию в первом запросе, при втором запросе может вполне себе отранжироваться на 21ю. И с курсором 20 во втором запросе вы получите в выдаче то, что уже показывали. Почти наверняка это плохое решение, потому что пользователь уже видел этот контент и деняк больше вы не заработаете. Да и выглядит стьюпидно.
В зависимости от реализации поискового движка может возникнуть та же проблема.

Получается, надо где-то сохранить инфу о том, что мы пользователю уже показывали. Учитывая, что эта инфа валидна только для конкретного юзера, почти наверняка придётся каким-то образом получать данные с клиента (читай фронта). Давайте тогда запихнём эту инфу в курсор, который теперь будет иметь немного более сложный вид: это будет конкатенированная строка хешей объектов, которые мы отдали. И собственно из рексистемы отдадим эту строку как курсор клиенту, после чего он может прислать этот курсор обратно, а мы уже поймём по нему, что мы показывали, а что нет.

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

Собственно примерно поэтому и называется hashcash: вы кешируете множество объектов с помощью кеширования каждого из них.

Ещё из проблем тут есть:
- т.к. хеширование двух различных объектов может давать один хеш, вы можете отсеять что-то, что на самом деле не показывали, но, насколько я могу понимать, это обычно не крит, потому что с таким же успехом товар мог отфильтроваться на каком-нибудь другом этапе рекомендаций (отбор кандидатов, после ранжирования оказаться слишком низко, реранкинг). Если такое возникает часто, опять же можно посмотреть в сторону других хеш-функций, которые дают более разнообразные результаты;
- т.к. с каждым запросом кол-во показанных сущностей растёт, сам курсор тоже будет расти. Тут можно выбрать какое-то ограничение для длины курсора с трейдоффом кол-во показанного юзеру контента <-> технические возможности вашей системы. Если вы понимаете, что из всех пользователей долистывает до 300ого айтема только полпроцента, может не так страшно не показывать больше 300 объектов? Ну и оч длинную строку парсить на бекенде не хочется, т.к. это потенциально таймауты (а ещё та же проблема на клиенте, а ещё нагрузка на сеть вырастает). Можно опять же выбрать хеш-функцию так, чтобы она давала более короткие хеши, но тут важно не переборщить, чтобы не получить прошлую проблему в большом объёме.

Концептуально про рекомендательные системы расскажу попозже.
👍141🤔1🤯1
#common

Рекомендательные системы 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 и отдаёте клиенту. Это классический подход. Надеюсь, ничего нового я вам тут не сказал.

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

Но я не настоящий сварщик в мльных штучках всяких. Просто интересно стало.
👍6🆒1
#cpp

Немножко отдельных фактов из одного доклада с 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
#common #cpp

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🔥21🤔1
#cpp

Немножко про оптимизации и 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 работает за длину строки. Соответственно, если вы используете её в циклах:

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.
👍112🔥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?

Хорошая и понятная статья про хранение векторов.

==========================
Оч устал за последнее время. Отваливаюсь отдыхать на пару недель.
🔥81
#common

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
👍103
#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).
Please open Telegram to view this post
VIEW IN TELEGRAM
👍122
#common

0. У Вадима Кравченко вышел большой пост про то, как быть хорошим ментором. Очень годный. Там ещё внутри миллион ссылок на другие более узкие статьи. Может я со временем их вычитаю и донесу частями самое интересное.

Рядом ещё могу вкинуть статью из вастрик клуба: Ты ментор. Не обосрись.

1. What’s Wrong With OpenAPI? Чуваки рассказывают, что не так с OpenAPI и что они сделали, чтобы жить лучше. Выглядит даже прикона.

2. Приконая статья про различные способы организовать ваши команды. Мы вообще работаем кросс-функциональным подходом. Словил себя на мысли, что часть недостатков такого подхода сказалась и на мне.

3. Интересная статья (правда по верхам) про эволюцию подходов к организации архитектуры вашего бекенда.
👍54🤔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, являющийся урезанной версией 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👌21🔥1
#cpp

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
👍102💔2
#cpp

C++ Zero Cost Conf 2023.

Трек в Белграде (link на трансляцию).

Первый доклад был про layout классов в C++. Как-то базово по ощущениям получилось. Имхо не для конференций такого левела (ну или я ту мач от неё ожидаю просто).

Второй — Акторная система YDB: история оптимизаций, позволивших обогнать конкурентов. Ещё не было ни одного доклада от челов из YDB, который мне зашёл, так что я скипнул. Мб потому что предметная область в целом не оч интересна.

Андрей Аксёнов рассказывал доклад под названием “Очередная крафтовая хеш-таблица”. Я в целом к его манере рассказывать привык, но какой-то конкретики в докладе не увидел. Можно послушать, но какие-то абстрактные рассуждения получились.
Хотя в целом я доклады Андрея люблю, потому что после них потом можно сидеть и гуглить всякие штуки довольно долго, просто потому что он упоминает много всего.

Был доклад от Александра Малкова про grpc и userver. Слушать немножко тяжеловато.
Я полгода назад смотрел, что там и как поддерживается и в тот момент нужны были довольно большие приседания, чтобы получить то, что есть для http. Если ребята и правда там поддержали много всего нужного, круто.
Правда словил вайб, что чересчур реклама какая-то фреймворка, но может докладчику реально очень нравится. В целом мы на нём живём. Проблем не ощущается.

Доклад Александра Боргардрта не слушал, т.к. он рассказывал то же самое на C++ Russia в мае (мб конечно что-то поменялось, но я не завлёкся послушать фулл доклад ради каких-то точечных отличий).
Я правда послушал его ответы на вопросы и что-то как-то токсично..

Последний доклад — Парсим числа через SIMD от Сергея Слотина (у него в прошлом году был неплохой доклад про ускорение бинпоиска). Я походу не оч осознал задачу, но там ускоряется парсинг чисел в json. Что это за джсоны такие интересно, в которых чисел так много, что подобные приседания становятся полезны.

А ещё осознал, что мне не понравилось в плане съёмок. Я как-то привык к CppCon и считаю, что там оч хорошо эта часть сделана. В записях там всегда есть преза и отдельным окошком докладывающий. Тут же иногда меняются планы (я вот слайд читал -> план поменялся -> не могу читать слайд -> жду, пока вернётся стандартный план), зачем-то показываются слушающие. Не прям удобно.

Потраченного времени не жаль, но верю, что ребята могут и сделают в будущем лучше.
👍163👎1
#common #cpp

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
👍274😈3😁2🤯1
#common

Мои ребята делают стартап со стримингом музыки.

Суть кратко:
- есть треки, на которые вы можете покупать nft;
- когда этот трек слушается на платформе, вы (пользователь) получаете доход в токенах;
- исполнитель получает доход как обычно + часть токенов с продаж nft;
- вы можете поддерживать артистов и зарабатывать на угадывании трендов.

На днях они выложили статью про то, как у них работает так называемая tokenomics, как они её моделировали, какие есть проблемные места. Учитывая, что я не прям в этой всей теме, получилось интересно.
🤯15🤡6🔥41👍1
#common #algo

Сегодня немножко математики.

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

Предположим у нас есть следующее дп (не будем расписывать в общем виде):

dp[i] = 2dp[i-1] + 3dp[i-2] - dp[i-3]

с какой-то базой. Например

dp[0, 1, 2] = {1, 2, 3}

Предположим, мы хотим вычислить несколько конкретных значений (dp[10^3], dp[10^15], то есть значения с очень большими номером), а не все (обычно так и бывает).

Давайте посмотрим на это в виде матриц. Базовая матрица у нас имеет вид (это не определитель, я просто скобки не умею нормально рисовать):

| 1 |
a = | 2 |
| 3 |


где a_i это соответственно dp_i, i \in [0; 2].

Если мы перемножим матрицу ниже на столбец выше

| 0 1 0|
M = | 0 0 1|
|-1 3 2|


мы получим вектор

| 2 |
| 3 |
| 11 |


в котором мы по факту получили сдвиг в нашей дп-шной последовательности на 1 вперёд (то есть вычислили dp[2], dp[3] и dp[4]).

А теперь предположим, мы хотим найти dp[n]. Конечно, эту последовательность умножений можно повторять раз за разом n - 3 раза, пока мы не получим нужный элемент. Но в чём тогда был смысл подобных движений? Давайте сначала нашу матрицу перехода бинарно возведём в (n-3)-ю степень и только потом умножим вектор a. Чтобы получить ответ, достаточно будет взять последний элемент получившегося столбца:

ans = (bin_pow(M, n - 3) * a)[2];

То есть по факту вы можете считать любую подобную “линейную” последовательность быстро для любого адекватного значения n. Я когда-то, например, писал нахождение n-ого числа Фибоначчи на компиляции. Тут правда надо быть аккуратным с глубиной рекурсии и возможно подкрутить флаг -ftemplate-depth.
👍19🤯52🔥2
#common

1. Ubiquitous Caching: a Journey of Building Efficient Distributed and In-Process Caches at Twitter.
Доклад прикольный, но у infoq есть проблема, что они выкладывают именно расшифровку (т.е. картиночки вы не увидите). А сам доклад слушать тяжеловато (какие-то траблы со звуком). Может лучше тогда посмотреть короткий видос про устройство Segcache тут.

2. Building a strong knowledge sharing culture on engineering teams.
Это скорее заметка, которая рекламит конкретный продукт. И заголовок у неё кликбейтный. Но проблемы с документацией ребята описали хорошо.

3. HTTP/3 From A To Z: Core Concepts.

4. Should That Be a Microservice? Keep These Six Factors in Mind.

Ну и на этом пока всё.

Неделя получилась плотнее обычного по постам, но это выброс : )
👍61🤯1