335 lines
37 KiB
Markdown
335 lines
37 KiB
Markdown
# Проверки поэтапного переноса из Owniverse.U
|
||
|
||
`test_simulation_optimizations.gd` сравнивает прямые формулы дрейфа с прежней
|
||
матрицей, включая целые количества больше E27 и накопленные остатки.
|
||
Тестовый ledger сверяет выборочное округление с полным на каждом поколении
|
||
нагруженной симуляции. `test_ui_updates.gd` проверяет обновление по публикации,
|
||
выбору объекта и изменению параметров мира, а также пропуск повторных расчётов
|
||
для неизменившегося состояния.
|
||
|
||
Динамический размер партий проверяется в `test_batch_boundaries.gd`: скорости
|
||
1–6, сохранение больших целых количеств и дробных остатков, повторное
|
||
укрупнение и снижение скорости. Высокие скорости допускают приближение
|
||
времени переходов; сравнение со старым состоянием целиком не является
|
||
проверкой эквивалентности такого режима.
|
||
|
||
`test_coarse_generations.gd` проверяет отсутствие повторного перехода C0.0,
|
||
C0.1 и C0.2 внутри одного интервала, старое смещение сетки партий,
|
||
продолжение эволюции в следующем интервале, большие целые количества и
|
||
дробные остатки. `test_frame_evolution.gd` сравнивает целый и разбитый по
|
||
кадрам расчёт, включая возобновление сохранённого незавершённого шага.
|
||
|
||
## Производительность на сохранении
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/benchmark_saved_state.gd -- /tmp/save.adventure.json
|
||
```
|
||
|
||
Читает диагностический JSON формата 4 в отдельные экземпляры мира и замеряет
|
||
шесть последовательных шагов на скоростях 1, 4, 6. Запись сохранений не вызывается.
|
||
Последним аргументом можно задать длительность шага в реальных секундах
|
||
(по умолчанию 1/60). Это время расчёта симуляции, без отрисовки интерфейса.
|
||
|
||
На диагностической копии с 120 партиями прежняя рациональная арифметика после
|
||
оптимизации пустых окон давала 90–99 мс на шаг 1/60 секунды на скорости 6.
|
||
После перехода на целые блоки и устранения повторного построения физических
|
||
количеств повторный замер дал 53–58 мс. При шаге 0,1 секунды: 65–67 мс вместо
|
||
117–138 мс. Это замеры расчёта на данной
|
||
копии, а не обещание FPS. Для сравнения старый JSON был преобразован только
|
||
во временный тестовый файл; в загрузчик игры миграция не добавлена.
|
||
Разреженные сценарии сравниваются с полным расчётом в `test_short_lived_events.gd`.
|
||
|
||
## Целые блоки и дробные остатки
|
||
|
||
`godot --headless --path Owniverse.GI --script res://tests/test_exact_math.gd`
|
||
|
||
Независимый эталон Python Fraction: 160 пар с произведением внутри E27; целые
|
||
проверяются точно, дроби — с допуском 10^-12. Также границы int64/E27, сериализация,
|
||
рецепты с дробными стоимостями, сохранение сумм, время и 100 циклов с меняющимися
|
||
долями. Все остальные тесты используют числовое сравнение и сериализацию через
|
||
`E`, а не сравнение ссылок на большие числа. Границы точности и новый формат
|
||
сохранения описаны в [документации арифметики](../world/owniverse/math/README.md).
|
||
|
||
## Модули симуляции
|
||
|
||
`godot --headless --path Owniverse.GI --script res://tests/test_module_lifecycle.gd`
|
||
|
||
Проверяются отдельные экземпляры модулей и состояния для каждой вселенной, слабая обратная ссылка, освобождение модулей вместе с миром и защита от некорректного delta. Остальные тесты используют прямые API batches, ledger, production, automatic, stellar, life, environment и evolution; временные методы-переадресации Owniverse удалены. Описание границ модулей и порядка фаз: `../world/owniverse/README.md`.
|
||
|
||
## Математика распределений и каталог
|
||
|
||
`godot --headless --path Owniverse.GI --script res://tests/test_quantity_catalog.gd`
|
||
|
||
Проверяются целочисленность и границы распределения, сохранение суммы, разрешение равных остатков, накопление дробей и долгов округления, нормализация весов, размер производственной партии, загрузка каталога и его индексы. Архитектура описана в `../world/owniverse/README.md`. Сохранение игрока не используется.
|
||
|
||
## Иконки стадий жизни
|
||
|
||
Тест также проверяет модуль `item_presentation.gd`: отсутствие изменений входного состояния и списка закрытых иконок, решения по разблокировке, отложенную и принудительную публикацию количеств.
|
||
|
||
`godot --headless --path Owniverse.GI --script res://tests/test_life_icons.gd`
|
||
|
||
Проверяются одновременная разблокировка подробных и общих иконок EON/LP и C0.0/C0, количества свободных и связанных планет без двойного учёта, переключение подпанелей и очистка счётчика при исчезновении стадии. Сохранение игрока не используется.
|
||
|
||
## Параметры мира и индикаторы
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_world_indicators.gd
|
||
```
|
||
|
||
Проверяются сроки жизни существующих и новых звёзд при смене горючести, вероятности и мощность взрывов, влияние агрессии/порядка/просвещения на жизнь, накопление тенденций по времени и этикам, несколько событий за шаг, продолжение после сериализации, валидация сохранения, границы шкал и описания на двух языках. Сцена использует фикстуру без доступа к сохранению игрока.
|
||
|
||
## Прямой расчёт завершившихся сверхновых
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_short_lived_events.gd
|
||
```
|
||
|
||
Сравниваются ускоренный путь и прежний путь с созданием всех партий, отключаемый только в тестовой фикстуре. Проверяются LRG, HRG, RSG и RHG на границах коротких и длинных интервалов, существующие SN, нехватка энергии, ресурсы, бласты, привязки, остатки округления и живые партии. Дополнительно проверяется, что длинный интервал создаёт не более двух живых окон и одну запись завершений, сохраняя общее количество. Сохранение игрока этот тест не использует.
|
||
|
||
`test_stellar_rules.gd` также проверяет автоматическое создание HBD: долю 0,1% нового автоматического водорода, появление по одному со случайным округлением, сохранение оплаченного остатка топлива, отсутствие повторного начисления на старый запас, продолжение случайной последовательности после сериализации и переход HBD → CBD с сохранением счётчика BD. Формулы описаны в `../simulation_rules.md`.
|
||
|
||
## Фоновое сохранение и частота обновления интерфейса
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_async_save.gd
|
||
godot --headless --path Owniverse.GI --script res://tests/test_ui_updates.gd
|
||
```
|
||
|
||
Сохранение: основной поток создаёт глубокий снимок состояния и параметров. Рабочий поток проверяет данные, записывает зашифрованный временный файл, проверяет чтением и заменяет основное сохранение; диагностический JSON создаётся из того же снимка. Одновременно работает один писатель. Для каждого имени файла хранится только последний ожидающий запрос. `request_save()` подтверждает постановку в очередь; результат записи сообщает сигнал `save_completed`. При закрытии игра ждёт `saves_drained`, при приостановке приложения и уничтожении менеджера очередь завершается через `flush_saves()`. Копирование снимка остаётся в основном потоке.
|
||
|
||
Тест сохранений пишет только в уникальную временную папку `/tmp`, затем удаляет собственные файлы. Проверяются независимость снимка, работа кадров при записи, объединение запросов, диагностическая копия, сохранность предыдущего файла при ошибках и завершение очереди при уничтожении менеджера. Сообщения `Invalid save`, `Save blocked` и `Cannot write` в этом тесте ожидаемы.
|
||
|
||
Тест интерфейса проверяет передачу количеств из presentation в контролы, очистку очереди после публикации, отложенное и принудительное обновление, производство по нажатию и формат времени. Stage в текущей реализации применяет очередь счётчиков и время каждый кадр; интервал 0,1 секунды используется для индикаторов. Рефакторинг этот порядок не меняет. Тест использует игровую сцену с отключёнными загрузкой, сохранением и автоматической симуляцией, не обращаясь к сохранению игрока.
|
||
|
||
## Время: шаг 0,1 игрового года
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_time.gd
|
||
```
|
||
|
||
Команда выполняется из общей папки проектов. Тесты используют искусственные состояния; сохранение игрока не читается и не перезаписывается.
|
||
|
||
Проверяются границы десятых долей года, производство ресурсов на этих границах, накопление дробного остатка, скорости 1–10 000 лет/сек, продолжение после загрузки и недопустимые интервалы.
|
||
|
||
Схема состояния `0.3.0` хранит:
|
||
|
||
- `time.datetime` — накопленный возраст, включая необработанную дробную часть;
|
||
- `time.year` — последний рассчитанный возраст с точностью до десятой доли года.
|
||
|
||
Сохранения используют формат 4 и актуальную структуру состояния. Проверяются целостность данных, фоновая запись и повторная загрузка; поддержка и миграция прежних форматов отсутствуют.
|
||
|
||
Файл с несовместимым номером формата при загрузке переносится в `*.adventure.incompatible.bak` (при совпадении имени добавляется числовой суффикс). Игра начинает новую сессию и может сохранять её в формате 4. Прогресс несовместимого файла не переносится, исходные байты сохраняются в архиве. Ошибка архивирования, расшифровки или повреждение данных текущего формата по-прежнему блокируют перезапись.
|
||
|
||
## Согласованные правила эволюции
|
||
|
||
Сохраняются постепенные интервальные окна GI: срок жизни делится на 40 возрастных групп; начало преобразования — через `lifespan`, длительность окна — `lifespan / 40` при `lifespan > 10`, иначе `lifespan`.
|
||
|
||
На скоростях 4–6 узкие звёздные партии могут объединяться по границе полного
|
||
цикла смерти шириной 200/2000/20000 игровых лет. Прежнее окно шире цикла
|
||
сохраняется: долгоживущие звёзды не дробятся. Из существующих партий сливаются
|
||
только две или более одного типа, чей оставшийся переход полностью попадает
|
||
в общий цикл; широкие и пересекающие границу партии сохраняются.
|
||
`test_batch_boundaries` проверяет объединение, точные большие количества и
|
||
остатки, однократное изъятие, сериализацию, сохранение широких окон,
|
||
распределённые рождения и интеграл производства.
|
||
|
||
`benchmark_large_star_cohorts.gd` создаёт отдельные большие диагностические
|
||
сохранения со старой и новой звёздной сеткой при одинаковых количествах и
|
||
входных интервалах рождения. Переключатель `batches.cycle_stars` используется
|
||
только для сравнительного замера, в игре он включён. Парные прогоны меняют
|
||
порядок моделей и сбрасывают исходное состояние перед каждым запуском:
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/benchmark_large_star_cohorts.gd -- cycles=50 repeats=2
|
||
```
|
||
|
||
Сохранения и полные профили записываются в `tests/artifacts/large_star_cohorts_*`;
|
||
сохранение игрока не затрагивается. `generate_only` проверяет запись/чтение и
|
||
исходное равенство физических количеств без запуска замеров.
|
||
|
||
`benchmark_many_objects.gd` создаёт новую диагностическую вселенную со всеми
|
||
типами объектов и цивилизаций и 4800 синтетическими звёздными партиями,
|
||
попадающими группами в общие циклы. Это отдельный стресс-сценарий большого
|
||
числа партий; старый и новый расчёты используют одинаковый исходный состав.
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/benchmark_many_objects.gd -- cycles=40 repeats=2
|
||
```
|
||
|
||
Файлы сценария: `tests/artifacts/many_objects_fragmented_stars_*`.
|
||
|
||
Вероятностные исходы используют накопление дробного остатка GI, без случайного округления. Этот же подход выбран для будущего создания планет. Шансы планет записаны долями (`0.01` = 1%), а альтернативные исходы звёзд — процентами (`%0.1` = 0.1%). Парсер планет сохраняет дроби и поддерживает один элемент без `&`.
|
||
|
||
Уязвимость к бластам читается из `blastability` таблицы GI: HP, EON и цивилизации до C1.2 включительно; C1.3 и выше неуязвимы. Это ограничение относится к бластам уровня 1; локальное уничтожение планет уровня 2 в Unity охватывает все планеты.
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_stellar_rules.gd
|
||
```
|
||
|
||
Проверяются парсер планет, граница уязвимости, накопление редких исходов, сериализация остатков и постепенное преобразование реальной партии RD в TWD.
|
||
|
||
Создание планет подключено к ручному и автоматическому производству звёзд. Бласты обрабатываются как события, а не ресурс. Выжившие планеты переходят преемнику звезды пропорционально числу реально полученных преемников; при отсутствии преемника освобождаются. Локальное разрушение выполняется перед передачей.
|
||
|
||
Дополнительно проверяются частичная передача, альтернативные преемники, уровни разрушения 1/2, накопление слабых бластов, сохранения, ручное/автоматическое создание планет и несколько поколений за длинный шаг. Правила и формулы описаны в `../simulation_rules.md`.
|
||
|
||
## Описания выбранных объектов
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_descriptions.gd
|
||
```
|
||
|
||
Источник текста — `Global.Templates[Global.Locale]`. Заголовок берётся из `Name`, основной текст — из `Description`. Поддерживаются ключи с классом (`RD.Entity.Description`) и старые ключи без класса (`RD.Description`).
|
||
|
||
`%valueN%` заменяется значением переменной, имя которой записано в поле `valueN` этой строки. Поддерживаются `amount`, `colored costs`, `lifespan`, `voids`, `he / h`, `metallicity`, показатели энергии, плотности и скорости времени. `amount1`–`amount4` сил пока отображаются как `—`; неизвестные непустые обозначения тоже дают `—`, пустые поля — пустую строку. `[p]` превращается в разделение абзацев, цветовые BBCode-теги сохраняются.
|
||
|
||
Доступные требования зелёные, недоступные красные. Для объектов со связанными экземплярами выводится доступное количество и серым общее; для планет — общее количество. Скорость изменения вычисляется по реальному времени и обновляется раз в 0,25 секунды. Доли He/H вычисляются из текущих свободных запасов; при пустой смеси показывается `—`. Металличность и плотность используют расчёт среды GI. Счётчики исходной материи и этики Unity в текущей GI отсутствуют и здесь не добавляются.
|
||
|
||
## Первичная жизнь
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_life.gd
|
||
```
|
||
|
||
Проверяются абиогенез, деградация, повторные попытки без создания дополнительных планет, появление C0.0 на прежней звезде, остатки вероятностей, сохранение/продолжение, однократная инициализация старых планет и отсутствие воскрешения после бластов.
|
||
|
||
Проверка загрузки этических правил, распределений и отделения космических цивилизаций:
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_civilization_state.gd
|
||
```
|
||
|
||
Тест цивилизаций дополнительно проверяет общий звёздный бюджет, повторное использование резерва при переходе, звёзды в структурах, массовый эквивалент SMBH, блокировку неподдержанного прогресса, каскад деградации и повторное применение ограничений без потерь.
|
||
|
||
|
||
## Полная эволюция цивилизаций
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/test_civilization_evolution.gd
|
||
```
|
||
|
||
Проверяются реальные коэффициенты C0.2, войны и побочный ущерб C2.0, замена планет колониями с сохранением хозяина, дипломатическое влияние, бонус периода от структур и деградация при их отсутствии. Ускоренная модель проходит все стадии C0.2 → C4.4 → Ct. Проверки контролируют сохранение населения, целочисленность этик, валидацию сохранения и детерминированное продолжение после сериализации.
|
||
|
||
## Профиль цивилизаций на шестой скорости
|
||
|
||
```sh
|
||
godot --headless --path Owniverse.GI --script res://tests/benchmark_civilizations.gd -- frames=20 profile
|
||
godot --path Owniverse.GI --script res://tests/benchmark_civilizations.gd -- ui scenario=mixed frames=10 profile
|
||
```
|
||
|
||
Сценарии `mixed`, `large`, `fragmented` заполняют все стадии и этики, синхронизируя
|
||
переходы; `large` использует по 6e20 цивилизаций, `fragmented` — десять партий.
|
||
Это искусственные стрессовые сценарии. `scenario=stable` использует штатные
|
||
сроки стадий. Параметр `snapshot=/tmp/civil` сохраняет состояние каждого сценария
|
||
для сравнения до и после изменения. UI запускает настоящую сцену со страницей
|
||
жизни и реальным delta, без загрузки и записи сохранения игрока.
|
||
|
||
`profile` выводит суммарные микросекунды и число вызовов по этапам. Вложенные
|
||
этапы входят во время родительских; складывать их нельзя. В игре сбор выключен.
|
||
Кэши физических количеств принадлежат отдельному миру и проверяют изменения
|
||
количеств, вложенных привязок и индексов каталога. Кэш структуры правил не входит
|
||
в сохранение: при изменении Claim/бонусов нужно перестроить индексы каталога,
|
||
при изменении home в существующей таблице — invalidate_civilization_layout().
|
||
|
||
После оптимизации на M4 Pro, по 20 шагов 1/60 секунды, среднее время:
|
||
`mixed` 295 → 235 мс, `large` 666 → 539 мс, `fragmented` 359 → 282 мс.
|
||
Полные состояния всех трёх сценариев после 20 шагов совпали до и после изменения.
|
||
Дополнительно тест распределений сравнивает новый путь с прежним на 300
|
||
случайных наборах, включая 1e40 и накопленные долги округления.
|
||
|
||
## Расчёт между кадрами
|
||
|
||
`godot --headless --path Owniverse.GI --script res://tests/test_frame_evolution.gd`
|
||
|
||
Stage вызывает `evolute_frame`: транзакция получает бюджет 40 мс и уступает
|
||
следующему кадру внутри создания партий и обработки стадий. Расчёт остаётся
|
||
на главном потоке, состояние публикуется только после полного завершения.
|
||
Расчёт запускается после накопления 0,2 реальной секунды и обрабатывает весь ожидающий интервал. Время, поступившее во время расчёта, обрабатывается следующим циклом. При перегрузке шаг растёт; пять завершений в секунду остаются целевой частотой, а не гарантией.
|
||
|
||
Производство во время транзакции выполняется после её завершения. Запрошенное
|
||
время, активный интервал, скорость и очередь производства входят в сохранение;
|
||
при загрузке незавершённый интервал пересчитывается с опубликованного состояния.
|
||
Сброс мира отменяет отложенный расчёт. Тест проверяет точное совпадение непрерывного
|
||
и распределённого расчёта, очереди, переключение скорости, восстановление и отмену.
|
||
|
||
На M4 Pro, 180 кадров реального UI на шестой скорости: смешанный сценарий
|
||
15.9 FPS (максимальный кадр 98 мс), большие количества 17.5 FPS (106 мс),
|
||
много партий 16.3 FPS (97 мс).
|
||
Это FPS отрисовки, а не число завершённых транзакций симуляции в секунду.
|
||
|
||
Индекс партий переиспользуется внутри одной транзакции, отдельно для каждого
|
||
массива партий. Замена массива при изъятии или переход к другому состоянию
|
||
сбрасывает индекс. Сортировка выполняется при нарушении порядка новых окон,
|
||
а не после каждого добавления в уже существующее окно. `test_batch_boundaries`
|
||
проверяет объединение, раннюю вставку, замену массива и изоляцию состояний.
|
||
На копии текущего сохранения с 785 партиями шесть шагов на скорости 6:
|
||
среднее время 167 → 145 мс; полное состояние после шести шагов совпало точно.
|
||
|
||
Следующий этап исключает арифметическую работу при прибавлении нуля,
|
||
временные знаменатели при умножении целых блоков и нулевые вклады этик.
|
||
На той же копии сохранения среднее время шага скорости 6 снизилось с 145
|
||
до 138 мс; полный результат после шести шагов совпал с исходным состоянием.
|
||
Тест появления разума задаёт свой progress явно, поскольку проверяет
|
||
сохранение хозяина планеты, а не актуальное значение коэффициента баланса.
|
||
|
||
Следующие шесть оптимизаций: однократное изъятие умирающих партий;
|
||
поверхностные снимки неизменяемых таблиц связей; пропуск планирования
|
||
цивилизаций без переходов; кэш нормализованных этик и произведений поклонения;
|
||
пропуск укрупнения для нуля/одной партии; кэш вероятностей переходов.
|
||
Кэши принадлежат миру и не входят в сохранение. Изменение количества,
|
||
этик, коэффициентов правил или модификаторов обнаруживается по значениям.
|
||
Суммирование поклонения сохраняет прежний порядок дробных операций.
|
||
`test_simulation_optimizations` проверяет также обновление этих кэшей.
|
||
|
||
Полное состояние совпало с реализацией до этих шести изменений после каждого
|
||
из восьми шагов на скоростях 1, 4 и 6, отдельно на большом фрагментированном
|
||
сценарии и на диагностической копии сохранения (48 сравнений).
|
||
На M4 Pro шесть шагов по 0.1 секунды скорости 6: 103.3 мс первый,
|
||
70.9–76.0 мс последующие; пропускная способность без UI 1.278.
|
||
Замер 600 кадров с реальным UI: 12.24 FPS, p95 97.16 мс, максимум 104.86 мс,
|
||
520 завершённых транзакций, отношение обработанного времени к поступившему
|
||
1.057. Замер включает сокращение накопленного отставания сохранения;
|
||
это результат конкретного сценария на Mac, а не гарантия для Android.
|
||
|
||
Целевая частота запуска — пять циклов в секунду. Размер шага равен всему накопленному интервалу. Checkpoint сохраняет границу активного расчёта и ожидающее реальное время. Тест проверяет все шесть скоростей, смену скорости, задержку запуска и накопление времени во время расчёта.
|
||
|
||
Дипломатический drift использует шесть взвешенных вкладов вместо матрицы
|
||
пар этик: вес = сумма вкладов + 195 × свой вклад − противоположный вклад.
|
||
Для колониальной матрицы коэффициенты 9, 951 и 5 соответственно.
|
||
Целая численность и механизм сохранения ошибок распределения не меняются,
|
||
но порядок операций над дробными весами отличается от матричного варианта.
|
||
Тест сравнивает с матрицей на точно представимых коэффициентах и проверяет
|
||
целочисленное сохранение популяций до 1e40 на десятичных коэффициентах.
|
||
Пустые популяции пропускают drift; численность выходов суммируется один раз.
|
||
На профильной копии при шаге 0.5 число вызовов снизилось 1038 → 849
|
||
за шесть шагов, но время drift осталось около 303 мс суммарно;
|
||
заметного ускорения полного расчёта в этом сценарии не зафиксировано.
|
||
|
||
`test_split_optimizations.gd` сравнивает распределение и все сохранённые
|
||
остатки с прежним scalar split в последовательных вызовах: пустые ветви,
|
||
единственная ветвь, равные веса, десятичные веса, положительные/отрицательные
|
||
остатки, количества до 1e80. Также проверяет grouped mul_div против scalar.
|
||
Split переиспользует разложение количества и знаменателя между ветвями,
|
||
кэширует ключи остатков (не более 512 наборов) и получает веса дрифта массивом.
|
||
Вторая нормализация пропускается только при целых долях с нужной суммой:
|
||
при дробях даже единичное отношение может изменить округление, поэтому
|
||
прежний путь сохраняется. Единственная положительная ветвь обрабатывается
|
||
напрямую только при целом количестве, неотрицательных весах и нулевых остатках.
|
||
На профильной копии время drift за шесть шагов 0.5 снизилось примерно
|
||
303 → 287 мс, но общее время осталось около 1.31 секунды.
|
||
|
||
Обычный drift сокращает целое количество из отношения
|
||
count*weight/(200*count), колониальный — из отношения с 1000.
|
||
Целая часть делится на малый знаменатель; для дроби используются исходные
|
||
масштабированные числитель и знаменатель, чтобы сохранить float округление.
|
||
`test_split_optimizations` проверяет совпадение со scalar mul_div до 1e80.
|
||
Производство новорождённых сохраняет только created_from → amount вместо
|
||
глубокой копии партий. Повторное мгновенное рождение с теми же сроками,
|
||
длительностью и сеткой сразу добавляется в найденную партию.
|
||
Индекс ближайшей границы позволяет повторным проходам пропускать будущие
|
||
партии; замена массива, добавление и масштабирование сбрасывают индекс.
|
||
Тест границ проверяет объединение и сброс обоих индексов.
|
||
Адаптивный шаг дополнительно учитывает сглаженное отношение реальной
|
||
длительности транзакции к обработанному времени. Если отношение выше 0.85
|
||
и долг больше одного шага, ступень увеличивается раньше порога двух шагов.
|
||
Долг и граница сохраняются как прежде; измерения являются временным кэшем.
|