Files
TheTree_mock/tests/performance/test_digest_performance.gd
T
Takeshi 5dde0c5ec2 Record original-alignment implementation and traceability checkpoint
Capture current source, content data, tests, asset licenses, and original-specification traceability. Original implementation remains incomplete. The accepted matching UI generation passed 154 suites and 12,253 checks, with eight rendered staff-contract screens reviewed. Latest native Windows and full-original acceptance remain outstanding.
2026-09-30 23:52:24 +09:00

255 lines
12 KiB
GDScript

extends SceneTree
const Digest = preload("res://scripts/core/simulation_digest.gd")
const Reference = preload("res://tests/performance/fixtures/simulation_digest_v1_reference.gd")
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("Simulation digest: v1 byte compatibility and growing-history benchmark")
_test_text_chunk_boundaries()
_test_scalar_cache_boundaries()
_test_value_types()
_test_dictionary_layouts()
_test_mutable_values()
_test_limits_and_rejections()
_test_growing_histories()
print("Digest performance: %d checks, %d failures" % [_checks, _failures])
quit(_failures)
func _test_value_types() -> void:
var values: Array = [
null, false, true, -9223372036854775807, 9223372036854775807,
0, 0.0, -0.0, -0.125, 1.25e100, "", "厨房\u0000🌳", &"", &"運搬",
Vector2(-1.5, 2.25), Vector2i(-3, 7), Rect2(-1.5, 2.25, 3.5, 4.75),
Rect2i(-3, 7, 2, 8), Vector3(-1.5, 2.25, 3.5), Vector3i(-3, 7, 2),
Vector4(-1.5, 2.25, 3.5, 4.75), Vector4i(-3, 7, 2, 8), Color(0.25, 0.5, 0.75, 1),
[], {}, PackedByteArray([0, 127, 255]), PackedInt32Array([-3, 0, 7]),
PackedInt64Array([-9223372036854775807, 9223372036854775807]),
PackedFloat32Array([-1.25, 0.0, 2.5]), PackedFloat64Array([-1.25, -0.0, 1.25e100]),
PackedStringArray(["", "日本語", "🌳"]), PackedVector2Array([Vector2(-1, 2)]),
PackedVector3Array([Vector3(-1, 2, 3)]), PackedColorArray([Color(0.25, 0.5, 0.75, 1)]),
]
var strings: Array[String] = ["", "a", "厨房"]
var names: Array[StringName] = [&"", &"a", &"厨房"]
values.append(strings)
values.append(names)
for index in values.size():
_expect_bytes(values[index], "canonical bytes for value type %d, sample %d" % [typeof(values[index]), index])
_expect_bytes({"values": values}, "nested complete supported value matrix")
_expect(Digest.compute([1, 2, 3]) == "4e32bb2921e1b879d5b8778789d3bc7e46559d4448e95f9c2fed427d7e5c7b93", "published v1 reference digest remains unchanged")
func _test_dictionary_layouts() -> void:
var rows: Array = []
# Native Variant equality can equate String and StringName; cache buckets must
# keep their type tags apart even when the textual schemas are identical.
for index in 60:
var row := {}
var keys: Array = ["z", "aa", "日本語", "a", "\u0000", "", "001"]
if index % 2 == 1:
keys.reverse()
for key: String in keys:
row[StringName(key) if index % 3 == 0 else key] = {"index": index, "key": key}
rows.append(row)
_expect_bytes(rows, "cached String and StringName schemas preserve tags and byte order")
var mixed := {}
mixed["text"] = "String"
mixed[&"name"] = "StringName"
mixed[7] = "integer"
mixed[1.25] = "float"
mixed[Vector2i(2, 3)] = "vector"
mixed[["a", 1]] = "array key"
mixed[{"inner": "key"}] = "dictionary key"
_expect_bytes([mixed, mixed.duplicate(true)], "mixed and mutable dictionary keys use the uncached encoder")
var forward := {"a": 1, "b": 2, "ccc": 3}
var reverse := {"ccc": 3, "b": 2, "a": 1}
_expect(Digest.canonical_bytes(forward) == Digest.canonical_bytes(reverse), "canonical order ignores dictionary insertion order")
# Exceed each bound to check graceful uncached fallback, without relying on a
# process-global cache or on the hash of an incomplete key signature.
var many_schemas: Array = []
for index in 600:
many_schemas.append({"schema_%d" % index: index, "nested": {"value": index}})
_expect_bytes(many_schemas, "more than the bounded layout capacity remains byte-identical")
# These two complete key arrays collide under Godot 4.7's native hash. The
# cache must still compare complete contents, never index by hash alone.
var collision_left := {"schema.96ac180f": 1, "value": 2}
var collision_right := {"schema.c5015a12": 3, "value": 4}
if hash(collision_left.keys()) == hash(collision_right.keys()):
print(" Native layout hash-collision fixture exercised")
_expect_bytes([collision_left, collision_right, collision_left], "different key layouts with colliding native hashes remain distinct")
var many_keys := {}
for index in 80:
many_keys["key_%d" % index] = index
_expect_bytes(many_keys, "large dictionaries remain byte-identical")
_expect_bytes({"長".repeat(200): 1}, "long keys bypass the cache")
func _test_mutable_values() -> void:
var nested := {"history": [{"minute": 1, "amount": 3}], "queue": [1, 2]}
var before := Digest.compute(nested)
nested["history"][0]["amount"] = 4
nested["queue"].append(3)
_expect_bytes(nested, "nested value mutations cannot reuse stale encoded values")
_expect(Digest.compute(nested) != before, "nested mutation changes the hash")
nested.erase("queue")
nested[&"queue"] = [1, 2]
_expect_bytes(nested, "key type mutation between digest calls is observed")
var left := {"key": {"value": 1}}
var right := {"key": {"value": 2}}
_expect_bytes([left, right], "reused schema reads each dictionary's current values")
var canonical := Digest.canonical_bytes(nested)
canonical.fill(0)
_expect_bytes(nested, "caller mutation of previous output cannot corrupt another call")
func _test_limits_and_rejections() -> void:
var typed_leaf: Array[String] = ["leaf"]
_expect_bytes(_nested(typed_leaf, 127), "typed string item at exact nesting limit is valid")
_expect_bytes(_nested({"leaf": 1}, 127), "cached dictionary key at exact nesting limit is valid")
_expect_bytes(_nested([], 128), "empty container at nesting limit is valid")
var rejected: Array = [_nested(typed_leaf, 128), _nested({"leaf": 1}, 128), _nested(null, 129), NAN, INF, -INF, Vector2(NAN, 1), PackedFloat64Array([INF]), {"nested": NAN}, NodePath("runtime/object")]
var object := RefCounted.new()
rejected.append(object)
var errors_enabled := Engine.print_error_messages
Engine.print_error_messages = false
for index in rejected.size():
var legacy: PackedByteArray = Reference.canonical_bytes(rejected[index])
var current: PackedByteArray = Digest.canonical_bytes(rejected[index])
_expect(legacy.is_empty() and current.is_empty(), "unsupported or over-depth sample %d still rejects" % index)
Engine.print_error_messages = errors_enabled
func _test_growing_histories() -> void:
var reports: Array[Dictionary] = []
for history_count in [100, 1000, 5000]:
var snapshot := _history_snapshot(history_count)
_expect_bytes(snapshot, "growing-history snapshot with %d records" % history_count)
_expect(Digest.compute(snapshot) == Reference.compute(snapshot), "growing-history SHA-256 with %d records" % history_count)
# Warm both encoders, then alternate execution order. Timing is reported,
# never asserted: CPU load and editor/preview activity vary across machines.
Reference.compute(snapshot)
Digest.compute(snapshot)
var reference_samples: Array[float] = []
var optimized_samples: Array[float] = []
for sample in 5:
if sample % 2 == 0:
reference_samples.append(_measure(Reference.compute, snapshot))
optimized_samples.append(_measure(Digest.compute, snapshot))
else:
optimized_samples.append(_measure(Digest.compute, snapshot))
reference_samples.append(_measure(Reference.compute, snapshot))
reference_samples.sort()
optimized_samples.sort()
var reference_ms: float = reference_samples[2]
var optimized_ms: float = optimized_samples[2]
var report := {
"history_records": history_count,
"canonical_bytes": Digest.canonical_bytes(snapshot).size(),
"baseline_median_ms": snappedf(reference_ms, 0.001),
"optimized_median_ms": snappedf(optimized_ms, 0.001),
"speedup": snappedf(reference_ms / optimized_ms, 0.001),
}
reports.append(report)
print("DIGEST_BENCHMARK %s" % JSON.stringify(report))
print("DIGEST_PERFORMANCE_REPORT %s" % JSON.stringify({"samples_per_encoder": 5, "metric": "median wall time including SHA-256", "fixture": "synthetic event, ledger, and service histories; complete histories retained", "results": reports}))
var ids: Array[String] = []
for index in 10000:
ids.append("facility.operation.finished:%08d:reception.1:guest.%08d" % [index, index / 4])
var id_snapshot := {"version": 1, "published_ids": ids, "recent_events": []}
_expect_bytes(id_snapshot, "append-only published event ID history remains complete")
var id_reference_samples: Array[float] = []
var id_optimized_samples: Array[float] = []
for sample in 5:
id_reference_samples.append(_measure(Reference.compute, id_snapshot))
id_optimized_samples.append(_measure(Digest.compute, id_snapshot))
id_reference_samples.sort()
id_optimized_samples.sort()
print("DIGEST_EVENT_HISTORY_BENCHMARK %s" % JSON.stringify({"published_ids": ids.size(), "canonical_bytes": Digest.canonical_bytes(id_snapshot).size(), "baseline_median_ms": snappedf(id_reference_samples[2], 0.001), "optimized_median_ms": snappedf(id_optimized_samples[2], 0.001), "speedup": snappedf(id_reference_samples[2] / id_optimized_samples[2], 0.001)}))
func _history_snapshot(count: int) -> Dictionary:
var published_ids: Array[String] = []
var recent_events: Array[Dictionary] = []
var entries: Array[Dictionary] = []
var receipts: Array[Dictionary] = []
for index in count:
var event_id := "event.economy.ledger.%08d" % index
published_ids.append(event_id)
entries.append({
"entry_id": "entry.%08d" % index, "absolute_minute": index * 3,
"entry_type": "operating_expense" if index % 3 == 0 else "service_revenue",
"amount": -12 if index % 3 == 0 else 35, "account_id": "cash",
"counterparty_id": "guest.%05d" % (index % 250), "reference_id": event_id,
"metadata": {"facility_id": "cafeteria.1", "quantity": 1, "description": "基本の食事"},
})
receipts.append({"receipt_id": "receipt.%08d" % index, "minute": index * 3, "guest_id": "guest.%05d" % (index % 250), "facility_id": "cafeteria.1", "satisfied": true, "price": 35})
if index >= maxi(count - 128, 0):
recent_events.append({"event_id": event_id, "event_type": "ledger.posted", "absolute_minute": index * 3, "source_service": "economy", "entity_ids": ["cafeteria.1"], "payload": {"amount": 35, "quantity": 1}, "causation_command_id": index})
return {"version": 7, "minute": count * 3, "domain_events": {"version": 1, "published_ids": published_ids, "recent_events": recent_events}, "economy": {"version": 1, "balance": 5000 + count * 12, "entries": entries}, "operations": {"version": 1, "receipts": receipts}}
func _nested(leaf: Variant, levels: int) -> Variant:
var result: Variant = leaf
for index in levels:
result = [result]
return result
func _measure(encoder: Callable, snapshot: Dictionary) -> float:
var started := Time.get_ticks_usec()
encoder.call(snapshot)
return float(Time.get_ticks_usec() - started) / 1000.0
func _expect_bytes(value: Variant, label: String) -> void:
var expected: PackedByteArray = Reference.canonical_bytes(value)
var actual: PackedByteArray = Digest.canonical_bytes(value)
_expect(not expected.is_empty() and actual == expected, label)
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" PASS: %s" % label)
else:
_failures += 1
push_error(" FAIL: %s" % label)
func _test_scalar_cache_boundaries() -> void:
var values: Array = []
for i in 700:
values.append(i-350)
values.append("entry_%d" % i)
values.append(StringName("entry_%d" % i))
values.append("x".repeat(129))
values.append(StringName("x".repeat(129)))
values.append("utf8_" + String.chr(0x1f333))
values.append(values.duplicate())
_expect_bytes(values, "bounded scalar cache saturation preserves exact canonical bytes")
_expect_bytes([1,"1",&"1",1,"1",&"1"], "scalar cache preserves int string and name type tags")
var previous := Digest.compute(values)
values[0] = -999999
_expect_bytes(values, "new digest reads changed input after prior cache lifetime")
_expect(Digest.compute(values) != previous, "per-call scalar caches never retain previous input")
func _test_text_chunk_boundaries() -> void:
for count: int in [63,64,65,129]:
var strings: Array[String] = []
var names: Array[StringName] = []
for index in count:
var text := "field_%d:" % index + String.chr(0x1f333) + String.chr(0) + "end"
strings.append(text)
names.append(StringName(text))
_expect_bytes(strings, "UTF8 and NUL string chunk boundary %d" % count)
_expect_bytes(names, "StringName chunk boundary %d" % count)