From e04d2179a1fb3108643fa331c2d37440cfcbefb4 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Tue, 26 May 2026 09:40:40 -0700 Subject: [PATCH] [vm] Fix quadratic cost of SNAPSHOT_BACKTRACE. Observed this taking ~16% of debug gen_snapshot time. Observed this taking ~52% of debug AppJIT training time. TEST=ci Change-Id: I945e4f3a8794e607e1402150a10f0d328fa1e677 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506260 Commit-Queue: Ryan Macnak Reviewed-by: Alexander Aprelev --- runtime/vm/app_snapshot.cc | 24 +++++++++++------------- runtime/vm/globals.h | 4 ++++ runtime/vm/heap/heap.cc | 7 +++++++ runtime/vm/heap/heap.h | 13 +++++++++++++ runtime/vm/image_snapshot.h | 4 ---- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/runtime/vm/app_snapshot.cc b/runtime/vm/app_snapshot.cc index fdc1d793eeb..496798daf5e 100644 --- a/runtime/vm/app_snapshot.cc +++ b/runtime/vm/app_snapshot.cc @@ -636,7 +636,6 @@ class Serializer : public ThreadStackResource { #if defined(SNAPSHOT_BACKTRACE) ObjectPtr current_parent_; - GrowableArray parent_pairs_; #endif #if defined(DART_PRECOMPILER) @@ -7754,8 +7753,7 @@ Serializer::Serializer(Thread* thread, profile_writer_(profile_writer) #if defined(SNAPSHOT_BACKTRACE) , - current_parent_(Object::null()), - parent_pairs_() + current_parent_(Object::null()) #endif #if defined(DART_PRECOMPILER) , @@ -8759,8 +8757,7 @@ void Serializer::Push(ObjectPtr object, intptr_t cid_override) { num_written_objects_++; } #if defined(SNAPSHOT_BACKTRACE) - parent_pairs_.Add(&Object::Handle(zone_, object)); - parent_pairs_.Add(&Object::Handle(zone_, current_parent_)); + heap()->SetSnapshotParent(object, &Object::Handle(zone_, current_parent_)); #endif } } @@ -8851,19 +8848,17 @@ void Serializer::UnexpectedObject(ObjectPtr raw_object, const char* message) { #if defined(SNAPSHOT_BACKTRACE) ObjectPtr Serializer::ParentOf(ObjectPtr object) const { - for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) { - if (parent_pairs_[i]->ptr() == object) { - return parent_pairs_[i + 1]->ptr(); - } + Object* parent = heap()->GetSnapshotParent(object); + if (parent != nullptr) { + return parent->ptr(); } return Object::null(); } ObjectPtr Serializer::ParentOf(const Object& object) const { - for (intptr_t i = 0; i < parent_pairs_.length(); i += 2) { - if (parent_pairs_[i]->ptr() == object.ptr()) { - return parent_pairs_[i + 1]->ptr(); - } + Object* parent = heap()->GetSnapshotParent(object.ptr()); + if (parent != nullptr) { + return parent->ptr(); } return Object::null(); } @@ -9104,6 +9099,9 @@ ZoneGrowableArray* Serializer::Serialize(SerializationRoots* roots) { PrintSnapshotSizes(); heap()->ResetObjectIdTable(); +#if defined(SNAPSHOT_BACKTRACE) + heap()->ResetSnapshotParentTable(); +#endif return objects_; } diff --git a/runtime/vm/globals.h b/runtime/vm/globals.h index be935af8b17..dfe3945167e 100644 --- a/runtime/vm/globals.h +++ b/runtime/vm/globals.h @@ -149,6 +149,10 @@ const intptr_t kDefaultNewGenSemiMaxSize = (kWordSize <= 4) ? 8 : 16; #define TARGET_HAS_FAST_WRITE_WRITE_FENCE 1 #define HOST_HAS_FAST_WRITE_WRITE_FENCE 1 +#if defined(DEBUG) +#define SNAPSHOT_BACKTRACE +#endif + // The expression OFFSET_OF(type, field) computes the byte-offset of // the specified field relative to the containing type. // diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc index 4fcf27ea5d1..d58c7112aa5 100644 --- a/runtime/vm/heap/heap.cc +++ b/runtime/vm/heap/heap.cc @@ -879,6 +879,13 @@ void Heap::ResetObjectIdTable() { old_weak_tables_[kObjectIds]->Reset(); } +#if defined(SNAPSHOT_BACKTRACE) +void Heap::ResetSnapshotParentTable() { + new_weak_tables_[kSnapshotParents]->Reset(); + old_weak_tables_[kSnapshotParents]->Reset(); +} +#endif + intptr_t Heap::GetWeakEntry(ObjectPtr raw_obj, WeakSelector sel) const { if (raw_obj->IsImmediateOrOldObject()) { return old_weak_tables_[sel]->GetValue(raw_obj); diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h index 5ae65318cee..4c6f671ccf9 100644 --- a/runtime/vm/heap/heap.h +++ b/runtime/vm/heap/heap.h @@ -47,6 +47,9 @@ class Heap { #endif kCanonicalHashes, kObjectIds, +#if defined(SNAPSHOT_BACKTRACE) + kSnapshotParents, +#endif kLoadingUnits, #if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER) kHeapSamplingData, @@ -202,6 +205,16 @@ class Heap { } void ResetObjectIdTable(); +#if defined(SNAPSHOT_BACKTRACE) + void SetSnapshotParent(ObjectPtr obj, Object* parent) { + SetWeakEntry(obj, kSnapshotParents, reinterpret_cast(parent)); + } + Object* GetSnapshotParent(ObjectPtr obj) const { + return reinterpret_cast(GetWeakEntry(obj, kSnapshotParents)); + } + void ResetSnapshotParentTable(); +#endif + void SetLoadingUnit(ObjectPtr raw_obj, intptr_t unit_id) { ASSERT(Thread::Current()->IsDartMutatorThread()); SetWeakEntry(raw_obj, kLoadingUnits, unit_id); diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h index 88372d2c044..9cb4c220548 100644 --- a/runtime/vm/image_snapshot.h +++ b/runtime/vm/image_snapshot.h @@ -22,10 +22,6 @@ #include "vm/type_testing_stubs.h" #include "vm/v8_snapshot_writer.h" -#if defined(DEBUG) -#define SNAPSHOT_BACKTRACE -#endif - namespace dart { // Forward declarations.