Enhance civilization flow mechanics and adaptive quality control

- Implemented runtime-only adaptive quality controller to optimize simulation performance based on CPU time.
- Added adaptive quality thresholds and recovery cycles to dynamically adjust simulation fidelity.
- Introduced single-step solving method for civilization transitions to improve performance under high loads.
- Updated benchmark scripts to include speed parameter for testing various simulation speeds.
- Added tests for adaptive quality adjustments and their effects on civilization flow calculations.
- Documented results of optimization attempts and their impact on performance metrics.
This commit is contained in:
Kirill
2026-10-08 22:05:08 +03:00
parent 459fbb2a4d
commit 02c9843e1b
11 changed files with 891 additions and 14 deletions
@@ -0,0 +1,49 @@
{
"date": "2026-10-08",
"machine": "Apple M4 Pro",
"godot": "4.7.2.stable.official.ed1daf0bf",
"renderer": "headless",
"delta_seconds": 0.2,
"profile_enabled": true,
"ui": false,
"adaptation": {
"controller": "runtime-only exponential moving average of full-cycle CPU time; 0.8 previous + 0.2 latest",
"quality_thresholds_ms": {
"quality_1": 200,
"quality_2": 220,
"quality_3": 300,
"quality_2_to_3_when_over_ms": 200,
"recovery_below_ms": 140,
"recovery_cycles": 30
},
"quality_effects": {
"quality_1": "one-transition civilization approximation, at most 3 stellar/life generations per cycle, temporal grouping x2",
"quality_2": "same civilization approximation, at most 1 generation per cycle, temporal grouping x4",
"quality_3": "same civilization approximation, at most 1 generation per cycle, temporal grouping x8, alternate halves of batch types each cycle"
}
},
"inputs": {
"all_objects_speed6.adventure.json": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
"many_objects_fragmented_stars_cycle.adventure.json": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
},
"runs": [
{"speed": 1, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 165.03665, "min_ms": 151.514, "max_ms": 170.171, "cycles_per_second": 6.05926, "game_years": 12, "validation": "OK"},
{"speed": 1, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 128.463817, "min_ms": 124.475, "max_ms": 144.05, "cycles_per_second": 7.784293, "game_years": 12, "validation": "OK"},
{"speed": 2, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 181.431217, "min_ms": 171.92, "max_ms": 196.005, "cycles_per_second": 5.511731, "game_years": 120, "validation": "OK"},
{"speed": 2, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 131.78055, "min_ms": 126.963, "max_ms": 148.01, "cycles_per_second": 7.588373, "game_years": 120, "validation": "OK"},
{"speed": 3, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 175.235683, "min_ms": 166.031, "max_ms": 203.456, "cycles_per_second": 5.7066, "game_years": 1200, "validation": "OK"},
{"speed": 3, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 145.013367, "min_ms": 135.062, "max_ms": 162.768, "cycles_per_second": 6.895916, "game_years": 1200, "validation": "OK"},
{"speed": 4, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 159.705883, "min_ms": 150.404, "max_ms": 221.101, "cycles_per_second": 6.26151, "game_years": 12000, "validation": "OK"},
{"speed": 4, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 177.064483, "min_ms": 170.624, "max_ms": 189.729, "cycles_per_second": 5.64766, "game_years": 12000, "validation": "OK"},
{"speed": 5, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 174.857517, "min_ms": 158.809, "max_ms": 270.078, "cycles_per_second": 5.718942, "game_years": 120000, "validation": "OK"},
{"speed": 5, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 185.601233, "min_ms": 131.409, "max_ms": 273.8, "cycles_per_second": 5.387895, "game_years": 120000, "validation": "OK"},
{"speed": 6, "save": "all_objects_speed6.adventure.json", "cycles": 60, "mean_ms": 190.570967, "min_ms": 159.38, "max_ms": 345.149, "cycles_per_second": 5.247389, "game_years": 1200000, "validation": "OK"},
{"speed": 6, "save": "many_objects_fragmented_stars_cycle.adventure.json", "cycles": 60, "mean_ms": 184.426, "min_ms": 149.645, "max_ms": 366.767, "cycles_per_second": 5.422229, "game_years": 1200000, "validation": "OK"}
],
"notes": [
"Each run begins in exact mode and adapts during its 60-cycle series.",
"The mean meets 5 complete cycles/s at all six speeds in these synthetic diagnostic saves on this computer; maxima show occasional cycles still exceed 200 ms at speeds 3-6.",
"No Android device or UI performance was measured.",
"Diagnostic save inputs were not modified."
]
}
@@ -458,3 +458,41 @@ godot --headless --path . --script res://tests/benchmark_civilization_flows.gd -
Полные состояния до и после совпали после пяти последовательных интервалов на каждом сохранении (включая дробные интервалы). Тест оптимизаций прошёл: 1889 проверок без ошибок. Цель пяти полных циклов в секунду ещё не достигнута: тяжёлому циклу требуется ускорение примерно в 2,05 раза, чтобы уложиться в 200 мс.
Полные профили и SHA-256 входов: `civilization_flow_prepare_step3_results.json`.
## Проверка оптимизации частичного изъятия партий — 2026-10-08
Перенос связанного содержимого уже агрегируется по типам внутри поколения. Проверен вариант частичного изъятия, исключающий промежуточное сложение квоты с дробным переносом и последующее вычитание целой части. Возрастные интервалы и порядок поколений сохранялись.
Парный замер на тех же сохранениях: скорость 6, интервал 0,2 секунды, CPU-профиль, без UI; до → после → после → до, по 20 циклов на прогон (40 циклов на версию и сохранение).
| Сохранение | До, мс/цикл | После, мс/цикл |
|---|---:|---:|
| Все объекты | 350,516 | 349,835 |
| Много объектов и партий | 404,255 | 404,390 |
Изъятие партий на тяжёлом сохранении: 40,919 → 40,837 мс/цикл. Изменения полного цикла меньше 0,2%; подтверждённого выигрыша нет. Вариант **откачен**, код симуляции оставлен прежним. Полные состояния совпали в 10 последовательных сравнениях; границы партий — 117 проверок без ошибок. Входные файлы не изменены.
Результаты отклонённого варианта: `batch_withdrawal_step4_results.json`. Этот замер не доказывает достижение пяти полных циклов в секунду.
## Адаптивное упрощение симуляции — 2026-10-08
Добавлен runtime-контроллер качества, который измеряет сглаженное CPU-время полного цикла. Он повышает уровень упрощения при превышении бюджета 200 мс и снимает его после 30 циклов с запасом. Режим не сохраняется в файл: после загрузки точность постепенно подстраивается заново.
При нагрузке цивилизационный поток заменяется приближением не более чем с одним переходом на стадию за интервал. На наиболее грубом уровне типы возрастных партий делятся на две чередующиеся группы: каждая обрабатывается раз в два цикла. Временные группы при этом расширяются. Это сохраняет количества, но сдвигает сроки отдельных переходов и исключает несколько последовательных переходов цивилизации за один интервал.
Проверены два синтетических сохранения на Apple M4 Pro, Godot 4.7.2, headless, с CPU-профилем и интервалом 0,2 секунды. Каждый замер — 60 последовательных полных циклов; каждый запуск начинается в точном режиме и адаптируется по ходу серии.
| Скорость | Все объекты, мс/цикл (циклов/с) | Много объектов и партий, мс/цикл (циклов/с) |
|---:|---:|---:|
| 1 | 165,0 (6,06) | 128,5 (7,78) |
| 2 | 181,4 (5,51) | 131,8 (7,59) |
| 3 | 175,2 (5,71) | 145,0 (6,90) |
| 4 | 159,7 (6,26) | 177,1 (5,65) |
| 5 | 174,9 (5,72) | 185,6 (5,39) |
| 6 | 190,6 (5,25) | 184,4 (5,42) |
Среднее время на всех прогонах ниже 200 мс. Отдельные циклы всё ещё бывают длиннее: максимумы составили 203–367 мс на скоростях 3–6, включая первые циклы до срабатывания адаптации. Значит, замеры подтверждают требуемую среднюю пропускную способность на этом компьютере, но не гарантируют отсутствие кратковременных задержек. Android и работа с UI отдельно не проверялись.
Проверки: адаптивное качество — 18, хронология поколений — 28, границы партий — 117, оптимизации симуляции — 1889, планировщик кадров — 39; ошибок нет. Все 12 конечных диагностических состояний прошли валидацию. Хеши входных файлов: `44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67` и `1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a`.
Детали уровней, минимумы/максимумы и воспроизводимые результаты: `adaptive_quality_results.json`. Штатный benchmark принимает параметр `speed=1..6`.
@@ -0,0 +1,646 @@
{
"retained": false,
"conclusion": "No measurable full-cycle improvement; implementation reverted",
"delta_seconds": 0.2,
"exact_state_differential": true,
"input_sha256": {
"all_objects_speed6": "44bee3b3753fa51615f766d8f800a10cc2b70c8e7fb4ea12b6e18da7f07a7d67",
"many_objects_fragmented_stars_cycle": "1e95ef398923f26b912a305c84f9b3eb502d24cb14e8ae687a4becccf047324a"
},
"order": "before-after-after-before",
"runs": [
{
"cycles": 20,
"max_ms": 382.054,
"mean_ms": 347.474,
"min_ms": 291.952,
"model": "before",
"profile": {
"assign_homes": [
23851,
80
],
"civilization_flow_capacity": [
121403,
20
],
"civilization_flow_colonies": [
52415,
20
],
"civilization_flow_finish": [
135271,
20
],
"civilization_flow_ledger": [
880672,
20
],
"civilization_flow_plan": [
1718363,
20
],
"civilization_operator_build": [
61955,
20
],
"civilization_operator_solve": [
673740,
860
],
"enforce_capacity": [
316094,
80
],
"generation_products": [
190666,
40
],
"generation_withdrawal": [
545822,
60
],
"generations": [
2727124,
20
],
"initialize": [
113986,
20
],
"newborn_batches": [
121039,
3016
],
"physical_amounts_build": [
285729,
200
],
"production": [
373910,
20
],
"trends": [
358046,
20
]
},
"run": 1,
"save": "all_objects_speed6"
},
{
"cycles": 20,
"max_ms": 382.156,
"mean_ms": 349.53305,
"min_ms": 297.266,
"model": "after",
"profile": {
"assign_homes": [
23877,
80
],
"civilization_flow_capacity": [
121854,
20
],
"civilization_flow_colonies": [
53021,
20
],
"civilization_flow_finish": [
137725,
20
],
"civilization_flow_ledger": [
883114,
20
],
"civilization_flow_plan": [
1733356,
20
],
"civilization_operator_build": [
62368,
20
],
"civilization_operator_solve": [
680005,
860
],
"enforce_capacity": [
319290,
80
],
"generation_products": [
192497,
40
],
"generation_withdrawal": [
548252,
60
],
"generations": [
2737148,
20
],
"initialize": [
115930,
20
],
"newborn_batches": [
121459,
3016
],
"physical_amounts_build": [
287621,
200
],
"production": [
376359,
20
],
"trends": [
359971,
20
]
},
"run": 1,
"save": "all_objects_speed6"
},
{
"cycles": 20,
"max_ms": 387.438,
"mean_ms": 350.1367,
"min_ms": 298.254,
"model": "after",
"profile": {
"assign_homes": [
24181,
80
],
"civilization_flow_capacity": [
122008,
20
],
"civilization_flow_colonies": [
53315,
20
],
"civilization_flow_finish": [
137169,
20
],
"civilization_flow_ledger": [
885851,
20
],
"civilization_flow_plan": [
1736978,
20
],
"civilization_operator_build": [
62264,
20
],
"civilization_operator_solve": [
680485,
860
],
"enforce_capacity": [
319558,
80
],
"generation_products": [
193735,
40
],
"generation_withdrawal": [
547473,
60
],
"generations": [
2743162,
20
],
"initialize": [
115720,
20
],
"newborn_batches": [
121401,
3016
],
"physical_amounts_build": [
287832,
200
],
"production": [
376295,
20
],
"trends": [
360758,
20
]
},
"run": 2,
"save": "all_objects_speed6"
},
{
"cycles": 20,
"max_ms": 391.136,
"mean_ms": 353.55755,
"min_ms": 295.752,
"model": "before",
"profile": {
"assign_homes": [
23999,
80
],
"civilization_flow_capacity": [
125136,
20
],
"civilization_flow_colonies": [
53681,
20
],
"civilization_flow_finish": [
137614,
20
],
"civilization_flow_ledger": [
893234,
20
],
"civilization_flow_plan": [
1749507,
20
],
"civilization_operator_build": [
63052,
20
],
"civilization_operator_solve": [
683642,
860
],
"enforce_capacity": [
319168,
80
],
"generation_products": [
193901,
40
],
"generation_withdrawal": [
550377,
60
],
"generations": [
2768999,
20
],
"initialize": [
115976,
20
],
"newborn_batches": [
122734,
3016
],
"physical_amounts_build": [
292064,
200
],
"production": [
382112,
20
],
"trends": [
363057,
20
]
},
"run": 2,
"save": "all_objects_speed6"
},
{
"cycles": 20,
"max_ms": 418.952,
"mean_ms": 402.88125,
"min_ms": 362.784,
"model": "before",
"profile": {
"assign_homes": [
18182,
60
],
"civilization_flow_capacity": [
138911,
20
],
"civilization_flow_colonies": [
235353,
20
],
"civilization_flow_finish": [
139454,
20
],
"civilization_flow_ledger": [
1808094,
20
],
"civilization_flow_plan": [
1823967,
20
],
"civilization_operator_build": [
63710,
20
],
"civilization_operator_solve": [
684725,
860
],
"enforce_capacity": [
241433,
60
],
"generation_products": [
79394,
20
],
"generation_withdrawal": [
814774,
40
],
"generations": [
2390414,
20
],
"initialize": [
142892,
20
],
"newborn_batches": [
55241,
1490
],
"physical_amounts_build": [
359891,
140
],
"production": [
410388,
20
],
"trends": [
362721,
20
]
},
"run": 1,
"save": "many_objects_fragmented_stars_cycle"
},
{
"cycles": 20,
"max_ms": 425.202,
"mean_ms": 404.3497,
"min_ms": 355.885,
"model": "after",
"profile": {
"assign_homes": [
18056,
60
],
"civilization_flow_capacity": [
139522,
20
],
"civilization_flow_colonies": [
236599,
20
],
"civilization_flow_finish": [
140701,
20
],
"civilization_flow_ledger": [
1809113,
20
],
"civilization_flow_plan": [
1824567,
20
],
"civilization_operator_build": [
63452,
20
],
"civilization_operator_solve": [
684152,
860
],
"enforce_capacity": [
242790,
60
],
"generation_products": [
80500,
20
],
"generation_withdrawal": [
817704,
40
],
"generations": [
2406807,
20
],
"initialize": [
143427,
20
],
"newborn_batches": [
55885,
1490
],
"physical_amounts_build": [
363175,
140
],
"production": [
412428,
20
],
"trends": [
365215,
20
]
},
"run": 1,
"save": "many_objects_fragmented_stars_cycle"
},
{
"cycles": 20,
"max_ms": 418.858,
"mean_ms": 404.4306,
"min_ms": 357.106,
"model": "after",
"profile": {
"assign_homes": [
18133,
60
],
"civilization_flow_capacity": [
139021,
20
],
"civilization_flow_colonies": [
237100,
20
],
"civilization_flow_finish": [
139480,
20
],
"civilization_flow_ledger": [
1815717,
20
],
"civilization_flow_plan": [
1825515,
20
],
"civilization_operator_build": [
63298,
20
],
"civilization_operator_solve": [
685950,
860
],
"enforce_capacity": [
242932,
60
],
"generation_products": [
79477,
20
],
"generation_withdrawal": [
815781,
40
],
"generations": [
2400821,
20
],
"initialize": [
144101,
20
],
"newborn_batches": [
56140,
1490
],
"physical_amounts_build": [
362394,
140
],
"production": [
412986,
20
],
"trends": [
364511,
20
]
},
"run": 2,
"save": "many_objects_fragmented_stars_cycle"
},
{
"cycles": 20,
"max_ms": 422.492,
"mean_ms": 405.6284,
"min_ms": 355.699,
"model": "before",
"profile": {
"assign_homes": [
18208,
60
],
"civilization_flow_capacity": [
139305,
20
],
"civilization_flow_colonies": [
236973,
20
],
"civilization_flow_finish": [
141048,
20
],
"civilization_flow_ledger": [
1818465,
20
],
"civilization_flow_plan": [
1832211,
20
],
"civilization_operator_build": [
63519,
20
],
"civilization_operator_solve": [
688184,
860
],
"enforce_capacity": [
243601,
60
],
"generation_products": [
79839,
20
],
"generation_withdrawal": [
822003,
40
],
"generations": [
2410757,
20
],
"initialize": [
144424,
20
],
"newborn_batches": [
55500,
1490
],
"physical_amounts_build": [
363454,
140
],
"production": [
412711,
20
],
"trends": [
367008,
20
]
},
"run": 2,
"save": "many_objects_fragmented_stars_cycle"
}
],
"speed": 6
}