extends SceneTree ## Paired, read-only throughput benchmark for adaptive 5/4/3/2 Hz cadence. const E = preload("res://world/owniverse/math/exact.gd") const SAVE_PATHS: Array[String] = [ "res://tests/artifacts/all_objects_speed6.adventure.json", "res://tests/artifacts/many_objects_fragmented_stars_cycle.adventure.json" ] const RESULT_PATH := "res://tests/artifacts/dynamic_cadence_results.json" var cycles: int = 40 var repeats: int = 2 func _initialize() -> void: 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 create_world(initial: Dictionary, use_adaptive_cadence: bool): var world = load("res://world/owniverse/owniverse.gd").new() world.catalog.load_definitions(root.get_node("Global").OwniverseEntities) world.state = initial.duplicate(true) world.speed = 6 world.adaptive_cadence = use_adaptive_cadence world.frame_step_seconds = 0.2 world.cadence_level = 0 return world func dispose(world) -> void: for entity in world.catalog.entities.values(): entity.free() world.free() func measure(initial: Dictionary, adaptive: bool, save_name: String, repeat_index: int) -> Dictionary: var world = create_world(initial, adaptive) var elapsed_usec: int = 0 var requested_seconds: float = 0.0 var step_counts: Dictionary = {} var samples: Array[float] = [] var initial_clock: Variant = world.state.time.datetime for i in cycles: var step: float = world.frame_step_seconds step_counts[str(step)] = int(step_counts.get(str(step), 0)) + 1 requested_seconds += step var started: int = Time.get_ticks_usec() world.evolute(step) var usec: int = Time.get_ticks_usec() - started elapsed_usec += usec samples.append(float(usec) / 1000.0) var applied_years: Variant = E.subtract(world.state.time.datetime, initial_clock) var expected_years: float = requested_seconds * 100000.0 assert(absf(E.approx(applied_years) - expected_years) <= maxf(1.0, expected_years * 1e-10), "Clock advance differs from accumulated cadence interval") var validation: String = root.get_node("SaveManager").validate_data({ "format_version": 6, "app_version": "0.7.0", "ui": {"current_page": 0}, "state": world.state }) assert(validation.is_empty(), validation) samples.sort() var result: Dictionary = { "save": save_name, "mode": "adaptive_5_4_3_2" if adaptive else "fixed_5", "repeat": repeat_index, "cycles": cycles, "mean_ms_per_cycle": float(elapsed_usec) / (cycles * 1000.0), "p95_ms": samples[int(ceil(samples.size() * 0.95)) - 1], "max_ms": samples.back(), "cpu_cycles_per_second": float(cycles) * 1000000.0 / elapsed_usec, "simulated_seconds_per_cpu_second": requested_seconds * 1000000.0 / elapsed_usec, "requested_simulation_seconds": requested_seconds, "applied_game_years": E.fixed(applied_years), "cadence_steps": step_counts, "final_cadence_hz": 1.0 / world.frame_step_seconds, "final_adaptive_quality": world.adaptive_quality } print("CADENCE_RESULT ", JSON.stringify(result)) dispose(world) return result func run() -> void: 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 inputs: Dictionary = {} for path in SAVE_PATHS: var parsed = JSON.parse_string(FileAccess.get_file_as_string(path)) assert(parsed is Dictionary and parsed.get("format_version") == 6, "Invalid benchmark save: " + path) var payload: Dictionary = E.decode(restore_json_integers(parsed)) var validation: String = root.get_node("SaveManager").validate_data(payload) assert(validation.is_empty(), validation) inputs[path.get_file()] = payload.state var results: Array[Dictionary] = [] for save_name in inputs: var initial: Dictionary = inputs[save_name] # Alternate order to limit machine warm-up and thermal bias. for repeat_index in repeats: if repeat_index % 2 == 0: results.append(measure(initial, false, save_name, repeat_index)) results.append(measure(initial, true, save_name, repeat_index)) else: results.append(measure(initial, true, save_name, repeat_index)) results.append(measure(initial, false, save_name, repeat_index)) var output := FileAccess.open(RESULT_PATH, FileAccess.WRITE) assert(output != null) output.store_string(JSON.stringify({ "machine": OS.get_model_name(), "godot": Engine.get_version_info(), "speed": 6, "cycles_per_run": cycles, "repeats": repeats, "input_saves_unchanged": true, "results": results }, "\t", true, true)) output.close() print("Wrote paired cadence results: ", RESULT_PATH) quit()