1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "gc/g1/g1CollectedHeap.inline.hpp" 27 #include "gc/g1/g1ErgoVerbose.hpp" 28 #include "gc/g1/g1IHOPControl.hpp" 29 #include "gc/g1/g1Predictions.hpp" 30 #include "gc/shared/gcTrace.hpp" 31 32 G1IHOPControl::G1IHOPControl(double initial_ihop_percent, size_t target_occupancy) : 33 _ihop_percent(initial_ihop_percent), 34 _target_occupancy(target_occupancy) { 35 assert(_ihop_percent >= 0.0 && _ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent); 36 } 37 38 G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent, size_t target_occupancy) : 39 G1IHOPControl(ihop_percent, target_occupancy), 40 _last_allocation_time_s(0.0), 41 _last_allocated_bytes(0), 42 _last_marking_length_s(0.0) { 43 assert(_target_occupancy > 0, "Target occupancy must be larger than zero."); 44 } 45 46 void G1StaticIHOPControl::print() { 47 ergo_verbose6(ErgoIHOP, 48 "basic information", 49 ergo_format_reason("value update") 50 ergo_format_byte_perc("threshold") 51 ergo_format_byte("target occupancy") 52 ergo_format_byte("current occupancy") 53 ergo_format_double("recent old gen allocation rate") 54 ergo_format_ms("recent marking phase length"), 55 get_conc_mark_start_threshold(), 56 (double) get_conc_mark_start_threshold() / _target_occupancy * 100.0, 57 _target_occupancy, 58 G1CollectedHeap::heap()->used(), 59 _last_allocation_time_s > 0.0 ? _last_allocated_bytes / _last_allocation_time_s : 0.0, 60 _last_marking_length_s * 1000.0); 61 } 62 63 void G1StaticIHOPControl::send_jfr_event(G1NewTracer* tracer) { 64 tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), 65 _target_occupancy, 66 G1CollectedHeap::heap()->used(), 67 _last_allocated_bytes, 68 _last_allocation_time_s, 69 _last_marking_length_s); 70 } 71 72 #ifndef PRODUCT 73 static void test_update(G1IHOPControl* ctrl, double alloc_time, size_t alloc_amount, size_t young_size, double mark_time) { 74 for (int i = 0; i < 100; i++) { 75 ctrl->update_allocation_info(alloc_time, alloc_amount, young_size); 76 ctrl->update_time_to_mixed(mark_time); 77 } 78 } 79 80 void G1StaticIHOPControl::test() { 81 size_t const initial_ihop = 45; 82 83 G1StaticIHOPControl ctrl(initial_ihop, 100); 84 size_t threshold; 85 86 threshold = ctrl.get_conc_mark_start_threshold(); 87 assert(threshold == initial_ihop, 88 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 89 90 ctrl.update_allocation_info(100.0, 100, 100); 91 threshold = ctrl.get_conc_mark_start_threshold(); 92 assert(threshold == initial_ihop, 93 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 94 95 ctrl.update_time_to_mixed(1000.0); 96 threshold = ctrl.get_conc_mark_start_threshold(); 97 assert(threshold == initial_ihop, 98 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 99 100 // Whatever we pass, the IHOP value must stay the same. 101 test_update(&ctrl, 2, 10, 10, 3); 102 threshold = ctrl.get_conc_mark_start_threshold(); 103 assert(threshold == initial_ihop, 104 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 105 106 test_update(&ctrl, 12, 10, 10, 3); 107 threshold = ctrl.get_conc_mark_start_threshold(); 108 assert(threshold == initial_ihop, 109 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 110 } 111 #endif 112 113 G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent, size_t initial_target_occupancy, G1Predictions const* predictor) : 114 G1IHOPControl(ihop_percent, initial_target_occupancy), 115 _predictor(predictor), 116 _marking_times_s(10, 0.95), 117 _allocation_rate_s(10, 0.95), 118 _prev_unrestrained_young_size(0), 119 _current_threshold(0.0) 120 { 121 recalculate(); 122 } 123 124 void G1AdaptiveIHOPControl::recalculate() { 125 if (have_enough_data_for_prediction()) { 126 double pred_marking_time = _predictor->get_new_prediction(&_marking_times_s); 127 double pred_promotion_rate = _predictor->get_new_prediction(&_allocation_rate_s); 128 129 size_t predicted_needed_bytes_during_marking = 130 (pred_marking_time * pred_promotion_rate + 131 _prev_unrestrained_young_size); // In reality we would need the size of the young gen of the first mixed GC. This is a conservative estimate. 132 size_t predicted_initiating_threshold = predicted_needed_bytes_during_marking < _target_occupancy ? 133 _target_occupancy - predicted_needed_bytes_during_marking : 134 0; 135 _current_threshold = predicted_initiating_threshold; 136 } else { 137 // Use the initial value. 138 _current_threshold = _ihop_percent * _target_occupancy / 100.0; 139 } 140 } 141 142 bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const { 143 return (_marking_times_s.num() > 2) && (_allocation_rate_s.num() > 2); 144 } 145 146 void G1AdaptiveIHOPControl::set_target_occupancy(size_t target_occupancy) { 147 _target_occupancy = target_occupancy; 148 } 149 150 size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() { 151 recalculate(); 152 return _current_threshold; 153 } 154 155 void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) { 156 assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); 157 double allocation_rate = (double) allocated_bytes / allocation_time_s; 158 _allocation_rate_s.add(allocation_rate); 159 160 _last_allocation_bytes = allocated_bytes; 161 _prev_unrestrained_young_size = additional_buffer_size; 162 } 163 164 void G1AdaptiveIHOPControl::update_time_to_mixed(double marking_length_s) { 165 assert(marking_length_s >= 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); 166 _marking_times_s.add(marking_length_s); 167 } 168 169 void G1AdaptiveIHOPControl::print() { 170 ergo_verbose6(ErgoIHOP, 171 "basic information", 172 ergo_format_reason("value update") 173 ergo_format_byte_perc("threshold") 174 ergo_format_byte("target threshold") 175 ergo_format_byte("current occupancy") 176 ergo_format_double("recent old gen allocation rate") 177 ergo_format_double("recent marking phase length"), 178 get_conc_mark_start_threshold(), 179 _target_occupancy > 0 ? (double) get_conc_mark_start_threshold() / _target_occupancy * 100.0 : 0.0, 180 _target_occupancy, 181 G1CollectedHeap::heap()->used(), 182 _allocation_rate_s.last(), 183 _marking_times_s.last() 184 ); 185 ergo_verbose3(ErgoIHOP, 186 "adaptive IHOP information", 187 ergo_format_reason("value update") 188 ergo_format_double("predicted old gen allocation rate") 189 ergo_format_double("predicted marking phase length") 190 ergo_format_str("prediction active"), 191 _predictor->get_new_prediction(&_allocation_rate_s), 192 _predictor->get_new_prediction(&_marking_times_s), 193 have_enough_data_for_prediction() ? "true" : "false" 194 ); 195 } 196 197 void G1AdaptiveIHOPControl::send_jfr_event(G1NewTracer* tracer) { 198 tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(), 199 _target_occupancy, 200 G1CollectedHeap::heap()->used(), 201 _last_allocation_bytes, 202 _allocation_rate_s.last(), 203 _marking_times_s.last()); 204 tracer->report_adaptive_ihop_statistics(_prev_unrestrained_young_size, 205 _predictor->get_new_prediction(&_allocation_rate_s), 206 _predictor->get_new_prediction(&_marking_times_s), 207 have_enough_data_for_prediction()); 208 } 209 210 #ifndef PRODUCT 211 void G1AdaptiveIHOPControl::test() { 212 size_t const initial_threshold = 45; 213 size_t const young_size = 10; 214 size_t const target_size = 100; 215 216 // The final IHOP value is always 217 // target_size - (young_size + alloc_amount/alloc_time * marking_time) 218 219 G1Predictions pred(0.95); 220 G1AdaptiveIHOPControl ctrl(initial_threshold, target_size, &pred); 221 222 // First "load". 223 size_t const alloc_time1 = 2; 224 size_t const alloc_amount1 = 10; 225 size_t const marking_time1 = 2; 226 size_t const settled_ihop1 = target_size - (young_size + alloc_amount1/alloc_time1 * marking_time1); 227 228 size_t threshold; 229 threshold = ctrl.get_conc_mark_start_threshold(); 230 assert(threshold == initial_threshold, 231 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 232 ctrl.update_allocation_info(alloc_time1, alloc_amount1, young_size); 233 ctrl.update_time_to_mixed(marking_time1); 234 // Not enough data yet. 235 threshold = ctrl.get_conc_mark_start_threshold(); 236 assert(threshold == initial_threshold, 237 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 238 // Not enough data yet. 239 ctrl.update_allocation_info(alloc_time1, alloc_amount1, young_size); 240 ctrl.update_time_to_mixed(marking_time1); 241 threshold = ctrl.get_conc_mark_start_threshold(); 242 assert(threshold == initial_threshold, 243 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 244 245 test_update(&ctrl, alloc_time1, alloc_amount1, young_size, marking_time1); 246 247 threshold = ctrl.get_conc_mark_start_threshold(); 248 assert(threshold == settled_ihop1, 249 "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); 250 251 // Second "load". A bit higher allocation rate. 252 size_t const alloc_time2 = 2; 253 size_t const alloc_amount2 = 30; 254 size_t const marking_time2 = 2; 255 size_t const settled_ihop2 = target_size - (young_size + alloc_amount2/alloc_time2 * marking_time2); 256 257 test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); 258 259 threshold = ctrl.get_conc_mark_start_threshold(); 260 assert(threshold < settled_ihop1, 261 "Expected IHOP threshold to settle at a value lower than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); 262 263 // Third "load". Very high (impossible) allocation rate. 264 size_t const alloc_time3 = 1; 265 size_t const alloc_amount3 = 50; 266 size_t const marking_time3 = 2; 267 size_t const settled_ihop3 = 0; // target_size - (young_size + alloc_amount2/alloc_time2 * marking_time2); 268 269 test_update(&ctrl, alloc_time3, alloc_amount3, young_size, marking_time3); 270 threshold = ctrl.get_conc_mark_start_threshold(); 271 272 assert(threshold == settled_ihop3, 273 "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); 274 275 // And back to some arbitrary value. 276 test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); 277 278 threshold = ctrl.get_conc_mark_start_threshold(); 279 assert(threshold > settled_ihop3, 280 "Expected IHOP threshold to settle at value larger than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); 281 } 282 283 void IHOP_test() { 284 G1StaticIHOPControl::test(); 285 G1AdaptiveIHOPControl::test(); 286 } 287 #endif