8227434: G1 predictions may over/underflow with high variance input
authortschatzl
Fri, 29 Nov 2019 10:20:16 +0100
changeset 59320 11ff4e485670
parent 59319 9ee940f1de90
child 59321 5775e4825e58
8227434: G1 predictions may over/underflow with high variance input Summary: Clamp G1 prediction output to sensible values. Reviewed-by: lkorinth, sjohanss
src/hotspot/share/gc/g1/g1Analytics.cpp
src/hotspot/share/gc/g1/g1Analytics.hpp
src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
src/hotspot/share/gc/g1/g1IHOPControl.cpp
src/hotspot/share/gc/g1/g1IHOPControl.hpp
src/hotspot/share/gc/g1/g1Policy.cpp
src/hotspot/share/gc/g1/g1Predictions.hpp
src/hotspot/share/gc/g1/survRateGroup.cpp
test/hotspot/gtest/gc/g1/test_g1Predictions.cpp
--- a/src/hotspot/share/gc/g1/g1Analytics.cpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1Analytics.cpp	Fri Nov 29 10:20:16 2019 +0100
@@ -125,12 +125,16 @@
   return seq->num() >= 3;
 }
 
-double G1Analytics::get_new_prediction(TruncatedSeq const* seq) const {
-  return _predictor->get_new_prediction(seq);
+double G1Analytics::get_new_unit_prediction(TruncatedSeq const* seq) const {
+  return _predictor->get_new_unit_prediction(seq);
 }
 
 size_t G1Analytics::get_new_size_prediction(TruncatedSeq const* seq) const {
-  return (size_t)get_new_prediction(seq);
+  return (size_t)get_new_lower_zero_bound_prediction(seq);
+}
+
+double G1Analytics::get_new_lower_zero_bound_prediction(TruncatedSeq const* seq) const {
+  return _predictor->get_new_lower_zero_bound_prediction(seq);
 }
 
 int G1Analytics::num_alloc_rate_ms() const {
@@ -225,50 +229,50 @@
 }
 
 double G1Analytics::predict_alloc_rate_ms() const {
-  return get_new_prediction(_alloc_rate_ms_seq);
+  return get_new_lower_zero_bound_prediction(_alloc_rate_ms_seq);
 }
 
 double G1Analytics::predict_concurrent_refine_rate_ms() const {
-  return get_new_prediction(_concurrent_refine_rate_ms_seq);
+  return get_new_lower_zero_bound_prediction(_concurrent_refine_rate_ms_seq);
 }
 
 double G1Analytics::predict_logged_cards_rate_ms() const {
-  return get_new_prediction(_logged_cards_rate_ms_seq);
+  return get_new_lower_zero_bound_prediction(_logged_cards_rate_ms_seq);
 }
 
 double G1Analytics::predict_young_card_merge_to_scan_ratio() const {
-  return get_new_prediction(_young_card_merge_to_scan_ratio_seq);
+  return get_new_unit_prediction(_young_card_merge_to_scan_ratio_seq);
 }
 
 size_t G1Analytics::predict_scan_card_num(size_t rs_length, bool for_young_gc) const {
   if (for_young_gc || !enough_samples_available(_mixed_card_merge_to_scan_ratio_seq)) {
-    return (size_t) (rs_length * predict_young_card_merge_to_scan_ratio());
+    return (size_t)(rs_length * predict_young_card_merge_to_scan_ratio());
   } else {
-    return (size_t) (rs_length * get_new_prediction(_mixed_card_merge_to_scan_ratio_seq));
+    return (size_t)(rs_length * get_new_unit_prediction(_mixed_card_merge_to_scan_ratio_seq));
   }
 }
 
 double G1Analytics::predict_card_merge_time_ms(size_t card_num, bool for_young_gc) const {
   if (for_young_gc || !enough_samples_available(_mixed_cost_per_card_merge_ms_seq)) {
-    return card_num * get_new_prediction(_young_cost_per_card_merge_ms_seq);
+    return card_num * get_new_lower_zero_bound_prediction(_young_cost_per_card_merge_ms_seq);
   } else {
-    return card_num * get_new_prediction(_mixed_cost_per_card_merge_ms_seq);
+    return card_num * get_new_lower_zero_bound_prediction(_mixed_cost_per_card_merge_ms_seq);
   }
 }
 
 double G1Analytics::predict_card_scan_time_ms(size_t card_num, bool for_young_gc) const {
   if (for_young_gc || !enough_samples_available(_mixed_cost_per_card_scan_ms_seq)) {
-    return card_num * get_new_prediction(_young_cost_per_card_scan_ms_seq);
+    return card_num * get_new_lower_zero_bound_prediction(_young_cost_per_card_scan_ms_seq);
   } else {
-    return card_num * get_new_prediction(_mixed_cost_per_card_scan_ms_seq);
+    return card_num * get_new_lower_zero_bound_prediction(_mixed_cost_per_card_scan_ms_seq);
   }
 }
 
 double G1Analytics::predict_object_copy_time_ms_during_cm(size_t bytes_to_copy) const {
   if (!enough_samples_available(_cost_per_byte_ms_during_cm_seq)) {
-    return (1.1 * bytes_to_copy) * get_new_prediction(_copy_cost_per_byte_ms_seq);
+    return (1.1 * bytes_to_copy) * get_new_lower_zero_bound_prediction(_copy_cost_per_byte_ms_seq);
   } else {
-    return bytes_to_copy * get_new_prediction(_cost_per_byte_ms_during_cm_seq);
+    return bytes_to_copy * get_new_lower_zero_bound_prediction(_cost_per_byte_ms_during_cm_seq);
   }
 }
 
@@ -276,32 +280,32 @@
   if (during_concurrent_mark) {
     return predict_object_copy_time_ms_during_cm(bytes_to_copy);
   } else {
-    return bytes_to_copy * get_new_prediction(_copy_cost_per_byte_ms_seq);
+    return bytes_to_copy * get_new_lower_zero_bound_prediction(_copy_cost_per_byte_ms_seq);
   }
 }
 
 double G1Analytics::predict_constant_other_time_ms() const {
-  return get_new_prediction(_constant_other_time_ms_seq);
+  return get_new_lower_zero_bound_prediction(_constant_other_time_ms_seq);
 }
 
 double G1Analytics::predict_young_other_time_ms(size_t young_num) const {
-  return young_num * get_new_prediction(_young_other_cost_per_region_ms_seq);
+  return young_num * get_new_lower_zero_bound_prediction(_young_other_cost_per_region_ms_seq);
 }
 
 double G1Analytics::predict_non_young_other_time_ms(size_t non_young_num) const {
-  return non_young_num * get_new_prediction(_non_young_other_cost_per_region_ms_seq);
+  return non_young_num * get_new_lower_zero_bound_prediction(_non_young_other_cost_per_region_ms_seq);
 }
 
 double G1Analytics::predict_remark_time_ms() const {
-  return get_new_prediction(_concurrent_mark_remark_times_ms);
+  return get_new_lower_zero_bound_prediction(_concurrent_mark_remark_times_ms);
 }
 
 double G1Analytics::predict_cleanup_time_ms() const {
-  return get_new_prediction(_concurrent_mark_cleanup_times_ms);
+  return get_new_lower_zero_bound_prediction(_concurrent_mark_cleanup_times_ms);
 }
 
 size_t G1Analytics::predict_rs_length() const {
-  return get_new_size_prediction(_rs_length_seq) + get_new_prediction(_rs_length_diff_seq);
+  return get_new_size_prediction(_rs_length_seq) + get_new_size_prediction(_rs_length_diff_seq);
 }
 
 size_t G1Analytics::predict_pending_cards() const {
--- a/src/hotspot/share/gc/g1/g1Analytics.hpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1Analytics.hpp	Fri Nov 29 10:20:16 2019 +0100
@@ -84,8 +84,9 @@
   // The constant used is random but "small".
   bool enough_samples_available(TruncatedSeq const* seq) const;
 
-  double get_new_prediction(TruncatedSeq const* seq) const;
+  double get_new_unit_prediction(TruncatedSeq const* seq) const;
   size_t get_new_size_prediction(TruncatedSeq const* seq) const;
+  double get_new_lower_zero_bound_prediction(TruncatedSeq const* seq) const;
 
 public:
   G1Analytics(const G1Predictions* predictor);
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Fri Nov 29 10:20:16 2019 +0100
@@ -2588,7 +2588,8 @@
   // and do_marking_step() is not being called serially.
   bool do_stealing = do_termination && !is_serial;
 
-  double diff_prediction_ms = _g1h->policy()->predictor().get_new_prediction(&_marking_step_diff_ms);
+  G1Predictions const& predictor = _g1h->policy()->predictor();
+  double diff_prediction_ms = predictor.get_new_lower_zero_bound_prediction(&_marking_step_diff_ms);
   _time_target_ms = time_target_ms - diff_prediction_ms;
 
   // set up the variables that are used in the work-based scheme to
--- a/src/hotspot/share/gc/g1/g1IHOPControl.cpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1IHOPControl.cpp	Fri Nov 29 10:20:16 2019 +0100
@@ -113,6 +113,10 @@
     );
 }
 
+double G1AdaptiveIHOPControl::get_new_prediction(TruncatedSeq const* seq) const {
+  return _predictor->get_new_lower_zero_bound_prediction(seq);
+}
+
 bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const {
   return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) &&
          ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples);
@@ -120,8 +124,8 @@
 
 size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() {
   if (have_enough_data_for_prediction()) {
-    double pred_marking_time = _predictor->get_new_prediction(&_marking_times_s);
-    double pred_promotion_rate = _predictor->get_new_prediction(&_allocation_rate_s);
+    double pred_marking_time = get_new_prediction(&_marking_times_s);
+    double pred_promotion_rate = get_new_prediction(&_allocation_rate_s);
     size_t pred_promotion_size = (size_t)(pred_marking_time * pred_promotion_rate);
 
     size_t predicted_needed_bytes_during_marking =
@@ -168,8 +172,8 @@
                       actual_target,
                       G1CollectedHeap::heap()->used(),
                       _last_unrestrained_young_size,
-                      _predictor->get_new_prediction(&_allocation_rate_s),
-                      _predictor->get_new_prediction(&_marking_times_s) * 1000.0,
+                      get_new_prediction(&_allocation_rate_s),
+                      get_new_prediction(&_marking_times_s) * 1000.0,
                       have_enough_data_for_prediction() ? "true" : "false");
 }
 
@@ -179,7 +183,7 @@
                                           actual_target_threshold(),
                                           G1CollectedHeap::heap()->used(),
                                           _last_unrestrained_young_size,
-                                          _predictor->get_new_prediction(&_allocation_rate_s),
-                                          _predictor->get_new_prediction(&_marking_times_s),
+                                          get_new_prediction(&_allocation_rate_s),
+                                          get_new_prediction(&_marking_times_s),
                                           have_enough_data_for_prediction());
 }
--- a/src/hotspot/share/gc/g1/g1IHOPControl.hpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1IHOPControl.hpp	Fri Nov 29 10:20:16 2019 +0100
@@ -123,6 +123,9 @@
   // as there is no marking or mixed gc that could impact its size too much.
   size_t _last_unrestrained_young_size;
 
+  // Get a new prediction bounded below by zero from the given sequence.
+  double get_new_prediction(TruncatedSeq const* seq) const;
+
   bool have_enough_data_for_prediction() const;
 
   // The "actual" target threshold the algorithm wants to keep during and at the
--- a/src/hotspot/share/gc/g1/g1Policy.cpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1Policy.cpp	Fri Nov 29 10:20:16 2019 +0100
@@ -914,11 +914,7 @@
 double G1Policy::predict_yg_surv_rate(int age, SurvRateGroup* surv_rate_group) const {
   TruncatedSeq* seq = surv_rate_group->get_seq(age);
   guarantee(seq->num() > 0, "There should be some young gen survivor samples available. Tried to access with age %d", age);
-  double pred = _predictor.get_new_prediction(seq);
-  if (pred > 1.0) {
-    pred = 1.0;
-  }
-  return pred;
+  return _predictor.get_new_unit_prediction(seq);
 }
 
 double G1Policy::accum_yg_surv_rate_pred(int age) const {
--- a/src/hotspot/share/gc/g1/g1Predictions.hpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1Predictions.hpp	Fri Nov 29 10:20:16 2019 +0100
@@ -57,6 +57,14 @@
   double get_new_prediction(TruncatedSeq const* seq) const {
     return seq->davg() + _sigma * stddev_estimate(seq);
   }
+
+  double get_new_unit_prediction(TruncatedSeq const* seq) const {
+    return clamp(get_new_prediction(seq), 0.0, 1.0);
+  }
+
+  double get_new_lower_zero_bound_prediction(TruncatedSeq const* seq) const {
+    return MAX2(get_new_prediction(seq), 0.0);
+  }
 };
 
 #endif // SHARE_GC_G1_G1PREDICTIONS_HPP
--- a/src/hotspot/share/gc/g1/survRateGroup.cpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/src/hotspot/share/gc/g1/survRateGroup.cpp	Fri Nov 29 10:20:16 2019 +0100
@@ -116,8 +116,7 @@
   double accum = 0.0;
   double pred = 0.0;
   for (size_t i = 0; i < _stats_arrays_length; ++i) {
-    pred = predictor.get_new_prediction(_surv_rate_pred[i]);
-    if (pred > 1.0) pred = 1.0;
+    pred = predictor.get_new_unit_prediction(_surv_rate_pred[i]);
     accum += pred;
     _accum_surv_rate_pred[i] = accum;
   }
--- a/test/hotspot/gtest/gc/g1/test_g1Predictions.cpp	Fri Nov 29 10:20:14 2019 +0100
+++ b/test/hotspot/gtest/gc/g1/test_g1Predictions.cpp	Fri Nov 29 10:20:16 2019 +0100
@@ -26,6 +26,8 @@
 #include "gc/g1/g1Predictions.hpp"
 #include "unittest.hpp"
 
+#include "utilities/ostream.hpp"
+
 static const double epsilon = 1e-6;
 
 // Some basic formula tests with confidence = 0.0
@@ -96,3 +98,51 @@
   double p4 = predictor.get_new_prediction(&s);
   ASSERT_GT(p4, p3) << "Fourth prediction must be greater than third";
 }
+
+// Some tests to verify bounding between [0 .. 1]
+TEST_VM(G1Predictions, unit_predictions) {
+  G1Predictions predictor(0.5);
+  TruncatedSeq s;
+
+  double p0 = predictor.get_new_unit_prediction(&s);
+  ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
+
+  s.add(100.0);
+  double p1 = predictor.get_new_unit_prediction(&s);
+  ASSERT_NEAR(p1, 1.0, epsilon);
+
+  // Feed the sequence additional positive values to test the high bound.
+  for (int i = 0; i < 3; i++) {
+    s.add(2.0);
+  }
+  ASSERT_NEAR(predictor.get_new_unit_prediction(&s), 1.0, epsilon);
+
+  // Feed the sequence additional large negative value to test the low bound.
+  for (int i = 0; i < 4; i++) {
+    s.add(-200.0);
+  }
+  ASSERT_NEAR(predictor.get_new_unit_prediction(&s), 0.0, epsilon);
+}
+
+// Some tests to verify bounding between [0 .. +inf]
+TEST_VM(G1Predictions, lower_bound_zero_predictions) {
+  G1Predictions predictor(0.5);
+  TruncatedSeq s;
+
+  double p0 = predictor.get_new_lower_zero_bound_prediction(&s);
+  ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
+
+  s.add(100.0);
+  // Feed the sequence additional positive values to see that the high bound is not
+  // bounded by e.g. 1.0
+  for (int i = 0; i < 3; i++) {
+    s.add(2.0);
+  }
+  ASSERT_GT(predictor.get_new_lower_zero_bound_prediction(&s), 1.0);
+
+  // Feed the sequence additional large negative value to test the low bound.
+  for (int i = 0; i < 4; i++) {
+    s.add(-200.0);
+  }
+  ASSERT_NEAR(predictor.get_new_lower_zero_bound_prediction(&s), 0.0, epsilon);
+}