8227434: G1 predictions may over/underflow with high variance input
Summary: Clamp G1 prediction output to sensible values.
Reviewed-by: lkorinth, sjohanss
--- 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);
+}