< prev index next >

src/hotspot/share/gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.cpp

Print this page
*** 25,11 ***
  #include "precompiled.hpp"
  
  #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp"
  #include "gc/shenandoah/shenandoahCollectionSet.hpp"
  #include "gc/shenandoah/shenandoahFreeSet.hpp"
! #include "gc/shenandoah/shenandoahHeap.inline.hpp"
  #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
  #include "logging/log.hpp"
  #include "logging/logTag.hpp"
  #include "utilities/quickSort.hpp"
  
--- 25,11 ---
  #include "precompiled.hpp"
  
  #include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp"
  #include "gc/shenandoah/shenandoahCollectionSet.hpp"
  #include "gc/shenandoah/shenandoahFreeSet.hpp"
! #include "gc/shenandoah/shenandoahGeneration.hpp"
  #include "gc/shenandoah/shenandoahHeapRegion.inline.hpp"
  #include "logging/log.hpp"
  #include "logging/logTag.hpp"
  #include "utilities/quickSort.hpp"
  

*** 52,12 ***
  // that the true value of our estimate is outside the interval. These are used
  // as bounds on the adjustments applied at the outcome of a GC cycle.
  const double ShenandoahAdaptiveHeuristics::MINIMUM_CONFIDENCE = 0.319; // 25%
  const double ShenandoahAdaptiveHeuristics::MAXIMUM_CONFIDENCE = 3.291; // 99.9%
  
! ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics() :
!   ShenandoahHeuristics(),
    _margin_of_error_sd(ShenandoahAdaptiveInitialConfidence),
    _spike_threshold_sd(ShenandoahAdaptiveInitialSpikeThreshold),
    _last_trigger(OTHER) { }
  
  ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() {}
--- 52,12 ---
  // that the true value of our estimate is outside the interval. These are used
  // as bounds on the adjustments applied at the outcome of a GC cycle.
  const double ShenandoahAdaptiveHeuristics::MINIMUM_CONFIDENCE = 0.319; // 25%
  const double ShenandoahAdaptiveHeuristics::MAXIMUM_CONFIDENCE = 3.291; // 99.9%
  
! ShenandoahAdaptiveHeuristics::ShenandoahAdaptiveHeuristics(ShenandoahGeneration* generation) :
!   ShenandoahHeuristics(generation),
    _margin_of_error_sd(ShenandoahAdaptiveInitialConfidence),
    _spike_threshold_sd(ShenandoahAdaptiveInitialSpikeThreshold),
    _last_trigger(OTHER) { }
  
  ShenandoahAdaptiveHeuristics::~ShenandoahAdaptiveHeuristics() {}

*** 82,11 ***
    // Therefore, we start by sorting the regions by garbage. Then we unconditionally add the best candidates
    // before we meet min_garbage. Then we add all candidates that fit with a garbage threshold before
    // we hit max_cset. When max_cset is hit, we terminate the cset selection. Note that in this scheme,
    // ShenandoahGarbageThreshold is the soft threshold which would be ignored until min_garbage is hit.
  
!   size_t capacity    = ShenandoahHeap::heap()->soft_max_capacity();
    size_t max_cset    = (size_t)((1.0 * capacity / 100 * ShenandoahEvacReserve) / ShenandoahEvacWaste);
    size_t free_target = (capacity / 100 * ShenandoahMinFreeThreshold) + max_cset;
    size_t min_garbage = (free_target > actual_free ? (free_target - actual_free) : 0);
  
    log_info(gc, ergo)("Adaptive CSet Selection. Target Free: " SIZE_FORMAT "%s, Actual Free: "
--- 82,11 ---
    // Therefore, we start by sorting the regions by garbage. Then we unconditionally add the best candidates
    // before we meet min_garbage. Then we add all candidates that fit with a garbage threshold before
    // we hit max_cset. When max_cset is hit, we terminate the cset selection. Note that in this scheme,
    // ShenandoahGarbageThreshold is the soft threshold which would be ignored until min_garbage is hit.
  
!   size_t capacity    = _generation->soft_max_capacity();
    size_t max_cset    = (size_t)((1.0 * capacity / 100 * ShenandoahEvacReserve) / ShenandoahEvacWaste);
    size_t free_target = (capacity / 100 * ShenandoahMinFreeThreshold) + max_cset;
    size_t min_garbage = (free_target > actual_free ? (free_target - actual_free) : 0);
  
    log_info(gc, ergo)("Adaptive CSet Selection. Target Free: " SIZE_FORMAT "%s, Actual Free: "

*** 194,38 ***
  static double saturate(double value, double min, double max) {
    return MAX2(MIN2(value, max), min);
  }
  
  bool ShenandoahAdaptiveHeuristics::should_start_gc() {
!   ShenandoahHeap* heap = ShenandoahHeap::heap();
!   size_t max_capacity = heap->max_capacity();
!   size_t capacity = heap->soft_max_capacity();
!   size_t available = heap->free_set()->available();
!   size_t allocated = heap->bytes_allocated_since_gc_start();
  
    // Make sure the code below treats available without the soft tail.
    size_t soft_tail = max_capacity - capacity;
    available = (available > soft_tail) ? (available - soft_tail) : 0;
  
    // Track allocation rate even if we decide to start a cycle for other reasons.
    double rate = _allocation_rate.sample(allocated);
    _last_trigger = OTHER;
  
    size_t min_threshold = capacity / 100 * ShenandoahMinFreeThreshold;
    if (available < min_threshold) {
!     log_info(gc)("Trigger: Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)",
                   byte_size_in_proper_unit(available),     proper_unit_for_byte_size(available),
                   byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold));
      return true;
    }
  
    const size_t max_learn = ShenandoahLearningSteps;
    if (_gc_times_learned < max_learn) {
      size_t init_threshold = capacity / 100 * ShenandoahInitFreeThreshold;
      if (available < init_threshold) {
!       log_info(gc)("Trigger: Learning " SIZE_FORMAT " of " SIZE_FORMAT ". Free (" SIZE_FORMAT "%s) is below initial threshold (" SIZE_FORMAT "%s)",
!                    _gc_times_learned + 1, max_learn,
                     byte_size_in_proper_unit(available),      proper_unit_for_byte_size(available),
                     byte_size_in_proper_unit(init_threshold), proper_unit_for_byte_size(init_threshold));
        return true;
      }
    }
--- 194,47 ---
  static double saturate(double value, double min, double max) {
    return MAX2(MIN2(value, max), min);
  }
  
  bool ShenandoahAdaptiveHeuristics::should_start_gc() {
!   size_t max_capacity = _generation->max_capacity();
!   size_t capacity = _generation->soft_max_capacity();
!   size_t available = _generation->available();
!   size_t allocated = _generation->bytes_allocated_since_gc_start();
! 
+   log_debug(gc)("should_start_gc (%s)? available: " SIZE_FORMAT ", soft_max_capacity: " SIZE_FORMAT
+                 ", max_capacity: " SIZE_FORMAT ", allocated: " SIZE_FORMAT,
+                 _generation->name(), available, capacity, max_capacity, allocated);
  
    // Make sure the code below treats available without the soft tail.
    size_t soft_tail = max_capacity - capacity;
    available = (available > soft_tail) ? (available - soft_tail) : 0;
  
    // Track allocation rate even if we decide to start a cycle for other reasons.
    double rate = _allocation_rate.sample(allocated);
    _last_trigger = OTHER;
  
    size_t min_threshold = capacity / 100 * ShenandoahMinFreeThreshold;
+ 
+   log_debug(gc)("  available adjusted to: " SIZE_FORMAT ", min_threshold: " SIZE_FORMAT ", ShenandoahMinFreeThreshold: " SIZE_FORMAT,
+                 available, min_threshold, ShenandoahMinFreeThreshold);
+ 
    if (available < min_threshold) {
!     log_info(gc)("Trigger (%s): Free (" SIZE_FORMAT "%s) is below minimum threshold (" SIZE_FORMAT "%s)",
+                  _generation->name(),
                   byte_size_in_proper_unit(available),     proper_unit_for_byte_size(available),
                   byte_size_in_proper_unit(min_threshold), proper_unit_for_byte_size(min_threshold));
      return true;
    }
  
+   // Check if we need to learn a bit about the application
    const size_t max_learn = ShenandoahLearningSteps;
    if (_gc_times_learned < max_learn) {
      size_t init_threshold = capacity / 100 * ShenandoahInitFreeThreshold;
      if (available < init_threshold) {
!       log_info(gc)("Trigger (%s): Learning " SIZE_FORMAT " of " SIZE_FORMAT ". Free (" SIZE_FORMAT "%s) is below initial threshold (" SIZE_FORMAT "%s)",
!                    _generation->name(), _gc_times_learned + 1, max_learn,
                     byte_size_in_proper_unit(available),      proper_unit_for_byte_size(available),
                     byte_size_in_proper_unit(init_threshold), proper_unit_for_byte_size(init_threshold));
        return true;
      }
    }

*** 241,13 ***
    allocation_headroom -= MIN2(allocation_headroom, spike_headroom);
    allocation_headroom -= MIN2(allocation_headroom, penalties);
  
    double avg_cycle_time = _gc_time_history->davg() + (_margin_of_error_sd * _gc_time_history->dsd());
    double avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd);
    if (avg_cycle_time > allocation_headroom / avg_alloc_rate) {
!     log_info(gc)("Trigger: Average GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (margin of error = %.2f)",
!                  avg_cycle_time * 1000,
                   byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate),
                   byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom),
                   _margin_of_error_sd);
  
      log_info(gc, ergo)("Free headroom: " SIZE_FORMAT "%s (free) - " SIZE_FORMAT "%s (spike) - " SIZE_FORMAT "%s (penalties) = " SIZE_FORMAT "%s",
--- 250,16 ---
    allocation_headroom -= MIN2(allocation_headroom, spike_headroom);
    allocation_headroom -= MIN2(allocation_headroom, penalties);
  
    double avg_cycle_time = _gc_time_history->davg() + (_margin_of_error_sd * _gc_time_history->dsd());
    double avg_alloc_rate = _allocation_rate.upper_bound(_margin_of_error_sd);
+   log_debug(gc)("%s: average GC time: %.2f ms, allocation rate: %.0f %s/s",
+     _generation->name(), avg_cycle_time * 1000, byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate));
+ 
    if (avg_cycle_time > allocation_headroom / avg_alloc_rate) {
!     log_info(gc)("Trigger (%s): Average GC time (%.2f ms) is above the time for average allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (margin of error = %.2f)",
!                  _generation->name(), avg_cycle_time * 1000,
                   byte_size_in_proper_unit(avg_alloc_rate), proper_unit_for_byte_size(avg_alloc_rate),
                   byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom),
                   _margin_of_error_sd);
  
      log_info(gc, ergo)("Free headroom: " SIZE_FORMAT "%s (free) - " SIZE_FORMAT "%s (spike) - " SIZE_FORMAT "%s (penalties) = " SIZE_FORMAT "%s",

*** 260,12 ***
      return true;
    }
  
    bool is_spiking = _allocation_rate.is_spiking(rate, _spike_threshold_sd);
    if (is_spiking && avg_cycle_time > allocation_headroom / rate) {
!     log_info(gc)("Trigger: Average GC time (%.2f ms) is above the time for instantaneous allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (spike threshold = %.2f)",
!                  avg_cycle_time * 1000,
                   byte_size_in_proper_unit(rate), proper_unit_for_byte_size(rate),
                   byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom),
                   _spike_threshold_sd);
      _last_trigger = SPIKE;
      return true;
--- 272,12 ---
      return true;
    }
  
    bool is_spiking = _allocation_rate.is_spiking(rate, _spike_threshold_sd);
    if (is_spiking && avg_cycle_time > allocation_headroom / rate) {
!     log_info(gc)("Trigger (%s): Average GC time (%.2f ms) is above the time for instantaneous allocation rate (%.0f %sB/s) to deplete free headroom (" SIZE_FORMAT "%s) (spike threshold = %.2f)",
!                  _generation->name(), avg_cycle_time * 1000,
                   byte_size_in_proper_unit(rate), proper_unit_for_byte_size(rate),
                   byte_size_in_proper_unit(allocation_headroom), proper_unit_for_byte_size(allocation_headroom),
                   _spike_threshold_sd);
      _last_trigger = SPIKE;
      return true;
< prev index next >