< prev index next >

src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp

Print this page
rev 56898 : 8087198: G1 card refinement: batching, sorting
Reviewed-by: tschatzl

@@ -224,26 +224,109 @@
   _completed_buffers_tail = NULL;
   _num_cards = 0;
   return result;
 }
 
-bool G1DirtyCardQueueSet::refine_buffer(BufferNode* node,
-                                        uint worker_id,
-                                        size_t* total_refined_cards) {
-  G1RemSet* rem_set = G1CollectedHeap::heap()->rem_set();
-  size_t size = buffer_size();
-  void** buffer = BufferNode::make_buffer_from_node(node);
-  size_t i = node->index();
-  assert(i <= size, "invariant");
-  for ( ; (i < size) && !SuspendibleThreadSet::should_yield(); ++i) {
-    CardTable::CardValue* cp = static_cast<CardTable::CardValue*>(buffer[i]);
-    rem_set->refine_card_concurrently(cp, worker_id);
-  }
-  *total_refined_cards += (i - node->index());
-  node->set_index(i);
-  return i == size;
-}
+class G1RefineBufferedCards : public StackObj {
+  BufferNode* const _node;
+  void** const _node_buffer;
+  const size_t _node_buffer_size;
+  size_t* _total_refined_cards;
+  CardTable::CardValue** const _cards;
+  G1RemSet* const _g1rs;
+
+  static inline int compare_card(const void* p1,
+                                 const void* p2) {
+    return *(CardTable::CardValue**)p1 - *(CardTable::CardValue**)p2;
+  }
+
+  void sort_cards(size_t n) {
+    qsort(_cards, n, sizeof(CardTable::CardValue*), compare_card);
+  }
+
+  size_t collect_and_clean_cards() {
+    size_t i = _node->index();
+    size_t num_collected = 0;
+    assert(i <= _node_buffer_size, "invariant");
+    // We don't check for SuspendibleThreadSet::should_yield(), because
+    // collecting, cleaning and abandoning the cards is fast.
+    for ( ; i < _node_buffer_size; ++i) {
+      CardTable::CardValue* cp = static_cast<CardTable::CardValue*>(_node_buffer[i]);
+      if (_g1rs->clean_card_before_refine(cp)) {
+        _cards[num_collected] = cp;
+        num_collected++;
+      } else {
+        // Skipped cards are considered as refined.
+        *_total_refined_cards += 1;
+      }
+    }
+    _node->set_index(i);
+    return num_collected;
+  }
+
+  bool refine_collected_cards(uint worker_id, size_t num_collected) {
+    while (num_collected > 0) {
+      if (SuspendibleThreadSet::should_yield()) {
+        abandon_cards(num_collected);
+        return false;
+      }
+      num_collected--;
+      *_total_refined_cards += 1;
+      _g1rs->refine_card_concurrently(_cards[num_collected], worker_id);
+    }
+    return true;
+  }
+
+  void abandon_cards(size_t num_collected) {
+    assert(num_collected <= _node_buffer_size, "sanity");
+    for (size_t i = 0; i < num_collected; ++i) {
+      *_cards[i] = G1CardTable::dirty_card_val();
+    }
+    size_t buffer_index = _node_buffer_size - num_collected;
+    memcpy(_node_buffer + buffer_index, _cards, num_collected * sizeof(CardTable::CardValue*));
+    _node->set_index(buffer_index);
+  }
+
+public:
+  G1RefineBufferedCards(BufferNode* node,
+                        size_t node_buffer_size,
+                        size_t* total_refined_cards) :
+                        _node(node),
+                        _node_buffer(BufferNode::make_buffer_from_node(node)),
+                        _node_buffer_size(node_buffer_size),
+                        _total_refined_cards(total_refined_cards),
+                        _cards(NEW_RESOURCE_ARRAY(CardTable::CardValue*,
+                                                  node_buffer_size)),
+                        _g1rs(G1CollectedHeap::heap()->rem_set()) {}
+
+  ~G1RefineBufferedCards() {
+    FREE_RESOURCE_ARRAY(CardTable::CardValue*, _cards, _node_buffer_size);
+  }
+
+  // Refine the cards in the BufferNode "_node" from its index to buffer_size.
+  // Stops processing if SuspendibleThreadSet::should_yield() is true.
+  // Returns true if the entire buffer was processed, false if there
+  // is a pending yield request.  The node's index is updated to exclude
+  // the processed elements, e.g. up to the element before processing
+  // stopped, or one past the last element if the entire buffer was
+  // processed. Increments *_total_refined_cards by the number of cards
+  // processed and removed from the buffer.
+  bool refine(uint worker_id) {
+    size_t n = collect_and_clean_cards();
+    // This fence serves two purposes. First, the cards must be cleaned
+    // before processing the contents. Second, we can't proceed with
+    // processing a region until after the read of the region's top in
+    // collect_and_clean_cards(), for synchronization with possibly concurrent
+    // humongous object allocation (see comment at the StoreStore fence before
+    // setting the regions' tops in humongous allocation path).
+    // It's okay that reading region's top and reading region's type were racy
+    // wrto each other. We need both set, in any order, to proceed.
+    OrderAccess::fence();
+    sort_cards(n);
+    return refine_collected_cards(worker_id, n);
+  }
+};
 
 #ifndef ASSERT
 #define assert_fully_consumed(node, buffer_size)
 #else
 #define assert_fully_consumed(node, buffer_size)                \

@@ -276,11 +359,15 @@
 
 bool G1DirtyCardQueueSet::mut_process_buffer(BufferNode* node) {
   uint worker_id = _free_ids.claim_par_id(); // temporarily claim an id
   uint counter_index = worker_id - par_ids_start();
   size_t* counter = &_mutator_refined_cards_counters[counter_index];
-  bool result = refine_buffer(node, worker_id, counter);
+  ResourceMark rm;
+  G1RefineBufferedCards buffered_cards(node,
+                                       buffer_size(),
+                                       counter);
+  bool result = buffered_cards.refine(worker_id);
   _free_ids.release_par_id(worker_id); // release the id
 
   if (result) {
     assert_fully_consumed(node, buffer_size());
   }

@@ -291,20 +378,25 @@
                                                                size_t stop_at,
                                                                size_t* total_refined_cards) {
   BufferNode* node = get_completed_buffer(stop_at);
   if (node == NULL) {
     return false;
-  } else if (refine_buffer(node, worker_id, total_refined_cards)) {
+  } else {
+    G1RefineBufferedCards buffered_cards(node,
+                                         buffer_size(),
+                                         total_refined_cards);
+    if (buffered_cards.refine(worker_id)) {
     assert_fully_consumed(node, buffer_size());
     // Done with fully processed buffer.
     deallocate_buffer(node);
     return true;
   } else {
     // Return partially processed buffer to the queue.
     enqueue_completed_buffer(node);
     return true;
   }
+  }
 }
 
 void G1DirtyCardQueueSet::abandon_logs() {
   assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
   abandon_completed_buffers();
< prev index next >