Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""Summarize ERR PERF windows from performance logs (also accepts older console logs).
Script wall_ms_per_s measures scoped elapsed time, not CPU utilization.
Parent/child scopes overlap; engine values are sampled gauges, not scopes.
"""
import argparse
import csv
import math
import re
import sys
from collections import defaultdict
from pathlib import Path
FIELDS = (
'run', 'window', 'elapsed_ms', 'final', 'phase_start', 'phase_end',
'metric', 'samples', 'sum', 'min', 'max', 'zero', 'over50', 'over100',
)
INTEGER_FIELDS = ('window', 'elapsed_ms', 'phase_start', 'phase_end',
'samples', 'zero', 'over50', 'over100')
SCRIPT_METRICS = {
'mission_tick_ms', 'maintenance_ms', 'civilians_ms', 'forces_ms',
'observers_ms', 'hq_tick_ms', 'listeners_ms', 'hq_prep_ms',
'hq_hunters_ms', 'hq_garrisons_ms', 'hq_convoys_ms', 'hq_bodies_ms',
'hq_radio_index_ms', 'hq_reports_ms', 'hq_planner_ms', 'hq_support_ms',
'hq_journal_ms', 'garrison_work_ms', 'performance_flush_ms',
}
def read_rows(paths):
seen = {}
for path in paths:
with path.open(encoding='utf-8', errors='replace') as stream:
for number, line in enumerate(stream, 1):
marker = line.find('ERR PERF run=')
if marker < 0:
continue
values = dict(re.findall(r'(\w+)=([^\s]+)', line[marker:]))
try:
row = {key: values[key] for key in FIELDS}
for key in INTEGER_FIELDS:
row[key] = int(row[key])
if row[key] < 0:
raise ValueError(f'negative {key}')
for key in ('sum', 'min', 'max'):
row[key] = float(row[key])
if not math.isfinite(row[key]) or row[key] < 0:
raise ValueError(f'invalid {key}')
if row['elapsed_ms'] == 0 or row['samples'] == 0:
raise ValueError('empty window/sample count')
if row['min'] > row['max']:
raise ValueError('min exceeds max')
if not 0 <= row['over100'] <= row['over50'] <= row['samples']:
raise ValueError('invalid threshold counts')
if row['zero'] > row['samples']:
raise ValueError('invalid zero count')
except (KeyError, ValueError) as error:
raise ValueError(f'{path}:{number}: malformed performance row: {error}') from error
key = row['run'], row['window'], row['metric']
if key in seen:
if seen[key] != row:
raise ValueError(f'{path}:{number}: conflicting duplicate {key}')
continue
seen[key] = row
return list(seen.values())
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('logs', nargs='+', type=Path)
parser.add_argument('--csv', action='store_true', help='output individual window rows as CSV')
args = parser.parse_args()
try:
rows = read_rows(args.logs)
if not rows:
raise ValueError('no ERR PERF measurement rows found')
except (OSError, ValueError) as error:
parser.error(str(error))
if args.csv:
writer = csv.DictWriter(sys.stdout, fieldnames=FIELDS)
writer.writeheader()
writer.writerows(rows)
return
grouped = defaultdict(list)
for row in rows:
grouped[row['run'], row['metric']].append(row)
print('Scope times overlap: do not add mission_tick, hq_tick and HQ children.')
print('Wall timings have 1 ms resolution; zero does not prove zero cost.')
print('Engine metrics are ~1 Hz samples of engine-reported values, NOT AI-only time.')
previous_run = None
for (run, metric), items in sorted(grouped.items()):
if run != previous_run:
print(f'\nRun {run}')
print(f'{"metric":26s} {"samples":>8s} {"mean":>10s} {"max":>10s} {"zero":>8s} {"wall_ms/s":>10s}')
previous_run = run
count = sum(item['samples'] for item in items)
total = sum(item['sum'] for item in items)
elapsed = sum(item['elapsed_ms'] for item in items)
maximum = max(item['max'] for item in items)
zero = sum(item['zero'] for item in items)
rate = f'{1000 * total / elapsed:.3f}' if metric in SCRIPT_METRICS else '-'
print(f'{metric:26s} {count:8d} {total / count:10.3f} {maximum:10.3f} {zero:8d} {rate:>10s}')
print('\nMeans/rates include only windows containing that metric; use --csv to compare matching windows and player counts.')
print('No native-AI timing or predicted offload speedup is inferred.')
if __name__ == '__main__':
main()