Перейти к содержанию

JSON Performance Baseline

Первый harness и одностендовый baseline реализованы. Базовый исходник Liza фиксируется на merge-коммите T2-A b320611; измерение не ждёт реализации T2-B. Полный SHA коммита и SHA-256 всех входов записываются в будущий отчёт.

Сравниваемый контракт

Основное сравнение — один потоковый алгоритм синтаксической проверки на Liza, C++ и Rust. Реализации сохраняют состояния между чанками, предел глубины 64, UTF-8 validation, строгие surrogate pairs, лексическую проверку чисел и абсолютные байтовые смещения ошибок. DOM, преобразование чисел в float и скрытое накопление токенов не допускаются. Различие принятия входа или позиции ошибки сначала разбирается как несовпадение контрактов, а не результат производительности.

Специализированные JSON-библиотеки образуют отдельную таблицу с собственными ограничениями и режимами; они не подменяют сравнение одинакового алгоритма.

Воспроизводимый корпус

Генератор с фиксированным seed создаёт ASCII-объекты, Unicode, escapes и surrogate pairs, длинные строки и числа, широкие массивы, вложенность на границе лимита. Объёмы включают маленькие файлы и потоки значительно больше чанка 2048 байтов. Отдельный отрицательный корпус содержит усечённые токены, неверный UTF-8, лишние байты, повреждённые escapes и превышение глубины.

До замеров все реализации сравниваются по исходу и смещению ошибки на общем корпусе, включая разбиения внутри UTF-8 и escape-последовательностей. Для малых примеров проверяются все одиночные точки разбиения; для больших — фиксированные и воспроизводимые случайные разбиения. Отрицательные входы измеряются отдельно: быстрое отклонение в начале не считается обработкой всего файла.

Два независимых измерения

  1. Ядро валидатора: данные загружены заранее, одинаковые границы чанков; учитывается обработка данных, исключаются запуск процесса и файловый I/O.
  2. CLI: настоящий liza-json check и эквивалентные C++/Rust инструменты; отдельно учитываются запуск, чтение файла и завершение. Тёплый файловый cache явно отмечается; результаты не выдаются за холодное чтение диска.

Основная конфигурация — release/O2 с записанными полными командами. O0 служит дополнительной диагностикой. Проверки границ и переполнения не отключаются молча ради ускорения; различия режимов безопасности явно перечисляются.

Каждая конфигурация получает прогрев и не менее 10 измеряемых повторов. Порядок реализаций перемешивается с фиксированным seed. Сохраняются сырые времена, медиана, разброс, MiB/s по реально обработанным байтам и доступные показатели RSS. Аллокации ядра отделяются от запуска и файлового слоя. Результат проверки потребляется драйвером, чтобы оптимизатор не удалил работу.

Диагностика стоимости состояния

Помимо основного сравнения сохраняются два C++ варианта одного автомата: изменение состояния на месте и передача/возврат состояния по значению. Оба проходят тот же корпус и используют те же чанки. Они сопоставляются с текущим обновлением JsonState в Liza; Rust остаётся отдельной реализацией алгоритма. Размер состояния, вызовы копирования и объём stack storage в оптимизированном IR/assembly фиксируются отдельно от времени. Наличие return-by-value в исходнике само по себе не означает физического копирования после оптимизации. Разница времени не приписывается lowering без проверки сгенерированного кода.

Приёмка baseline

Отчёт содержит ОС, CPU, память, версии компиляторов, flags, SHA исходников и бинарников, seed, хеши корпуса, размеры чанков, команды и сырые результаты. Первое измерение на одной машине не обобщается на все архитектуры. CI проверяет корректность корпуса и драйвера; жёсткий порог скорости на shared runners не вводится. До появления повторяемых измерений сравнительных заявлений нет.

Генератор, эквивалентные валидаторы и драйвер находятся в benchmarks/json/. Корректность проверяется отдельно на O0/O2; автоматического порога скорости нет. T2-B затем сравнивается с этим неизменным baseline на той же машине и корпусе. Это измерение стоимости, не формальное доказательство корректности алгоритма.

Порядок работ: harness и согласованность трёх реализаций → первое измерение на одной машине → реализация T2-B → повторное измерение на неизменном корпусе. Многоплатформенное сравнение не блокирует T2-B. StringBuilder следует отдельно.

Первое измерение

Это первый срез методологии: результаты специализированных библиотек не включены; отрицательный корпус проверяет корректность, но ещё не измеряет задержку отказа. CLI C++/Rust использует общий C-драйвер файлового I/O, а Liza — настоящее приложение. Измерения CLI поэтому не изолируют стоимость парсера. Количество аллокаций не инструментировано; RSS относится ко всему процессу.

First JSON baseline: Apple M4

One machine, 16 GiB RAM, macOS; Apple Clang 21.0.0 and Rust 1.99.1. Pinned Liza source: b3206117a225f6f852cec5bef18928fe1bd43856. Explicit host target and generic CPU, O2; Rust overflow checks enabled. Ten samples per cell, ~256 KiB documents, 2048-byte chunks. Core samples repeat whole documents to target 4 MiB. These are warm, scalar measurements, not a cross-machine language ranking. All 6,785 partitioned cases and 615 CLI checks passed before measurement.

Median core MiB/s (higher is faster):

Workload Liza C++ by-value C++ in-place Rust
ascii 42.79 70.04 445.13 418.70
depth64 29.98 69.50 305.75 283.17
escaped 55.23 68.51 529.88 471.97
long-string 65.19 70.86 632.88 589.21
numbers 39.48 70.26 436.62 423.02
unicode 55.74 71.33 539.64 522.41

State size: Liza/C++ 184 bytes, Rust 176 bytes. State layout is measured, not assumed identical. Clang stack diagnostics for Liza report 4480 bytes for liza___liza_json_step, 608 bytes for liza_json.feed, and 224 bytes for the driver bridge stream_feed. These are individual frame sizes, not a sum or peak call-chain measurement. C++ stack records and all optimized IR/assembly are included for inspection.

The by-value C++ variant is materially slower than the in-place variant on this machine. This makes state update/copying a concrete optimization candidate. It does not by itself establish that every Liza slowdown comes from copying, or that these speeds generalize beyond the corpus. Static memcpy sites are not executed copy counts. No compiler optimization is included in this PR.

Raw timings, min/max, CLI results, commands, source/binary hashes and tool versions are in results/apple-m4/report.json and samples.json. CLI results include startup and filesystem costs: C++/Rust share a C I/O driver, while Liza runs its actual CLI/runtime. They must not be read as isolated parser measurements. Negative inputs are correctness-only in this initial baseline.

Первое отдельное исследование lowering A0–A3 сохраняет этот baseline неизменным и измеряет кандидат компилятора на том же корпусе.