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

Парсим протобаф на скорости больше 2 Гб/с.: как я научился любить хвостовую рекурсию в C

Время на прочтение10 мин
Количество просмотров12K
Всего голосов 25: ↑24 и ↓1+39
Комментарии12

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

TCO (Tail Call Optimization), включая tail-recursion optimization, работают во всех современных компиляторах из-коробки при указания флага оптимизации.
Например, для GCC это -foptimize-sibling-calls, который включается при -O2, -O3 или -Os.

Проблема в том, что эта оптимизация (как и любая другая) не гарантирована, особенно в достаточно запутанной схеме вызовов, как у парсера сырых данных. Здесь же явно указывается как вести себя компилятору, что собственно и дает ощутимый прирост производительности.

по какой-то причине компилятор не хочет генерировать jmp qword ptr [rsi + 8*rax], вместо этого он загружает в rax и затем выполняет jmp rax. Это небольшие проблемы генерирования кода, которые, надеюсь, в Clang скоро исправят без излишних затруднений.

https://reviews.llvm.org/D101718
Если вдруг на хабре есть коммитеры llvm, помогите с ревью.

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь
Я считаю, что нельзя сравнивать скорости от считывания текстового файла json и бинарного protobuf, т.к. в одних и тех-же GB может быть разное количество данных. Представьте себе массив int[10000] в формате json и protobuf и сколько при этом будут иметь размеры файлы.
Даже если они распарсят одновременно, последний по вашей метрике проиграет.
НЛО прилетело и опубликовало эту надпись здесь

Загрузить, проверить типы и выходы за границы, убедиться в совместимости протоколов, подставить значения по умолчанию по необходимости.
Распарсив JSON вы не получаете что-то похожее на изначальный объект — например у вас нет гарантии что это будет 10000 интов, у вас будет просто массив значений, а значит вы должны проверить, что их 10000, а так же что каждое из 10000 это int. А, и конечно это ещё надо будет разложить в объекты — simdjson по сути размечает итераторы на проходе парсинга, в этот момент реальные значения не извлечены и никуда не скопированы.

Интересно, а ведь можно вот прямо взять и написать goto и гарантированно получить именно то что надо без магии с атрибутами и проблем с портируемостью.

Как я понял — нет, потому что компилятор попытается соптимизировать цикл целиком, распределить регистры автоматически и не справится догадаться, как лучше. А если делаем через функции, то это форсирует нахождение первых аргументов в определённых регистрах (по ABI), что является нехилой подсказкой.

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