diff src/share/vm/utilities/taskqueue.cpp @ 546:05c6d52fa7a9

6690928: Use spinning in combination with yields for workstealing termination. Summary: Substitute a spin loop for most calls to yield() to reduce the stress on the system. Reviewed-by: tonyp
author jmasa
date Sun, 08 Feb 2009 13:18:01 -0800
parents 23673011938d
children 0fbdb4381b99
line wrap: on
line diff
--- a/src/share/vm/utilities/taskqueue.cpp	Fri Feb 06 01:38:50 2009 +0300
+++ b/src/share/vm/utilities/taskqueue.cpp	Sun Feb 08 13:18:01 2009 -0800
@@ -25,6 +25,12 @@
 # include "incls/_precompiled.incl"
 # include "incls/_taskqueue.cpp.incl"
 
+#ifdef TRACESPINNING
+uint ParallelTaskTerminator::_total_yields = 0;
+uint ParallelTaskTerminator::_total_spins = 0;
+uint ParallelTaskTerminator::_total_peeks = 0;
+#endif
+
 bool TaskQueueSuper::peek() {
   return _bottom != _age.top();
 }
@@ -70,14 +76,61 @@
   Atomic::inc(&_offered_termination);
 
   uint yield_count = 0;
+  // Number of hard spin loops done since last yield
+  uint hard_spin_count = 0;
+  // Number of iterations in the hard spin loop.
+  uint hard_spin_limit = WorkStealingHardSpins;
+
+  // If WorkStealingSpinToYieldRatio is 0, no hard spinning is done.
+  // If it is greater than 0, then start with a small number
+  // of spins and increase number with each turn at spinning until
+  // the count of hard spins exceeds WorkStealingSpinToYieldRatio.
+  // Then do a yield() call and start spinning afresh.
+  if (WorkStealingSpinToYieldRatio > 0) {
+    hard_spin_limit = WorkStealingHardSpins >> WorkStealingSpinToYieldRatio;
+    hard_spin_limit = MAX2(hard_spin_limit, 1U);
+  }
+  // Remember the initial spin limit.
+  uint hard_spin_start = hard_spin_limit;
+
+  // Loop waiting for all threads to offer termination or
+  // more work.
   while (true) {
+    // Are all threads offering termination?
     if (_offered_termination == _n_threads) {
-      //inner_termination_loop();
       return true;
     } else {
+      // Look for more work.
+      // Periodically sleep() instead of yield() to give threads
+      // waiting on the cores the chance to grab this code
       if (yield_count <= WorkStealingYieldsBeforeSleep) {
+        // Do a yield or hardspin.  For purposes of deciding whether
+        // to sleep, count this as a yield.
         yield_count++;
-        yield();
+
+        // Periodically call yield() instead spinning
+        // After WorkStealingSpinToYieldRatio spins, do a yield() call
+        // and reset the counts and starting limit.
+        if (hard_spin_count > WorkStealingSpinToYieldRatio) {
+          yield();
+          hard_spin_count = 0;
+          hard_spin_limit = hard_spin_start;
+#ifdef TRACESPINNING
+          _total_yields++;
+#endif
+        } else {
+          // Hard spin this time
+          // Increase the hard spinning period but only up to a limit.
+          hard_spin_limit = MIN2(2*hard_spin_limit,
+                                 (uint) WorkStealingHardSpins);
+          for (uint j = 0; j < hard_spin_limit; j++) {
+            SpinPause();
+          }
+          hard_spin_count++;
+#ifdef TRACESPINNING
+          _total_spins++;
+#endif
+        }
       } else {
         if (PrintGCDetails && Verbose) {
          gclog_or_tty->print_cr("ParallelTaskTerminator::offer_termination() "
@@ -92,6 +145,9 @@
         sleep(WorkStealingSleepMillis);
       }
 
+#ifdef TRACESPINNING
+      _total_peeks++;
+#endif
       if (peek_in_queue_set() ||
           (terminator != NULL && terminator->should_exit_termination())) {
         Atomic::dec(&_offered_termination);
@@ -101,6 +157,16 @@
   }
 }
 
+#ifdef TRACESPINNING
+void ParallelTaskTerminator::print_termination_counts() {
+  gclog_or_tty->print_cr("ParallelTaskTerminator Total yields: %lld  "
+    "Total spins: %lld  Total peeks: %lld",
+    total_yields(),
+    total_spins(),
+    total_peeks());
+}
+#endif
+
 void ParallelTaskTerminator::reset_for_reuse() {
   if (_offered_termination != 0) {
     assert(_offered_termination == _n_threads,