Implement civilization flow mechanics and profiling for resource allocation
- Added `civilization_flow.gd` to manage continuous expected flows and resource allocation within civilizations. - Introduced `allocate_profiled` and `whole_quotas` functions in `quantity_math.gd` for precise stock allocation based on float shares. - Enhanced `credit` logic in `object_ledger.gd` to handle production remainders more effectively. - Updated `append_batch` in `object_batches.gd` to prevent batch creation for invalid IDs. - Introduced `ratio_approx` function in `exact.gd` for better handling of float profiles. - Added a benchmark script `benchmark_civilization_flows.gd` to evaluate the performance of civilization flow mechanics. - Refactored various functions to improve clarity and efficiency in handling resource flows and allocations.
This commit is contained in:
@@ -92,3 +92,290 @@ godot --path Owniverse.GI --script res://tests/benchmark_full_ui.gd
|
||||
Финальная APK повторно проверена на Samsung SM-N950F, Android 9, тем же полным сценарием и 120 кадрами. Два последовательных прогона дали **14,57 и 15,02 FPS**, средний кадр — **68,61 и 66,58 мс**, p95 — **113,03 и 123,03 мс**, по одной завершённой транзакции из 119 интервалов. Опубликовано 10 000 лет из 647 331 и 634 134 запрошенных; пропускная способность — 0,01545 и 0,01577. В последнем профиле `generations` заняли 5,120 с, `civilization_prepare` — 3,162 с, войны — 1,026 с, переходы — 0,728 с, дрейф этик — 0,474 с. Самый длинный непрерывный участок составил **458,14 мс** в `deep_copy_and_coarsen`, максимальный кадр — 976 мс. Предыдущий одиночный запуск старой benchmark-сборки показал 1,83 FPS, поэтому сравнивать его с этой парой напрямую нельзя; оба финальных прогона выполнены на одной APK и подтверждают результат около 15 FPS.
|
||||
|
||||
На компьютере прошли тесты состояния (70), эволюции (555), оптимизаций симуляции (364), индикаторов (63) и жизни (25). Полный набор всё ещё сообщает о двух отказах в `test_descriptions.gd` при обновлении выбранного описания и смене языка; оба относятся к UI-описаниям, а не к модели этик. В логах Android ошибок GDScript/Godot за прогон не найдено.
|
||||
|
||||
|
||||
## Непрерывная модель цивилизаций — 2026-10-06
|
||||
|
||||
Сравнены исходная симуляция из коммита `6ee66a6` и новая модель потоков. Оба запуска используют один диагностический JSON, скорость 6, 100 последовательных полных циклов с запросом 0,1 секунды каждый и включённый CPU-профиль. Подготовка каталога и чтение JSON не включены в длительность цикла. Запуски выполнены последовательно, без параллельных тестов. Сохранение игрока не читалось и не изменялось.
|
||||
|
||||
| Полный цикл и пропускная способность | До | После |
|
||||
|---|---:|---:|
|
||||
| Среднее время полного цикла | 811,292 мс | 401,796 мс |
|
||||
| Минимум / максимум | 488,914 / 963,141 мс | 323,427 / 435,607 мс |
|
||||
| Завершённых полных циклов в секунду | 1,233 | 2,489 |
|
||||
| Обработанных секунд симуляции на секунду CPU | 0,123 | 0,249 |
|
||||
| Применено игровых лет за 100 циклов | 1 000 000 | 1 000 000 |
|
||||
|
||||
Полный цикл ускорился в **2,02 раза**, среднее время сократилось на **50,5%**. Цель **100 мс / 10 полных циклов в секунду не достигнута**: для неё требуется ещё примерно 4,02-кратное ускорение текущей версии. При непрерывном запросе времени на шестой скорости и шаге 0,1 секунды этот стресс-сценарий продолжит накапливать долг. Это измерение полных завершённых шагов симуляции, без показателя FPS.
|
||||
|
||||
Текущие внешние интервалы CPU-профиля, по убыванию среднего времени на цикл:
|
||||
|
||||
| Расчёт | Среднее за цикл |
|
||||
|---|---:|
|
||||
| Поколения звёзд и биологической жизни | 151,6 мс |
|
||||
| Подготовка непрерывных потоков цивилизаций | 98,5 мс |
|
||||
| Применение потоков к количествам и владельцам | 53,2 мс |
|
||||
| Производство за период | 28,7 мс |
|
||||
| Тренды | 18,1 мс |
|
||||
| Дрейф, проверка вместимости и поклонение после потоков | 8,8 мс |
|
||||
| Инициализация жизни | 6,1 мс |
|
||||
| Ограничение потоков ресурсными бюджетами | 5,8 мс |
|
||||
| Применение колонизации | 5,5 мс |
|
||||
|
||||
Внутри подготовки потоков разреженный оператор занимает 24,5 мс на цикл; этот вложенный интервал нельзя прибавлять к 98,5 мс. Внешние интервалы не покрывают всё время: остальная работа включает копирование транзакции, построение физических снимков и обновление представления.
|
||||
|
||||
Модель C0.2–C4.4 теперь использует точные целые количества и шесть долей float без возрастных партий. Оператор учитывает несколько последовательных переходов за интервал. Конечные количества и ресурсные потолки сохраняются точно; пропорции потоков и владельцев приближены float и квантованием долей до 15 десятичных знаков. Изменяется распределение событий во времени, поэтому тесты проверяют законы новой модели, сохранение количества, владение, ресурсные ограничения и воспроизводимое продолжение сохранения.
|
||||
|
||||
Регрессия: 27 из 28 наборов прошли. Новые переходы — 1895 проверок; арифметика распределения и кеши — 1889; кадровое выполнение — 24. В `test_descriptions.gd` сохраняются две ранее обнаруженные ошибки обновления выбранного описания и языка; этот UI-код не изменён в данном рефакторинге.
|
||||
|
||||
Повторение из папки `Owniverse.GI`:
|
||||
|
||||
```sh
|
||||
godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -- tests/artifacts/all_objects_speed6.adventure.json 0.1 profile cycles=100
|
||||
```
|
||||
|
||||
## Фиксированные пять циклов в секунду — 2026-10-07
|
||||
|
||||
Текущая версия после отката агрегации и возврата 40 возрастных групп.
|
||||
Планировщик использует шаг 0,2 реальной секунды на всех скоростях. Замер:
|
||||
диагностическое сохранение со всеми объектами и цивилизациями, скорость 6,
|
||||
100 полных синхронных циклов с CPU-профилем, без отрисовки интерфейса.
|
||||
Проверка сохранения: OK. Исходное сохранение не изменялось.
|
||||
|
||||
| Метрика | Результат |
|
||||
|---|---:|
|
||||
| Среднее время полного цикла | 439,228 мс |
|
||||
| Минимум / максимум | 340,370 / 483,152 мс |
|
||||
| Завершённых полных циклов в секунду | 2,277 |
|
||||
| Требуется циклов в секунду | 5 |
|
||||
| Обработано игровых лет | 2 000 000 |
|
||||
| Запрошенное время сценария | 20 с |
|
||||
| Время расчёта 100 циклов | 43,923 с |
|
||||
| Пропускная способность | 0,455 с симуляции / с расчёта |
|
||||
|
||||
Цель 200 мс на полный цикл не достигнута: по среднему времени требуется
|
||||
ускорение примерно в 2,20 раза. Увеличение шага с 0,1 до 0,2 секунды улучшило
|
||||
отношение обработанного времени к затратам, но при непрерывном поступлении
|
||||
времени этот стресс-сценарий продолжит накапливать долг. Это измерение
|
||||
пропускной способности симуляции, без оценки FPS. Один прогон не задаёт
|
||||
гарантированную производительность для других сохранений или устройств.
|
||||
|
||||
Внешние интервалы CPU-профиля, по убыванию среднего времени на цикл:
|
||||
|
||||
| Расчёт | Среднее на цикл |
|
||||
|---|---:|
|
||||
| Поколения звёзд и биологической жизни | 168,294 мс |
|
||||
| Подготовка непрерывных потоков цивилизаций | 111,835 мс |
|
||||
| Применение потоков к количествам и владельцам | 59,516 мс |
|
||||
| Производство за период | 29,248 мс |
|
||||
| Тренды | 17,787 мс |
|
||||
| Завершение цивилизационных потоков | 8,864 мс |
|
||||
| Ограничение потоков вместимостью | 6,439 мс |
|
||||
| Инициализация жизни | 6,172 мс |
|
||||
| Колонизация | 6,111 мс |
|
||||
|
||||
Вложенные интервалы нельзя прибавлять к внешним: построение физических
|
||||
снимков — 27,845 мс/цикл; изъятие партий — 42,015 мс/цикл; решение оператора
|
||||
цивилизаций — 31,835 мс/цикл. Проверки кадрового планировщика:
|
||||
35 проверок, 0 ошибок, включая все шесть скоростей, сохранение долга,
|
||||
возобновление расчёта и пять завершённых циклов на одну секунду запроса.
|
||||
|
||||
```sh
|
||||
godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -- tests/artifacts/all_objects_speed6.adventure.json 0.2 profile cycles=100
|
||||
```
|
||||
|
||||
## Новые звёздные партии с целым переходом за цикл — 2026-10-07
|
||||
|
||||
На скоростях 4–6 новые звёзды одного типа объединяются по общей границе
|
||||
завершения жизни и переходят целиком. Временная сетка равна одному полному
|
||||
циклу (200/2000/20000 игровых лет). Старые постепенные партии из сохранения
|
||||
не преобразуются; SN/GRB сохраняют прежнюю обработку.
|
||||
|
||||
Повторён тот же замер: диагностическое сохранение, скорость 6, шаг 0,2 с,
|
||||
100 полных циклов с CPU-профилем. Проверка сохранения OK.
|
||||
|
||||
| Метрика | До | После |
|
||||
|---|---:|---:|
|
||||
| Среднее время полного цикла | 439,228 мс | 445,690 мс |
|
||||
| Минимум / максимум | 340,370 / 483,152 мс | 345,276 / 486,647 мс |
|
||||
| Полных циклов в секунду | 2,277 | 2,244 |
|
||||
| Пропускная способность | 0,455 | 0,449 |
|
||||
| Обработано игровых лет | 2 000 000 | 2 000 000 |
|
||||
| Поколения, среднее на цикл | 168,294 мс | 171,039 мс |
|
||||
| Обработка партий новорождённых, среднее на цикл | 7,768 мс | 6,199 мс |
|
||||
| Вызовы обработки партий новорождённых | 16 147 | 13 885 |
|
||||
|
||||
Общего ускорения на этой выборке нет: среднее выросло примерно на 1,5%.
|
||||
Обработка новорождённых занимает на 20% меньше времени, но это небольшая
|
||||
доля полного цикла. Замер включает существующие постепенные партии;
|
||||
он не доказывает ускорения вселенной, целиком созданной по новой схеме.
|
||||
Цель пяти полных циклов в секунду остаётся недостигнутой.
|
||||
|
||||
Проверки: границы партий — 109, звёздные правила — 333, короткие события —
|
||||
841, укрупнённые поколения — 28, кадровое выполнение — 35; все без ошибок.
|
||||
Тестируются точные большие количества, целый однократный переход,
|
||||
распределённые рождения, сохранения и интеграл производства.
|
||||
|
||||
## Первоначальный замер больших сохранений до исправления сетки — 2026-10-07
|
||||
|
||||
Созданы `large_star_cohorts_legacy.adventure.json` и
|
||||
`large_star_cohorts_cycle.adventure.json`: по 6×10^60 звёзд каждого из 15
|
||||
типов (главная последовательность и красные гиганты), остальные объекты,
|
||||
жизнь и цивилизации взяты из диагностического сценария. Возраст — 10^12 лет.
|
||||
Для каждого типа заново сформирована одинаковая история рождения длиной
|
||||
`min(эффективный срок жизни, 2 млн лет)` перед началом завершения жизни.
|
||||
Исходные физические количества и этические профили совпадают строго.
|
||||
Сохранения валидируются и проходят точное сравнение состояния после записи
|
||||
и чтения JSON с полной точностью float. Сохранение игрока не изменялось.
|
||||
|
||||
Старая схема измеряется переключателем `batches.cycle_stars = false`, новая
|
||||
включена по умолчанию. В обоих вариантах одинаковый остальной код симуляции,
|
||||
скорость 6, шаг 0,2 с и CPU-профиль. Два прогона по 50 полных циклов на модель,
|
||||
порядок запусков: старая → новая → новая → старая. Перед каждым прогоном
|
||||
восстанавливается исходное состояние; каждый обрабатывает 1 млн игровых лет.
|
||||
|
||||
| Метрика | Старая схема | Новая схема |
|
||||
|---|---:|---:|
|
||||
| Среднее первого прогона | 417,265 мс | 423,419 мс |
|
||||
| Среднее второго прогона | 431,081 мс | 427,011 мс |
|
||||
| Среднее всех 100 циклов на модель | 424,173 мс | 425,215 мс |
|
||||
| Полных циклов в секунду | 2,358 | 2,352 |
|
||||
| Пропускная способность | 0,472 | 0,470 |
|
||||
| Звёздных партий в исходном сохранении | 118 | 1 300 |
|
||||
| Пиковое число звёздных партий | 132 | 1 300 |
|
||||
| Поколения, среднее на цикл | 157,711 мс | 147,906 мс |
|
||||
| Производство, среднее на цикл | 28,692 мс | 34,704 мс |
|
||||
| Применение потоков цивилизаций, среднее на цикл | 58,642 мс | 62,160 мс |
|
||||
| Изъятие партий, вложенное в поколения | 37,015 мс | 41,379 мс |
|
||||
| Партии новорождённых, вложенное в поколения | 7,648 мс | 4,248 мс |
|
||||
|
||||
**Устойчивого выигрыша полного цикла не обнаружено.** Разница средних —
|
||||
около +0,25% у новой схемы; в первом парном сравнении она медленнее, во втором
|
||||
быстрее. Поколения занимают примерно на 6,2% меньше времени, но производство
|
||||
стало дороже примерно на 21%. Пять полных циклов в секунду не достигнуты.
|
||||
|
||||
Причина роста числа партий: для долгоживущих звёзд сетка одного цикла
|
||||
значительно мельче `lifespan / 40`. Например, у RD прежняя ширина — 1 млрд
|
||||
лет, новая на скорости 6 — 20 тыс. лет. Поэтому изменение не всегда
|
||||
укрупняет партии: на таком наборе исходных рождений их стало в 11 раз больше.
|
||||
Итоговое временное распределение переходов у моделей различается; совпадение
|
||||
их конечных состояний не является условием этого замера.
|
||||
|
||||
Для дальнейшего упрощения следует сохранять широкие группы долгоживущих
|
||||
звёзд и применять целый переход только к партиям, уже укладывающимся в один
|
||||
цикл. Это предложение в данном замере не реализовано.
|
||||
|
||||
```sh
|
||||
godot --headless --path . --script res://tests/benchmark_large_star_cohorts.gd -- cycles=50 repeats=2
|
||||
```
|
||||
|
||||
Полные результаты первоначального замера: `large_star_cohorts_before_dynamic_fix_results.json`.
|
||||
Набор с 1300 партиями сохранён в `large_star_cohorts_before_dynamic_fix_cycle.adventure.json`.
|
||||
Последующие файлы без этого суффикса относятся к исправленной динамической схеме.
|
||||
|
||||
## Исправленное динамическое укрупнение без дробления — 2026-10-07
|
||||
|
||||
Ошибка первоначальной реализации устранена: циклическая сетка больше не
|
||||
заменяет широкие возрастные окна долгоживущих звёзд. При создании новая сетка
|
||||
используется только если прежнее окно не шире цикла. Иначе используется
|
||||
прежнее широкое окно. Из существующих партий объединяются две или более
|
||||
одного типа, чей оставшийся интервал завершения целиком лежит в общем цикле.
|
||||
Партии, пересекающие границу циклов или занимающие несколько циклов, не делятся.
|
||||
|
||||
Повторён парный замер на отдельных больших сохранениях, с теми же исходными
|
||||
количествами и входными интервалами рождения: скорость 6, шаг 0,2 с,
|
||||
два прогона по 50 циклов на модель в порядке старая → новая → новая → старая.
|
||||
Каждый прогон обрабатывает 1 млн игровых лет; состояния валидируются до и
|
||||
после, записанные файлы проходят точное сравнение после чтения.
|
||||
|
||||
| Метрика | Старая схема | Исправленная динамическая |
|
||||
|---|---:|---:|
|
||||
| Исходных звёздных партий | 118 | 113 |
|
||||
| Пиковое число партий | 132 | 127 |
|
||||
| Число партий после 50 циклов | 130 | 125 |
|
||||
| Среднее первого прогона | 420,388 мс | 424,307 мс |
|
||||
| Среднее второго прогона | 431,605 мс | 428,498 мс |
|
||||
| Среднее всех 100 циклов на модель | 425,996 мс | 426,402 мс |
|
||||
| Полных циклов в секунду | 2,347 | 2,345 |
|
||||
| Поколения, среднее на цикл | 158,622 мс | 157,606 мс |
|
||||
| Партии новорождённых, вложенный интервал | 7,679 мс | 6,693 мс |
|
||||
|
||||
На этом наборе партиям стало меньше примерно на 4,2%, вместо прежнего
|
||||
ошибочного роста до 1300. Проверено, что долгая группа RD остаётся одной
|
||||
широкой партией и её окно не меняется при добавлении новых звёзд.
|
||||
Новый тест запрещает рост числа исходных партий в диагностическом сценарии.
|
||||
|
||||
Устойчивого ускорения полного цикла по этим прогонам нет: разница средних
|
||||
около +0,10%; знак разницы в отдельных парных сравнениях меняется.
|
||||
Обработка партий новорождённых занимает примерно на 13% меньше времени,
|
||||
но её доля недостаточна для заметного выигрыша всего расчёта.
|
||||
Цель пяти циклов в секунду по-прежнему не достигнута.
|
||||
|
||||
Проверки: границы партий — 117, звёздные правила — 333, короткие события —
|
||||
841, кадровое выполнение — 35; все без ошибок. Дополнительные проверки
|
||||
охватывают сохранение широких окон, объединение старых партий с точными
|
||||
количествами и остатками, соседние циклы и повторное укрупнение.
|
||||
|
||||
Актуальные сохранения: `large_star_cohorts_legacy.adventure.json` и
|
||||
`large_star_cohorts_cycle.adventure.json`; результаты: `large_star_cohorts_results.json`.
|
||||
|
||||
## Новая вселенная с множеством объектов и мелких звёздных партий — 2026-10-07
|
||||
|
||||
Создано отдельное состояние с нуля по текущим таблицам: 150 типов объектов,
|
||||
все 44 стадии этических цивилизаций (включая Ct), 15 типов живых звёзд.
|
||||
Запасы большинства объектов — 10^60 каждого типа; планет и цивилизаций —
|
||||
10^28. Жизнь размещена у четырёх типов карликов, четверть звёзд каждого типа
|
||||
связана со структурами. Этические профили различаются между стадиями.
|
||||
Высокий запас пространства сохраняет жизнь при бластах. Интерфейс не отрисовывался.
|
||||
|
||||
Для проверки укрупнения заданы валидные синтетические исторические партии:
|
||||
40 последовательных циклов завершения × 8 узких партий × 15 типов звёзд.
|
||||
Все восемь партий типа целиком завершаются в общем цикле; это целевой
|
||||
стресс-сценарий большого числа партий, а не утверждение, что обычное создание
|
||||
с неизменным временем жизни обязательно порождает такую фрагментацию.
|
||||
Исходный состав и интервалы одинаковы для обеих моделей. Долгоживущие звёзды
|
||||
не дробились алгоритмом: ему переданы уже существующие узкие партии.
|
||||
|
||||
До укрупнения — 4800 звёздных партий. Старая схема сохраняет 4560,
|
||||
исправленная динамическая — 600. Созданы два отдельных диагностических файла,
|
||||
проверены запись/чтение без изменения состояния и валидация до и после каждого
|
||||
прогона. Сохранение игрока не затрагивалось.
|
||||
|
||||
Два прогона по 40 полных циклов на модель; порядок старая → новая → новая →
|
||||
старая. Скорость 6, фиксированный шаг 0,2 с, CPU-профиль включён.
|
||||
Каждый прогон восстанавливает исходное состояние и обрабатывает 800 тыс. лет.
|
||||
|
||||
| Метрика | Старая схема | Динамическое укрупнение |
|
||||
|---|---:|---:|
|
||||
| Исходных звёздных партий после укрупнения | 4 560 | 600 |
|
||||
| Пиковое число партий | 4 560 | 607 |
|
||||
| Среднее первого прогона | 612,606 мс | 530,285 мс |
|
||||
| Среднее второго прогона | 624,625 мс | 532,578 мс |
|
||||
| Среднее всех 80 циклов на модель | 618,616 мс | 531,431 мс |
|
||||
| Полных циклов в секунду | 1,617 | 1,882 |
|
||||
| Обработанных секунд симуляции / секунду расчёта | 0,323 | 0,376 |
|
||||
| Поколения, среднее на цикл | 211,916 мс | 138,339 мс |
|
||||
| Производство, среднее на цикл | 51,201 мс | 38,038 мс |
|
||||
| Подготовка потоков цивилизаций | 113,371 мс | 113,270 мс |
|
||||
| Применение потоков цивилизаций | 114,343 мс | 113,820 мс |
|
||||
| Колонизация | 55,284 мс | 55,291 мс |
|
||||
| Обработка партий новорождённых, вложенная в поколения | 29,464 мс | 2,967 мс |
|
||||
|
||||
**Выигрыш полного цикла подтверждён обоими парными сравнениями:** время
|
||||
сократилось на 14,1%, ускорение около 1,16 раза. Поколения занимают на 34,7%
|
||||
меньше времени. Механизм даёт эффект при множестве подходящих для объединения
|
||||
партий; в предыдущем сценарии с небольшим числом широких партий эффект был
|
||||
незаметен. Изменение моментов переходов допускается моделью: конечные состояния
|
||||
двух схем не обязаны совпадать, но обе полностью обработали запрошенный интервал.
|
||||
|
||||
Пять циклов в секунду не достигнуты: для данного сценария требуется ещё
|
||||
примерно 2,66-кратное ускорение. Главные оставшиеся затраты — поколения и
|
||||
подготовка/применение цивилизационных потоков; колонизация также заметна.
|
||||
|
||||
Сохранения: `many_objects_fragmented_stars_legacy.adventure.json` и
|
||||
`many_objects_fragmented_stars_cycle.adventure.json`.
|
||||
Полные результаты: `many_objects_fragmented_stars_results.json`.
|
||||
|
||||
```sh
|
||||
godot --headless --path . --script res://tests/benchmark_many_objects.gd -- cycles=40 repeats=2
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user