extends SceneTree ## Paired old/new cohort timing on separate generated large diagnostic saves. const E = preload("res://world/owniverse/math/exact.gd") var cycles: int = 50 var repeats: int = 2 var artifact_prefix: String = "large_star_cohorts" func _initialize(): call_deferred("run") func restore_json_integers(value): if value is float and value == floorf(value) and absf(value) <= 9007199254740991: return int(value) if value is Dictionary: for key in value: value[key] = restore_json_integers(value[key]) elif value is Array: for i in value.size(): value[i] = restore_json_integers(value[i]) return value func dispose(world): for entity in world.catalog.entities.values(): entity.free() world.free() func world_from(state: Dictionary, use_cycles: bool): var world = load("res://world/owniverse/owniverse.gd").new() world.catalog.load_definitions(root.get_node("Global").OwniverseEntities) world.state = state.duplicate(true) world.speed = 6 world.batches.cycle_stars = use_cycles return world func cohort_count(world) -> int: var total: int = 0 for id in world.state.batches: if world.catalog.entities[id].group_as == "Star": total += world.state.batches[id].size() return total func make_state(initial: Dictionary, use_cycles: bool) -> Dictionary: var world = world_from(initial, use_cycles) var anchor: Variant = E.of("1e12") var shift: Variant = E.subtract(anchor, world.state.time.datetime) for id in world.state.batches: for batch: Dictionary in world.state.batches[id]: for field in ["created_from", "created_to", "start_year", "end_year", "processed_until"]: if batch.has(field): batch[field] = E.add(batch[field], shift) for id in world.state.batch_origins: world.state.batch_origins[id] = E.add(world.state.batch_origins[id], shift) world.state.time = {"datetime": anchor, "year": anchor} var physical: Dictionary = world.ledger.physical_amounts(world.state) var populations: Dictionary = {} for id in world.catalog.entities: var entity = world.catalog.entities[id] if entity.group_as != "Star" or E.le(entity.lifespan, 0) or E.le(physical.get(id, 0), 0): continue world.state.batches[id] = [] world.state.batch_origins.erase(id) var lifespan: Variant = world.batches.effective_lifespan(world.state, id) var birth_from: Variant = E.subtract(anchor, lifespan) var duration: Variant = E.minimum(lifespan, 2000000) world.batches.append_batch(world.state, id, physical[id], birth_from, E.add(birth_from, duration)) populations[id] = E.fixed(physical[id]) var payload: Dictionary = {"format_version": 6, "app_version": "0.7.0", "ui": {"current_page": 0}, "state": world.state, "simulation": {"active_clock": {}, "requested_clock": {}, "productions": [], "speed": 6}} var error: String = root.get_node("SaveManager").validate_data(payload) assert(error.is_empty(), error) var suffix: String = "cycle" if use_cycles else "legacy" var path: String = "res://tests/artifacts/%s_%s.adventure.json" % [artifact_prefix, suffix] var output = FileAccess.open(path, FileAccess.WRITE) assert(output != null) output.store_string(JSON.stringify(E.encode(payload), " ", true, true)) output.close() print("Generated %s: star_types=%d stellar_cohorts=%d populations=%s" % [suffix, populations.size(), cohort_count(world), str(populations)]) var restored: Dictionary = E.decode(restore_json_integers(JSON.parse_string(FileAccess.get_file_as_string(path)))) assert(E.eq(restored.state, world.state), "Diagnostic file roundtrip changed initial state") dispose(world) return restored.state func measure(initial: Dictionary, use_cycles: bool, run_index: int) -> Dictionary: var world = world_from(initial, use_cycles) world.profile_enabled = true var samples: Array = [] var total_usec: int = 0 var initial_year: Variant = world.state.time.datetime var peak_cohorts: int = cohort_count(world) for cycle in cycles: var started: int = Time.get_ticks_usec() world.evolute(0.2) var elapsed: int = Time.get_ticks_usec() - started total_usec += elapsed samples.append(elapsed / 1000.0) peak_cohorts = maxi(peak_cohorts, cohort_count(world)) var applied: Variant = E.subtract(world.state.time.datetime, initial_year) assert(E.eq(applied, cycles * 20000)) var error: String = root.get_node("SaveManager").validate_data({"format_version": 6, "app_version": "0.7.0", "ui": {"current_page": 0}, "state": world.state}) assert(error.is_empty(), error) samples.sort() var result: Dictionary = {"model": "cycle" if use_cycles else "legacy", "repeat": run_index, "cycles": cycles, "mean_ms": total_usec / (1000.0 * cycles), "min_ms": samples.front(), "max_ms": samples.back(), "p95_ms": samples[int(ceil(samples.size() * 0.95)) - 1], "cycles_per_second": cycles * 1000000.0 / total_usec, "throughput": cycles * 200000.0 / total_usec, "game_years": E.fixed(applied), "peak_stellar_cohorts": peak_cohorts, "final_stellar_cohorts": cohort_count(world), "profile": world.profile.duplicate(true)} print("PAIRED_RESULT ", JSON.stringify(result)) dispose(world) return result func run(): for arg in OS.get_cmdline_user_args(): if arg.begins_with("cycles="): cycles = maxi(1, arg.trim_prefix("cycles=").to_int()) if arg.begins_with("repeats="): repeats = maxi(1, arg.trim_prefix("repeats=").to_int()) var raw = JSON.parse_string(FileAccess.get_file_as_string("res://tests/artifacts/all_objects_speed6.adventure.json")) var payload: Dictionary = E.decode(restore_json_integers(raw)) assert(root.get_node("SaveManager").validate_data(payload).is_empty()) var legacy: Dictionary = make_state(payload.state, false) var current: Dictionary = make_state(payload.state, true) var reference = world_from(legacy, false) var candidate = world_from(current, true) assert(E.eq(reference.ledger.physical_amounts(reference.state), candidate.ledger.physical_amounts(candidate.state))) assert(E.eq(reference.state.ethics, candidate.state.ethics)) assert(cohort_count(candidate) <= cohort_count(reference), "Dynamic widening increased the fixture cohort count") dispose(reference) dispose(candidate) if OS.get_cmdline_user_args().has("generate_only"): print("Generated states validate and survive disk roundtrip; physical amounts and ethics match") quit() return var results: Array = [] for repeat_index in repeats: var order: Array = [false, true] if repeat_index % 2 == 0 else [true, false] for use_cycles in order: results.append(measure(current if use_cycles else legacy, use_cycles, repeat_index + 1)) var output = FileAccess.open("res://tests/artifacts/%s_results.json" % artifact_prefix, FileAccess.WRITE) output.store_string(JSON.stringify({"delta_seconds": 0.2, "speed": 6, "cycles_per_run": cycles, "repeats": repeats, "profile_enabled": true, "runs": results}, " ")) output.close() quit()