Mod-union table implementation: reference caching

Implementation which uses a map of card pointers to reference array to keep track of image space references to the alloc space.

Speed/memory usage improvements over bitmap implementation. Approximated memory usage is ~4 * number_references compared to the previous 128k due a bitmap spanning the zygote space.

For the system server, memory usage is approximately 100k due to ~20000 references to the alloc space.

Performance increases since we no longer scan objects in the imagespace when we mark the references of the mod union table.

Change-Id: I449b8b3edeba712a535f6a3b14d81743bcd6f5a0
diff --git a/src/heap.cc b/src/heap.cc
index 144f766..005609b 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -234,7 +234,7 @@
   }
 
   // Allocate the mod-union table
-  ModUnionTableBitmap* mod_union_table = new ModUnionTableBitmap(this);
+  ModUnionTableReferenceCache* mod_union_table = new ModUnionTableReferenceCache(this);
   mod_union_table->Init();
   mod_union_table_ = mod_union_table;
 
@@ -705,11 +705,11 @@
 
     // Processes the cards we cleared earlier and adds their objects into the mod-union table.
     mod_union_table_->Update(&mark_sweep);
-    timings.AddSplit("UpdateModUnionBitmap");
+    timings.AddSplit("UpdateModUnionTable");
 
     // Scans all objects in the mod-union table.
     mod_union_table_->MarkReferences(&mark_sweep);
-    timings.AddSplit("ProcessModUnionBitmap");
+    timings.AddSplit("MarkImageToAllocSpaceReferences");
 
     // Recursively mark all the non-image bits set in the mark bitmap.
     mark_sweep.RecursiveMark();
diff --git a/src/mark_sweep.h b/src/mark_sweep.h
index 28a5523..5f275a4 100644
--- a/src/mark_sweep.h
+++ b/src/mark_sweep.h
@@ -274,12 +274,15 @@
   size_t array_count_;
   size_t other_count_;
 
+  friend class AddIfReachesAllocSpaceVisitor; // Used by mod-union table.
   friend class CheckObjectVisitor;
   friend class InternTableEntryIsUnmarked;
   friend class MarkIfReachesAllocspaceVisitor;
   friend class ModUnionClearCardVisitor;
+  friend class ModUnionReferenceVisitor;
   friend class ModUnionVisitor;
   friend class ModUnionTableBitmap;
+  friend class ModUnionTableReferenceCache;
 
   DISALLOW_COPY_AND_ASSIGN(MarkSweep);
 };
diff --git a/src/mod_union_table.cc b/src/mod_union_table.cc
index 58b6a18..3b0de40 100644
--- a/src/mod_union_table.cc
+++ b/src/mod_union_table.cc
@@ -32,7 +32,7 @@
   }
 
   // Extra parameters are required since we use this same visitor signature for checking objects.
-  void operator ()(const Object* obj, const Object* ref, MemberOffset offset, bool is_static) const {
+  void operator ()(const Object* obj, const Object* ref, const MemberOffset& /* offset */, bool /* is_static */) const {
     // TODO: Optimize?
     // TODO: C++0x auto
     const Spaces& spaces = mark_sweep_->heap_->GetSpaces();
@@ -41,9 +41,6 @@
         bitmap_->Set(obj);
       }
     }
-
-    // Avoid warnings.
-    (void)obj;(void)offset;(void)is_static;
   }
 
  private:
@@ -86,7 +83,7 @@
 ModUnionTableBitmap::ModUnionTableBitmap(Heap* heap) : heap_(heap) {
   // Prevent fragmentation of the heap which is caused by resizing of the vector.
   // TODO: Make a new vector which uses madvise (basically same as a mark stack).
-  cleared_cards_.reserve(4 * KB);
+  cleared_cards_.reserve(32);
 }
 
 ModUnionTableBitmap::~ModUnionTableBitmap() {
@@ -94,7 +91,7 @@
 }
 
 void ModUnionTableBitmap::Init() {
-  const std::vector<Space*>& spaces = heap_->GetSpaces();
+  const Spaces& spaces = heap_->GetSpaces();
 
   // Create one heap bitmap per image space.
   for (size_t i = 0; i < spaces.size(); ++i) {
@@ -113,8 +110,8 @@
 
 void ModUnionTableBitmap::ClearCards() {
   CardTable* card_table = heap_->GetCardTable();
-  for (BitmapMap::iterator cur = bitmaps_.begin(); cur != bitmaps_.end(); ++cur) {
-    const Space* space = cur->first;
+  for (BitmapMap::iterator it = bitmaps_.begin(); it != bitmaps_.end(); ++it) {
+    const Space* space = it->first;
     ModUnionClearCardVisitor visitor(&cleared_cards_);
     // Clear dirty cards in the this image space and update the corresponding mod-union bits.
     card_table->VisitClear(space->Begin(), space->End(), visitor);
@@ -123,7 +120,6 @@
 
 void ModUnionTableBitmap::Update(MarkSweep* mark_sweep) {
   CardTable* card_table = heap_->GetCardTable();
-
   while (!cleared_cards_.empty()) {
     byte* card = cleared_cards_.back();
     cleared_cards_.pop_back();
@@ -164,5 +160,111 @@
   }
 }
 
-}  // namespace art
 
+ModUnionTableReferenceCache::ModUnionTableReferenceCache(Heap* heap) : heap_(heap) {
+  cleared_cards_.reserve(32);
+}
+
+ModUnionTableReferenceCache::~ModUnionTableReferenceCache() {
+
+}
+
+void ModUnionTableReferenceCache::Init() {
+
+}
+
+void ModUnionTableReferenceCache::ClearCards() {
+  const Spaces& spaces = heap_->GetSpaces();
+  CardTable* card_table = heap_->GetCardTable();
+
+  // Create one heap bitmap per image space.
+  for (size_t i = 0; i < spaces.size(); ++i) {
+    if (spaces[i]->IsImageSpace()) {
+      ModUnionClearCardVisitor visitor(&cleared_cards_);
+      // Clear dirty cards in the this image space and update the corresponding mod-union bits.
+      card_table->VisitClear(spaces[i]->Begin(), spaces[i]->End(), visitor);
+    }
+  }
+}
+
+class AddIfReachesAllocSpaceVisitor {
+ public:
+  explicit AddIfReachesAllocSpaceVisitor(
+        MarkSweep* const mark_sweep,
+        ModUnionTableReferenceCache::ReferenceArray* references)
+    : mark_sweep_(mark_sweep),
+      references_(references) {
+  }
+
+  // Extra parameters are required since we use this same visitor signature for checking objects.
+  void operator ()(const Object* /* obj */, const Object* ref, const MemberOffset& /* offset */, bool /* is_static */) const {
+    if (mark_sweep_->heap_->GetAllocSpace()->Contains(ref)) {
+      references_->push_back(ref);
+    }
+  }
+
+ private:
+  MarkSweep* const mark_sweep_;
+  ModUnionTableReferenceCache::ReferenceArray* references_;
+};
+
+class ModUnionReferenceVisitor {
+ public:
+  explicit ModUnionReferenceVisitor(
+        MarkSweep* const mark_sweep,
+        ModUnionTableReferenceCache::ReferenceArray* references)
+    : mark_sweep_(mark_sweep),
+      references_(references) {
+  }
+
+  void operator ()(Object* obj) const {
+    DCHECK(obj != NULL);
+    // We don't have an early exit since we use the visitor pattern, an early
+    // exit should significantly speed this up.
+    AddIfReachesAllocSpaceVisitor visitor(mark_sweep_, references_);
+    mark_sweep_->VisitObjectReferences(obj, visitor);
+  }
+ private:
+  MarkSweep* const mark_sweep_;
+  ModUnionTableReferenceCache::ReferenceArray* references_;
+};
+
+void ModUnionTableReferenceCache::Update(MarkSweep* mark_sweep) {
+  CardTable* card_table = heap_->GetCardTable();
+  while (!cleared_cards_.empty()) {
+    byte* card = cleared_cards_.back();
+    cleared_cards_.pop_back();
+
+    // Update the corresponding references for the card
+    // TODO: C++0x auto
+    ReferenceMap::iterator found = references_.find(card);
+    if (found == references_.end()) {
+      references_.Put(card, ReferenceArray());
+      found = references_.find(card);
+    }
+
+    // Clear and re-compute alloc space references associated with this card.
+    ReferenceArray& cards_references = found->second;
+    cards_references.clear();
+    ModUnionReferenceVisitor visitor(mark_sweep, &cards_references);
+    uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
+    uintptr_t end = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card + 1));
+    SpaceBitmap* live_bitmap =
+        heap_->FindSpaceFromObject(reinterpret_cast<Object*>(start))->GetLiveBitmap();
+    live_bitmap->VisitMarkedRange(start, end, visitor);
+  }
+}
+
+void ModUnionTableReferenceCache::MarkReferences(MarkSweep* mark_sweep) {
+  // TODO: C++0x auto
+  size_t count = 0;
+  for (ReferenceMap::const_iterator it = references_.begin(); it != references_.end(); ++it) {
+    for (ReferenceArray::const_iterator it_ref = it->second.begin(); it_ref != it->second.end(); ++it_ref ) {
+      mark_sweep->MarkObject(*it_ref);
+      ++count;
+    }
+  }
+  VLOG(heap) << "Marked " << count << " references in mod union table";
+}
+
+}  // namespace art
diff --git a/src/mod_union_table.h b/src/mod_union_table.h
index 2d75a01..a9111ee 100644
--- a/src/mod_union_table.h
+++ b/src/mod_union_table.h
@@ -69,6 +69,36 @@
   Heap* heap_;
 };
 
+// Reference caching implementation. Caches references pointing to alloc space(s)
+// for each card.
+class ModUnionTableReferenceCache : public ModUnionTable {
+ public:
+  typedef std::vector<const Object*> ReferenceArray;
+  typedef SafeMap<const byte*, ReferenceArray > ReferenceMap;
+
+  ModUnionTableReferenceCache(Heap* heap);
+  virtual ~ModUnionTableReferenceCache();
+
+  void Init();
+
+  // Clear image space cards.
+  void ClearCards();
+
+  // Update table based on cleared cards.
+  void Update(MarkSweep* mark_sweep);
+
+  // Mark all references to the alloc space(s).
+  void MarkReferences(MarkSweep* mark_sweep);
+ private:
+  // Cleared card array, used to update the mod-union table.
+  std::vector<byte*> cleared_cards_;
+
+  // Maps from dirty cards to their corresponding alloc space references.
+  ReferenceMap references_;
+
+  Heap* heap_;
+};
+
 }  // namespace art
 
 #endif  // ART_SRC_MOD_UNION_TABLE_H_