Skip to content

perf(canonical-json): быстрый путь для чисел, уже точно представимых на шкале (E1) - #8

Merged
alexnikolskiy merged 1 commit into
mainfrom
perf/e1-quantize-fast-path
Jul 28, 2026
Merged

perf(canonical-json): быстрый путь для чисел, уже точно представимых на шкале (E1)#8
alexnikolskiy merged 1 commit into
mainfrom
perf/e1-quantize-fast-path

Conversation

@alexnikolskiy

Copy link
Copy Markdown
Contributor

Шаг E1 волны E (analysis/21–22; ~16 % профиля). Значений не двигает.

Идея

decimal.js строит значение из String(n) — из кратчайшего представления, которое round-trip'ится обратно в то же число. Значит, когда это представление и так короче восьми знаков после запятой и записано без экспоненты, весь круг «строка → DecimaltoDecimalPlacestoFixed → строка» возвращает ровно исходную строку: округлять нечего, переводить в фиксированную нотацию нечего. Такие числа — большинство артефакта: индексы баров, метки времени, размеры, цены с биржевым тиком.

Условие быстрого пути намеренно консервативно:

  • есть e ⇒ медленный путь (1e-7 обязано стать 0.0000001, 1e21 — развернуться);
  • дробная часть длиннее шкалы ⇒ медленный путь (есть что округлять по HALF_EVEN);
  • -0 отдельным случаем не нужен: String(-0) в JS и так "0".

Почему это не сдвиг значений, а не «должно быть не сдвигом»

Тест держит рядом дословную копию прежней реализации как эталон и сверяет обе ветки:

  • на кромках: границы шкалы (0.123456785, 1.000000005), экспоненты с обеих сторон (1e-71e-9, 1e201.5e22), MAX_SAFE_INTEGER, MIN_VALUE, EPSILON, -0;
  • на 20 000 случайных величин двенадцати порядков (детерминированный генератор — выборка воспроизводима при падении).

Гейты

  • pnpm typecheck0
  • pnpm test116 passed / 0 failed
  • pnpm gate:determinism → clean; pnpm gate:tapes → 3 tape(s) OK
  • Парный замер сериализатора (taskset -c 2,3, минимум из 5, 40 000 строк артефакта): 4463 → 1868 мс, ×2.39. Окно было шумным (параллельно шёл тестовый прогон backtester'а), поэтому абсолюты завышены — отношение при таком разрыве устойчиво, но переснимается в тихом окне перед приёмкой волны.

…на шкале (E1)

`decimal.js` строит значение из `String(n)` — из кратчайшего представления,
которое round-trip'ится обратно в то же число. Значит, когда это
представление и так короче восьми знаков после запятой и записано без
экспоненты, весь круг «строка → Decimal → toDecimalPlaces → toFixed →
строка» возвращает РОВНО исходную строку: округлять нечего, переводить в
фиксированную нотацию нечего. Такие числа — большинство артефакта: индексы
баров, метки времени, размеры, цены с биржевым тиком.

Условие намеренно консервативно: есть `e` — медленный путь (1e-7 обязано
стать 0.0000001); дробная часть длиннее шкалы — медленный путь (есть что
округлять по HALF_EVEN). `-0` отдельным случаем не нужен: `String(-0)` в JS
и так `"0"`.

Значений не двигает, и это проверено, а не заявлено: тест держит рядом
дословную копию прежней реализации как эталон и сверяет обе ветки на
кромках (границы шкалы, экспоненты с обеих сторон, MAX_SAFE_INTEGER,
MIN_VALUE, EPSILON) и на 20 000 случайных величин двенадцати порядков.

Гейты: pnpm typecheck → 0; pnpm test → 116 passed / 0 failed;
gate:determinism и gate:tapes чистые. Парный замер сериализатора на
40 000 строк артефакта: 4463 → 1868 мс (×2.39).
@alexnikolskiy
alexnikolskiy merged commit eb57ff4 into main Jul 28, 2026
4 checks passed
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.

1 participant