mirror of
https://github.com/darlinghq/darling-openjdk.git
synced 2024-11-30 15:50:29 +00:00
8227434: G1 predictions may over/underflow with high variance input
Clamp G1 prediction output to sensible values. Reviewed-by: lkorinth, sjohanss
This commit is contained in:
parent
5bcd794646
commit
257c3930f9
@ -125,12 +125,16 @@ bool G1Analytics::enough_samples_available(TruncatedSeq const* seq) const {
|
||||
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 @@ void G1Analytics::report_rs_length(double rs_length) {
|
||||
}
|
||||
|
||||
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 @@ double G1Analytics::predict_object_copy_time_ms(size_t bytes_to_copy, bool durin
|
||||
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 {
|
||||
|
@ -84,8 +84,9 @@ class G1Analytics: public CHeapObj<mtGC> {
|
||||
// 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);
|
||||
|
@ -2588,7 +2588,8 @@ void G1CMTask::do_marking_step(double time_target_ms,
|
||||
// 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
|
||||
|
@ -113,6 +113,10 @@ size_t G1AdaptiveIHOPControl::actual_target_threshold() const {
|
||||
);
|
||||
}
|
||||
|
||||
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 @@ bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const {
|
||||
|
||||
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 @@ void G1AdaptiveIHOPControl::print() {
|
||||
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 @@ void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer) {
|
||||
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());
|
||||
}
|
||||
|
@ -123,6 +123,9 @@ class G1AdaptiveIHOPControl : public G1IHOPControl {
|
||||
// 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
|
||||
|
@ -914,11 +914,7 @@ void G1Policy::print_phases() {
|
||||
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 {
|
||||
|
@ -57,6 +57,14 @@ class G1Predictions {
|
||||
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
|
||||
|
@ -116,8 +116,7 @@ void SurvRateGroup::finalize_predictions(const G1Predictions& predictor) {
|
||||
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;
|
||||
}
|
||||
|
@ -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 @@ TEST_VM(G1Predictions, average_stdev_predictions) {
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user