Skip to content

Conversation

@nmaks2012
Copy link

@nmaks2012 nmaks2012 commented Oct 23, 2025

SAX-парсинг JSON в пользовательские типы

Этот PR добавляет возможность эффективного парсинга JSON напрямую в пользовательские структуры данных, минуя промежуточное DOM-представление, с использованием SAX-парсеров.

Основные изменения

DOM-подход

// Парсинг через промежуточное DOM-представление
formats::json::Value json = formats::json::FromString(doc);
std::unordered_map<std::string, int> data = json.As<std::unordered_map<std::string, int>>();

SAX-подход

// Прямой парсинг в целевую структуру
std::unordered_map<std::string, int> data = formats::json::FromStringAs<std::unordered_map<std::string, int>>(doc);

Под капотом:

  1. Создание специализированного парсера через parser::CreateParser()
  2. SAX-обработка: токены JSON обрабатываются напрямую соответствующими парсерами
  3. Типизированное построение: данные сразу складываются в целевую структуру
  4. Отсутствие промежуточных преобразований: JSON → целевой тип за один проход

Бенчмарки

Таблица:
Running ./userver-universal-benchmark
Run on (16 X 3686.4 MHz CPU s)
CPU Caches:
  L1 Data 32 KiB (x16)
  L1 Instruction 32 KiB (x16)
  L2 Unified 4096 KiB (x16)
  L3 Unified 16384 KiB (x1)
Load Average: 1.13, 1.28, 0.95
***WARNING*** Library was built as DEBUG. Timings may be affected.
SAX DOM Perf
Time CPU Iterations Time CPU Iterations Time, ns CPU, ns Time, % CPU, %
SaxParseSimpleStructFromStringAs/1 5116 ns 5116 ns 275306 DomParseSimpleStruct/1 5322 ns 5321 ns 264468 206 205 3,87%
SaxParseSimpleStructFromStringAs/4 6303 ns 6302 ns 221304 DomParseSimpleStruct/4 6615 ns 6614 ns 209661 312 312 4,72%
SaxParseSimpleStructFromStringAs/16 10328 ns 10328 ns 134939 DomParseSimpleStruct/16 10941 ns 10940 ns 128994 613 612 5,60%
SaxParseSimpleStructFromStringAs/64 24819 ns 24819 ns 56478 DomParseSimpleStruct/64 26508 ns 26507 ns 53255 1689 1688 6,37%
SaxParseSimpleStructFromStringAs/256 82940 ns 82938 ns 16699 DomParseSimpleStruct/256 90222 ns 90216 ns 15589 7282 7278 8,07%
SaxParseSimpleStructFromStringAs/1024 316202 ns 316190 ns 4411 DomParseSimpleStruct/1024 339438 ns 339426 ns 4123 23236 23236 6,85%
SaxParseComplexStructFromStringAs/1 9834 ns 9833 ns 143433 DomParseComplexStruct/1 10169 ns 10169 ns 138846 335 336 3,29%
SaxParseComplexStructFromStringAs/4 26788 ns 26788 ns 52468 DomParseComplexStruct/4 29051 ns 29048 ns 49034 2262 2260 7,79%
SaxParseComplexStructFromStringAs/16 156112 ns 156107 ns 8951 DomParseComplexStruct/16 169352 ns 169338 ns 8209 13240 13231 7,82%
SaxParseComplexStructFromStringAs/64 1650762 ns 1650732 ns 834 DomParseComplexStruct/64 1851327 ns 1851213 ns 750 200565 200481 10,83%
SaxParseComplexStructFromStringAs/256 24558738 ns 24557241 ns 57 DomParseComplexStruct/256 27121241 ns 27120259 ns 52 2562503 2563018 9,45%
SaxParseVectorIntFromStringAs/1 867 ns 867 ns 158161 DomParseVectorInt/1 1558 ns 1558 ns 893788 691 691 44,35%
SaxParseVectorIntFromStringAs/4 1468 ns 1468 ns 957918 DomParseVectorInt/4 2721 ns 2721 ns 521929 1253 1253 46,05%
SaxParseVectorIntFromStringAs/16 3896 ns 3895 ns 359462 DomParseVectorInt/16 6835 ns 6835 ns 204210 2939 2940 43,00%
SaxParseVectorIntFromStringAs/64 13377 ns 13377 ns 104359 DomParseVectorInt/64 22020 ns 22019 ns 6241 8643 8642 39,25%
SaxParseVectorIntFromStringAs/256 53308 ns 53307 ns 26285 DomParseVectorInt/256 83907 ns 83903 ns 16612 30599 30596 36,47%
SaxParseVectorIntFromStringAs/1024 214101 ns 214089 ns 6598 DomParseVectorInt/1024 328359 ns 328341 ns 4262 114258 114252 34,80%
SaxParseMapStringIntFromStringAs/1 1172 ns 1172 ns 1189449 DomParseMapStringInt/1 2557 ns 2557 ns 547625 1385 1385 54,17%
SaxParseMapStringIntFromStringAs/4 3220 ns 3220 ns 428236 DomParseMapStringInt/4 6965 ns 6962 ns 197936 3745 3742 53,77%
SaxParseMapStringIntFromStringAs/16 12724 ns 12723 ns 110924 DomParseMapStringInt/16 23668 ns 23667 ns 60758 10944 10944 46,24%
SaxParseMapStringIntFromStringAs/64 55152 ns 55150 ns 25446 DomParseMapStringInt/64 104215 ns 104211 ns 13357 49063 49061 47,08%
SaxParseMapStringIntFromStringAs/256 240604 ns 240589 ns 5795 DomParseMapStringInt/256 464444 ns 464414 ns 3009 223840 223825 48,20%
SaxParseMapStringIntFromStringAs/1024 1045431 ns 1045376 ns 1345 DomParseMapStringInt/1024 2091133 ns 2091057 ns 666 1045702 1045681 50,01%
  • Значительное ускорение до 50% для STL контейнеров любой вложенности
  • Умеренное ускорение для пользовательских типов - 3-10% (Меньший выигрыш из-за использования JsonValueParser)

Технические детали

Для STL контейнеров:

// Прямой парсинг - нет промежуточных преобразований
std::vector<std::vector<int>> items = formats::json::FromStringAs<std::vector<std::vector<int>>>(json);
// Используется ArrayParser → ElementParser цепочка парсеров

Для пользовательских типов:

struct User{
   int id;
   std::string name;
}
// Двойное преобразование через JsonValueParser
User user = formats::json::FromStringAs<User>(json);
// Используется JsonValueParser → Value → As<User>() цепочка парсеров

Причины разной производительности между STL и пользовательскими типами

STL контейнеры:

  • Прямое построение контейнера из JSON токенов
  • Нулевые промежуточные преобразования

Пользовательские типы:

  • Промежуточное построение formats::json::Value
  • Дополнительный вызов As() с проверками

В будущем можно поработать над улучшением производительности для пользовательских типов.

Closes #987


Note: by creating a PR or an issue you automatically agree to the CLA. See CONTRIBUTING.md. Feel free to remove this note, the agreement holds.

nmaks2012 and others added 3 commits October 22, 2025 21:12
- Added FromStreamAs<T>  and FromStringAs<T>for direct streaming JSON parsing
- Added test coverage to include both FromStringAs and FromStreamAs methods

Tests: added comprehensive tests for both parsing methods including error cases
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSON SAX parsing for chaotic

1 participant