src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp
changeset 52925 9c18c9d839d3
child 53383 5dc89efc08f0
equal deleted inserted replaced
52924:420ff459906f 52925:9c18c9d839d3
       
     1 /*
       
     2  * Copyright (c) 2018, Red Hat, Inc. All rights reserved.
       
     3  *
       
     4  * This code is free software; you can redistribute it and/or modify it
       
     5  * under the terms of the GNU General Public License version 2 only, as
       
     6  * published by the Free Software Foundation.
       
     7  *
       
     8  * This code is distributed in the hope that it will be useful, but WITHOUT
       
     9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    11  * version 2 for more details (a copy is included in the LICENSE file that
       
    12  * accompanied this code).
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License version
       
    15  * 2 along with this work; if not, write to the Free Software Foundation,
       
    16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    17  *
       
    18  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    19  * or visit www.oracle.com if you need additional information or have any
       
    20  * questions.
       
    21  *
       
    22  */
       
    23 
       
    24 #include "precompiled.hpp"
       
    25 
       
    26 #include "gc/shenandoah/shenandoahFreeSet.hpp"
       
    27 #include "gc/shenandoah/shenandoahHeap.hpp"
       
    28 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
       
    29 #include "gc/shenandoah/shenandoahPacer.hpp"
       
    30 
       
    31 /*
       
    32  * In normal concurrent cycle, we have to pace the application to let GC finish.
       
    33  *
       
    34  * Here, we do not know how large would be the collection set, and what are the
       
    35  * relative performances of the each stage in the concurrent cycle, and so we have to
       
    36  * make some assumptions.
       
    37  *
       
    38  * For concurrent mark, there is no clear notion of progress. The moderately accurate
       
    39  * and easy to get metric is the amount of live objects the mark had encountered. But,
       
    40  * that does directly correlate with the used heap, because the heap might be fully
       
    41  * dead or fully alive. We cannot assume either of the extremes: we would either allow
       
    42  * application to run out of memory if we assume heap is fully dead but it is not, and,
       
    43  * conversely, we would pacify application excessively if we assume heap is fully alive
       
    44  * but it is not. So we need to guesstimate the particular expected value for heap liveness.
       
    45  * The best way to do this is apparently recording the past history.
       
    46  *
       
    47  * For concurrent evac and update-refs, we are walking the heap per-region, and so the
       
    48  * notion of progress is clear: we get reported the "used" size from the processed regions
       
    49  * and use the global heap-used as the baseline.
       
    50  *
       
    51  * The allocatable space when GC is running is "free" at the start of cycle, but the
       
    52  * accounted budget is based on "used". So, we need to adjust the tax knowing that.
       
    53  * Also, since we effectively count the used space three times (mark, evac, update-refs),
       
    54  * we need to multiply the tax by 3. Example: for 10 MB free and 90 MB used, GC would
       
    55  * come back with 3*90 MB budget, and thus for each 1 MB of allocation, we have to pay
       
    56  * 3*90 / 10 MBs. In the end, we would pay back the entire budget.
       
    57  */
       
    58 
       
    59 void ShenandoahPacer::setup_for_mark() {
       
    60   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
    61 
       
    62   size_t live = update_and_get_progress_history();
       
    63   size_t free = _heap->free_set()->available();
       
    64 
       
    65   size_t non_taxable = free * ShenandoahPacingCycleSlack / 100;
       
    66   size_t taxable = free - non_taxable;
       
    67 
       
    68   double tax = 1.0 * live / taxable; // base tax for available free space
       
    69   tax *= 3;                          // mark is phase 1 of 3, claim 1/3 of free for it
       
    70   tax *= ShenandoahPacingSurcharge;  // additional surcharge to help unclutter heap
       
    71 
       
    72   restart_with(non_taxable, tax);
       
    73 
       
    74   log_info(gc, ergo)("Pacer for Mark. Expected Live: " SIZE_FORMAT "M, Free: " SIZE_FORMAT
       
    75                      "M, Non-Taxable: " SIZE_FORMAT "M, Alloc Tax Rate: %.1fx",
       
    76                      live / M, free / M, non_taxable / M, tax);
       
    77 }
       
    78 
       
    79 void ShenandoahPacer::setup_for_evac() {
       
    80   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
    81 
       
    82   size_t used = _heap->collection_set()->used();
       
    83   size_t free = _heap->free_set()->available();
       
    84 
       
    85   size_t non_taxable = free * ShenandoahPacingCycleSlack / 100;
       
    86   size_t taxable = free - non_taxable;
       
    87 
       
    88   double tax = 1.0 * used / taxable; // base tax for available free space
       
    89   tax *= 2;                          // evac is phase 2 of 3, claim 1/2 of remaining free
       
    90   tax = MAX2<double>(1, tax);        // never allocate more than GC processes during the phase
       
    91   tax *= ShenandoahPacingSurcharge;  // additional surcharge to help unclutter heap
       
    92 
       
    93   restart_with(non_taxable, tax);
       
    94 
       
    95   log_info(gc, ergo)("Pacer for Evacuation. Used CSet: " SIZE_FORMAT "M, Free: " SIZE_FORMAT
       
    96                      "M, Non-Taxable: " SIZE_FORMAT "M, Alloc Tax Rate: %.1fx",
       
    97                      used / M, free / M, non_taxable / M, tax);
       
    98 }
       
    99 
       
   100 void ShenandoahPacer::setup_for_updaterefs() {
       
   101   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
   102 
       
   103   size_t used = _heap->used();
       
   104   size_t free = _heap->free_set()->available();
       
   105 
       
   106   size_t non_taxable = free * ShenandoahPacingCycleSlack / 100;
       
   107   size_t taxable = free - non_taxable;
       
   108 
       
   109   double tax = 1.0 * used / taxable; // base tax for available free space
       
   110   tax *= 1;                          // update-refs is phase 3 of 3, claim the remaining free
       
   111   tax = MAX2<double>(1, tax);        // never allocate more than GC processes during the phase
       
   112   tax *= ShenandoahPacingSurcharge;  // additional surcharge to help unclutter heap
       
   113 
       
   114   restart_with(non_taxable, tax);
       
   115 
       
   116   log_info(gc, ergo)("Pacer for Update Refs. Used: " SIZE_FORMAT "M, Free: " SIZE_FORMAT
       
   117                      "M, Non-Taxable: " SIZE_FORMAT "M, Alloc Tax Rate: %.1fx",
       
   118                      used / M, free / M, non_taxable / M, tax);
       
   119 }
       
   120 
       
   121 /*
       
   122  * Traversal walks the entire heap once, and therefore we have to make assumptions about its
       
   123  * liveness, like concurrent mark does.
       
   124  */
       
   125 
       
   126 void ShenandoahPacer::setup_for_traversal() {
       
   127   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
   128 
       
   129   size_t live = update_and_get_progress_history();
       
   130   size_t free = _heap->free_set()->available();
       
   131 
       
   132   size_t non_taxable = free * ShenandoahPacingCycleSlack / 100;
       
   133   size_t taxable = free - non_taxable;
       
   134 
       
   135   double tax = 1.0 * live / taxable; // base tax for available free space
       
   136   tax *= ShenandoahPacingSurcharge;  // additional surcharge to help unclutter heap
       
   137 
       
   138   restart_with(non_taxable, tax);
       
   139 
       
   140   log_info(gc, ergo)("Pacer for Traversal. Expected Live: " SIZE_FORMAT "M, Free: " SIZE_FORMAT
       
   141                      "M, Non-Taxable: " SIZE_FORMAT "M, Alloc Tax Rate: %.1fx",
       
   142                      live / M, free / M, non_taxable / M, tax);
       
   143 }
       
   144 
       
   145 /*
       
   146  * In idle phase, we have to pace the application to let control thread react with GC start.
       
   147  *
       
   148  * Here, we have rendezvous with concurrent thread that adds up the budget as it acknowledges
       
   149  * it had seen recent allocations. It will naturally pace the allocations if control thread is
       
   150  * not catching up. To bootstrap this feedback cycle, we need to start with some initial budget
       
   151  * for applications to allocate at.
       
   152  */
       
   153 
       
   154 void ShenandoahPacer::setup_for_idle() {
       
   155   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
   156 
       
   157   size_t initial = _heap->capacity() * ShenandoahPacingIdleSlack / 100;
       
   158   double tax = 1;
       
   159 
       
   160   restart_with(initial, tax);
       
   161 
       
   162   log_info(gc, ergo)("Pacer for Idle. Initial: " SIZE_FORMAT "M, Alloc Tax Rate: %.1fx",
       
   163                      initial / M, tax);
       
   164 }
       
   165 
       
   166 size_t ShenandoahPacer::update_and_get_progress_history() {
       
   167   if (_progress == -1) {
       
   168     // First initialization, report some prior
       
   169     Atomic::store((intptr_t)PACING_PROGRESS_ZERO, &_progress);
       
   170     return (size_t) (_heap->capacity() * 0.1);
       
   171   } else {
       
   172     // Record history, and reply historical data
       
   173     _progress_history->add(_progress);
       
   174     Atomic::store((intptr_t)PACING_PROGRESS_ZERO, &_progress);
       
   175     return (size_t) (_progress_history->avg() * HeapWordSize);
       
   176   }
       
   177 }
       
   178 
       
   179 void ShenandoahPacer::restart_with(size_t non_taxable_bytes, double tax_rate) {
       
   180   size_t initial = (size_t)(non_taxable_bytes * tax_rate) >> LogHeapWordSize;
       
   181   STATIC_ASSERT(sizeof(size_t) <= sizeof(intptr_t));
       
   182   Atomic::xchg((intptr_t)initial, &_budget);
       
   183   Atomic::store(tax_rate, &_tax_rate);
       
   184   Atomic::inc(&_epoch);
       
   185 }
       
   186 
       
   187 bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) {
       
   188   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
   189 
       
   190   intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
       
   191 
       
   192   intptr_t cur = 0;
       
   193   intptr_t new_val = 0;
       
   194   do {
       
   195     cur = Atomic::load(&_budget);
       
   196     if (cur < tax && !force) {
       
   197       // Progress depleted, alas.
       
   198       return false;
       
   199     }
       
   200     new_val = cur - tax;
       
   201   } while (Atomic::cmpxchg(new_val, &_budget, cur) != cur);
       
   202   return true;
       
   203 }
       
   204 
       
   205 void ShenandoahPacer::unpace_for_alloc(intptr_t epoch, size_t words) {
       
   206   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
   207 
       
   208   if (_epoch != epoch) {
       
   209     // Stale ticket, no need to unpace.
       
   210     return;
       
   211   }
       
   212 
       
   213   intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
       
   214   Atomic::add(tax, &_budget);
       
   215 }
       
   216 
       
   217 intptr_t ShenandoahPacer::epoch() {
       
   218   return Atomic::load(&_epoch);
       
   219 }
       
   220 
       
   221 void ShenandoahPacer::pace_for_alloc(size_t words) {
       
   222   assert(ShenandoahPacing, "Only be here when pacing is enabled");
       
   223 
       
   224   // Fast path: try to allocate right away
       
   225   if (claim_for_alloc(words, false)) {
       
   226     return;
       
   227   }
       
   228 
       
   229   size_t max = ShenandoahPacingMaxDelay;
       
   230   double start = os::elapsedTime();
       
   231 
       
   232   size_t total = 0;
       
   233   size_t cur = 0;
       
   234 
       
   235   while (true) {
       
   236     // We could instead assist GC, but this would suffice for now.
       
   237     // This code should also participate in safepointing.
       
   238     // Perform the exponential backoff, limited by max.
       
   239 
       
   240     cur = cur * 2;
       
   241     if (total + cur > max) {
       
   242       cur = (max > total) ? (max - total) : 0;
       
   243     }
       
   244     cur = MAX2<size_t>(1, cur);
       
   245 
       
   246     os::sleep(Thread::current(), cur, true);
       
   247 
       
   248     double end = os::elapsedTime();
       
   249     total = (size_t)((end - start) * 1000);
       
   250 
       
   251     if (total > max) {
       
   252       // Spent local time budget to wait for enough GC progress.
       
   253       // Breaking out and allocating anyway, which may mean we outpace GC,
       
   254       // and start Degenerated GC cycle.
       
   255       _delays.add(total);
       
   256 
       
   257       // Forcefully claim the budget: it may go negative at this point, and
       
   258       // GC should replenish for this and subsequent allocations
       
   259       claim_for_alloc(words, true);
       
   260       break;
       
   261     }
       
   262 
       
   263     if (claim_for_alloc(words, false)) {
       
   264       // Acquired enough permit, nice. Can allocate now.
       
   265       _delays.add(total);
       
   266       break;
       
   267     }
       
   268   }
       
   269 }
       
   270 
       
   271 void ShenandoahPacer::print_on(outputStream* out) const {
       
   272   out->print_cr("ALLOCATION PACING:");
       
   273   out->cr();
       
   274 
       
   275   out->print_cr("Max pacing delay is set for " UINTX_FORMAT " ms.", ShenandoahPacingMaxDelay);
       
   276   out->cr();
       
   277 
       
   278   out->print_cr("Higher delay would prevent application outpacing the GC, but it will hide the GC latencies");
       
   279   out->print_cr("from the STW pause times. Pacing affects the individual threads, and so it would also be");
       
   280   out->print_cr("invisible to the usual profiling tools, but would add up to end-to-end application latency.");
       
   281   out->print_cr("Raise max pacing delay with care.");
       
   282   out->cr();
       
   283 
       
   284   out->print_cr("Actual pacing delays histogram:");
       
   285   out->cr();
       
   286 
       
   287   out->print_cr("%10s - %10s  %12s%12s", "From", "To", "Count", "Sum");
       
   288 
       
   289   size_t total_count = 0;
       
   290   size_t total_sum = 0;
       
   291   for (int c = _delays.min_level(); c <= _delays.max_level(); c++) {
       
   292     int l = (c == 0) ? 0 : 1 << (c - 1);
       
   293     int r = 1 << c;
       
   294     size_t count = _delays.level(c);
       
   295     size_t sum   = count * (r - l) / 2;
       
   296     total_count += count;
       
   297     total_sum   += sum;
       
   298 
       
   299     out->print_cr("%7d ms - %7d ms: " SIZE_FORMAT_W(12) SIZE_FORMAT_W(12) " ms", l, r, count, sum);
       
   300   }
       
   301   out->print_cr("%23s: " SIZE_FORMAT_W(12) SIZE_FORMAT_W(12) " ms", "Total", total_count, total_sum);
       
   302   out->cr();
       
   303 }