Re[7]: Засада с эквивалентным ключом в std::unordered_map
От: · Великобритания  
Дата: 21.10.24 14:56
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>·>А вообще, конечно, смешно. Парсим охрененно избыточный медленный json и экономим на спичках, считая байты... Хочешь перформанс — бери SBE или protobuf хотя бы.

W>Забавно, но если сравнивать самые быстрые парсеры, то simdjson выигрывает у protobuf, даже несмотря на то, что парсить ему приходится куда более объёмный сериализованный текст.
Вроде SBE затачивался под скорость и latency.

W>Бинарный формат protobuf отравлен varint'ами — в нём для чтения очередного поля нужно разобрать предыдущее — и ни у кого не получается это место хорошо на уровне инструкций распараллелить.

Любопытно. А чем json лучше в этом? Там вроде каждое поле таким образом "отравлено".

W>Но, совет всё равно хороший, так как и другое, скорее всего, будет работать на порядок быстрее чем самодельный парсер с std::unordered_map внутри.

Именно. И я почему-то уверен, что он перформанс микробенчмарком на map.find вместо замера общего перформанса обработки запроса включая парсинг и т.п.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: Засада с эквивалентным ключом в std::unordered_map
От: watchmaker  
Дата: 21.10.24 16:47
Оценка: 18 (4) +1
Здравствуйте, ·, Вы писали:

W>>Бинарный формат protobuf отравлен varint'ами — в нём для чтения очередного поля нужно разобрать предыдущее — и ни у кого не получается это место хорошо на уровне инструкций распараллелить.

·>Любопытно. А чем json лучше в этом? Там вроде каждое поле таким образом "отравлено".

Тем что в нём структура документа описывается символами []{}",: , которые в любом контексте выглядят одинаково. И у CPU есть соответствующие инструкции, которые, например, берут длинную строку и возвращают битовую маску позиций, где в ней находятся эти символы.
После чего определение границ структур сводится к поиску парных скобок. То есть для '"' нужно найти парную '"' — и всё что между ними — это строка.
В современных архитектурах (x86,arm,risc-v) даже есть инструкция для таких операций с битами.
Конечно, есть случаи экранированных '\"' внутри строки, но это точно так же сводится к получению битовой маски вхождения символа '\' и нескольким побитовым операциям.

Можно примерно схему в https://arxiv.org/abs/1902.08318 почитать.


А в varint нужно каждый раз смотреть на старший бит в байте на предмет того, является ли продолжением предыдущего элемента или уже новым. Но при этом размеры сообщений, строк и packed-массивов кодируются длиной. То есть для них уже не работает трюк с выделением маски старших битов и нахождением в нём групп единиц, разделённых нулями через умножение полиномов, так как неизвестно находился ли бит внутри строки или нет. А чтобы это узнать — нужно прочитать длину строки. И мы вынуждены либо использовать последовательный разбор, либо пытаться сделать разбор со всех позиций сразу, чтобы потом выбрать настоящую. Но и то и другое скорости не добавляет.
Отредактировано 21.10.2024 21:01 watchmaker . Предыдущая версия . Еще …
Отредактировано 21.10.2024 19:02 watchmaker . Предыдущая версия .
Re[6]: Засада с эквивалентным ключом в std::unordered_map
От: Videoman Россия https://hts.tv/
Дата: 21.10.24 17:54
Оценка:
Здравствуйте, ·, Вы писали:

·>А вообще, конечно, смешно. Парсим охрененно избыточный медленный json и экономим на спичках, считая байты... Хочешь перформанс — бери SBE или protobuf хотя бы.


Вообще JSON парсится влет и быстро, с ним нет проблем. Проблема в промежуточном слое, который был сделан в угоду гибкости (ну или из-за отсутствия до сих пор в C++ рефлексии) и тормозит уже десериализация во внутренние типы. Скорость сейчас устраивает, просто хотел подточить углы сделать запас.
Re: Засада с эквивалентным ключом в std::unordered_map
От: Videoman Россия https://hts.tv/
Дата: 21.10.24 17:58
Оценка:
Кстати по ходу обсуждения появился ещё один вопрос: если начать плавно переходить на С++20, то как с полнотой его поддержки в GCC сейчас обстоят дела?

Когда я фиксировал стандарт в прошлый раз на С+17, то обнаружил, что MSVC нормально поддерживал С++17, почти полностью, CLang — тоже, а вот с GCC были проблемы, приходилось ставить 10-ю или даже 11-ю версию. А как сейчас дела с 20-м стандартом?
Re[2]: Засада с эквивалентным ключом в std::unordered_map
От: Великий Реверс google
Дата: 21.10.24 18:09
Оценка:
если как марти будешь сидеть на каком нибудь старом гцц 7.2
то да
проблем много
начиная с самой главной
с++20 он в этой версии только начинал поддерживать

а если поставить самый последний гцц
то проблем нет
с++26 по чуть чуть добавляют
Re[3]: Засада с эквивалентным ключом в std::unordered_map
От: Videoman Россия https://hts.tv/
Дата: 21.10.24 18:32
Оценка:
Здравствуйте, Великий Реверс, Вы писали:

ВР>если как марти будешь сидеть на каком нибудь старом гцц 7.2


Не знаю как там с 7.2, но на 9-м я столкнулся с тем, что не были реализованы некоторые части стандартной библиотеки 17-го стандарта.
Re[9]: Засада с эквивалентным ключом в std::unordered_map
От: · Великобритания  
Дата: 21.10.24 19:24
Оценка:
Здравствуйте, watchmaker, Вы писали:

w> Конечно, есть случаи экранированных '\"' внутри строки, но это точно так же сводится к получению битовой маски вхождения символа '\t' и нескольким побитовым операциям.

Спасибо, интересно.
А как с числами быть? Надо уметь конвертировать десятичное представление в int, да ещё и 1000000 может быть записано как 1.0E6.

w> Можно примерно схему в https://arxiv.org/abs/1902.08318 почитать.

Гы, деньги хотят.

w> А чтобы это узнать — нужно прочитать длину строки. И мы вынуждены либо использовать последовательный разбор, либо пытаться сделать разбор со всех позиций сразу, чтобы потом выбрать настоящую. Но и то и другое скорости не добавляет.

Прикольно. Т.е. я правильно понял, что закрывающий символ, даже с учётом экранирования, можно найти быстрее, чем сразу же прочитать длину?! Совершенно контринтуитивно.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Засада с эквивалентным ключом в std::unordered_map
От: Великий Реверс google
Дата: 21.10.24 20:03
Оценка:
https://en.cppreference.com/w/cpp/compiler_support/17
https://en.cppreference.com/w/cpp/compiler_support/20
Re[9]: Засада с эквивалентным ключом в std::unordered_map
От: landerhigh Пират  
Дата: 21.10.24 21:09
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>Можно примерно схему в https://arxiv.org/abs/1902.08318 почитать.


Спасибо, интересно.

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

Согласен с тем, что varint может быть проблемой. Но, для пересылки одного 32-битного int потребуется 6 байтов максимум. В случае json — это до десяти байтов плюс разделители, итого 11 или 12. В два раза больше.
Но это вырожденный случай, число "800" (16-битный int) потребует 3 байтов в protobuf и тех же трех в json плюс один или два разделителя.
но в случае с protobuf, это уже включает идентификатор поля, а в случае в json идентификатор поля — это отдельная строка черт его знает какой длины, для которой еще нужно будет посчитать хеш...
Re[10]: Засада с эквивалентным ключом в std::unordered_map
От: landerhigh Пират  
Дата: 21.10.24 21:11
Оценка: +1
Здравствуйте, ·, Вы писали:

w>> Можно примерно схему в https://arxiv.org/abs/1902.08318 почитать.

·>Гы, деньги хотят.

У меня получилось PDF скачать.
Re[10]: Засада с эквивалентным ключом в std::unordered_map
От: watchmaker  
Дата: 22.10.24 00:04
Оценка:
Здравствуйте, landerhigh, Вы писали:


L>Правда, я не уверен, что так уж корректно измерять скорость работы парсера в гигабайтах в секунду.


Да нормально — это же метрика для сравнения разных реализаций json парсеров между собой. У них цель быстрее распарсить готовый json-документ, а не соревноваться с парсерами альтернативных форматов сериализации.



L> Но, для пересылки одного 32-битного int потребуется 6 байтов максимум. В случае json — это до десяти байтов плюс разделители, итого 11 или 12. В два раза больше.

L>Но это вырожденный случай

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

L>но в случае с protobuf, это уже включает идентификатор поля, а в случае в json идентификатор поля — это отдельная строка черт его знает какой длины


Понятно, что в среднем json более «рыхлый».
Тем удивительнее, что simdjson умудряется оказываться быстрее даже при пересчёте на размер исходных данных. Выигрыш сильно меньше, но парадоксально, что он вообще есть.
Re[10]: Засада с эквивалентным ключом в std::unordered_map
От: watchmaker  
Дата: 22.10.24 00:31
Оценка: +1
Здравствуйте, ·, Вы писали:

·>А как с числами быть?


Конвертировать медленно и мучительно. Какие-то части парсера будут медленнее работать из-за текстовой природы формата.

w>> А чтобы это узнать — нужно прочитать длину строки. И мы вынуждены либо использовать последовательный разбор, либо пытаться сделать разбор со всех позиций сразу, чтобы потом выбрать настоящую. Но и то и другое скорости не добавляет.

·>Т.е. я правильно понял, что закрывающий символ, даже с учётом экранирования, можно найти быстрее, чем сразу же прочитать длину?!

Скорее выигрышь в том, что одной операцией можно найти все открывающие и закрывающие символы в целом блоке данных, где таких последовательностией может быть больше одной.
А с явной длиной нужно сначала её прочитать, и лишь потом сместиться в потоке на указанное смещение к началу следующей последовательности. Тут между ними возникает зависимость, мешающая параллелизму.
Ну и свою роль ещё играет не самый удачный выбор способа записи самих varint'ов и тегов в protobuf.
Re[11]: Засада с эквивалентным ключом в std::unordered_map
От: landerhigh Пират  
Дата: 22.10.24 07:08
Оценка: +1
Здравствуйте, watchmaker, Вы писали:

L>>Правда, я не уверен, что так уж корректно измерять скорость работы парсера в гигабайтах в секунду.

W>Да нормально — это же метрика для сравнения разных реализаций json парсеров между собой. У них цель быстрее распарсить готовый json-документ, а не соревноваться с парсерами альтернативных форматов сериализации.

Для сравнения json парсеров между собой — да. Но подобная метрика не подходит, если хочется проверить, не выгоднее ли использовать simdjson, нежели protobuf.

W>Понятно, что в среднем json более «рыхлый».

W>Тем удивительнее, что simdjson умудряется оказываться быстрее даже при пересчёте на размер исходных данных. Выигрыш сильно меньше, но парадоксально, что он вообще есть.

Понятное дело, что в целом нужно рассматривать не только производительность парсера, но и в целом размер данных, пересылаемых по сети. Тут в общем binary протоколы будут в выигрыше
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.